@nymphjs/nymph 1.0.0-beta.8 → 1.0.0-beta.81
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 +382 -0
- package/README.md +9 -19
- package/dist/Entity.d.ts +269 -7
- package/dist/Entity.js +491 -105
- package/dist/Entity.js.map +1 -1
- package/dist/Entity.test.js +25 -3
- package/dist/Entity.test.js.map +1 -1
- package/dist/Entity.types.d.ts +225 -19
- package/dist/Nymph.d.ts +430 -11
- package/dist/Nymph.js +686 -168
- package/dist/Nymph.js.map +1 -1
- package/dist/Nymph.types.d.ts +65 -7
- package/dist/Nymph.types.js +2 -1
- package/dist/Nymph.types.js.map +1 -1
- package/dist/conf/d.d.ts +30 -0
- package/dist/conf/defaults.js +27 -0
- package/dist/conf/defaults.js.map +1 -1
- package/dist/driver/NymphDriver.d.ts +136 -38
- package/dist/driver/NymphDriver.js +264 -131
- package/dist/driver/NymphDriver.js.map +1 -1
- package/dist/driver/testArtifacts.d.ts +1 -1
- package/dist/driver/testArtifacts.js +499 -51
- package/dist/driver/testArtifacts.js.map +1 -1
- package/dist/errors/EntityInvalidDataError.d.ts +6 -0
- package/dist/errors/EntityInvalidDataError.js +7 -1
- package/dist/errors/EntityInvalidDataError.js.map +1 -1
- package/dist/errors/EntityUniqueConstraintError.d.ts +3 -0
- package/dist/errors/EntityUniqueConstraintError.js +11 -0
- package/dist/errors/EntityUniqueConstraintError.js.map +1 -0
- package/dist/errors/InvalidStateError.d.ts +3 -0
- package/dist/errors/InvalidStateError.js +11 -0
- package/dist/errors/InvalidStateError.js.map +1 -0
- package/dist/errors/QueryFailedError.js +1 -0
- package/dist/errors/QueryFailedError.js.map +1 -1
- package/dist/errors/index.d.ts +2 -0
- package/dist/errors/index.js +2 -0
- package/dist/errors/index.js.map +1 -1
- package/dist/testArtifacts.d.ts +17 -5
- package/dist/testArtifacts.js +31 -27
- package/dist/testArtifacts.js.map +1 -1
- package/dist/testMocks.d.ts +13 -10
- package/dist/testMocks.js +32 -13
- package/dist/testMocks.js.map +1 -1
- package/dist/utils.d.ts +1 -1
- package/dist/utils.js +21 -5
- package/dist/utils.js.map +1 -1
- package/package.json +16 -14
- package/src/Entity.test.ts +22 -5
- package/src/Entity.ts +423 -134
- package/src/Entity.types.ts +32 -66
- package/src/Nymph.ts +609 -176
- package/src/Nymph.types.ts +131 -26
- package/src/conf/d.ts +12 -0
- package/src/conf/defaults.ts +31 -0
- package/src/driver/NymphDriver.ts +289 -311
- package/src/driver/testArtifacts.ts +572 -181
- package/src/errors/EntityUniqueConstraintError.ts +6 -0
- package/src/errors/InvalidStateError.ts +6 -0
- package/src/errors/index.ts +4 -0
- package/src/nymphJoiProps.ts +2 -2
- package/src/testArtifacts.ts +18 -19
- package/src/testMocks.ts +55 -30
- package/src/utils.ts +22 -8
- package/tsconfig.json +3 -3
- package/typedoc.json +4 -0
|
@@ -5,7 +5,9 @@ import strtotime from 'locutus/php/datetime/strtotime';
|
|
|
5
5
|
import { guid } from '@nymphjs/guid';
|
|
6
6
|
|
|
7
7
|
import type Nymph from '../Nymph';
|
|
8
|
-
import {
|
|
8
|
+
import type { Selector, Options, FormattedSelector } from '../Nymph.types';
|
|
9
|
+
import Entity, { type EntityInstanceType } from '../Entity';
|
|
10
|
+
import type {
|
|
9
11
|
EntityConstructor,
|
|
10
12
|
EntityData,
|
|
11
13
|
EntityInterface,
|
|
@@ -13,7 +15,6 @@ import {
|
|
|
13
15
|
SerializedEntityData,
|
|
14
16
|
} from '../Entity.types';
|
|
15
17
|
import { InvalidParametersError, UnableToConnectError } from '../errors';
|
|
16
|
-
import { Selector, Options, FormattedSelector } from '../Nymph.types';
|
|
17
18
|
import { xor } from '../utils';
|
|
18
19
|
|
|
19
20
|
// from: https://stackoverflow.com/a/6969486/664915
|
|
@@ -28,7 +29,7 @@ export default abstract class NymphDriver {
|
|
|
28
29
|
protected nymph: Nymph = new Proxy({} as Nymph, {
|
|
29
30
|
get() {
|
|
30
31
|
throw new Error(
|
|
31
|
-
'Attempted access of Nymph instance before driver initialization!'
|
|
32
|
+
'Attempted access of Nymph instance before driver initialization!',
|
|
32
33
|
);
|
|
33
34
|
},
|
|
34
35
|
});
|
|
@@ -67,13 +68,14 @@ export default abstract class NymphDriver {
|
|
|
67
68
|
abstract connect(): Promise<boolean>;
|
|
68
69
|
|
|
69
70
|
abstract isConnected(): boolean;
|
|
71
|
+
protected abstract internalTransaction(name: string): Promise<any>;
|
|
70
72
|
abstract startTransaction(name: string): Promise<Nymph>;
|
|
71
73
|
abstract commit(name: string): Promise<boolean>;
|
|
72
74
|
abstract rollback(name: string): Promise<boolean>;
|
|
73
75
|
abstract inTransaction(): Promise<boolean>;
|
|
74
76
|
abstract deleteEntityByID(
|
|
75
77
|
guid: string,
|
|
76
|
-
classConstructor: EntityConstructor
|
|
78
|
+
classConstructor: EntityConstructor,
|
|
77
79
|
): Promise<boolean>;
|
|
78
80
|
abstract deleteEntityByID(guid: string, className?: string): Promise<boolean>;
|
|
79
81
|
abstract deleteUID(name: string): Promise<boolean>;
|
|
@@ -83,9 +85,19 @@ export default abstract class NymphDriver {
|
|
|
83
85
|
*/
|
|
84
86
|
abstract disconnect(): Promise<boolean>;
|
|
85
87
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
88
|
+
/**
|
|
89
|
+
* Detect whether the database needs to be migrated.
|
|
90
|
+
*
|
|
91
|
+
* If true, the database should be exported with an old version of Nymph, then
|
|
92
|
+
* imported into a fresh database with this version.
|
|
93
|
+
*/
|
|
94
|
+
abstract needsMigration(): Promise<boolean>;
|
|
95
|
+
|
|
96
|
+
abstract exportDataIterator(): AsyncGenerator<
|
|
97
|
+
{ type: 'comment' | 'uid' | 'entity'; content: string },
|
|
98
|
+
void,
|
|
99
|
+
undefined | false
|
|
100
|
+
>;
|
|
89
101
|
abstract getEntities<T extends EntityConstructor = EntityConstructor>(
|
|
90
102
|
options: Options<T> & { return: 'count' },
|
|
91
103
|
...selectors: Selector[]
|
|
@@ -97,37 +109,21 @@ export default abstract class NymphDriver {
|
|
|
97
109
|
abstract getEntities<T extends EntityConstructor = EntityConstructor>(
|
|
98
110
|
options?: Options<T>,
|
|
99
111
|
...selectors: Selector[]
|
|
100
|
-
): Promise<
|
|
112
|
+
): Promise<EntityInstanceType<T>[]>;
|
|
101
113
|
abstract getEntities<T extends EntityConstructor = EntityConstructor>(
|
|
102
114
|
options?: Options<T>,
|
|
103
115
|
...selectors: Selector[]
|
|
104
|
-
): Promise<
|
|
105
|
-
protected abstract getEntitiesSync<
|
|
106
|
-
T extends EntityConstructor = EntityConstructor
|
|
107
|
-
>(
|
|
108
|
-
options: Options<T> & { return: 'count' },
|
|
109
|
-
...selectors: Selector[]
|
|
110
|
-
): number;
|
|
111
|
-
protected abstract getEntitiesSync<
|
|
112
|
-
T extends EntityConstructor = EntityConstructor
|
|
113
|
-
>(
|
|
114
|
-
options: Options<T> & { return: 'guid' },
|
|
115
|
-
...selectors: Selector[]
|
|
116
|
-
): string[];
|
|
117
|
-
protected abstract getEntitiesSync<
|
|
118
|
-
T extends EntityConstructor = EntityConstructor
|
|
119
|
-
>(
|
|
120
|
-
options?: Options<T>,
|
|
121
|
-
...selectors: Selector[]
|
|
122
|
-
): ReturnType<T['factorySync']>[];
|
|
123
|
-
protected abstract getEntitiesSync<
|
|
124
|
-
T extends EntityConstructor = EntityConstructor
|
|
125
|
-
>(
|
|
126
|
-
options?: Options<T>,
|
|
127
|
-
...selectors: Selector[]
|
|
128
|
-
): ReturnType<T['factorySync']>[] | string[] | number;
|
|
116
|
+
): Promise<EntityInstanceType<T>[] | string[] | number>;
|
|
129
117
|
abstract getUID(name: string): Promise<number | null>;
|
|
130
|
-
abstract
|
|
118
|
+
abstract importEntity(entity: {
|
|
119
|
+
guid: string;
|
|
120
|
+
cdate: number;
|
|
121
|
+
mdate: number;
|
|
122
|
+
tags: string[];
|
|
123
|
+
sdata: SerializedEntityData;
|
|
124
|
+
etype: string;
|
|
125
|
+
}): Promise<void>;
|
|
126
|
+
abstract importUID(uid: { name: string; value: number }): Promise<void>;
|
|
131
127
|
abstract newUID(name: string): Promise<number | null>;
|
|
132
128
|
abstract renameUID(oldName: string, newName: string): Promise<boolean>;
|
|
133
129
|
abstract saveEntity(entity: EntityInterface): Promise<boolean>;
|
|
@@ -147,7 +143,7 @@ export default abstract class NymphDriver {
|
|
|
147
143
|
protected posixRegexMatch(
|
|
148
144
|
pattern: string,
|
|
149
145
|
subject: string,
|
|
150
|
-
caseInsensitive = false
|
|
146
|
+
caseInsensitive = false,
|
|
151
147
|
) {
|
|
152
148
|
const posixClasses = [
|
|
153
149
|
// '[[:<:]]',
|
|
@@ -204,9 +200,9 @@ export default abstract class NymphDriver {
|
|
|
204
200
|
if (!fhandle) {
|
|
205
201
|
throw new InvalidParametersError('Provided filename is not writeable.');
|
|
206
202
|
}
|
|
207
|
-
await this.
|
|
208
|
-
fs.writeSync(fhandle,
|
|
209
|
-
}
|
|
203
|
+
for await (let entry of this.exportDataIterator()) {
|
|
204
|
+
fs.writeSync(fhandle, entry.content);
|
|
205
|
+
}
|
|
210
206
|
fs.closeSync(fhandle);
|
|
211
207
|
} catch (e: any) {
|
|
212
208
|
return false;
|
|
@@ -215,130 +211,155 @@ export default abstract class NymphDriver {
|
|
|
215
211
|
}
|
|
216
212
|
|
|
217
213
|
public async exportPrint() {
|
|
218
|
-
await this.
|
|
219
|
-
console.log(
|
|
220
|
-
}
|
|
214
|
+
for await (let entry of this.exportDataIterator()) {
|
|
215
|
+
console.log(entry.content);
|
|
216
|
+
}
|
|
221
217
|
return true;
|
|
222
218
|
}
|
|
223
219
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
guid: string,
|
|
228
|
-
tags: string[],
|
|
229
|
-
sdata: SerializedEntityData,
|
|
230
|
-
etype: string
|
|
231
|
-
) => Promise<void>,
|
|
232
|
-
saveUIDCallback: (name: string, value: number) => Promise<void>,
|
|
233
|
-
startTransactionCallback: (() => Promise<void>) | null = null,
|
|
234
|
-
commitTransactionCallback: (() => Promise<void>) | null = null
|
|
220
|
+
public async importDataIterator(
|
|
221
|
+
lines: Iterable<string>,
|
|
222
|
+
transaction?: boolean,
|
|
235
223
|
) {
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
throw new InvalidParametersError('Provided filename is unreadable.');
|
|
224
|
+
const first = lines[Symbol.iterator]().next();
|
|
225
|
+
|
|
226
|
+
if (first.done || first.value !== '#nex2') {
|
|
227
|
+
throw new Error('Tried to import a file that is not a NEX v2 file.');
|
|
241
228
|
}
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
let tags: string[] = [];
|
|
246
|
-
let etype = '__undefined';
|
|
247
|
-
if (startTransactionCallback) {
|
|
248
|
-
await startTransactionCallback();
|
|
229
|
+
|
|
230
|
+
if (transaction) {
|
|
231
|
+
await this.internalTransaction('nymph-import');
|
|
249
232
|
}
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
/^\s
|
|
257
|
-
|
|
258
|
-
const propMatch = text.match(/^\s*([^=]+)\s*=\s*(.*\S)\s*$/);
|
|
259
|
-
const uidMatch = text.match(/^\s*<([^>]+)>\[(\d+)\]\s*$/);
|
|
260
|
-
if (uidMatch) {
|
|
261
|
-
// Add the UID.
|
|
262
|
-
await saveUIDCallback(uidMatch[1], Number(uidMatch[2]));
|
|
263
|
-
} else if (entityMatch) {
|
|
264
|
-
// Save the current entity.
|
|
265
|
-
if (guid) {
|
|
266
|
-
await saveEntityCallback(guid, tags, sdata, etype);
|
|
267
|
-
guid = null;
|
|
268
|
-
tags = [];
|
|
269
|
-
sdata = {};
|
|
270
|
-
etype = '__undefined';
|
|
233
|
+
try {
|
|
234
|
+
let guid: string | null = null;
|
|
235
|
+
let sdata: SerializedEntityData = {};
|
|
236
|
+
let tags: string[] = [];
|
|
237
|
+
let etype = '__undefined';
|
|
238
|
+
for (let line of lines) {
|
|
239
|
+
if (line.match(/^\s*#/)) {
|
|
240
|
+
continue;
|
|
271
241
|
}
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
242
|
+
const entityMatch = line.match(
|
|
243
|
+
/^\s*{([0-9A-Fa-f]+)}<([-\w_]+)>\[([^\]]*)\]\s*$/,
|
|
244
|
+
);
|
|
245
|
+
const propMatch = line.match(/^\s*([^=]+)\s*=\s*(.*\S)\s*$/);
|
|
246
|
+
const uidMatch = line.match(/^\s*<([^>]+)>\[(\d+)\]\s*$/);
|
|
247
|
+
if (uidMatch) {
|
|
248
|
+
// Add the UID.
|
|
249
|
+
await this.importUID({
|
|
250
|
+
name: uidMatch[1],
|
|
251
|
+
value: Number(uidMatch[2]),
|
|
252
|
+
});
|
|
253
|
+
} else if (entityMatch) {
|
|
254
|
+
// Save the current entity.
|
|
255
|
+
if (guid) {
|
|
256
|
+
const cdate = Number(JSON.parse(sdata.cdate));
|
|
257
|
+
delete sdata.cdate;
|
|
258
|
+
const mdate = Number(JSON.parse(sdata.mdate));
|
|
259
|
+
delete sdata.mdate;
|
|
260
|
+
await this.importEntity({ guid, cdate, mdate, tags, sdata, etype });
|
|
261
|
+
guid = null;
|
|
262
|
+
tags = [];
|
|
263
|
+
sdata = {};
|
|
264
|
+
etype = '__undefined';
|
|
265
|
+
}
|
|
266
|
+
// Record the new entity's info.
|
|
267
|
+
guid = entityMatch[1];
|
|
268
|
+
etype = entityMatch[2];
|
|
269
|
+
tags = entityMatch[3].split(',');
|
|
270
|
+
} else if (propMatch) {
|
|
271
|
+
// Add the variable to the new entity.
|
|
272
|
+
if (guid) {
|
|
273
|
+
sdata[propMatch[1]] = propMatch[2];
|
|
274
|
+
}
|
|
280
275
|
}
|
|
276
|
+
// Clear the entity cache.
|
|
277
|
+
this.entityCache = {};
|
|
281
278
|
}
|
|
282
|
-
//
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
279
|
+
// Save the last entity.
|
|
280
|
+
if (guid) {
|
|
281
|
+
const cdate = Number(JSON.parse(sdata.cdate));
|
|
282
|
+
delete sdata.cdate;
|
|
283
|
+
const mdate = Number(JSON.parse(sdata.mdate));
|
|
284
|
+
delete sdata.mdate;
|
|
285
|
+
await this.importEntity({ guid, cdate, mdate, tags, sdata, etype });
|
|
286
|
+
}
|
|
287
|
+
if (transaction) {
|
|
288
|
+
await this.commit('nymph-import');
|
|
289
|
+
}
|
|
290
|
+
} catch (e: any) {
|
|
291
|
+
await this.rollback('nymph-import');
|
|
292
|
+
throw e;
|
|
291
293
|
}
|
|
292
294
|
return true;
|
|
293
295
|
}
|
|
294
296
|
|
|
297
|
+
public async importData(text: string, transaction?: boolean) {
|
|
298
|
+
return await this.importDataIterator(text.split('\n'), transaction);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
public async import(filename: string, transaction?: boolean) {
|
|
302
|
+
let rl: ReadLines;
|
|
303
|
+
try {
|
|
304
|
+
rl = new ReadLines(filename);
|
|
305
|
+
} catch (e: any) {
|
|
306
|
+
throw new InvalidParametersError('Provided filename is unreadable.');
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const lines = {
|
|
310
|
+
*[Symbol.iterator]() {
|
|
311
|
+
let line: false | Buffer;
|
|
312
|
+
while ((line = rl.next())) {
|
|
313
|
+
yield line.toString('utf8');
|
|
314
|
+
}
|
|
315
|
+
},
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
return await this.importDataIterator(lines, transaction);
|
|
319
|
+
}
|
|
320
|
+
|
|
295
321
|
public checkData(
|
|
296
322
|
data: EntityData,
|
|
297
323
|
sdata: SerializedEntityData,
|
|
298
324
|
selectors: Selector[],
|
|
299
325
|
guid: string | null = null,
|
|
300
|
-
tags: string[] | null = null
|
|
326
|
+
tags: string[] | null = null,
|
|
301
327
|
) {
|
|
302
|
-
|
|
328
|
+
try {
|
|
329
|
+
const formattedSelectors = this.formatSelectors(selectors).selectors;
|
|
303
330
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
331
|
+
for (const curSelector of formattedSelectors) {
|
|
332
|
+
const type = curSelector.type;
|
|
333
|
+
const typeIsNot = type === '!&' || type === '!|';
|
|
334
|
+
const typeIsOr = type === '|' || type === '!|';
|
|
335
|
+
let pass = !typeIsOr;
|
|
336
|
+
for (const k in curSelector) {
|
|
337
|
+
const key = k as keyof Selector;
|
|
338
|
+
const value = curSelector[key];
|
|
312
339
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
340
|
+
if (key === 'type' || value == null) {
|
|
341
|
+
continue;
|
|
342
|
+
}
|
|
316
343
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
xor(
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
throw new Error("Can't use checkData on qref clauses.");
|
|
344
|
+
const clauseNot = key.substring(0, 1) === '!';
|
|
345
|
+
if (key === 'selector' || key === '!selector') {
|
|
346
|
+
const tmpArr = (
|
|
347
|
+
Array.isArray(value) ? value : [value]
|
|
348
|
+
) as Selector[];
|
|
349
|
+
pass = xor(
|
|
350
|
+
this.checkData(data, sdata, tmpArr, guid, tags),
|
|
351
|
+
xor(typeIsNot, clauseNot),
|
|
352
|
+
);
|
|
327
353
|
} else {
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
if
|
|
332
|
-
|
|
333
|
-
|
|
354
|
+
if (key === 'qref' || key === '!qref') {
|
|
355
|
+
throw new Error("Can't use checkData on qref clauses.");
|
|
356
|
+
} else {
|
|
357
|
+
// Check if it doesn't pass any for &, check if it passes any for |.
|
|
358
|
+
for (const curValue of value) {
|
|
359
|
+
if (curValue == null) {
|
|
360
|
+
continue;
|
|
361
|
+
}
|
|
334
362
|
|
|
335
|
-
if (
|
|
336
|
-
((key === 'guid' || key === '!guid') && guid == null) ||
|
|
337
|
-
((key === 'tag' || key === '!tag') && tags == null)
|
|
338
|
-
) {
|
|
339
|
-
// Skip because it has already been checked (by the query).
|
|
340
|
-
pass = true;
|
|
341
|
-
} else {
|
|
342
363
|
const propName = (curValue as string[])[0];
|
|
343
364
|
|
|
344
365
|
// Unserialize the data for this variable.
|
|
@@ -356,7 +377,7 @@ export default abstract class NymphDriver {
|
|
|
356
377
|
case '!tag':
|
|
357
378
|
pass = xor(
|
|
358
379
|
(tags ?? []).indexOf(propName) !== -1,
|
|
359
|
-
xor(typeIsNot, clauseNot)
|
|
380
|
+
xor(typeIsNot, clauseNot),
|
|
360
381
|
);
|
|
361
382
|
break;
|
|
362
383
|
case 'defined':
|
|
@@ -367,7 +388,7 @@ export default abstract class NymphDriver {
|
|
|
367
388
|
case '!truthy':
|
|
368
389
|
pass = xor(
|
|
369
390
|
propName in data && data[propName],
|
|
370
|
-
xor(typeIsNot, clauseNot)
|
|
391
|
+
xor(typeIsNot, clauseNot),
|
|
371
392
|
);
|
|
372
393
|
break;
|
|
373
394
|
case 'ref':
|
|
@@ -377,9 +398,9 @@ export default abstract class NymphDriver {
|
|
|
377
398
|
propName in data &&
|
|
378
399
|
this.entityReferenceSearch(
|
|
379
400
|
data[propName],
|
|
380
|
-
testRefValue
|
|
401
|
+
testRefValue,
|
|
381
402
|
),
|
|
382
|
-
xor(typeIsNot, clauseNot)
|
|
403
|
+
xor(typeIsNot, clauseNot),
|
|
383
404
|
);
|
|
384
405
|
break;
|
|
385
406
|
case 'equal':
|
|
@@ -391,7 +412,7 @@ export default abstract class NymphDriver {
|
|
|
391
412
|
propName in data &&
|
|
392
413
|
JSON.stringify(data[propName]) ===
|
|
393
414
|
JSON.stringify(testEqualValue),
|
|
394
|
-
xor(typeIsNot, clauseNot)
|
|
415
|
+
xor(typeIsNot, clauseNot),
|
|
395
416
|
);
|
|
396
417
|
break;
|
|
397
418
|
case 'contain':
|
|
@@ -402,9 +423,9 @@ export default abstract class NymphDriver {
|
|
|
402
423
|
pass = xor(
|
|
403
424
|
propName in data &&
|
|
404
425
|
JSON.stringify(data[propName]).indexOf(
|
|
405
|
-
JSON.stringify(testContainValue)
|
|
426
|
+
JSON.stringify(testContainValue),
|
|
406
427
|
) !== -1,
|
|
407
|
-
xor(typeIsNot, clauseNot)
|
|
428
|
+
xor(typeIsNot, clauseNot),
|
|
408
429
|
);
|
|
409
430
|
break;
|
|
410
431
|
case 'like':
|
|
@@ -419,9 +440,9 @@ export default abstract class NymphDriver {
|
|
|
419
440
|
escapeRegExp(testLikeValue)
|
|
420
441
|
.replace('%', () => '.*')
|
|
421
442
|
.replace('_', () => '.') +
|
|
422
|
-
'$'
|
|
443
|
+
'$',
|
|
423
444
|
).test(data[propName]),
|
|
424
|
-
xor(typeIsNot, clauseNot)
|
|
445
|
+
xor(typeIsNot, clauseNot),
|
|
425
446
|
);
|
|
426
447
|
break;
|
|
427
448
|
case 'match':
|
|
@@ -433,7 +454,7 @@ export default abstract class NymphDriver {
|
|
|
433
454
|
pass = xor(
|
|
434
455
|
propName in data &&
|
|
435
456
|
this.posixRegexMatch(testMatchValue, data[propName]),
|
|
436
|
-
xor(typeIsNot, clauseNot)
|
|
457
|
+
xor(typeIsNot, clauseNot),
|
|
437
458
|
);
|
|
438
459
|
break;
|
|
439
460
|
case 'imatch':
|
|
@@ -447,9 +468,9 @@ export default abstract class NymphDriver {
|
|
|
447
468
|
this.posixRegexMatch(
|
|
448
469
|
testIMatchValue,
|
|
449
470
|
data[propName],
|
|
450
|
-
true
|
|
471
|
+
true,
|
|
451
472
|
),
|
|
452
|
-
xor(typeIsNot, clauseNot)
|
|
473
|
+
xor(typeIsNot, clauseNot),
|
|
453
474
|
);
|
|
454
475
|
break;
|
|
455
476
|
case 'gt':
|
|
@@ -459,7 +480,7 @@ export default abstract class NymphDriver {
|
|
|
459
480
|
)[1] as number;
|
|
460
481
|
pass = xor(
|
|
461
482
|
propName in data && data[propName] > testGTValue,
|
|
462
|
-
xor(typeIsNot, clauseNot)
|
|
483
|
+
xor(typeIsNot, clauseNot),
|
|
463
484
|
);
|
|
464
485
|
break;
|
|
465
486
|
case 'gte':
|
|
@@ -469,7 +490,7 @@ export default abstract class NymphDriver {
|
|
|
469
490
|
)[1] as number;
|
|
470
491
|
pass = xor(
|
|
471
492
|
propName in data && data[propName] >= testGTEValue,
|
|
472
|
-
xor(typeIsNot, clauseNot)
|
|
493
|
+
xor(typeIsNot, clauseNot),
|
|
473
494
|
);
|
|
474
495
|
break;
|
|
475
496
|
case 'lt':
|
|
@@ -479,7 +500,7 @@ export default abstract class NymphDriver {
|
|
|
479
500
|
)[1] as number;
|
|
480
501
|
pass = xor(
|
|
481
502
|
propName in data && data[propName] < testLTValue,
|
|
482
|
-
xor(typeIsNot, clauseNot)
|
|
503
|
+
xor(typeIsNot, clauseNot),
|
|
483
504
|
);
|
|
484
505
|
break;
|
|
485
506
|
case 'lte':
|
|
@@ -489,26 +510,33 @@ export default abstract class NymphDriver {
|
|
|
489
510
|
)[1] as number;
|
|
490
511
|
pass = xor(
|
|
491
512
|
propName in data && data[propName] <= testLTEValue,
|
|
492
|
-
xor(typeIsNot, clauseNot)
|
|
513
|
+
xor(typeIsNot, clauseNot),
|
|
493
514
|
);
|
|
494
515
|
break;
|
|
495
516
|
}
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
517
|
+
|
|
518
|
+
if (!xor(typeIsOr, pass)) {
|
|
519
|
+
break;
|
|
520
|
+
}
|
|
499
521
|
}
|
|
500
522
|
}
|
|
501
523
|
}
|
|
524
|
+
if (!xor(typeIsOr, pass)) {
|
|
525
|
+
break;
|
|
526
|
+
}
|
|
502
527
|
}
|
|
503
|
-
if (!
|
|
504
|
-
|
|
528
|
+
if (!pass) {
|
|
529
|
+
return false;
|
|
505
530
|
}
|
|
506
531
|
}
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
532
|
+
return true;
|
|
533
|
+
} catch (e: any) {
|
|
534
|
+
this.nymph.config.debugError(
|
|
535
|
+
'nymph',
|
|
536
|
+
`Failed to check entity data: ${e}`,
|
|
537
|
+
);
|
|
538
|
+
throw e;
|
|
510
539
|
}
|
|
511
|
-
return true;
|
|
512
540
|
}
|
|
513
541
|
|
|
514
542
|
/**
|
|
@@ -547,7 +575,7 @@ export default abstract class NymphDriver {
|
|
|
547
575
|
| EntityInterface
|
|
548
576
|
| EntityReference
|
|
549
577
|
| string
|
|
550
|
-
| (EntityInterface | EntityReference | string)[]
|
|
578
|
+
| (EntityInterface | EntityReference | string)[],
|
|
551
579
|
) {
|
|
552
580
|
// Get the GUID, if the passed $entity is an object.
|
|
553
581
|
const guids: string[] = [];
|
|
@@ -590,7 +618,7 @@ export default abstract class NymphDriver {
|
|
|
590
618
|
|
|
591
619
|
public formatSelectors(
|
|
592
620
|
selectors: Selector[],
|
|
593
|
-
options: Options = {}
|
|
621
|
+
options: Options = {},
|
|
594
622
|
): {
|
|
595
623
|
selectors: FormattedSelector[];
|
|
596
624
|
qrefs: [Options, ...FormattedSelector[]][];
|
|
@@ -625,10 +653,9 @@ export default abstract class NymphDriver {
|
|
|
625
653
|
for (let i = 0; i < tmpArr.length; i++) {
|
|
626
654
|
const name = tmpArr[i][0];
|
|
627
655
|
const [qrefOptions, ...qrefSelectors] = tmpArr[i][1];
|
|
628
|
-
const QrefEntityClass =
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
: qrefOptions.class ?? this.nymph.getEntityClass('Entity');
|
|
656
|
+
const QrefEntityClass = qrefOptions.class
|
|
657
|
+
? this.nymph.getEntityClass(qrefOptions.class)
|
|
658
|
+
: this.nymph.getEntityClass('Entity');
|
|
632
659
|
const newOptions = {
|
|
633
660
|
...qrefOptions,
|
|
634
661
|
class: QrefEntityClass,
|
|
@@ -637,7 +664,7 @@ export default abstract class NymphDriver {
|
|
|
637
664
|
const newSelectors = this.formatSelectors(qrefSelectors, options);
|
|
638
665
|
qrefs.push(
|
|
639
666
|
[newOptions, ...newSelectors.selectors],
|
|
640
|
-
...newSelectors.qrefs
|
|
667
|
+
...newSelectors.qrefs,
|
|
641
668
|
);
|
|
642
669
|
formatArr[i] = [name, [newOptions, ...newSelectors.selectors]];
|
|
643
670
|
}
|
|
@@ -685,12 +712,12 @@ export default abstract class NymphDriver {
|
|
|
685
712
|
|
|
686
713
|
protected iterateSelectorsForQuery(
|
|
687
714
|
selectors: FormattedSelector[],
|
|
688
|
-
callback: (
|
|
689
|
-
key: string
|
|
690
|
-
value: any
|
|
691
|
-
typeIsOr: boolean
|
|
692
|
-
typeIsNot: boolean
|
|
693
|
-
) => string
|
|
715
|
+
callback: (data: {
|
|
716
|
+
key: string;
|
|
717
|
+
value: any;
|
|
718
|
+
typeIsOr: boolean;
|
|
719
|
+
typeIsNot: boolean;
|
|
720
|
+
}) => string,
|
|
694
721
|
) {
|
|
695
722
|
const queryParts = [];
|
|
696
723
|
for (const curSelector of selectors) {
|
|
@@ -703,7 +730,7 @@ export default abstract class NymphDriver {
|
|
|
703
730
|
if (key === 'type') {
|
|
704
731
|
continue;
|
|
705
732
|
}
|
|
706
|
-
let curQuery = callback(key, value, typeIsOr, typeIsNot);
|
|
733
|
+
let curQuery = callback({ key, value, typeIsOr, typeIsNot });
|
|
707
734
|
if (curQuery) {
|
|
708
735
|
if (curSelectorQuery) {
|
|
709
736
|
curSelectorQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
@@ -719,71 +746,14 @@ export default abstract class NymphDriver {
|
|
|
719
746
|
return queryParts;
|
|
720
747
|
}
|
|
721
748
|
|
|
722
|
-
|
|
723
|
-
* Get the first entity to match all options/selectors, synchronously.
|
|
724
|
-
*
|
|
725
|
-
* This should really only be used internally by the Entity class. It's a bad
|
|
726
|
-
* idea to get entities synchronously. The Entity class uses this to load
|
|
727
|
-
* sleeping references.
|
|
728
|
-
*
|
|
729
|
-
* @param options The options.
|
|
730
|
-
* @param selectors Unlimited optional selectors to search for, or a single GUID. If none are given, all entities are searched for the given options.
|
|
731
|
-
* @returns An entity, or null on failure or nothing found.
|
|
732
|
-
*/
|
|
733
|
-
public getEntitySync<T extends EntityConstructor = EntityConstructor>(
|
|
734
|
-
options: Options<T> & { return: 'count' },
|
|
735
|
-
...selectors: Selector[]
|
|
736
|
-
): number;
|
|
737
|
-
public getEntitySync<T extends EntityConstructor = EntityConstructor>(
|
|
738
|
-
options: Options<T> & { return: 'guid' },
|
|
739
|
-
...selectors: Selector[]
|
|
740
|
-
): string | null;
|
|
741
|
-
public getEntitySync<T extends EntityConstructor = EntityConstructor>(
|
|
742
|
-
options: Options<T>,
|
|
743
|
-
...selectors: Selector[]
|
|
744
|
-
): ReturnType<T['factorySync']> | null;
|
|
745
|
-
public getEntitySync<T extends EntityConstructor = EntityConstructor>(
|
|
746
|
-
options: Options<T> & { return: 'count' },
|
|
747
|
-
guid: string
|
|
748
|
-
): number;
|
|
749
|
-
public getEntitySync<T extends EntityConstructor = EntityConstructor>(
|
|
750
|
-
options: Options<T> & { return: 'guid' },
|
|
751
|
-
guid: string
|
|
752
|
-
): string | null;
|
|
753
|
-
public getEntitySync<T extends EntityConstructor = EntityConstructor>(
|
|
754
|
-
options: Options<T>,
|
|
755
|
-
guid: string
|
|
756
|
-
): ReturnType<T['factorySync']> | null;
|
|
757
|
-
public getEntitySync<T extends EntityConstructor = EntityConstructor>(
|
|
758
|
-
options: Options<T> = {},
|
|
759
|
-
...selectors: Selector[] | string[]
|
|
760
|
-
): ReturnType<T['factorySync']> | string | number | null {
|
|
761
|
-
// Set up options and selectors.
|
|
762
|
-
if (typeof selectors[0] === 'string') {
|
|
763
|
-
selectors = [{ type: '&', guid: selectors[0] }];
|
|
764
|
-
}
|
|
765
|
-
options.limit = 1;
|
|
766
|
-
const entities = this.getEntitiesSync(
|
|
767
|
-
options,
|
|
768
|
-
...(selectors as Selector[])
|
|
769
|
-
);
|
|
770
|
-
if (options.return === 'count') {
|
|
771
|
-
return entities as unknown as number;
|
|
772
|
-
}
|
|
773
|
-
if (!entities || !entities.length) {
|
|
774
|
-
return null;
|
|
775
|
-
}
|
|
776
|
-
return entities[0];
|
|
777
|
-
}
|
|
778
|
-
|
|
779
|
-
protected getEntitesRowLike<T extends EntityConstructor = EntityConstructor>(
|
|
749
|
+
protected getEntitiesRowLike<T extends EntityConstructor = EntityConstructor>(
|
|
780
750
|
options: Options<T> & { return: 'count' },
|
|
781
751
|
selectors: Selector[],
|
|
782
|
-
performQueryCallback: (
|
|
783
|
-
options: Options<T
|
|
784
|
-
|
|
785
|
-
etype: string
|
|
786
|
-
) => {
|
|
752
|
+
performQueryCallback: (data: {
|
|
753
|
+
options: Options<T>;
|
|
754
|
+
selectors: FormattedSelector[];
|
|
755
|
+
etype: string;
|
|
756
|
+
}) => {
|
|
787
757
|
result: any;
|
|
788
758
|
},
|
|
789
759
|
rowFetchCallback: () => any,
|
|
@@ -798,16 +768,16 @@ export default abstract class NymphDriver {
|
|
|
798
768
|
getDataNameAndSValueCallback: (row: any) => {
|
|
799
769
|
name: string;
|
|
800
770
|
svalue: string;
|
|
801
|
-
}
|
|
771
|
+
},
|
|
802
772
|
): { result: any; process: () => number | Error };
|
|
803
|
-
protected
|
|
773
|
+
protected getEntitiesRowLike<T extends EntityConstructor = EntityConstructor>(
|
|
804
774
|
options: Options<T> & { return: 'guid' },
|
|
805
775
|
selectors: Selector[],
|
|
806
|
-
performQueryCallback: (
|
|
807
|
-
options: Options<T
|
|
808
|
-
|
|
809
|
-
etype: string
|
|
810
|
-
) => {
|
|
776
|
+
performQueryCallback: (data: {
|
|
777
|
+
options: Options<T>;
|
|
778
|
+
selectors: FormattedSelector[];
|
|
779
|
+
etype: string;
|
|
780
|
+
}) => {
|
|
811
781
|
result: any;
|
|
812
782
|
},
|
|
813
783
|
rowFetchCallback: () => any,
|
|
@@ -822,16 +792,16 @@ export default abstract class NymphDriver {
|
|
|
822
792
|
getDataNameAndSValueCallback: (row: any) => {
|
|
823
793
|
name: string;
|
|
824
794
|
svalue: string;
|
|
825
|
-
}
|
|
795
|
+
},
|
|
826
796
|
): { result: any; process: () => string[] | Error };
|
|
827
|
-
protected
|
|
797
|
+
protected getEntitiesRowLike<T extends EntityConstructor = EntityConstructor>(
|
|
828
798
|
options: Options<T>,
|
|
829
799
|
selectors: Selector[],
|
|
830
|
-
performQueryCallback: (
|
|
831
|
-
options: Options<T
|
|
832
|
-
|
|
833
|
-
etype: string
|
|
834
|
-
) => {
|
|
800
|
+
performQueryCallback: (data: {
|
|
801
|
+
options: Options<T>;
|
|
802
|
+
selectors: FormattedSelector[];
|
|
803
|
+
etype: string;
|
|
804
|
+
}) => {
|
|
835
805
|
result: any;
|
|
836
806
|
},
|
|
837
807
|
rowFetchCallback: () => any,
|
|
@@ -846,16 +816,19 @@ export default abstract class NymphDriver {
|
|
|
846
816
|
getDataNameAndSValueCallback: (row: any) => {
|
|
847
817
|
name: string;
|
|
848
818
|
svalue: string;
|
|
849
|
-
}
|
|
850
|
-
): {
|
|
851
|
-
|
|
819
|
+
},
|
|
820
|
+
): {
|
|
821
|
+
result: any;
|
|
822
|
+
process: () => EntityInstanceType<T>[] | Error;
|
|
823
|
+
};
|
|
824
|
+
protected getEntitiesRowLike<T extends EntityConstructor = EntityConstructor>(
|
|
852
825
|
options: Options<T>,
|
|
853
826
|
selectors: Selector[],
|
|
854
|
-
performQueryCallback: (
|
|
855
|
-
options: Options<T
|
|
856
|
-
|
|
857
|
-
etype: string
|
|
858
|
-
) => {
|
|
827
|
+
performQueryCallback: (data: {
|
|
828
|
+
options: Options<T>;
|
|
829
|
+
selectors: FormattedSelector[];
|
|
830
|
+
etype: string;
|
|
831
|
+
}) => {
|
|
859
832
|
result: any;
|
|
860
833
|
},
|
|
861
834
|
rowFetchCallback: () => any,
|
|
@@ -870,10 +843,10 @@ export default abstract class NymphDriver {
|
|
|
870
843
|
getDataNameAndSValueCallback: (row: any) => {
|
|
871
844
|
name: string;
|
|
872
845
|
svalue: string;
|
|
873
|
-
}
|
|
846
|
+
},
|
|
874
847
|
): {
|
|
875
848
|
result: any;
|
|
876
|
-
process: () =>
|
|
849
|
+
process: () => EntityInstanceType<T>[] | string[] | number | Error;
|
|
877
850
|
} {
|
|
878
851
|
if (!this.isConnected()) {
|
|
879
852
|
throw new UnableToConnectError('not connected to DB');
|
|
@@ -886,12 +859,12 @@ export default abstract class NymphDriver {
|
|
|
886
859
|
['&', '!&', '|', '!|'].indexOf(selector.type) === -1
|
|
887
860
|
) {
|
|
888
861
|
throw new InvalidParametersError(
|
|
889
|
-
'Invalid query selector passed: ' + JSON.stringify(selector)
|
|
862
|
+
'Invalid query selector passed: ' + JSON.stringify(selector),
|
|
890
863
|
);
|
|
891
864
|
}
|
|
892
865
|
}
|
|
893
866
|
|
|
894
|
-
let entities:
|
|
867
|
+
let entities: EntityInstanceType<T>[] | string[] = [];
|
|
895
868
|
let count = 0;
|
|
896
869
|
const EntityClass =
|
|
897
870
|
options.class ?? (this.nymph.getEntityClass('Entity') as T);
|
|
@@ -901,6 +874,7 @@ export default abstract class NymphDriver {
|
|
|
901
874
|
if (
|
|
902
875
|
this.nymph.config.cache &&
|
|
903
876
|
!options.skipCache &&
|
|
877
|
+
selectors.length &&
|
|
904
878
|
'guid' in selectors[0] &&
|
|
905
879
|
typeof selectors[0].guid === 'number'
|
|
906
880
|
) {
|
|
@@ -912,10 +886,10 @@ export default abstract class NymphDriver {
|
|
|
912
886
|
(Object.keys(selectors[0]).length === 2 ||
|
|
913
887
|
(Object.keys(selectors[0]).length === 3 && 'tag' in selectors[0]))
|
|
914
888
|
) {
|
|
915
|
-
const entity = this.pullCache<
|
|
889
|
+
const entity = this.pullCache<EntityInstanceType<T>>(
|
|
916
890
|
selectors[0]['guid'],
|
|
917
891
|
EntityClass.class,
|
|
918
|
-
!!options.skipAc
|
|
892
|
+
!!options.skipAc,
|
|
919
893
|
);
|
|
920
894
|
if (
|
|
921
895
|
entity != null &&
|
|
@@ -924,7 +898,7 @@ export default abstract class NymphDriver {
|
|
|
924
898
|
entity.hasTag(
|
|
925
899
|
...(Array.isArray(selectors[0].tag)
|
|
926
900
|
? selectors[0].tag
|
|
927
|
-
: [selectors[0].tag])
|
|
901
|
+
: [selectors[0].tag]),
|
|
928
902
|
))
|
|
929
903
|
) {
|
|
930
904
|
const guid = entity.guid;
|
|
@@ -946,15 +920,16 @@ export default abstract class NymphDriver {
|
|
|
946
920
|
try {
|
|
947
921
|
const formattedSelectors = this.formatSelectors(selectors, options);
|
|
948
922
|
this.nymph.runQueryCallbacks(options, formattedSelectors.selectors);
|
|
949
|
-
for (let
|
|
950
|
-
const [options, ...selectors] =
|
|
923
|
+
for (let i = 0; i < formattedSelectors.qrefs.length; i++) {
|
|
924
|
+
const [options, ...selectors] = formattedSelectors.qrefs[i];
|
|
951
925
|
this.nymph.runQueryCallbacks(options, selectors);
|
|
926
|
+
formattedSelectors.qrefs[i] = [options, ...selectors];
|
|
952
927
|
}
|
|
953
|
-
const { result } = performQueryCallback(
|
|
928
|
+
const { result } = performQueryCallback({
|
|
954
929
|
options,
|
|
955
|
-
formattedSelectors.selectors,
|
|
956
|
-
etype
|
|
957
|
-
);
|
|
930
|
+
selectors: formattedSelectors.selectors,
|
|
931
|
+
etype,
|
|
932
|
+
});
|
|
958
933
|
|
|
959
934
|
return {
|
|
960
935
|
result,
|
|
@@ -994,9 +969,7 @@ export default abstract class NymphDriver {
|
|
|
994
969
|
// Make sure that $row is incremented :)
|
|
995
970
|
row = rowFetchCallback();
|
|
996
971
|
}
|
|
997
|
-
const entity = EntityClass.factorySync() as
|
|
998
|
-
T['factorySync']
|
|
999
|
-
>;
|
|
972
|
+
const entity = EntityClass.factorySync() as EntityInstanceType<T>;
|
|
1000
973
|
entity.$nymph = this.nymph;
|
|
1001
974
|
if (options.skipAc != null) {
|
|
1002
975
|
entity.$useSkipAc(!!options.skipAc);
|
|
@@ -1036,34 +1009,37 @@ export default abstract class NymphDriver {
|
|
|
1036
1009
|
|
|
1037
1010
|
protected async saveEntityRowLike(
|
|
1038
1011
|
entity: EntityInterface,
|
|
1039
|
-
saveNewEntityCallback: (
|
|
1040
|
-
entity: EntityInterface
|
|
1041
|
-
guid: string
|
|
1042
|
-
tags: string[]
|
|
1043
|
-
data: EntityData
|
|
1044
|
-
sdata: SerializedEntityData
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1012
|
+
saveNewEntityCallback: (data: {
|
|
1013
|
+
entity: EntityInterface;
|
|
1014
|
+
guid: string;
|
|
1015
|
+
tags: string[];
|
|
1016
|
+
data: EntityData;
|
|
1017
|
+
sdata: SerializedEntityData;
|
|
1018
|
+
uniques: string[];
|
|
1019
|
+
cdate: number;
|
|
1020
|
+
etype: string;
|
|
1021
|
+
}) => Promise<boolean>,
|
|
1022
|
+
saveExistingEntityCallback: (data: {
|
|
1023
|
+
entity: EntityInterface;
|
|
1024
|
+
guid: string;
|
|
1025
|
+
tags: string[];
|
|
1026
|
+
data: EntityData;
|
|
1027
|
+
sdata: SerializedEntityData;
|
|
1028
|
+
uniques: string[];
|
|
1029
|
+
mdate: number;
|
|
1030
|
+
etype: string;
|
|
1031
|
+
}) => Promise<boolean>,
|
|
1057
1032
|
startTransactionCallback: (() => Promise<void>) | null = null,
|
|
1058
1033
|
commitTransactionCallback:
|
|
1059
1034
|
| ((success: boolean) => Promise<boolean>)
|
|
1060
|
-
| null = null
|
|
1035
|
+
| null = null,
|
|
1061
1036
|
) {
|
|
1062
1037
|
// Get a modified date.
|
|
1063
1038
|
const mdate = Date.now();
|
|
1064
1039
|
const tags = difference(entity.tags, ['']);
|
|
1065
1040
|
const data = entity.$getData();
|
|
1066
1041
|
const sdata = entity.$getSData();
|
|
1042
|
+
const uniques = await entity.$getUniques();
|
|
1067
1043
|
const EntityClass = entity.constructor as EntityConstructor;
|
|
1068
1044
|
const etype = EntityClass.ETYPE;
|
|
1069
1045
|
if (startTransactionCallback) {
|
|
@@ -1073,15 +1049,16 @@ export default abstract class NymphDriver {
|
|
|
1073
1049
|
if (entity.guid == null) {
|
|
1074
1050
|
const cdate = mdate;
|
|
1075
1051
|
const newId = guid();
|
|
1076
|
-
success = await saveNewEntityCallback(
|
|
1052
|
+
success = await saveNewEntityCallback({
|
|
1077
1053
|
entity,
|
|
1078
|
-
newId,
|
|
1054
|
+
guid: newId,
|
|
1079
1055
|
tags,
|
|
1080
1056
|
data,
|
|
1081
1057
|
sdata,
|
|
1058
|
+
uniques,
|
|
1082
1059
|
cdate,
|
|
1083
|
-
etype
|
|
1084
|
-
);
|
|
1060
|
+
etype,
|
|
1061
|
+
});
|
|
1085
1062
|
if (success) {
|
|
1086
1063
|
entity.guid = newId;
|
|
1087
1064
|
entity.cdate = cdate;
|
|
@@ -1092,15 +1069,16 @@ export default abstract class NymphDriver {
|
|
|
1092
1069
|
if (this.nymph.config.cache) {
|
|
1093
1070
|
this.cleanCache(entity.guid);
|
|
1094
1071
|
}
|
|
1095
|
-
success = await saveExistingEntityCallback(
|
|
1072
|
+
success = await saveExistingEntityCallback({
|
|
1096
1073
|
entity,
|
|
1097
|
-
entity.guid,
|
|
1074
|
+
guid: entity.guid,
|
|
1098
1075
|
tags,
|
|
1099
1076
|
data,
|
|
1100
1077
|
sdata,
|
|
1078
|
+
uniques,
|
|
1101
1079
|
mdate,
|
|
1102
|
-
etype
|
|
1103
|
-
);
|
|
1080
|
+
etype,
|
|
1081
|
+
});
|
|
1104
1082
|
if (success) {
|
|
1105
1083
|
entity.mdate = mdate;
|
|
1106
1084
|
}
|
|
@@ -1116,7 +1094,7 @@ export default abstract class NymphDriver {
|
|
|
1116
1094
|
entity.mdate as number,
|
|
1117
1095
|
entity.tags,
|
|
1118
1096
|
data,
|
|
1119
|
-
sdata
|
|
1097
|
+
sdata,
|
|
1120
1098
|
);
|
|
1121
1099
|
}
|
|
1122
1100
|
return success;
|
|
@@ -1130,10 +1108,10 @@ export default abstract class NymphDriver {
|
|
|
1130
1108
|
* @param useSkipAc Whether to tell the entity to use skip_ac.
|
|
1131
1109
|
* @returns The entity or null if it's not cached.
|
|
1132
1110
|
*/
|
|
1133
|
-
protected pullCache<T extends
|
|
1111
|
+
protected pullCache<T extends Entity>(
|
|
1134
1112
|
guid: string,
|
|
1135
1113
|
className: string,
|
|
1136
|
-
useSkipAc = false
|
|
1114
|
+
useSkipAc = false,
|
|
1137
1115
|
): T | null {
|
|
1138
1116
|
// Increment the entity access count.
|
|
1139
1117
|
if (!(guid in this.entityCount)) {
|
|
@@ -1151,7 +1129,7 @@ export default abstract class NymphDriver {
|
|
|
1151
1129
|
entity.tags = this.entityCache[guid]['tags'];
|
|
1152
1130
|
entity.$putData(
|
|
1153
1131
|
this.entityCache[guid]['data'],
|
|
1154
|
-
this.entityCache[guid]['sdata']
|
|
1132
|
+
this.entityCache[guid]['sdata'],
|
|
1155
1133
|
);
|
|
1156
1134
|
return entity;
|
|
1157
1135
|
}
|
|
@@ -1175,7 +1153,7 @@ export default abstract class NymphDriver {
|
|
|
1175
1153
|
mdate: number,
|
|
1176
1154
|
tags: string[],
|
|
1177
1155
|
data: EntityData,
|
|
1178
|
-
sdata: SerializedEntityData
|
|
1156
|
+
sdata: SerializedEntityData,
|
|
1179
1157
|
) {
|
|
1180
1158
|
if (guid == null) {
|
|
1181
1159
|
return;
|
|
@@ -1204,7 +1182,7 @@ export default abstract class NymphDriver {
|
|
|
1204
1182
|
// Find which entity has been accessed the least.
|
|
1205
1183
|
const least =
|
|
1206
1184
|
Object.entries(this.entityCount)
|
|
1207
|
-
.sort(([
|
|
1185
|
+
.sort(([_guidA, countA], [_guidB, countB]) => countA - countB)
|
|
1208
1186
|
.pop()?.[0] ?? null;
|
|
1209
1187
|
if (least == null) {
|
|
1210
1188
|
// This should never happen.
|