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