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