@prestizni-software/client-dem 0.4.102 → 0.4.104
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;
|
|
@@ -33,36 +25,21 @@ export class AutoUpdatedClientObject {
|
|
|
33
25
|
toChangeOnParents = [];
|
|
34
26
|
callbacks;
|
|
35
27
|
loadReferencesAsync = async () => {
|
|
36
|
-
if (this.isLoaded) {
|
|
37
|
-
try {
|
|
38
|
-
this.generateSettersAndGetters();
|
|
39
|
-
await this.loadForceReferences();
|
|
40
|
-
for (const thing of this.toChangeOnParents) {
|
|
41
|
-
await this.setValue__(thing.key, thing.value, true, false, true);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
catch (error) {
|
|
45
|
-
this.loggers.error("Error loading references");
|
|
46
|
-
this.loggers.error(error.message);
|
|
47
|
-
this.loggers.error(error.stack);
|
|
48
|
-
}
|
|
49
|
-
this.isLoadingReferences = false;
|
|
50
|
-
return;
|
|
51
|
-
}
|
|
52
|
-
await this.waitForPreloaded();
|
|
53
|
-
this.generateSettersAndGetters();
|
|
54
28
|
try {
|
|
29
|
+
if (!this.isLoaded) {
|
|
30
|
+
await this.waitForPreloaded();
|
|
31
|
+
}
|
|
32
|
+
this.generateSettersAndGetters();
|
|
55
33
|
await this.loadForceReferences();
|
|
56
34
|
for (const thing of this.toChangeOnParents) {
|
|
57
35
|
await this.setValue__(thing.key, thing.value, true, false, true);
|
|
58
36
|
}
|
|
59
|
-
this.isLoadingReferences = false;
|
|
60
37
|
}
|
|
61
38
|
catch (error) {
|
|
39
|
+
this.loggers.error("Error loading references: " + error.message);
|
|
40
|
+
}
|
|
41
|
+
finally {
|
|
62
42
|
this.isLoadingReferences = false;
|
|
63
|
-
this.loggers.error("Error loading references");
|
|
64
|
-
this.loggers.error(error.message);
|
|
65
|
-
this.loggers.error(error.stack);
|
|
66
43
|
}
|
|
67
44
|
};
|
|
68
45
|
/** @deprecated Use loadReferencesAsync instead */
|
|
@@ -74,7 +51,6 @@ export class AutoUpdatedClientObject {
|
|
|
74
51
|
!loggers ||
|
|
75
52
|
!className ||
|
|
76
53
|
!parentManager ||
|
|
77
|
-
!callback ||
|
|
78
54
|
!emitter) {
|
|
79
55
|
this.classParam = classParam;
|
|
80
56
|
this.socket = socket;
|
|
@@ -91,16 +67,13 @@ export class AutoUpdatedClientObject {
|
|
|
91
67
|
!loggers &&
|
|
92
68
|
!className &&
|
|
93
69
|
!parentManager &&
|
|
94
|
-
!callback &&
|
|
95
70
|
!emitter)
|
|
96
71
|
return;
|
|
97
72
|
else
|
|
98
73
|
throw new Error("Missing arguments???");
|
|
99
74
|
}
|
|
100
75
|
this.classParam = classParam;
|
|
101
|
-
|
|
102
|
-
processIsRefProperties(data, this, undefined, [], loggers);
|
|
103
|
-
}
|
|
76
|
+
this.socket = socket;
|
|
104
77
|
this.isServer = isServer;
|
|
105
78
|
this.emitter = emitter;
|
|
106
79
|
this.isLoadingReferences = true;
|
|
@@ -109,53 +82,19 @@ export class AutoUpdatedClientObject {
|
|
|
109
82
|
this.className = className;
|
|
110
83
|
this.properties = Reflect.getMetadata("props", classParam.prototype);
|
|
111
84
|
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;
|
|
85
|
+
this.loggers = {
|
|
86
|
+
debug: (s) => loggers.debug(`[DEM - ${this.className}: ${this.data?._id ?? "not loaded"}] ${s}`),
|
|
87
|
+
info: (s) => loggers.info(`[DEM - ${this.className}: ${this.data?._id ?? "not loaded"}] ${s}`),
|
|
88
|
+
warn: (s) => loggers.warn(`[DEM - ${this.className}: ${this.data?._id ?? "not loaded"}] ${s}`),
|
|
89
|
+
error: (s) => loggers.error(`[DEM - ${this.className}: ${this.data?._id ?? "not loaded"}] ${s}`),
|
|
90
|
+
};
|
|
145
91
|
if (typeof data === "string") {
|
|
92
|
+
this.data = { _id: data };
|
|
146
93
|
if (this.isServer) {
|
|
147
94
|
this.isLoading = false;
|
|
148
|
-
this.
|
|
95
|
+
this.generateSettersAndGetters();
|
|
149
96
|
return;
|
|
150
97
|
}
|
|
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
98
|
this.socket.emit(EVENT_GET + this.className + data, null, (res) => {
|
|
160
99
|
if (!res.success) {
|
|
161
100
|
this.isLoading = false;
|
|
@@ -169,54 +108,43 @@ export class AutoUpdatedClientObject {
|
|
|
169
108
|
this.emitter.emit(EVENT_INTERNAL_PRE_LOADED + this.EmitterID);
|
|
170
109
|
this.openSockets();
|
|
171
110
|
});
|
|
172
|
-
this.data = { _id: data };
|
|
173
111
|
}
|
|
174
112
|
else {
|
|
175
113
|
this.isLoading = true;
|
|
176
114
|
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);
|
|
115
|
+
for (const key of this.properties || []) {
|
|
182
116
|
const isRef = getMetadataRecursive("isRef", this, key);
|
|
183
|
-
if (isRef) {
|
|
117
|
+
if (isRef && this.data[key]) {
|
|
184
118
|
if (Array.isArray(this.data[key])) {
|
|
185
119
|
this.data[key] = this.data[key].map((obj) => obj._id?.toString() ?? obj?.toString());
|
|
186
120
|
}
|
|
187
121
|
else {
|
|
188
|
-
this.data[key] =
|
|
189
|
-
this.data[key]?._id?.toString() ??
|
|
190
|
-
this.data[key]?.toString();
|
|
122
|
+
this.data[key] = this.data[key]?._id?.toString() ?? this.data[key]?.toString();
|
|
191
123
|
}
|
|
192
124
|
}
|
|
193
125
|
}
|
|
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)
|
|
126
|
+
if ((!this.data._id || this.data._id === "") && !this.isServer) {
|
|
203
127
|
this.handleNewObject(data);
|
|
128
|
+
}
|
|
204
129
|
else {
|
|
205
130
|
this.isLoading = false;
|
|
206
131
|
if (!this.isServer)
|
|
207
132
|
this.openSockets();
|
|
208
133
|
}
|
|
209
134
|
}
|
|
210
|
-
|
|
135
|
+
// We use a microtask to ensure subclass field initializers have finished
|
|
136
|
+
// before we apply our getters. This is critical for TypeScript subclasses.
|
|
137
|
+
Promise.resolve().then(() => {
|
|
138
|
+
this.generateSettersAndGetters();
|
|
139
|
+
});
|
|
211
140
|
}
|
|
212
141
|
async waitForPreloaded() {
|
|
213
142
|
if (this.isLoaded)
|
|
214
143
|
return;
|
|
215
144
|
await new Promise((resolve, reject) => {
|
|
216
145
|
this.emitter.once(EVENT_INTERNAL_PRE_LOADED + this.EmitterID, (failed, reason) => {
|
|
217
|
-
if (failed)
|
|
146
|
+
if (failed)
|
|
218
147
|
reject(new Error(reason));
|
|
219
|
-
}
|
|
220
148
|
else
|
|
221
149
|
resolve();
|
|
222
150
|
});
|
|
@@ -224,31 +152,6 @@ export class AutoUpdatedClientObject {
|
|
|
224
152
|
}
|
|
225
153
|
handleNewObject(data) {
|
|
226
154
|
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
155
|
this.socket.emit(EVENT_NEW + this.className, data, (res) => {
|
|
253
156
|
if (!res.success) {
|
|
254
157
|
this.isLoading = false;
|
|
@@ -259,7 +162,6 @@ export class AutoUpdatedClientObject {
|
|
|
259
162
|
this.data = res.data;
|
|
260
163
|
this.generateSettersAndGetters();
|
|
261
164
|
this.isLoading = false;
|
|
262
|
-
this.loggers.debug("Created new object: " + this.data._id);
|
|
263
165
|
this.emitter.emit(EVENT_INTERNAL_PRE_LOADED + this.EmitterID);
|
|
264
166
|
if (!this.isServer)
|
|
265
167
|
this.openSockets();
|
|
@@ -286,27 +188,18 @@ export class AutoUpdatedClientObject {
|
|
|
286
188
|
const res = await this.handleUpdateRequest(update);
|
|
287
189
|
if (ack && typeof ack === "function")
|
|
288
190
|
ack(res);
|
|
289
|
-
return res;
|
|
290
191
|
});
|
|
291
192
|
}
|
|
292
193
|
async handleUpdateRequest(update) {
|
|
293
194
|
try {
|
|
294
195
|
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
196
|
if (this.isLoaded)
|
|
298
197
|
this.callbacks.update(this, update.key);
|
|
299
198
|
return { success: true, data: undefined, message: "" };
|
|
300
199
|
}
|
|
301
200
|
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
|
-
};
|
|
201
|
+
this.loggers.error(`[${this.data._id}] Error applying patch: ${error.message}`);
|
|
202
|
+
return { success: false, message: "Error applying update: " + error.message };
|
|
310
203
|
}
|
|
311
204
|
}
|
|
312
205
|
generateSettersAndGetters() {
|
|
@@ -315,35 +208,56 @@ export class AutoUpdatedClientObject {
|
|
|
315
208
|
for (const key of this.properties) {
|
|
316
209
|
if (typeof key !== "string")
|
|
317
210
|
continue;
|
|
318
|
-
const k = key;
|
|
319
211
|
const isRef = getMetadataRecursive("isRef", this, key);
|
|
212
|
+
// CRITICAL: Overwrite any instance property to ensure the getter is used.
|
|
213
|
+
delete this[key];
|
|
320
214
|
Object.defineProperty(this, key, {
|
|
321
215
|
get: () => {
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
.filter(Boolean);
|
|
327
|
-
return filtered;
|
|
216
|
+
const val = this.data ? this.data[key] : undefined;
|
|
217
|
+
if (isRef && val) {
|
|
218
|
+
if (Array.isArray(val)) {
|
|
219
|
+
return val.map((id) => this.findReference(id, key)).filter(Boolean);
|
|
328
220
|
}
|
|
329
221
|
else {
|
|
330
|
-
return this.findReference(
|
|
222
|
+
return this.findReference(val, key);
|
|
331
223
|
}
|
|
332
224
|
}
|
|
333
|
-
return
|
|
225
|
+
return val;
|
|
334
226
|
},
|
|
335
227
|
enumerable: true,
|
|
336
228
|
configurable: true,
|
|
337
229
|
});
|
|
338
230
|
}
|
|
339
231
|
}
|
|
232
|
+
getValue(key_) {
|
|
233
|
+
const key = key_;
|
|
234
|
+
const parts = key.split(".");
|
|
235
|
+
let value = this;
|
|
236
|
+
for (const part of parts) {
|
|
237
|
+
if (value === undefined || value === null)
|
|
238
|
+
return undefined;
|
|
239
|
+
// Try instance first (getters), then fallback to data
|
|
240
|
+
const nextValue = value[part];
|
|
241
|
+
if (nextValue !== undefined) {
|
|
242
|
+
value = nextValue;
|
|
243
|
+
}
|
|
244
|
+
else if (value.data && value.data[part] !== undefined) {
|
|
245
|
+
value = value.data[part];
|
|
246
|
+
}
|
|
247
|
+
else {
|
|
248
|
+
return undefined;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
return value;
|
|
252
|
+
}
|
|
340
253
|
findReference(id, key) {
|
|
341
|
-
if (
|
|
342
|
-
return
|
|
254
|
+
if (!id)
|
|
255
|
+
return undefined;
|
|
256
|
+
const idStr = id.toString();
|
|
343
257
|
if (this.parentManager.cache.references[key])
|
|
344
|
-
return this.parentManager.cache.references[key].getObject(
|
|
258
|
+
return this.parentManager.cache.references[key].getObject(idStr);
|
|
345
259
|
for (const manager of Object.values(this.parentManager.managers)) {
|
|
346
|
-
const result = manager.getObject(
|
|
260
|
+
const result = manager.getObject(idStr);
|
|
347
261
|
if (result) {
|
|
348
262
|
this.parentManager.cache.references[key] = manager;
|
|
349
263
|
return result;
|
|
@@ -352,326 +266,80 @@ export class AutoUpdatedClientObject {
|
|
|
352
266
|
return undefined;
|
|
353
267
|
}
|
|
354
268
|
async setValue(key, val) {
|
|
355
|
-
|
|
356
|
-
return result;
|
|
269
|
+
return await this.setValue__(key, val);
|
|
357
270
|
}
|
|
358
271
|
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
272
|
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)) {
|
|
273
|
+
const isRef = getMetadataRecursive("isRef", this, key);
|
|
274
|
+
let valueToStore = val;
|
|
275
|
+
if (isRef) {
|
|
276
|
+
valueToStore = Array.isArray(val)
|
|
277
|
+
? val.map((v) => v._id?.toString() ?? v.toString())
|
|
278
|
+
: (val?._id ?? val?.toString() ?? val);
|
|
279
|
+
}
|
|
280
|
+
// Optimization: skip if value hasn't changed
|
|
281
|
+
const currentVal = this.getValue(key);
|
|
282
|
+
const currentValId = Array.isArray(currentVal)
|
|
283
|
+
? currentVal.map((v) => v._id?.toString() ?? v.toString())
|
|
284
|
+
: (currentVal?._id ?? currentVal?.toString());
|
|
285
|
+
if (_.isEqual(currentValId, valueToStore))
|
|
404
286
|
return { success: true, msg: "" };
|
|
405
|
-
}
|
|
406
287
|
const path = key.split(".");
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
288
|
+
if (path.length > 1) {
|
|
289
|
+
// Deep path handling for references
|
|
290
|
+
let obj = this.data;
|
|
291
|
+
for (let i = 0; i < path.length - 1; i++) {
|
|
292
|
+
if (typeof obj[path[i]] === 'string' || ObjectId.isValid(obj[path[i]])) {
|
|
293
|
+
const ref = await this.resolveReference(obj[path[i]].toString());
|
|
294
|
+
if (!ref)
|
|
295
|
+
throw new Error("Could not resolve reference on path: " + key);
|
|
296
|
+
return await ref.setValue(path.slice(i + 1).join("."), val);
|
|
416
297
|
}
|
|
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
298
|
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
299
|
}
|
|
472
|
-
throw new Error(`Property ${lastPath} not found in class ${this.className}, did you mean ${nearest ?? "--No similar prop found--"}?`);
|
|
473
300
|
}
|
|
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
|
-
}
|
|
301
|
+
const res = await this.setValueInternal(key, valueToStore, silent, noUpdate);
|
|
302
|
+
if (res.success) {
|
|
303
|
+
// Apply update to this.data (handling deep paths)
|
|
304
|
+
const pathArr = key.split(".");
|
|
305
|
+
let obj = this.data;
|
|
306
|
+
for (let i = 0; i < pathArr.length - 1; i++) {
|
|
307
|
+
obj = obj[pathArr[i]];
|
|
568
308
|
}
|
|
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;
|
|
309
|
+
obj[pathArr[pathArr.length - 1]] = valueToStore;
|
|
310
|
+
await this.findAndLoadReferences(key, valueToStore);
|
|
311
|
+
if (isRef && this.parentManager.isLoaded)
|
|
312
|
+
await this.contactChildren();
|
|
313
|
+
if (this.isLoaded)
|
|
314
|
+
this.callbacks.update(this, key);
|
|
597
315
|
}
|
|
598
|
-
|
|
599
|
-
this.loggers.warn("Failed to set value for " + this.className + "\n" + message);
|
|
600
|
-
return { success: false, msg: message };
|
|
601
|
-
}
|
|
602
|
-
const pathArr = lastPath.split(".");
|
|
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
|
-
};
|
|
316
|
+
return res;
|
|
627
317
|
}
|
|
628
318
|
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
|
-
};
|
|
319
|
+
this.loggers.error(`Error setting value ${key}: ${error.message}`);
|
|
320
|
+
return { success: false, msg: error.message };
|
|
646
321
|
}
|
|
647
322
|
}
|
|
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
|
-
];
|
|
323
|
+
async setValueInternal(key, value, silent = false, noUpdate = false) {
|
|
324
|
+
if (silent) {
|
|
325
|
+
return { success: true, msg: "Silent" };
|
|
660
326
|
}
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
327
|
+
return new Promise((resolve) => {
|
|
328
|
+
this.socket.emit(EVENT_UPDATE + this.className + this.data._id, { _id: this.data._id.toString(), key, value }, (res) => {
|
|
329
|
+
resolve({ success: res.success, msg: res.message ?? (res.success ? "Success" : "Error") });
|
|
330
|
+
});
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
makeUpdate(key, value) {
|
|
334
|
+
return { _id: this.data._id.toString(), key, value };
|
|
335
|
+
}
|
|
336
|
+
async resolveReference(id) {
|
|
337
|
+
for (const manager of Object.values(this.parentManager.managers)) {
|
|
338
|
+
const obj = manager.getObject(id);
|
|
339
|
+
if (obj)
|
|
340
|
+
return obj;
|
|
673
341
|
}
|
|
674
|
-
return
|
|
342
|
+
return null;
|
|
675
343
|
}
|
|
676
344
|
async findAndLoadReferences(lastPath, value) {
|
|
677
345
|
const isRef = getMetadataRecursive("isRef", this, lastPath);
|
|
@@ -685,121 +353,21 @@ export class AutoUpdatedClientObject {
|
|
|
685
353
|
if (result)
|
|
686
354
|
break;
|
|
687
355
|
}
|
|
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
356
|
if (result && typeof result.loadMissingReferences === "function") {
|
|
710
357
|
await result.loadMissingReferences();
|
|
711
358
|
}
|
|
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
|
-
}
|
|
716
|
-
}
|
|
717
|
-
}
|
|
718
|
-
getValue(key_) {
|
|
719
|
-
const key = key_;
|
|
720
|
-
if (!key.includes(".")) {
|
|
721
|
-
let val = this[key];
|
|
722
|
-
val ??= this.data[key];
|
|
723
|
-
return val;
|
|
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
359
|
}
|
|
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
360
|
}
|
|
788
361
|
}
|
|
789
|
-
async
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
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;
|
|
362
|
+
async wipeSelf() {
|
|
363
|
+
if (this.data.Wiped)
|
|
364
|
+
return;
|
|
365
|
+
const _id = this.data._id.toString();
|
|
366
|
+
for (const key of Object.keys(this.data)) {
|
|
367
|
+
delete this.data[key];
|
|
801
368
|
}
|
|
802
|
-
|
|
369
|
+
this.data = { Wiped: true };
|
|
370
|
+
this.loggers.info(`[${_id}] ${this.className} object wiped`);
|
|
803
371
|
}
|
|
804
372
|
async loadForceReferences(obj = this.data, proto = this, alreadySeen = []) {
|
|
805
373
|
const props = Reflect.getMetadata("props", proto) || [];
|
|
@@ -808,284 +376,128 @@ export class AutoUpdatedClientObject {
|
|
|
808
376
|
continue;
|
|
809
377
|
const isRef = Reflect.getMetadata("isRef", proto, key);
|
|
810
378
|
const pointer = Reflect.getMetadata("refsTo", proto, key);
|
|
811
|
-
if (pointer &&
|
|
812
|
-
obj === this.data &&
|
|
813
|
-
obj[key] &&
|
|
814
|
-
!alreadySeen.includes(obj))
|
|
379
|
+
if (pointer && obj === this.data && obj[key] && !alreadySeen.includes(obj)) {
|
|
815
380
|
await this.createdWithParent(pointer.split(":"), obj[key]);
|
|
381
|
+
}
|
|
816
382
|
if (obj[key] && !alreadySeen.includes(obj[key]))
|
|
817
383
|
alreadySeen.push(obj[key]);
|
|
818
|
-
if (isRef)
|
|
384
|
+
if (isRef)
|
|
819
385
|
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);
|
|
386
|
+
const val = obj[key];
|
|
387
|
+
if (val && typeof val === "object") {
|
|
388
|
+
const nestedProto = Object.getPrototypeOf(val);
|
|
389
|
+
if (nestedProto && !alreadySeen.includes(val)) {
|
|
390
|
+
alreadySeen.push(val);
|
|
391
|
+
await this.loadForceReferences(val, nestedProto, alreadySeen);
|
|
392
|
+
}
|
|
831
393
|
}
|
|
832
394
|
}
|
|
833
395
|
}
|
|
834
396
|
async handleLoad(obj, key, alreadySeen) {
|
|
835
|
-
if (!this.parentManager)
|
|
836
|
-
throw new Error("No manager");
|
|
837
397
|
const refIds = Array.isArray(obj[key]) ? obj[key] : [obj[key]];
|
|
838
398
|
for (const refId of refIds) {
|
|
839
399
|
if (refId) {
|
|
840
400
|
const idStr = refId.toString();
|
|
841
|
-
|
|
842
|
-
if (
|
|
843
|
-
const
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
401
|
+
let result = globalCache.objects[idStr]?.object;
|
|
402
|
+
if (!result) {
|
|
403
|
+
for (const manager of Object.values(this.parentManager.managers)) {
|
|
404
|
+
result = manager.getObject(idStr);
|
|
405
|
+
if (result)
|
|
406
|
+
break;
|
|
847
407
|
}
|
|
848
|
-
continue;
|
|
849
408
|
}
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
alreadySeen.push(idStr);
|
|
854
|
-
await result.loadForceReferences(undefined, undefined, alreadySeen);
|
|
855
|
-
break;
|
|
856
|
-
}
|
|
409
|
+
if (result && !alreadySeen.includes(idStr)) {
|
|
410
|
+
alreadySeen.push(idStr);
|
|
411
|
+
await result.loadForceReferences(undefined, undefined, alreadySeen);
|
|
857
412
|
}
|
|
858
413
|
}
|
|
859
414
|
}
|
|
860
415
|
}
|
|
861
416
|
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());
|
|
417
|
+
if (pointer.length !== 2)
|
|
418
|
+
return;
|
|
419
|
+
const parentId = parent._id?.toString() ?? parent.toString();
|
|
420
|
+
const obj = this.parentManager.managers[pointer[0]]?.getObject(parentId);
|
|
876
421
|
if (!obj)
|
|
877
422
|
return;
|
|
878
423
|
const val = obj.getValue(pointer[1]);
|
|
424
|
+
const myId = this.data._id.toString();
|
|
879
425
|
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");
|
|
426
|
+
const ids = val.map(v => v._id?.toString() ?? v.toString());
|
|
427
|
+
if (!ids.includes(myId)) {
|
|
428
|
+
await obj.setValue__(pointer[1], [...val, myId], true, false, true);
|
|
889
429
|
}
|
|
890
|
-
|
|
430
|
+
else {
|
|
891
431
|
await obj.contactChildren();
|
|
892
|
-
|
|
893
|
-
await obj.setValue__(pointer[1], [
|
|
894
|
-
...new Set([...filtred, this.data._id]),
|
|
895
|
-
], true, false, true);
|
|
432
|
+
}
|
|
896
433
|
}
|
|
897
|
-
else if (val?.toString()
|
|
434
|
+
else if ((val?._id?.toString() ?? val?.toString()) === myId) {
|
|
898
435
|
await obj.contactChildren();
|
|
899
|
-
|
|
900
|
-
|
|
436
|
+
}
|
|
437
|
+
else {
|
|
438
|
+
await obj.setValue__(pointer[1], myId, true, false, true);
|
|
439
|
+
}
|
|
901
440
|
}
|
|
902
441
|
async destroy(once = false) {
|
|
903
|
-
if (!once)
|
|
442
|
+
if (!once)
|
|
904
443
|
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
|
-
});
|
|
444
|
+
return new Promise((resolve) => {
|
|
445
|
+
this.socket.emit(EVENT_DELETE + this.className, this.data._id, (res) => {
|
|
446
|
+
resolve({ success: res.success, message: res.message ?? "" });
|
|
924
447
|
});
|
|
925
448
|
});
|
|
926
|
-
return res;
|
|
927
449
|
}
|
|
928
450
|
async checkForMissingRefs() {
|
|
929
451
|
for (const prop of this.properties) {
|
|
930
|
-
|
|
452
|
+
const pointer = getMetadataRecursive("refsTo", this, prop.toString());
|
|
931
453
|
if (pointer) {
|
|
932
|
-
|
|
933
|
-
if (
|
|
934
|
-
|
|
935
|
-
await this.findMissingObjectReference(prop, pointer);
|
|
454
|
+
const parts = pointer.split(":");
|
|
455
|
+
if (parts.length === 2)
|
|
456
|
+
await this.findMissingObjectReference(prop, parts);
|
|
936
457
|
}
|
|
937
458
|
}
|
|
938
459
|
}
|
|
939
460
|
async findMissingObjectReference(prop, pointer) {
|
|
940
|
-
if (this.checkedMissingRefs
|
|
461
|
+
if (this.checkedMissingRefs)
|
|
941
462
|
return;
|
|
942
463
|
this.checkedMissingRefs = true;
|
|
943
464
|
const ac = this.parentManager.managers[pointer[0]];
|
|
944
465
|
if (!ac)
|
|
945
|
-
|
|
466
|
+
return;
|
|
946
467
|
const targetId = this.data._id.toString();
|
|
947
|
-
const pointerKey = pointer[1];
|
|
948
|
-
const isNested = pointerKey.includes(".");
|
|
949
|
-
const pathParts = isNested ? pointerKey.split(".") : [];
|
|
950
468
|
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
469
|
for (const obj of allObjects) {
|
|
956
|
-
if (!obj.isLoaded)
|
|
470
|
+
if (!obj.isLoaded)
|
|
957
471
|
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
|
-
}
|
|
472
|
+
const val = obj.getValue(pointer[1]);
|
|
971
473
|
if (!val)
|
|
972
474
|
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) {
|
|
475
|
+
const ids = Array.isArray(val) ? val.map(v => v._id?.toString() ?? v.toString()) : [val._id?.toString() ?? val.toString()];
|
|
476
|
+
if (ids.includes(targetId)) {
|
|
991
477
|
this.data[prop] = obj._id;
|
|
992
478
|
return;
|
|
993
479
|
}
|
|
994
480
|
}
|
|
995
481
|
}
|
|
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
482
|
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
483
|
for (const prop of this.properties) {
|
|
1035
|
-
const pointer = getMetadataRecursive("refsTo", this, prop.toString());
|
|
1036
484
|
const isRef = getMetadataRecursive("isRef", this, prop.toString());
|
|
485
|
+
const pointer = getMetadataRecursive("refsTo", this, prop.toString());
|
|
1037
486
|
if (!isRef || pointer)
|
|
1038
487
|
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
|
-
}
|
|
488
|
+
const obj = this.getValue(prop);
|
|
1052
489
|
if (!obj)
|
|
1053
490
|
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}`);
|
|
491
|
+
const children = Array.isArray(obj) ? obj : [obj];
|
|
492
|
+
for (const child of children) {
|
|
493
|
+
if (child && typeof child.loadMissingReferences === "function") {
|
|
494
|
+
await child.loadMissingReferences();
|
|
1075
495
|
}
|
|
1076
496
|
}
|
|
1077
497
|
}
|
|
1078
498
|
}
|
|
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
499
|
}
|
|
1088
|
-
export function processIsRefProperties(instance, target, prefix
|
|
500
|
+
export function processIsRefProperties(instance, target, prefix, allProps, newData, loggers) {
|
|
1089
501
|
const props = Reflect.getMetadata("props", target) || [];
|
|
1090
502
|
for (const prop of props) {
|
|
1091
503
|
const path = prefix ? `${prefix}.${prop}` : prop;
|
|
@@ -1096,19 +508,17 @@ export function processIsRefProperties(instance, target, prefix = null, allProps
|
|
|
1096
508
|
if (Reflect.getMetadata("isRef", target, prop)) {
|
|
1097
509
|
if (Array.isArray(instance[prop]))
|
|
1098
510
|
newData[prop] = instance[prop]
|
|
1099
|
-
.map((item) => item?._id?.toString() ?? item?.toString()
|
|
511
|
+
.map((item) => item?._id?.toString() ?? item?.toString())
|
|
1100
512
|
.filter(Boolean);
|
|
1101
513
|
else
|
|
1102
514
|
newData[prop] =
|
|
1103
|
-
instance[prop]?._id?.toString() ??
|
|
1104
|
-
instance[prop]?.toString() ??
|
|
1105
|
-
undefined;
|
|
515
|
+
instance[prop]?._id?.toString() ?? instance[prop]?.toString();
|
|
1106
516
|
}
|
|
1107
517
|
const type = Reflect.getMetadata("design:type", target, prop);
|
|
1108
518
|
if (type?.prototype) {
|
|
1109
519
|
const nestedProps = Reflect.getMetadata("props", type.prototype);
|
|
1110
520
|
if (nestedProps && instance[prop]) {
|
|
1111
|
-
newData[prop] = processIsRefProperties(instance[prop], type.prototype, path, allProps,
|
|
521
|
+
newData[prop] = processIsRefProperties(instance[prop], type.prototype, path, allProps, {}, loggers).newData;
|
|
1112
522
|
}
|
|
1113
523
|
}
|
|
1114
524
|
}
|