@prestizni-software/client-dem 0.4.102 → 0.4.103

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