@prestizni-software/client-dem 0.4.101 → 0.4.103

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