@prestizni-software/client-dem 0.4.102 → 0.4.103
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.
|
@@ -2,25 +2,17 @@ import "reflect-metadata";
|
|
|
2
2
|
import _ from "lodash";
|
|
3
3
|
import { EVENT_INTERNAL_PRE_LOADED, EVENT_DELETE, EVENT_GET, EVENT_NEW, EVENT_UPDATE, globalCache, } from "./CommonTypes.js";
|
|
4
4
|
import { ObjectId } from "bson";
|
|
5
|
-
import { stringSimilarity } from "string-similarity-js";
|
|
6
5
|
export class AutoUpdatedClientObject {
|
|
7
|
-
//From server type for overlapping keys
|
|
8
6
|
entry;
|
|
9
7
|
preLoad;
|
|
10
8
|
registerSocket;
|
|
11
9
|
readyLoggers;
|
|
12
10
|
loadFromDB(a) { }
|
|
13
11
|
setValue_(a, b) { }
|
|
14
|
-
//-*--
|
|
15
12
|
socket;
|
|
16
13
|
data;
|
|
17
14
|
isServer = false;
|
|
18
|
-
loggers
|
|
19
|
-
info: () => { },
|
|
20
|
-
debug: () => { },
|
|
21
|
-
error: () => { },
|
|
22
|
-
warn: () => { },
|
|
23
|
-
};
|
|
15
|
+
loggers;
|
|
24
16
|
isLoading = true;
|
|
25
17
|
isLoadingReferences = true;
|
|
26
18
|
checkedMissingRefs = false;
|
|
@@ -42,9 +34,7 @@ export class AutoUpdatedClientObject {
|
|
|
42
34
|
}
|
|
43
35
|
}
|
|
44
36
|
catch (error) {
|
|
45
|
-
this.loggers.error("Error loading references");
|
|
46
|
-
this.loggers.error(error.message);
|
|
47
|
-
this.loggers.error(error.stack);
|
|
37
|
+
this.loggers.error("Error loading references: " + error.message);
|
|
48
38
|
}
|
|
49
39
|
this.isLoadingReferences = false;
|
|
50
40
|
return;
|
|
@@ -60,9 +50,7 @@ export class AutoUpdatedClientObject {
|
|
|
60
50
|
}
|
|
61
51
|
catch (error) {
|
|
62
52
|
this.isLoadingReferences = false;
|
|
63
|
-
this.loggers.error("Error loading references");
|
|
64
|
-
this.loggers.error(error.message);
|
|
65
|
-
this.loggers.error(error.stack);
|
|
53
|
+
this.loggers.error("Error loading references: " + error.message);
|
|
66
54
|
}
|
|
67
55
|
};
|
|
68
56
|
/** @deprecated Use loadReferencesAsync instead */
|
|
@@ -74,7 +62,6 @@ export class AutoUpdatedClientObject {
|
|
|
74
62
|
!loggers ||
|
|
75
63
|
!className ||
|
|
76
64
|
!parentManager ||
|
|
77
|
-
!callback ||
|
|
78
65
|
!emitter) {
|
|
79
66
|
this.classParam = classParam;
|
|
80
67
|
this.socket = socket;
|
|
@@ -91,16 +78,13 @@ export class AutoUpdatedClientObject {
|
|
|
91
78
|
!loggers &&
|
|
92
79
|
!className &&
|
|
93
80
|
!parentManager &&
|
|
94
|
-
!callback &&
|
|
95
81
|
!emitter)
|
|
96
82
|
return;
|
|
97
83
|
else
|
|
98
84
|
throw new Error("Missing arguments???");
|
|
99
85
|
}
|
|
100
86
|
this.classParam = classParam;
|
|
101
|
-
|
|
102
|
-
processIsRefProperties(data, this, undefined, [], loggers);
|
|
103
|
-
}
|
|
87
|
+
this.socket = socket;
|
|
104
88
|
this.isServer = isServer;
|
|
105
89
|
this.emitter = emitter;
|
|
106
90
|
this.isLoadingReferences = true;
|
|
@@ -109,53 +93,19 @@ export class AutoUpdatedClientObject {
|
|
|
109
93
|
this.className = className;
|
|
110
94
|
this.properties = Reflect.getMetadata("props", classParam.prototype);
|
|
111
95
|
this.callbacks = callback;
|
|
112
|
-
this.loggers
|
|
113
|
-
this.className
|
|
114
|
-
|
|
115
|
-
(this.data?._id ?? "not loaded")
|
|
116
|
-
"
|
|
117
|
-
|
|
118
|
-
this.loggers.info = (s) => loggers.info("[DEM - " +
|
|
119
|
-
this.className +
|
|
120
|
-
": " +
|
|
121
|
-
(this.data?._id ?? "not loaded") +
|
|
122
|
-
"] " +
|
|
123
|
-
s);
|
|
124
|
-
this.loggers.error = (s) => loggers.error("[DEM - " +
|
|
125
|
-
this.className +
|
|
126
|
-
": " +
|
|
127
|
-
(this.data?._id ?? "not loaded") +
|
|
128
|
-
"] " +
|
|
129
|
-
s);
|
|
130
|
-
this.loggers.warn = (s) => loggers.warn("[DEM - " +
|
|
131
|
-
this.className +
|
|
132
|
-
": " +
|
|
133
|
-
(this.data?._id ?? "not loaded") +
|
|
134
|
-
"] " +
|
|
135
|
-
s);
|
|
136
|
-
for (const prop of this.properties) {
|
|
137
|
-
if (typeof prop !== "string")
|
|
138
|
-
throw new Error("Property '" + prop.toString() + "' is not a string");
|
|
139
|
-
if (prop.includes("."))
|
|
140
|
-
throw new Error("Property '" +
|
|
141
|
-
prop.toString() +
|
|
142
|
-
"' constain the illegal character '.'");
|
|
143
|
-
}
|
|
144
|
-
this.socket = socket;
|
|
96
|
+
this.loggers = {
|
|
97
|
+
debug: (s) => loggers.debug(`[DEM - ${this.className}: ${this.data?._id ?? "not loaded"}] ${s}`),
|
|
98
|
+
info: (s) => loggers.info(`[DEM - ${this.className}: ${this.data?._id ?? "not loaded"}] ${s}`),
|
|
99
|
+
warn: (s) => loggers.warn(`[DEM - ${this.className}: ${this.data?._id ?? "not loaded"}] ${s}`),
|
|
100
|
+
error: (s) => loggers.error(`[DEM - ${this.className}: ${this.data?._id ?? "not loaded"}] ${s}`),
|
|
101
|
+
};
|
|
145
102
|
if (typeof data === "string") {
|
|
103
|
+
this.data = { _id: data };
|
|
146
104
|
if (this.isServer) {
|
|
147
105
|
this.isLoading = false;
|
|
148
|
-
this.
|
|
106
|
+
this.generateSettersAndGetters();
|
|
149
107
|
return;
|
|
150
108
|
}
|
|
151
|
-
if (!data || data === "" || data === "undefined") {
|
|
152
|
-
this.loggers.error("Cannot create a new AutoUpdatedClientClass with an empty string for ID. Data typeof: " +
|
|
153
|
-
typeof data +
|
|
154
|
-
" Data: " +
|
|
155
|
-
data);
|
|
156
|
-
throw new Error("Cannot create a new AutoUpdatedClientClass with an empty string for ID.");
|
|
157
|
-
}
|
|
158
|
-
this.loggers.debug("Getting new object from server " + this.className + " - " + data);
|
|
159
109
|
this.socket.emit(EVENT_GET + this.className + data, null, (res) => {
|
|
160
110
|
if (!res.success) {
|
|
161
111
|
this.isLoading = false;
|
|
@@ -169,38 +119,24 @@ export class AutoUpdatedClientObject {
|
|
|
169
119
|
this.emitter.emit(EVENT_INTERNAL_PRE_LOADED + this.EmitterID);
|
|
170
120
|
this.openSockets();
|
|
171
121
|
});
|
|
172
|
-
this.data = { _id: data };
|
|
173
122
|
}
|
|
174
123
|
else {
|
|
175
124
|
this.isLoading = true;
|
|
176
125
|
this.data = data;
|
|
177
|
-
const
|
|
178
|
-
for (const key of this.properties) {
|
|
179
|
-
if (typeof key !== "string")
|
|
180
|
-
throw new Error("Only string keys allowed. Not this shit: " + String(key));
|
|
181
|
-
dataKeys.splice(dataKeys.indexOf(key), 1);
|
|
126
|
+
for (const key of this.properties || []) {
|
|
182
127
|
const isRef = getMetadataRecursive("isRef", this, key);
|
|
183
|
-
if (isRef) {
|
|
128
|
+
if (isRef && this.data[key]) {
|
|
184
129
|
if (Array.isArray(this.data[key])) {
|
|
185
130
|
this.data[key] = this.data[key].map((obj) => obj._id?.toString() ?? obj?.toString());
|
|
186
131
|
}
|
|
187
132
|
else {
|
|
188
|
-
this.data[key] =
|
|
189
|
-
this.data[key]?._id?.toString() ??
|
|
190
|
-
this.data[key]?.toString();
|
|
133
|
+
this.data[key] = this.data[key]?._id?.toString() ?? this.data[key]?.toString();
|
|
191
134
|
}
|
|
192
135
|
}
|
|
193
136
|
}
|
|
194
|
-
if (
|
|
195
|
-
dataKeys.splice(dataKeys.indexOf("__v"), 1);
|
|
196
|
-
if (dataKeys.length > 0)
|
|
197
|
-
this.loggers.warn((dataKeys.length > 1 ? "Properties " : "Property ") +
|
|
198
|
-
dataKeys.join(", ") +
|
|
199
|
-
(dataKeys.length > 1 ? " were " : " was ") +
|
|
200
|
-
"unexpected. These properties are not known by the class. Please check your level of skill issue. Known properties are:\n" +
|
|
201
|
-
this.properties.join("\n"));
|
|
202
|
-
if ((!this.data._id || this.data._id === "") && !this.isServer)
|
|
137
|
+
if ((!this.data._id || this.data._id === "") && !this.isServer) {
|
|
203
138
|
this.handleNewObject(data);
|
|
139
|
+
}
|
|
204
140
|
else {
|
|
205
141
|
this.isLoading = false;
|
|
206
142
|
if (!this.isServer)
|
|
@@ -214,9 +150,8 @@ export class AutoUpdatedClientObject {
|
|
|
214
150
|
return;
|
|
215
151
|
await new Promise((resolve, reject) => {
|
|
216
152
|
this.emitter.once(EVENT_INTERNAL_PRE_LOADED + this.EmitterID, (failed, reason) => {
|
|
217
|
-
if (failed)
|
|
153
|
+
if (failed)
|
|
218
154
|
reject(new Error(reason));
|
|
219
|
-
}
|
|
220
155
|
else
|
|
221
156
|
resolve();
|
|
222
157
|
});
|
|
@@ -224,31 +159,6 @@ export class AutoUpdatedClientObject {
|
|
|
224
159
|
}
|
|
225
160
|
handleNewObject(data) {
|
|
226
161
|
this.isLoading = true;
|
|
227
|
-
if (!this.className)
|
|
228
|
-
throw new Error("Cannot create a new AutoUpdatedClientClass without a class name.");
|
|
229
|
-
this.loggers.debug(this.className + " - Requesting new object creation on server");
|
|
230
|
-
if (this.isServer)
|
|
231
|
-
for (const key of this.properties) {
|
|
232
|
-
if (typeof key !== "string")
|
|
233
|
-
continue;
|
|
234
|
-
let pointer = getMetadataRecursive("refsTo", this, key);
|
|
235
|
-
if (pointer) {
|
|
236
|
-
pointer = pointer.split(":");
|
|
237
|
-
if (pointer.length != 2)
|
|
238
|
-
throw new Error("population ref incorrectly defined. Sould be 'ParentClass:PropName.Path'");
|
|
239
|
-
const temp = data[key];
|
|
240
|
-
delete data[key];
|
|
241
|
-
if (temp)
|
|
242
|
-
this.toChangeOnParents.push({ key: key, value: temp });
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
try {
|
|
246
|
-
data = _.cloneDeep(data);
|
|
247
|
-
}
|
|
248
|
-
catch (error) {
|
|
249
|
-
this.loggers.error("Most likely cycled object: " + error.message);
|
|
250
|
-
this.loggers.error(error.stack);
|
|
251
|
-
}
|
|
252
162
|
this.socket.emit(EVENT_NEW + this.className, data, (res) => {
|
|
253
163
|
if (!res.success) {
|
|
254
164
|
this.isLoading = false;
|
|
@@ -259,7 +169,6 @@ export class AutoUpdatedClientObject {
|
|
|
259
169
|
this.data = res.data;
|
|
260
170
|
this.generateSettersAndGetters();
|
|
261
171
|
this.isLoading = false;
|
|
262
|
-
this.loggers.debug("Created new object: " + this.data._id);
|
|
263
172
|
this.emitter.emit(EVENT_INTERNAL_PRE_LOADED + this.EmitterID);
|
|
264
173
|
if (!this.isServer)
|
|
265
174
|
this.openSockets();
|
|
@@ -286,27 +195,18 @@ export class AutoUpdatedClientObject {
|
|
|
286
195
|
const res = await this.handleUpdateRequest(update);
|
|
287
196
|
if (ack && typeof ack === "function")
|
|
288
197
|
ack(res);
|
|
289
|
-
return res;
|
|
290
198
|
});
|
|
291
199
|
}
|
|
292
200
|
async handleUpdateRequest(update) {
|
|
293
201
|
try {
|
|
294
202
|
await this.setValue__(update.key, update.value, true);
|
|
295
|
-
this.loggers.debug(`Applied patch ${update.key} set to ${JSON.stringify(update.value)}`);
|
|
296
|
-
// Return success with the applied patch
|
|
297
203
|
if (this.isLoaded)
|
|
298
204
|
this.callbacks.update(this, update.key);
|
|
299
205
|
return { success: true, data: undefined, message: "" };
|
|
300
206
|
}
|
|
301
207
|
catch (error) {
|
|
302
|
-
this.loggers.error(`[${this.data._id}] Error applying patch: `
|
|
303
|
-
|
|
304
|
-
"\n" +
|
|
305
|
-
error.stack);
|
|
306
|
-
return {
|
|
307
|
-
success: false,
|
|
308
|
-
message: "Error applying update: " + error.message,
|
|
309
|
-
};
|
|
208
|
+
this.loggers.error(`[${this.data._id}] Error applying patch: ${error.message}`);
|
|
209
|
+
return { success: false, message: "Error applying update: " + error.message };
|
|
310
210
|
}
|
|
311
211
|
}
|
|
312
212
|
generateSettersAndGetters() {
|
|
@@ -315,35 +215,55 @@ export class AutoUpdatedClientObject {
|
|
|
315
215
|
for (const key of this.properties) {
|
|
316
216
|
if (typeof key !== "string")
|
|
317
217
|
continue;
|
|
318
|
-
const k = key;
|
|
319
218
|
const isRef = getMetadataRecursive("isRef", this, key);
|
|
219
|
+
// Delete the property if it exists on the instance to ensure the getter sticks.
|
|
220
|
+
delete this[key];
|
|
320
221
|
Object.defineProperty(this, key, {
|
|
321
222
|
get: () => {
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
.filter(Boolean);
|
|
327
|
-
return filtered;
|
|
223
|
+
const val = this.data ? this.data[key] : undefined;
|
|
224
|
+
if (isRef && val) {
|
|
225
|
+
if (Array.isArray(val)) {
|
|
226
|
+
return val.map((id) => this.findReference(id, key)).filter(Boolean);
|
|
328
227
|
}
|
|
329
228
|
else {
|
|
330
|
-
return this.findReference(
|
|
229
|
+
return this.findReference(val, key);
|
|
331
230
|
}
|
|
332
231
|
}
|
|
333
|
-
return
|
|
232
|
+
return val;
|
|
334
233
|
},
|
|
335
234
|
enumerable: true,
|
|
336
235
|
configurable: true,
|
|
337
236
|
});
|
|
338
237
|
}
|
|
339
238
|
}
|
|
239
|
+
getValue(key_) {
|
|
240
|
+
const key = key_;
|
|
241
|
+
const parts = key.split(".");
|
|
242
|
+
let value = this;
|
|
243
|
+
for (const part of parts) {
|
|
244
|
+
if (value === undefined || value === null)
|
|
245
|
+
return undefined;
|
|
246
|
+
const nextValue = value[part];
|
|
247
|
+
if (nextValue !== undefined) {
|
|
248
|
+
value = nextValue;
|
|
249
|
+
}
|
|
250
|
+
else if (value.data && value.data[part] !== undefined) {
|
|
251
|
+
value = value.data[part];
|
|
252
|
+
}
|
|
253
|
+
else {
|
|
254
|
+
return undefined;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
return value;
|
|
258
|
+
}
|
|
340
259
|
findReference(id, key) {
|
|
341
|
-
if (
|
|
342
|
-
return
|
|
260
|
+
if (!id)
|
|
261
|
+
return undefined;
|
|
262
|
+
const idStr = id.toString();
|
|
343
263
|
if (this.parentManager.cache.references[key])
|
|
344
|
-
return this.parentManager.cache.references[key].getObject(
|
|
264
|
+
return this.parentManager.cache.references[key].getObject(idStr);
|
|
345
265
|
for (const manager of Object.values(this.parentManager.managers)) {
|
|
346
|
-
const result = manager.getObject(
|
|
266
|
+
const result = manager.getObject(idStr);
|
|
347
267
|
if (result) {
|
|
348
268
|
this.parentManager.cache.references[key] = manager;
|
|
349
269
|
return result;
|
|
@@ -352,326 +272,80 @@ export class AutoUpdatedClientObject {
|
|
|
352
272
|
return undefined;
|
|
353
273
|
}
|
|
354
274
|
async setValue(key, val) {
|
|
355
|
-
|
|
356
|
-
return result;
|
|
275
|
+
return await this.setValue__(key, val);
|
|
357
276
|
}
|
|
358
277
|
async setValue__(key, val, silent = false, noGet = false, noUpdate = false) {
|
|
359
|
-
let message = "Setting value " + key + " of " + this.className + " to ";
|
|
360
|
-
const isRef = getMetadataRecursive("isRef", this, key);
|
|
361
|
-
if (isRef)
|
|
362
|
-
val = Array.isArray(val)
|
|
363
|
-
? val.map((v) => {
|
|
364
|
-
return v._id?.toString() ?? v;
|
|
365
|
-
})
|
|
366
|
-
: (val?._id ?? val);
|
|
367
278
|
try {
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
if (
|
|
381
|
-
val = val.extractedData._id;
|
|
382
|
-
if (Array.isArray(val))
|
|
383
|
-
val = val.map((v) => v instanceof AutoUpdatedClientObject ? v.extractedData._id : v);
|
|
384
|
-
let originalVal = this.getValue(key);
|
|
385
|
-
if (Array.isArray(originalVal)) {
|
|
386
|
-
originalVal = originalVal.map((v) => v instanceof AutoUpdatedClientObject ? v.extractedData._id : v);
|
|
387
|
-
}
|
|
388
|
-
else {
|
|
389
|
-
originalVal =
|
|
390
|
-
originalVal instanceof AutoUpdatedClientObject
|
|
391
|
-
? originalVal.extractedData._id
|
|
392
|
-
: originalVal;
|
|
393
|
-
}
|
|
394
|
-
if ((Array.isArray(originalVal) &&
|
|
395
|
-
Array.isArray(val) &&
|
|
396
|
-
originalVal.length === val.length &&
|
|
397
|
-
!originalVal.some((v) => !val.includes(v))) ||
|
|
398
|
-
(Array.isArray(originalVal) &&
|
|
399
|
-
!Array.isArray(val) &&
|
|
400
|
-
originalVal.includes(val)) ||
|
|
401
|
-
(!Array.isArray(originalVal) &&
|
|
402
|
-
!Array.isArray(val) &&
|
|
403
|
-
originalVal === val)) {
|
|
279
|
+
const isRef = getMetadataRecursive("isRef", this, key);
|
|
280
|
+
let valueToStore = val;
|
|
281
|
+
if (isRef) {
|
|
282
|
+
valueToStore = Array.isArray(val)
|
|
283
|
+
? val.map((v) => v._id?.toString() ?? v.toString())
|
|
284
|
+
: (val?._id ?? val?.toString() ?? val);
|
|
285
|
+
}
|
|
286
|
+
// Optimization: skip if value hasn't changed
|
|
287
|
+
const currentVal = this.getValue(key);
|
|
288
|
+
const currentValId = Array.isArray(currentVal)
|
|
289
|
+
? currentVal.map(v => v._id?.toString() ?? v.toString())
|
|
290
|
+
: (currentVal?._id ?? currentVal?.toString());
|
|
291
|
+
if (_.isEqual(currentValId, valueToStore))
|
|
404
292
|
return { success: true, msg: "" };
|
|
405
|
-
}
|
|
406
293
|
const path = key.split(".");
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
294
|
+
if (path.length > 1) {
|
|
295
|
+
// Deep path handling for references
|
|
296
|
+
let obj = this.data;
|
|
297
|
+
for (let i = 0; i < path.length - 1; i++) {
|
|
298
|
+
if (typeof obj[path[i]] === 'string' || ObjectId.isValid(obj[path[i]])) {
|
|
299
|
+
const ref = await this.resolveReference(obj[path[i]].toString());
|
|
300
|
+
if (!ref)
|
|
301
|
+
throw new Error("Could not resolve reference on path: " + key);
|
|
302
|
+
return await ref.setValue(path.slice(i + 1).join("."), val);
|
|
416
303
|
}
|
|
417
|
-
catch (error) {
|
|
418
|
-
message +=
|
|
419
|
-
"\n Error: likely undefined property on path: " +
|
|
420
|
-
path +
|
|
421
|
-
" on index: " +
|
|
422
|
-
i +
|
|
423
|
-
" with error: " +
|
|
424
|
-
error.message;
|
|
425
|
-
}
|
|
426
|
-
if (!temp) {
|
|
427
|
-
message +=
|
|
428
|
-
"\nLikely undefined property " +
|
|
429
|
-
path[i] +
|
|
430
|
-
" on path: " +
|
|
431
|
-
path +
|
|
432
|
-
" at index: " +
|
|
433
|
-
i;
|
|
434
|
-
this.loggers.warn("Failed to set value for " + this.className + "\n" + message);
|
|
435
|
-
return {
|
|
436
|
-
success: false,
|
|
437
|
-
msg: message,
|
|
438
|
-
};
|
|
439
|
-
}
|
|
440
|
-
lastClass = temp;
|
|
441
|
-
lastPath = path.slice(i + 1).join(".");
|
|
442
|
-
const res = await lastClass.setValue(lastPath, val);
|
|
443
|
-
if (!noUpdate)
|
|
444
|
-
await this.onUpdate(noUpdate);
|
|
445
|
-
return res;
|
|
446
|
-
}
|
|
447
|
-
else
|
|
448
304
|
obj = obj[path[i]];
|
|
449
|
-
}
|
|
450
|
-
if (lastClass !== this) {
|
|
451
|
-
message +=
|
|
452
|
-
"\n What the actual fuckity fuck error on path: " +
|
|
453
|
-
path +
|
|
454
|
-
" on index: " +
|
|
455
|
-
(path.length - 1);
|
|
456
|
-
this.loggers.error("Failed to set value for " + this.className + "\n" + message);
|
|
457
|
-
return {
|
|
458
|
-
success: false,
|
|
459
|
-
msg: message,
|
|
460
|
-
};
|
|
461
|
-
}
|
|
462
|
-
if (!this.properties.includes(lastPath) && !lastPath.includes(".")) {
|
|
463
|
-
let nearest = "";
|
|
464
|
-
for (const prop of this.properties) {
|
|
465
|
-
if (typeof prop !== "string")
|
|
466
|
-
continue;
|
|
467
|
-
if (stringSimilarity(lastPath, prop) > 0 &&
|
|
468
|
-
stringSimilarity(lastPath, prop) > stringSimilarity(nearest, prop)) {
|
|
469
|
-
nearest = prop;
|
|
470
|
-
}
|
|
471
305
|
}
|
|
472
|
-
throw new Error(`Property ${lastPath} not found in class ${this.className}, did you mean ${nearest ?? "--No similar prop found--"}?`);
|
|
473
306
|
}
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
if (!parentObj) {
|
|
482
|
-
message +=
|
|
483
|
-
"\n Failed to set value for " +
|
|
484
|
-
this.className +
|
|
485
|
-
" parent not found";
|
|
486
|
-
this.loggers.error(message);
|
|
487
|
-
return { success: false, msg: message };
|
|
488
|
-
}
|
|
489
|
-
if (parentObj.getValue(isPopulated[1]) &&
|
|
490
|
-
!Array.isArray(parentObj.getValue(isPopulated[1]))) {
|
|
491
|
-
message +=
|
|
492
|
-
"\nThis is a 1:1 relationship and the parent already has a parent with the ID: " +
|
|
493
|
-
(parentObj.getValue(isPopulated[1])._id?.toString() ??
|
|
494
|
-
parentObj.getValue(isPopulated[1]).toString()) +
|
|
495
|
-
this.loggers.error(message);
|
|
496
|
-
return { success: false, msg: message };
|
|
497
|
-
}
|
|
498
|
-
let res;
|
|
499
|
-
if (this.isServer) {
|
|
500
|
-
const value = parentObj.getValue(isPopulated[1]);
|
|
501
|
-
if (Array.isArray(value)) {
|
|
502
|
-
if (value
|
|
503
|
-
.map((v) => v._id?.toString() ?? v.toString())
|
|
504
|
-
.includes(this.data._id.toString()))
|
|
505
|
-
return {
|
|
506
|
-
success: true,
|
|
507
|
-
msg: message + "\nValue already set",
|
|
508
|
-
};
|
|
509
|
-
res = await parentObj.setValue__(isPopulated[1], value.concat(this.data._id.toString()), false, false, true);
|
|
510
|
-
}
|
|
511
|
-
else {
|
|
512
|
-
if ((value?._id?.toString() ?? value?.toString()) ===
|
|
513
|
-
this.data._id.toString())
|
|
514
|
-
return {
|
|
515
|
-
success: true,
|
|
516
|
-
msg: message + "\nValue already set",
|
|
517
|
-
};
|
|
518
|
-
res = await parentObj.setValue__(isPopulated[1], this.data._id.toString(), false, false, true);
|
|
519
|
-
}
|
|
520
|
-
}
|
|
521
|
-
else {
|
|
522
|
-
const value = parentObj.getValue(isPopulated[1]);
|
|
523
|
-
if (Array.isArray(value)
|
|
524
|
-
? value
|
|
525
|
-
.map((v) => v._id?.toString() ?? v.toString())
|
|
526
|
-
.includes(this.data._id.toString())
|
|
527
|
-
: (value._id?.toString() ?? value.toString()) ===
|
|
528
|
-
this.data._id.toString())
|
|
529
|
-
return { success: true, msg: message + "\nValue already set" };
|
|
530
|
-
({ res, val } = await this.preInnerSetValue(noGet, key, val, lastPath, silent, noUpdate));
|
|
531
|
-
}
|
|
532
|
-
success = res.success;
|
|
533
|
-
message +=
|
|
534
|
-
"\nReport from inner setValue function: " +
|
|
535
|
-
res.msg.split("\n").join("\n ");
|
|
536
|
-
}
|
|
537
|
-
else {
|
|
538
|
-
const originalParenValue = this.getValue(key).getValue(isPopulated[1]);
|
|
539
|
-
if (!originalParenValue ||
|
|
540
|
-
(Array.isArray(originalParenValue) &&
|
|
541
|
-
!originalParenValue.some((v) => (v._id?.toString() ?? v.toString()) === this._id.toString()))) {
|
|
542
|
-
message += "\n Value already set";
|
|
543
|
-
return { success: true, msg: message };
|
|
544
|
-
}
|
|
545
|
-
const parentObj = this.parentManager.managers[isPopulated[0]].getObject(originalVal);
|
|
546
|
-
if (!parentObj) {
|
|
547
|
-
message +=
|
|
548
|
-
"\n Failed to set value for " +
|
|
549
|
-
this.className +
|
|
550
|
-
" parent not found";
|
|
551
|
-
this.loggers.error(message);
|
|
552
|
-
return { success: false, msg: message };
|
|
553
|
-
}
|
|
554
|
-
let res;
|
|
555
|
-
if (Array.isArray(originalParenValue)) {
|
|
556
|
-
res = await parentObj.setValue__(isPopulated[1], originalParenValue
|
|
557
|
-
.map((v) => v._id?.toString() ?? v.toString())
|
|
558
|
-
.filter((v) => v !== this._id.toString()));
|
|
559
|
-
}
|
|
560
|
-
else {
|
|
561
|
-
res = await parentObj.setValue__(isPopulated[1], null);
|
|
562
|
-
}
|
|
563
|
-
success = res.success;
|
|
564
|
-
message +=
|
|
565
|
-
"\nReport from inner setValue function: " +
|
|
566
|
-
res.msg.split("\n").join("\n ");
|
|
567
|
-
}
|
|
307
|
+
const res = await this.setValueInternal(key, valueToStore, silent, noUpdate);
|
|
308
|
+
if (res.success) {
|
|
309
|
+
// Apply update to this.data (handling deep paths)
|
|
310
|
+
const pathArr = key.split(".");
|
|
311
|
+
let obj = this.data;
|
|
312
|
+
for (let i = 0; i < pathArr.length - 1; i++) {
|
|
313
|
+
obj = obj[pathArr[i]];
|
|
568
314
|
}
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
? val.map((v) => new ObjectId(v))
|
|
576
|
-
: new ObjectId(val);
|
|
577
|
-
let res;
|
|
578
|
-
({ res, val } = await this.preInnerSetValue(noGet, key, val, lastPath, silent, noUpdate));
|
|
579
|
-
if (res.success) {
|
|
580
|
-
const originalValue = obj[path.at(-1)];
|
|
581
|
-
if (!Array.isArray(val) && Array.isArray(originalValue)) {
|
|
582
|
-
if (!originalValue.includes(val))
|
|
583
|
-
originalValue.push(val);
|
|
584
|
-
val = originalValue;
|
|
585
|
-
}
|
|
586
|
-
else
|
|
587
|
-
obj[path.at(-1)] = val;
|
|
588
|
-
}
|
|
589
|
-
success = res.success;
|
|
590
|
-
message += "\nReport from inner setValue function: \n " + res.msg;
|
|
591
|
-
}
|
|
592
|
-
}
|
|
593
|
-
catch (error) {
|
|
594
|
-
success = false;
|
|
595
|
-
message += "\nError from inner setValue function: \n " + error.message;
|
|
596
|
-
message += error.stack;
|
|
597
|
-
}
|
|
598
|
-
if (!success) {
|
|
599
|
-
this.loggers.warn("Failed to set value for " + this.className + "\n" + message);
|
|
600
|
-
return { success: false, msg: message };
|
|
315
|
+
obj[pathArr[pathArr.length - 1]] = valueToStore;
|
|
316
|
+
await this.findAndLoadReferences(key, valueToStore);
|
|
317
|
+
if (isRef && this.parentManager.isLoaded)
|
|
318
|
+
await this.contactChildren();
|
|
319
|
+
if (this.isLoaded)
|
|
320
|
+
this.callbacks.update(this, key);
|
|
601
321
|
}
|
|
602
|
-
|
|
603
|
-
if (pathArr.length === 1) {
|
|
604
|
-
this.data[key] = val;
|
|
605
|
-
}
|
|
606
|
-
else {
|
|
607
|
-
const last = pathArr.splice(-1, 1);
|
|
608
|
-
let ref = this;
|
|
609
|
-
for (const p of pathArr) {
|
|
610
|
-
ref = ref[p];
|
|
611
|
-
}
|
|
612
|
-
ref[last.at(-1)] = val;
|
|
613
|
-
}
|
|
614
|
-
if (!noUpdate)
|
|
615
|
-
await this.onUpdate(noUpdate);
|
|
616
|
-
await this.findAndLoadReferences(lastPath, val);
|
|
617
|
-
const isRef = getMetadataRecursive("isRef", this, path.at(-1));
|
|
618
|
-
if (isRef && this.parentManager.isLoaded) {
|
|
619
|
-
await this.contactChildren();
|
|
620
|
-
}
|
|
621
|
-
if (this.isLoaded)
|
|
622
|
-
this.callbacks.update(this, key);
|
|
623
|
-
return {
|
|
624
|
-
success: true,
|
|
625
|
-
msg: "Successfully set " + key + " to " + val,
|
|
626
|
-
};
|
|
322
|
+
return res;
|
|
627
323
|
}
|
|
628
324
|
catch (error) {
|
|
629
|
-
this.loggers.error(
|
|
630
|
-
|
|
631
|
-
"\n" +
|
|
632
|
-
message +
|
|
633
|
-
"\n Random error here: " +
|
|
634
|
-
error.message +
|
|
635
|
-
"\n" +
|
|
636
|
-
error.stack);
|
|
637
|
-
this.loggers.error(error);
|
|
638
|
-
return {
|
|
639
|
-
success: false,
|
|
640
|
-
msg: message +
|
|
641
|
-
"\n Random error here: " +
|
|
642
|
-
error.message +
|
|
643
|
-
"\n" +
|
|
644
|
-
error.stack,
|
|
645
|
-
};
|
|
325
|
+
this.loggers.error(`Error setting value ${key}: ${error.message}`);
|
|
326
|
+
return { success: false, msg: error.message };
|
|
646
327
|
}
|
|
647
328
|
}
|
|
648
|
-
async
|
|
649
|
-
if (
|
|
650
|
-
|
|
651
|
-
this.getValue(key) &&
|
|
652
|
-
Array.isArray(this.getValue(key)) &&
|
|
653
|
-
!Array.isArray(val)) {
|
|
654
|
-
val = [
|
|
655
|
-
...new Set(this.getValue(key)
|
|
656
|
-
.concat(val)
|
|
657
|
-
.map((v) => v?._id?.toString() ?? new ObjectId(v))
|
|
658
|
-
.filter(Boolean)),
|
|
659
|
-
];
|
|
329
|
+
async setValueInternal(key, value, silent = false, noUpdate = false) {
|
|
330
|
+
if (silent) {
|
|
331
|
+
return { success: true, msg: "Silent" };
|
|
660
332
|
}
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
333
|
+
return new Promise((resolve) => {
|
|
334
|
+
this.socket.emit(EVENT_UPDATE + this.className + this.data._id, { _id: this.data._id.toString(), key, value }, (res) => {
|
|
335
|
+
resolve({ success: res.success, msg: res.message ?? (res.success ? "Success" : "Error") });
|
|
336
|
+
});
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
makeUpdate(key, value) {
|
|
340
|
+
return { _id: this.data._id.toString(), key, value };
|
|
341
|
+
}
|
|
342
|
+
async resolveReference(id) {
|
|
343
|
+
for (const manager of Object.values(this.parentManager.managers)) {
|
|
344
|
+
const obj = manager.getObject(id);
|
|
345
|
+
if (obj)
|
|
346
|
+
return obj;
|
|
673
347
|
}
|
|
674
|
-
return
|
|
348
|
+
return null;
|
|
675
349
|
}
|
|
676
350
|
async findAndLoadReferences(lastPath, value) {
|
|
677
351
|
const isRef = getMetadataRecursive("isRef", this, lastPath);
|
|
@@ -685,121 +359,21 @@ export class AutoUpdatedClientObject {
|
|
|
685
359
|
if (result)
|
|
686
360
|
break;
|
|
687
361
|
}
|
|
688
|
-
if (!result) {
|
|
689
|
-
for (const manager of Object.values(this.parentManager.managers)) {
|
|
690
|
-
try {
|
|
691
|
-
result = await manager.handleGetMissingObject(id?.toString());
|
|
692
|
-
if (result)
|
|
693
|
-
break;
|
|
694
|
-
}
|
|
695
|
-
catch (error) {
|
|
696
|
-
const _ = error;
|
|
697
|
-
}
|
|
698
|
-
}
|
|
699
|
-
if (!result) {
|
|
700
|
-
this.loggers.warn("Failed to update childerns parent for " +
|
|
701
|
-
this.className +
|
|
702
|
-
" updating " +
|
|
703
|
-
id +
|
|
704
|
-
"'s parent to " +
|
|
705
|
-
this.data._id);
|
|
706
|
-
continue;
|
|
707
|
-
}
|
|
708
|
-
}
|
|
709
362
|
if (result && typeof result.loadMissingReferences === "function") {
|
|
710
363
|
await result.loadMissingReferences();
|
|
711
364
|
}
|
|
712
|
-
else if (result) {
|
|
713
|
-
this.loggers.warn(`Object in reference resolution is missing loadMissingReferences function. Type: ${typeof result}, ID: ${result._id ?? result}`);
|
|
714
|
-
}
|
|
715
365
|
}
|
|
716
366
|
}
|
|
717
367
|
}
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
}
|
|
725
|
-
let value;
|
|
726
|
-
const parts = key.split(".");
|
|
727
|
-
for (const part of parts) {
|
|
728
|
-
try {
|
|
729
|
-
if (value !== undefined && value !== null) {
|
|
730
|
-
value = value[part];
|
|
731
|
-
}
|
|
732
|
-
else {
|
|
733
|
-
value = this[part];
|
|
734
|
-
}
|
|
735
|
-
}
|
|
736
|
-
catch (error) {
|
|
737
|
-
this.loggers.error(`Error getting value for ${this.className} on key ${key} at index ${part}: ${error.message}`);
|
|
738
|
-
return undefined;
|
|
739
|
-
}
|
|
740
|
-
}
|
|
741
|
-
return value;
|
|
742
|
-
}
|
|
743
|
-
async setValueInternal(key, value, silent = false, noUpdate = false) {
|
|
744
|
-
const update = this.makeUpdate(key, value);
|
|
745
|
-
const promise = new Promise((resolve) => {
|
|
746
|
-
if (silent) {
|
|
747
|
-
if (noUpdate)
|
|
748
|
-
return resolve({ success: true, msg: "Success - no update" });
|
|
749
|
-
return this.onUpdate(true).then(() => {
|
|
750
|
-
if (this.isLoaded)
|
|
751
|
-
this.callbacks.update(this, key);
|
|
752
|
-
return resolve({ success: true, msg: "Success - silent" });
|
|
753
|
-
});
|
|
754
|
-
}
|
|
755
|
-
try {
|
|
756
|
-
this.socket.emit(EVENT_UPDATE + this.className + this.data._id, update, async (res) => {
|
|
757
|
-
if (!res.success) {
|
|
758
|
-
this.loggers.error("Error sending update: " + res.message);
|
|
759
|
-
resolve({ success: false, msg: res.message });
|
|
760
|
-
return;
|
|
761
|
-
}
|
|
762
|
-
if (!noUpdate)
|
|
763
|
-
await this.onUpdate(noUpdate);
|
|
764
|
-
resolve({
|
|
765
|
-
success: res.success,
|
|
766
|
-
msg: res.message ?? "Success",
|
|
767
|
-
});
|
|
768
|
-
});
|
|
769
|
-
}
|
|
770
|
-
catch (error) {
|
|
771
|
-
this.loggers.error("Error sending update:" + error.message);
|
|
772
|
-
this.loggers.error(error.stack);
|
|
773
|
-
resolve({ success: false, msg: error.message });
|
|
774
|
-
}
|
|
775
|
-
});
|
|
776
|
-
return promise;
|
|
777
|
-
}
|
|
778
|
-
makeUpdate(key, value) {
|
|
779
|
-
try {
|
|
780
|
-
const id = this.data._id.toString();
|
|
781
|
-
return { _id: id, key, value };
|
|
782
|
-
}
|
|
783
|
-
catch (error) {
|
|
784
|
-
this.loggers.error("Probably missing the fucking identifier ['_id'] again: " +
|
|
785
|
-
error.message);
|
|
786
|
-
throw error;
|
|
787
|
-
}
|
|
788
|
-
}
|
|
789
|
-
async onUpdate(_) {
|
|
790
|
-
return;
|
|
791
|
-
}
|
|
792
|
-
// return a properly typed AutoUpdatedClientClass (or null)
|
|
793
|
-
// inside AutoUpdatedClientClass
|
|
794
|
-
async resolveReference(id) {
|
|
795
|
-
if (!this.parentManager)
|
|
796
|
-
throw new Error("No Manager");
|
|
797
|
-
for (const manager of Object.values(this.parentManager.managers)) {
|
|
798
|
-
const data = manager.getObject(id);
|
|
799
|
-
if (data)
|
|
800
|
-
return data;
|
|
368
|
+
async wipeSelf() {
|
|
369
|
+
if (this.data.Wiped)
|
|
370
|
+
return;
|
|
371
|
+
const _id = this.data._id.toString();
|
|
372
|
+
for (const key of Object.keys(this.data)) {
|
|
373
|
+
delete this.data[key];
|
|
801
374
|
}
|
|
802
|
-
|
|
375
|
+
this.data = { Wiped: true };
|
|
376
|
+
this.loggers.info(`[${_id}] ${this.className} object wiped`);
|
|
803
377
|
}
|
|
804
378
|
async loadForceReferences(obj = this.data, proto = this, alreadySeen = []) {
|
|
805
379
|
const props = Reflect.getMetadata("props", proto) || [];
|
|
@@ -808,307 +382,144 @@ export class AutoUpdatedClientObject {
|
|
|
808
382
|
continue;
|
|
809
383
|
const isRef = Reflect.getMetadata("isRef", proto, key);
|
|
810
384
|
const pointer = Reflect.getMetadata("refsTo", proto, key);
|
|
811
|
-
if (pointer &&
|
|
812
|
-
obj === this.data &&
|
|
813
|
-
obj[key] &&
|
|
814
|
-
!alreadySeen.includes(obj))
|
|
385
|
+
if (pointer && obj === this.data && obj[key] && !alreadySeen.includes(obj)) {
|
|
815
386
|
await this.createdWithParent(pointer.split(":"), obj[key]);
|
|
387
|
+
}
|
|
816
388
|
if (obj[key] && !alreadySeen.includes(obj[key]))
|
|
817
389
|
alreadySeen.push(obj[key]);
|
|
818
|
-
if (isRef)
|
|
390
|
+
if (isRef)
|
|
819
391
|
await this.handleLoad(obj, key, alreadySeen);
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
const nestedProto = Object.getPrototypeOf(val);
|
|
828
|
-
if (nestedProto && !alreadySeen.includes(val)) {
|
|
829
|
-
alreadySeen.push(val);
|
|
830
|
-
await this.loadForceReferences(val, nestedProto, alreadySeen);
|
|
392
|
+
const val = obj[key];
|
|
393
|
+
if (val && typeof val === "object") {
|
|
394
|
+
const nestedProto = Object.getPrototypeOf(val);
|
|
395
|
+
if (nestedProto && !alreadySeen.includes(val)) {
|
|
396
|
+
alreadySeen.push(val);
|
|
397
|
+
await this.loadForceReferences(val, nestedProto, alreadySeen);
|
|
398
|
+
}
|
|
831
399
|
}
|
|
832
400
|
}
|
|
833
401
|
}
|
|
834
402
|
async handleLoad(obj, key, alreadySeen) {
|
|
835
|
-
if (!this.parentManager)
|
|
836
|
-
throw new Error("No manager");
|
|
837
403
|
const refIds = Array.isArray(obj[key]) ? obj[key] : [obj[key]];
|
|
838
404
|
for (const refId of refIds) {
|
|
839
405
|
if (refId) {
|
|
840
406
|
const idStr = refId.toString();
|
|
841
|
-
|
|
842
|
-
if (
|
|
843
|
-
const
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
407
|
+
let result = globalCache.objects[idStr]?.object;
|
|
408
|
+
if (!result) {
|
|
409
|
+
for (const manager of Object.values(this.parentManager.managers)) {
|
|
410
|
+
result = manager.getObject(idStr);
|
|
411
|
+
if (result)
|
|
412
|
+
break;
|
|
847
413
|
}
|
|
848
|
-
continue;
|
|
849
414
|
}
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
alreadySeen.push(idStr);
|
|
854
|
-
await result.loadForceReferences(undefined, undefined, alreadySeen);
|
|
855
|
-
break;
|
|
856
|
-
}
|
|
415
|
+
if (result && !alreadySeen.includes(idStr)) {
|
|
416
|
+
alreadySeen.push(idStr);
|
|
417
|
+
await result.loadForceReferences(undefined, undefined, alreadySeen);
|
|
857
418
|
}
|
|
858
419
|
}
|
|
859
420
|
}
|
|
860
421
|
}
|
|
861
422
|
async createdWithParent(pointer, parent) {
|
|
862
|
-
if (pointer.length !== 2)
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
this.className +
|
|
867
|
-
", poiter must be 'className:pathToParentProperty'");
|
|
868
|
-
}
|
|
869
|
-
if (!parent)
|
|
870
|
-
throw new Error("Invalid pointer: " +
|
|
871
|
-
JSON.stringify(pointer) +
|
|
872
|
-
" for " +
|
|
873
|
-
this.className +
|
|
874
|
-
", parent is null");
|
|
875
|
-
const obj = this.parentManager.managers[pointer[0]]?.getObject(parent._id?.toString() ?? parent.toString());
|
|
423
|
+
if (pointer.length !== 2)
|
|
424
|
+
return;
|
|
425
|
+
const parentId = parent._id?.toString() ?? parent.toString();
|
|
426
|
+
const obj = this.parentManager.managers[pointer[0]]?.getObject(parentId);
|
|
876
427
|
if (!obj)
|
|
877
428
|
return;
|
|
878
429
|
const val = obj.getValue(pointer[1]);
|
|
430
|
+
const myId = this.data._id.toString();
|
|
879
431
|
if (Array.isArray(val)) {
|
|
880
|
-
const
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
await obj.setValue__(pointer[1], filtred, true, false, true);
|
|
884
|
-
this.loggers.warn("Array value changed from " +
|
|
885
|
-
originalLength +
|
|
886
|
-
" to " +
|
|
887
|
-
filtred.length +
|
|
888
|
-
" - some values were undefined");
|
|
432
|
+
const ids = val.map(v => v._id?.toString() ?? v.toString());
|
|
433
|
+
if (!ids.includes(myId)) {
|
|
434
|
+
await obj.setValue__(pointer[1], [...val, myId], true, false, true);
|
|
889
435
|
}
|
|
890
|
-
|
|
436
|
+
else {
|
|
891
437
|
await obj.contactChildren();
|
|
892
|
-
|
|
893
|
-
await obj.setValue__(pointer[1], [
|
|
894
|
-
...new Set([...filtred, this.data._id]),
|
|
895
|
-
], true, false, true);
|
|
438
|
+
}
|
|
896
439
|
}
|
|
897
|
-
else if (val?.toString()
|
|
440
|
+
else if ((val?._id?.toString() ?? val?.toString()) === myId) {
|
|
898
441
|
await obj.contactChildren();
|
|
899
|
-
|
|
900
|
-
|
|
442
|
+
}
|
|
443
|
+
else {
|
|
444
|
+
await obj.setValue__(pointer[1], myId, true, false, true);
|
|
445
|
+
}
|
|
901
446
|
}
|
|
902
447
|
async destroy(once = false) {
|
|
903
|
-
if (!once)
|
|
448
|
+
if (!once)
|
|
904
449
|
return await this.parentManager.deleteObject(this.data._id);
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
if (!res.success) {
|
|
909
|
-
this.loggers.error("Error deleting object from database - " +
|
|
910
|
-
this.className +
|
|
911
|
-
" - " +
|
|
912
|
-
this.data._id);
|
|
913
|
-
this.loggers.error(res.message);
|
|
914
|
-
resolve({
|
|
915
|
-
success: false,
|
|
916
|
-
message: res.message,
|
|
917
|
-
});
|
|
918
|
-
return;
|
|
919
|
-
}
|
|
920
|
-
resolve({
|
|
921
|
-
success: true,
|
|
922
|
-
message: "Deleted",
|
|
923
|
-
});
|
|
450
|
+
return new Promise((resolve) => {
|
|
451
|
+
this.socket.emit(EVENT_DELETE + this.className, this.data._id, (res) => {
|
|
452
|
+
resolve({ success: res.success, message: res.message ?? "" });
|
|
924
453
|
});
|
|
925
454
|
});
|
|
926
|
-
return res;
|
|
927
455
|
}
|
|
928
456
|
async checkForMissingRefs() {
|
|
929
457
|
for (const prop of this.properties) {
|
|
930
|
-
|
|
458
|
+
const pointer = getMetadataRecursive("refsTo", this, prop.toString());
|
|
931
459
|
if (pointer) {
|
|
932
|
-
|
|
933
|
-
if (
|
|
934
|
-
|
|
935
|
-
await this.findMissingObjectReference(prop, pointer);
|
|
460
|
+
const parts = pointer.split(":");
|
|
461
|
+
if (parts.length === 2)
|
|
462
|
+
await this.findMissingObjectReference(prop, parts);
|
|
936
463
|
}
|
|
937
464
|
}
|
|
938
465
|
}
|
|
939
466
|
async findMissingObjectReference(prop, pointer) {
|
|
940
|
-
if (this.checkedMissingRefs
|
|
467
|
+
if (this.checkedMissingRefs)
|
|
941
468
|
return;
|
|
942
469
|
this.checkedMissingRefs = true;
|
|
943
470
|
const ac = this.parentManager.managers[pointer[0]];
|
|
944
471
|
if (!ac)
|
|
945
|
-
|
|
472
|
+
return;
|
|
946
473
|
const targetId = this.data._id.toString();
|
|
947
|
-
const pointerKey = pointer[1];
|
|
948
|
-
const isNested = pointerKey.includes(".");
|
|
949
|
-
const pathParts = isNested ? pointerKey.split(".") : [];
|
|
950
474
|
const allObjects = Object.values(ac.objects);
|
|
951
|
-
const pendingObjects = allObjects.filter(obj => !obj.isLoaded);
|
|
952
|
-
if (pendingObjects.length > 0) {
|
|
953
|
-
await Promise.all(pendingObjects.map(obj => obj.waitForPreloaded()));
|
|
954
|
-
}
|
|
955
475
|
for (const obj of allObjects) {
|
|
956
|
-
if (!obj.isLoaded)
|
|
476
|
+
if (!obj.isLoaded)
|
|
957
477
|
await obj.waitForPreloaded();
|
|
958
|
-
|
|
959
|
-
let val;
|
|
960
|
-
if (isNested) {
|
|
961
|
-
val = obj;
|
|
962
|
-
for (const element of pathParts) {
|
|
963
|
-
if (!val)
|
|
964
|
-
break;
|
|
965
|
-
val = val[element] ?? val.data?.[element];
|
|
966
|
-
}
|
|
967
|
-
}
|
|
968
|
-
else {
|
|
969
|
-
val = obj[pointerKey] ?? obj.data?.[pointerKey];
|
|
970
|
-
}
|
|
478
|
+
const val = obj.getValue(pointer[1]);
|
|
971
479
|
if (!val)
|
|
972
480
|
continue;
|
|
973
|
-
|
|
974
|
-
if (
|
|
975
|
-
for (let i = 0; i < val.length; i++) {
|
|
976
|
-
const item = val[i];
|
|
977
|
-
if (!item)
|
|
978
|
-
continue;
|
|
979
|
-
const idStr = item._id ? item._id.toString() : item.toString();
|
|
980
|
-
if (idStr === targetId) {
|
|
981
|
-
found = true;
|
|
982
|
-
break;
|
|
983
|
-
}
|
|
984
|
-
}
|
|
985
|
-
}
|
|
986
|
-
else {
|
|
987
|
-
const idStr = val._id ? val._id.toString() : val.toString();
|
|
988
|
-
found = (idStr === targetId);
|
|
989
|
-
}
|
|
990
|
-
if (found) {
|
|
481
|
+
const ids = Array.isArray(val) ? val.map(v => v._id?.toString() ?? v.toString()) : [val._id?.toString() ?? val.toString()];
|
|
482
|
+
if (ids.includes(targetId)) {
|
|
991
483
|
this.data[prop] = obj._id;
|
|
992
484
|
return;
|
|
993
485
|
}
|
|
994
486
|
}
|
|
995
487
|
}
|
|
996
|
-
async wipeSelf() {
|
|
997
|
-
if (this.data.Wiped)
|
|
998
|
-
return;
|
|
999
|
-
const _id = this.data._id.toString();
|
|
1000
|
-
for (const key of Object.keys(this.data)) {
|
|
1001
|
-
delete this.data[key];
|
|
1002
|
-
}
|
|
1003
|
-
this.data = { Wiped: true };
|
|
1004
|
-
this.loggers.info(`[${_id}] ${this.className} object wiped`);
|
|
1005
|
-
}
|
|
1006
488
|
async contactChildren() {
|
|
1007
|
-
let childsManager = null;
|
|
1008
|
-
const findMissingObjectWithoutKnownManager = async (o) => {
|
|
1009
|
-
if (o instanceof AutoUpdatedClientObject)
|
|
1010
|
-
return o;
|
|
1011
|
-
if (childsManager)
|
|
1012
|
-
try {
|
|
1013
|
-
return await childsManager.handleGetMissingObject(o._id?.toString() ?? o.toString());
|
|
1014
|
-
}
|
|
1015
|
-
catch (error) {
|
|
1016
|
-
this.loggers.error("This should fucking not happen wtffffff");
|
|
1017
|
-
this.loggers.error(error.message);
|
|
1018
|
-
childsManager = null;
|
|
1019
|
-
}
|
|
1020
|
-
else {
|
|
1021
|
-
for (const manager of Object.values(this.parentManager.managers)) {
|
|
1022
|
-
try {
|
|
1023
|
-
const newO = await manager.handleGetMissingObject(o._id?.toString() ?? o.toString());
|
|
1024
|
-
childsManager = manager;
|
|
1025
|
-
return newO;
|
|
1026
|
-
}
|
|
1027
|
-
catch (e) {
|
|
1028
|
-
const _ = e;
|
|
1029
|
-
}
|
|
1030
|
-
}
|
|
1031
|
-
return undefined;
|
|
1032
|
-
}
|
|
1033
|
-
};
|
|
1034
489
|
for (const prop of this.properties) {
|
|
1035
|
-
const pointer = getMetadataRecursive("refsTo", this, prop.toString());
|
|
1036
490
|
const isRef = getMetadataRecursive("isRef", this, prop.toString());
|
|
491
|
+
const pointer = getMetadataRecursive("refsTo", this, prop.toString());
|
|
1037
492
|
if (!isRef || pointer)
|
|
1038
493
|
continue;
|
|
1039
|
-
|
|
1040
|
-
if (!obj || (Array.isArray(obj) && obj.length == 0)) {
|
|
1041
|
-
obj = this.getValueInternal(prop);
|
|
1042
|
-
try {
|
|
1043
|
-
if (Array.isArray(obj))
|
|
1044
|
-
obj = await Promise.all(obj.map(findMissingObjectWithoutKnownManager));
|
|
1045
|
-
else
|
|
1046
|
-
obj = await findMissingObjectWithoutKnownManager(obj);
|
|
1047
|
-
}
|
|
1048
|
-
catch (error) {
|
|
1049
|
-
const _ = error;
|
|
1050
|
-
}
|
|
1051
|
-
}
|
|
494
|
+
const obj = this.getValue(prop);
|
|
1052
495
|
if (!obj)
|
|
1053
496
|
continue;
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
await child.loadMissingReferences();
|
|
1059
|
-
}
|
|
1060
|
-
else if (child) {
|
|
1061
|
-
this.loggers.warn(`Object in property ${prop.toString()} is missing loadMissingReferences function. Type: ${typeof child}, ID: ${child._id ?? child}`);
|
|
1062
|
-
}
|
|
1063
|
-
}
|
|
1064
|
-
catch (error) {
|
|
1065
|
-
this.loggers.error(error.message);
|
|
1066
|
-
}
|
|
1067
|
-
}
|
|
1068
|
-
}
|
|
1069
|
-
else {
|
|
1070
|
-
if (obj && typeof obj.loadMissingReferences === "function") {
|
|
1071
|
-
await obj.loadMissingReferences();
|
|
1072
|
-
}
|
|
1073
|
-
else if (obj) {
|
|
1074
|
-
this.loggers.warn(`Object in property ${prop.toString()} is missing loadMissingReferences function. Type: ${typeof obj}, ID: ${obj._id ?? obj}`);
|
|
497
|
+
const children = Array.isArray(obj) ? obj : [obj];
|
|
498
|
+
for (const child of children) {
|
|
499
|
+
if (child && typeof child.loadMissingReferences === "function") {
|
|
500
|
+
await child.loadMissingReferences();
|
|
1075
501
|
}
|
|
1076
502
|
}
|
|
1077
503
|
}
|
|
1078
504
|
}
|
|
1079
|
-
getValueInternal(key) {
|
|
1080
|
-
const keys = key.toString().split(".");
|
|
1081
|
-
let value = this.data;
|
|
1082
|
-
for (const k of keys) {
|
|
1083
|
-
value = value[k];
|
|
1084
|
-
}
|
|
1085
|
-
return value;
|
|
1086
|
-
}
|
|
1087
505
|
}
|
|
1088
|
-
export function processIsRefProperties(instance, target, prefix
|
|
506
|
+
export function processIsRefProperties(instance, target, prefix, allProps, newData, loggers) {
|
|
1089
507
|
const props = Reflect.getMetadata("props", target) || [];
|
|
1090
508
|
for (const prop of props) {
|
|
1091
509
|
const path = prefix ? `${prefix}.${prop}` : prop;
|
|
1092
510
|
allProps.push(path);
|
|
1093
|
-
newData[prop] = ObjectId.isValid(instance[prop])
|
|
1094
|
-
? instance[prop]?.toString()
|
|
1095
|
-
: instance[prop];
|
|
511
|
+
newData[prop] = ObjectId.isValid(instance[prop]) ? instance[prop]?.toString() : instance[prop];
|
|
1096
512
|
if (Reflect.getMetadata("isRef", target, prop)) {
|
|
1097
513
|
if (Array.isArray(instance[prop]))
|
|
1098
|
-
newData[prop] = instance[prop]
|
|
1099
|
-
.map((item) => item?._id?.toString() ?? item?.toString() ?? undefined)
|
|
1100
|
-
.filter(Boolean);
|
|
514
|
+
newData[prop] = instance[prop].map((item) => item?._id?.toString() ?? item?.toString()).filter(Boolean);
|
|
1101
515
|
else
|
|
1102
|
-
newData[prop] =
|
|
1103
|
-
instance[prop]?._id?.toString() ??
|
|
1104
|
-
instance[prop]?.toString() ??
|
|
1105
|
-
undefined;
|
|
516
|
+
newData[prop] = instance[prop]?._id?.toString() ?? instance[prop]?.toString();
|
|
1106
517
|
}
|
|
1107
518
|
const type = Reflect.getMetadata("design:type", target, prop);
|
|
1108
519
|
if (type?.prototype) {
|
|
1109
520
|
const nestedProps = Reflect.getMetadata("props", type.prototype);
|
|
1110
521
|
if (nestedProps && instance[prop]) {
|
|
1111
|
-
newData[prop] = processIsRefProperties(instance[prop], type.prototype, path, allProps,
|
|
522
|
+
newData[prop] = processIsRefProperties(instance[prop], type.prototype, path, allProps, {}, loggers).newData;
|
|
1112
523
|
}
|
|
1113
524
|
}
|
|
1114
525
|
}
|