@prestizni-software/client-dem 0.4.102 → 0.4.104

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,25 +2,17 @@ import "reflect-metadata";
2
2
  import _ from "lodash";
3
3
  import { EVENT_INTERNAL_PRE_LOADED, EVENT_DELETE, EVENT_GET, EVENT_NEW, EVENT_UPDATE, globalCache, } from "./CommonTypes.js";
4
4
  import { ObjectId } from "bson";
5
- import { stringSimilarity } from "string-similarity-js";
6
5
  export class AutoUpdatedClientObject {
7
- //From server type for overlapping keys
8
6
  entry;
9
7
  preLoad;
10
8
  registerSocket;
11
9
  readyLoggers;
12
10
  loadFromDB(a) { }
13
11
  setValue_(a, b) { }
14
- //-*--
15
12
  socket;
16
13
  data;
17
14
  isServer = false;
18
- loggers = {
19
- info: () => { },
20
- debug: () => { },
21
- error: () => { },
22
- warn: () => { },
23
- };
15
+ loggers;
24
16
  isLoading = true;
25
17
  isLoadingReferences = true;
26
18
  checkedMissingRefs = false;
@@ -33,36 +25,21 @@ export class AutoUpdatedClientObject {
33
25
  toChangeOnParents = [];
34
26
  callbacks;
35
27
  loadReferencesAsync = async () => {
36
- if (this.isLoaded) {
37
- try {
38
- this.generateSettersAndGetters();
39
- await this.loadForceReferences();
40
- for (const thing of this.toChangeOnParents) {
41
- await this.setValue__(thing.key, thing.value, true, false, true);
42
- }
43
- }
44
- catch (error) {
45
- this.loggers.error("Error loading references");
46
- this.loggers.error(error.message);
47
- this.loggers.error(error.stack);
48
- }
49
- this.isLoadingReferences = false;
50
- return;
51
- }
52
- await this.waitForPreloaded();
53
- this.generateSettersAndGetters();
54
28
  try {
29
+ if (!this.isLoaded) {
30
+ await this.waitForPreloaded();
31
+ }
32
+ this.generateSettersAndGetters();
55
33
  await this.loadForceReferences();
56
34
  for (const thing of this.toChangeOnParents) {
57
35
  await this.setValue__(thing.key, thing.value, true, false, true);
58
36
  }
59
- this.isLoadingReferences = false;
60
37
  }
61
38
  catch (error) {
39
+ this.loggers.error("Error loading references: " + error.message);
40
+ }
41
+ finally {
62
42
  this.isLoadingReferences = false;
63
- this.loggers.error("Error loading references");
64
- this.loggers.error(error.message);
65
- this.loggers.error(error.stack);
66
43
  }
67
44
  };
68
45
  /** @deprecated Use loadReferencesAsync instead */
@@ -74,7 +51,6 @@ export class AutoUpdatedClientObject {
74
51
  !loggers ||
75
52
  !className ||
76
53
  !parentManager ||
77
- !callback ||
78
54
  !emitter) {
79
55
  this.classParam = classParam;
80
56
  this.socket = socket;
@@ -91,16 +67,13 @@ export class AutoUpdatedClientObject {
91
67
  !loggers &&
92
68
  !className &&
93
69
  !parentManager &&
94
- !callback &&
95
70
  !emitter)
96
71
  return;
97
72
  else
98
73
  throw new Error("Missing arguments???");
99
74
  }
100
75
  this.classParam = classParam;
101
- if (typeof data !== "string" && data._id) {
102
- processIsRefProperties(data, this, undefined, [], loggers);
103
- }
76
+ this.socket = socket;
104
77
  this.isServer = isServer;
105
78
  this.emitter = emitter;
106
79
  this.isLoadingReferences = true;
@@ -109,53 +82,19 @@ export class AutoUpdatedClientObject {
109
82
  this.className = className;
110
83
  this.properties = Reflect.getMetadata("props", classParam.prototype);
111
84
  this.callbacks = callback;
112
- this.loggers.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;
85
+ this.loggers = {
86
+ debug: (s) => loggers.debug(`[DEM - ${this.className}: ${this.data?._id ?? "not loaded"}] ${s}`),
87
+ info: (s) => loggers.info(`[DEM - ${this.className}: ${this.data?._id ?? "not loaded"}] ${s}`),
88
+ warn: (s) => loggers.warn(`[DEM - ${this.className}: ${this.data?._id ?? "not loaded"}] ${s}`),
89
+ error: (s) => loggers.error(`[DEM - ${this.className}: ${this.data?._id ?? "not loaded"}] ${s}`),
90
+ };
145
91
  if (typeof data === "string") {
92
+ this.data = { _id: data };
146
93
  if (this.isServer) {
147
94
  this.isLoading = false;
148
- this.data = { _id: data };
95
+ this.generateSettersAndGetters();
149
96
  return;
150
97
  }
151
- if (!data || data === "" || data === "undefined") {
152
- this.loggers.error("Cannot create a new AutoUpdatedClientClass with an empty string for ID. Data typeof: " +
153
- typeof data +
154
- " Data: " +
155
- data);
156
- throw new Error("Cannot create a new AutoUpdatedClientClass with an empty string for ID.");
157
- }
158
- this.loggers.debug("Getting new object from server " + this.className + " - " + data);
159
98
  this.socket.emit(EVENT_GET + this.className + data, null, (res) => {
160
99
  if (!res.success) {
161
100
  this.isLoading = false;
@@ -169,54 +108,43 @@ export class AutoUpdatedClientObject {
169
108
  this.emitter.emit(EVENT_INTERNAL_PRE_LOADED + this.EmitterID);
170
109
  this.openSockets();
171
110
  });
172
- this.data = { _id: data };
173
111
  }
174
112
  else {
175
113
  this.isLoading = true;
176
114
  this.data = data;
177
- const 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);
115
+ for (const key of this.properties || []) {
182
116
  const isRef = getMetadataRecursive("isRef", this, key);
183
- if (isRef) {
117
+ if (isRef && this.data[key]) {
184
118
  if (Array.isArray(this.data[key])) {
185
119
  this.data[key] = this.data[key].map((obj) => obj._id?.toString() ?? obj?.toString());
186
120
  }
187
121
  else {
188
- this.data[key] =
189
- this.data[key]?._id?.toString() ??
190
- this.data[key]?.toString();
122
+ this.data[key] = this.data[key]?._id?.toString() ?? this.data[key]?.toString();
191
123
  }
192
124
  }
193
125
  }
194
- if (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)
126
+ if ((!this.data._id || this.data._id === "") && !this.isServer) {
203
127
  this.handleNewObject(data);
128
+ }
204
129
  else {
205
130
  this.isLoading = false;
206
131
  if (!this.isServer)
207
132
  this.openSockets();
208
133
  }
209
134
  }
210
- this.generateSettersAndGetters();
135
+ // We use a microtask to ensure subclass field initializers have finished
136
+ // before we apply our getters. This is critical for TypeScript subclasses.
137
+ Promise.resolve().then(() => {
138
+ this.generateSettersAndGetters();
139
+ });
211
140
  }
212
141
  async waitForPreloaded() {
213
142
  if (this.isLoaded)
214
143
  return;
215
144
  await new Promise((resolve, reject) => {
216
145
  this.emitter.once(EVENT_INTERNAL_PRE_LOADED + this.EmitterID, (failed, reason) => {
217
- if (failed) {
146
+ if (failed)
218
147
  reject(new Error(reason));
219
- }
220
148
  else
221
149
  resolve();
222
150
  });
@@ -224,31 +152,6 @@ export class AutoUpdatedClientObject {
224
152
  }
225
153
  handleNewObject(data) {
226
154
  this.isLoading = true;
227
- if (!this.className)
228
- throw new Error("Cannot create a new AutoUpdatedClientClass without a class name.");
229
- this.loggers.debug(this.className + " - Requesting new object creation on server");
230
- if (this.isServer)
231
- for (const key of this.properties) {
232
- if (typeof key !== "string")
233
- continue;
234
- let pointer = getMetadataRecursive("refsTo", this, key);
235
- if (pointer) {
236
- pointer = pointer.split(":");
237
- if (pointer.length != 2)
238
- throw new Error("population ref incorrectly defined. Sould be 'ParentClass:PropName.Path'");
239
- const temp = data[key];
240
- delete data[key];
241
- if (temp)
242
- this.toChangeOnParents.push({ key: key, value: temp });
243
- }
244
- }
245
- try {
246
- data = _.cloneDeep(data);
247
- }
248
- catch (error) {
249
- this.loggers.error("Most likely cycled object: " + error.message);
250
- this.loggers.error(error.stack);
251
- }
252
155
  this.socket.emit(EVENT_NEW + this.className, data, (res) => {
253
156
  if (!res.success) {
254
157
  this.isLoading = false;
@@ -259,7 +162,6 @@ export class AutoUpdatedClientObject {
259
162
  this.data = res.data;
260
163
  this.generateSettersAndGetters();
261
164
  this.isLoading = false;
262
- this.loggers.debug("Created new object: " + this.data._id);
263
165
  this.emitter.emit(EVENT_INTERNAL_PRE_LOADED + this.EmitterID);
264
166
  if (!this.isServer)
265
167
  this.openSockets();
@@ -286,27 +188,18 @@ export class AutoUpdatedClientObject {
286
188
  const res = await this.handleUpdateRequest(update);
287
189
  if (ack && typeof ack === "function")
288
190
  ack(res);
289
- return res;
290
191
  });
291
192
  }
292
193
  async handleUpdateRequest(update) {
293
194
  try {
294
195
  await this.setValue__(update.key, update.value, true);
295
- this.loggers.debug(`Applied patch ${update.key} set to ${JSON.stringify(update.value)}`);
296
- // Return success with the applied patch
297
196
  if (this.isLoaded)
298
197
  this.callbacks.update(this, update.key);
299
198
  return { success: true, data: undefined, message: "" };
300
199
  }
301
200
  catch (error) {
302
- this.loggers.error(`[${this.data._id}] Error applying patch: ` +
303
- error.message +
304
- "\n" +
305
- error.stack);
306
- return {
307
- success: false,
308
- message: "Error applying update: " + error.message,
309
- };
201
+ this.loggers.error(`[${this.data._id}] Error applying patch: ${error.message}`);
202
+ return { success: false, message: "Error applying update: " + error.message };
310
203
  }
311
204
  }
312
205
  generateSettersAndGetters() {
@@ -315,35 +208,56 @@ export class AutoUpdatedClientObject {
315
208
  for (const key of this.properties) {
316
209
  if (typeof key !== "string")
317
210
  continue;
318
- const k = key;
319
211
  const isRef = getMetadataRecursive("isRef", this, key);
212
+ // CRITICAL: Overwrite any instance property to ensure the getter is used.
213
+ delete this[key];
320
214
  Object.defineProperty(this, key, {
321
215
  get: () => {
322
- 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;
216
+ const val = this.data ? this.data[key] : undefined;
217
+ if (isRef && val) {
218
+ if (Array.isArray(val)) {
219
+ return val.map((id) => this.findReference(id, key)).filter(Boolean);
328
220
  }
329
221
  else {
330
- return this.findReference(this.data[k], key);
222
+ return this.findReference(val, key);
331
223
  }
332
224
  }
333
- return this.data[k];
225
+ return val;
334
226
  },
335
227
  enumerable: true,
336
228
  configurable: true,
337
229
  });
338
230
  }
339
231
  }
232
+ getValue(key_) {
233
+ const key = key_;
234
+ const parts = key.split(".");
235
+ let value = this;
236
+ for (const part of parts) {
237
+ if (value === undefined || value === null)
238
+ return undefined;
239
+ // Try instance first (getters), then fallback to data
240
+ const nextValue = value[part];
241
+ if (nextValue !== undefined) {
242
+ value = nextValue;
243
+ }
244
+ else if (value.data && value.data[part] !== undefined) {
245
+ value = value.data[part];
246
+ }
247
+ else {
248
+ return undefined;
249
+ }
250
+ }
251
+ return value;
252
+ }
340
253
  findReference(id, key) {
341
- if (typeof id !== "string" && !ObjectId.isValid(id))
342
- return id;
254
+ if (!id)
255
+ return undefined;
256
+ const idStr = id.toString();
343
257
  if (this.parentManager.cache.references[key])
344
- return this.parentManager.cache.references[key].getObject(id.toString());
258
+ return this.parentManager.cache.references[key].getObject(idStr);
345
259
  for (const manager of Object.values(this.parentManager.managers)) {
346
- const result = manager.getObject(id.toString());
260
+ const result = manager.getObject(idStr);
347
261
  if (result) {
348
262
  this.parentManager.cache.references[key] = manager;
349
263
  return result;
@@ -352,326 +266,80 @@ export class AutoUpdatedClientObject {
352
266
  return undefined;
353
267
  }
354
268
  async setValue(key, val) {
355
- const result = await this.setValue__(key, val);
356
- return result;
269
+ return await this.setValue__(key, val);
357
270
  }
358
271
  async setValue__(key, val, silent = false, noGet = false, noUpdate = false) {
359
- let message = "Setting value " + key + " of " + this.className + " to ";
360
- const isRef = getMetadataRecursive("isRef", this, key);
361
- if (isRef)
362
- val = Array.isArray(val)
363
- ? val.map((v) => {
364
- return v._id?.toString() ?? v;
365
- })
366
- : (val?._id ?? val);
367
272
  try {
368
- 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)) {
273
+ const isRef = getMetadataRecursive("isRef", this, key);
274
+ let valueToStore = val;
275
+ if (isRef) {
276
+ valueToStore = Array.isArray(val)
277
+ ? val.map((v) => v._id?.toString() ?? v.toString())
278
+ : (val?._id ?? val?.toString() ?? val);
279
+ }
280
+ // Optimization: skip if value hasn't changed
281
+ const currentVal = this.getValue(key);
282
+ const currentValId = Array.isArray(currentVal)
283
+ ? currentVal.map((v) => v._id?.toString() ?? v.toString())
284
+ : (currentVal?._id ?? currentVal?.toString());
285
+ if (_.isEqual(currentValId, valueToStore))
404
286
  return { success: true, msg: "" };
405
- }
406
287
  const path = key.split(".");
407
- 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()));
288
+ if (path.length > 1) {
289
+ // Deep path handling for references
290
+ let obj = this.data;
291
+ for (let i = 0; i < path.length - 1; i++) {
292
+ if (typeof obj[path[i]] === 'string' || ObjectId.isValid(obj[path[i]])) {
293
+ const ref = await this.resolveReference(obj[path[i]].toString());
294
+ if (!ref)
295
+ throw new Error("Could not resolve reference on path: " + key);
296
+ return await ref.setValue(path.slice(i + 1).join("."), val);
416
297
  }
417
- catch (error) {
418
- message +=
419
- "\n Error: likely undefined property on path: " +
420
- path +
421
- " on index: " +
422
- i +
423
- " with error: " +
424
- error.message;
425
- }
426
- if (!temp) {
427
- message +=
428
- "\nLikely undefined property " +
429
- path[i] +
430
- " on path: " +
431
- path +
432
- " at index: " +
433
- i;
434
- this.loggers.warn("Failed to set value for " + this.className + "\n" + message);
435
- return {
436
- success: false,
437
- msg: message,
438
- };
439
- }
440
- lastClass = temp;
441
- lastPath = path.slice(i + 1).join(".");
442
- const res = await lastClass.setValue(lastPath, val);
443
- if (!noUpdate)
444
- await this.onUpdate(noUpdate);
445
- return res;
446
- }
447
- else
448
298
  obj = obj[path[i]];
449
- }
450
- if (lastClass !== this) {
451
- message +=
452
- "\n What the actual fuckity fuck error on path: " +
453
- path +
454
- " on index: " +
455
- (path.length - 1);
456
- this.loggers.error("Failed to set value for " + this.className + "\n" + message);
457
- return {
458
- success: false,
459
- msg: message,
460
- };
461
- }
462
- if (!this.properties.includes(lastPath) && !lastPath.includes(".")) {
463
- let nearest = "";
464
- for (const prop of this.properties) {
465
- if (typeof prop !== "string")
466
- continue;
467
- if (stringSimilarity(lastPath, prop) > 0 &&
468
- stringSimilarity(lastPath, prop) > stringSimilarity(nearest, prop)) {
469
- nearest = prop;
470
- }
471
299
  }
472
- throw new Error(`Property ${lastPath} not found in class ${this.className}, did you mean ${nearest ?? "--No similar prop found--"}?`);
473
300
  }
474
- 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
- }
301
+ const res = await this.setValueInternal(key, valueToStore, silent, noUpdate);
302
+ if (res.success) {
303
+ // Apply update to this.data (handling deep paths)
304
+ const pathArr = key.split(".");
305
+ let obj = this.data;
306
+ for (let i = 0; i < pathArr.length - 1; i++) {
307
+ obj = obj[pathArr[i]];
568
308
  }
569
- 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;
309
+ obj[pathArr[pathArr.length - 1]] = valueToStore;
310
+ await this.findAndLoadReferences(key, valueToStore);
311
+ if (isRef && this.parentManager.isLoaded)
312
+ await this.contactChildren();
313
+ if (this.isLoaded)
314
+ this.callbacks.update(this, key);
597
315
  }
598
- if (!success) {
599
- this.loggers.warn("Failed to set value for " + this.className + "\n" + message);
600
- return { success: false, msg: message };
601
- }
602
- const pathArr = lastPath.split(".");
603
- if (pathArr.length === 1) {
604
- this.data[key] = val;
605
- }
606
- else {
607
- const last = pathArr.splice(-1, 1);
608
- let ref = this;
609
- for (const p of pathArr) {
610
- ref = ref[p];
611
- }
612
- ref[last.at(-1)] = val;
613
- }
614
- if (!noUpdate)
615
- await this.onUpdate(noUpdate);
616
- await this.findAndLoadReferences(lastPath, val);
617
- const isRef = getMetadataRecursive("isRef", this, path.at(-1));
618
- if (isRef && this.parentManager.isLoaded) {
619
- await this.contactChildren();
620
- }
621
- if (this.isLoaded)
622
- this.callbacks.update(this, key);
623
- return {
624
- success: true,
625
- msg: "Successfully set " + key + " to " + val,
626
- };
316
+ return res;
627
317
  }
628
318
  catch (error) {
629
- this.loggers.error("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
- };
319
+ this.loggers.error(`Error setting value ${key}: ${error.message}`);
320
+ return { success: false, msg: error.message };
646
321
  }
647
322
  }
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
- ];
323
+ async setValueInternal(key, value, silent = false, noUpdate = false) {
324
+ if (silent) {
325
+ return { success: true, msg: "Silent" };
660
326
  }
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
- ];
327
+ return new Promise((resolve) => {
328
+ this.socket.emit(EVENT_UPDATE + this.className + this.data._id, { _id: this.data._id.toString(), key, value }, (res) => {
329
+ resolve({ success: res.success, msg: res.message ?? (res.success ? "Success" : "Error") });
330
+ });
331
+ });
332
+ }
333
+ makeUpdate(key, value) {
334
+ return { _id: this.data._id.toString(), key, value };
335
+ }
336
+ async resolveReference(id) {
337
+ for (const manager of Object.values(this.parentManager.managers)) {
338
+ const obj = manager.getObject(id);
339
+ if (obj)
340
+ return obj;
673
341
  }
674
- return { res, val };
342
+ return null;
675
343
  }
676
344
  async findAndLoadReferences(lastPath, value) {
677
345
  const isRef = getMetadataRecursive("isRef", this, lastPath);
@@ -685,121 +353,21 @@ export class AutoUpdatedClientObject {
685
353
  if (result)
686
354
  break;
687
355
  }
688
- if (!result) {
689
- for (const manager of Object.values(this.parentManager.managers)) {
690
- try {
691
- result = await manager.handleGetMissingObject(id?.toString());
692
- if (result)
693
- break;
694
- }
695
- catch (error) {
696
- const _ = error;
697
- }
698
- }
699
- if (!result) {
700
- this.loggers.warn("Failed to update childerns parent for " +
701
- this.className +
702
- " updating " +
703
- id +
704
- "'s parent to " +
705
- this.data._id);
706
- continue;
707
- }
708
- }
709
356
  if (result && typeof result.loadMissingReferences === "function") {
710
357
  await result.loadMissingReferences();
711
358
  }
712
- else if (result) {
713
- this.loggers.warn(`Object in reference resolution is missing loadMissingReferences function. Type: ${typeof result}, ID: ${result._id ?? result}`);
714
- }
715
- }
716
- }
717
- }
718
- getValue(key_) {
719
- const key = key_;
720
- if (!key.includes(".")) {
721
- let val = this[key];
722
- val ??= this.data[key];
723
- return val;
724
- }
725
- let value;
726
- const parts = key.split(".");
727
- for (const part of parts) {
728
- try {
729
- if (value !== undefined && value !== null) {
730
- value = value[part];
731
- }
732
- else {
733
- value = this[part];
734
- }
735
- }
736
- catch (error) {
737
- this.loggers.error(`Error getting value for ${this.className} on key ${key} at index ${part}: ${error.message}`);
738
- return undefined;
739
- }
740
- }
741
- return value;
742
- }
743
- async setValueInternal(key, value, silent = false, noUpdate = false) {
744
- const update = this.makeUpdate(key, value);
745
- const promise = new Promise((resolve) => {
746
- if (silent) {
747
- if (noUpdate)
748
- return resolve({ success: true, msg: "Success - no update" });
749
- return this.onUpdate(true).then(() => {
750
- if (this.isLoaded)
751
- this.callbacks.update(this, key);
752
- return resolve({ success: true, msg: "Success - silent" });
753
- });
754
- }
755
- try {
756
- this.socket.emit(EVENT_UPDATE + this.className + this.data._id, update, async (res) => {
757
- if (!res.success) {
758
- this.loggers.error("Error sending update: " + res.message);
759
- resolve({ success: false, msg: res.message });
760
- return;
761
- }
762
- if (!noUpdate)
763
- await this.onUpdate(noUpdate);
764
- resolve({
765
- success: res.success,
766
- msg: res.message ?? "Success",
767
- });
768
- });
769
- }
770
- catch (error) {
771
- this.loggers.error("Error sending update:" + error.message);
772
- this.loggers.error(error.stack);
773
- resolve({ success: false, msg: error.message });
774
359
  }
775
- });
776
- return promise;
777
- }
778
- makeUpdate(key, value) {
779
- try {
780
- const id = this.data._id.toString();
781
- return { _id: id, key, value };
782
- }
783
- catch (error) {
784
- this.loggers.error("Probably missing the fucking identifier ['_id'] again: " +
785
- error.message);
786
- throw error;
787
360
  }
788
361
  }
789
- async 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;
362
+ async wipeSelf() {
363
+ if (this.data.Wiped)
364
+ return;
365
+ const _id = this.data._id.toString();
366
+ for (const key of Object.keys(this.data)) {
367
+ delete this.data[key];
801
368
  }
802
- return null;
369
+ this.data = { Wiped: true };
370
+ this.loggers.info(`[${_id}] ${this.className} object wiped`);
803
371
  }
804
372
  async loadForceReferences(obj = this.data, proto = this, alreadySeen = []) {
805
373
  const props = Reflect.getMetadata("props", proto) || [];
@@ -808,284 +376,128 @@ export class AutoUpdatedClientObject {
808
376
  continue;
809
377
  const isRef = Reflect.getMetadata("isRef", proto, key);
810
378
  const pointer = Reflect.getMetadata("refsTo", proto, key);
811
- if (pointer &&
812
- obj === this.data &&
813
- obj[key] &&
814
- !alreadySeen.includes(obj))
379
+ if (pointer && obj === this.data && obj[key] && !alreadySeen.includes(obj)) {
815
380
  await this.createdWithParent(pointer.split(":"), obj[key]);
381
+ }
816
382
  if (obj[key] && !alreadySeen.includes(obj[key]))
817
383
  alreadySeen.push(obj[key]);
818
- if (isRef) {
384
+ if (isRef)
819
385
  await this.handleLoad(obj, key, alreadySeen);
820
- }
821
- 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);
386
+ const val = obj[key];
387
+ if (val && typeof val === "object") {
388
+ const nestedProto = Object.getPrototypeOf(val);
389
+ if (nestedProto && !alreadySeen.includes(val)) {
390
+ alreadySeen.push(val);
391
+ await this.loadForceReferences(val, nestedProto, alreadySeen);
392
+ }
831
393
  }
832
394
  }
833
395
  }
834
396
  async handleLoad(obj, key, alreadySeen) {
835
- if (!this.parentManager)
836
- throw new Error("No manager");
837
397
  const refIds = Array.isArray(obj[key]) ? obj[key] : [obj[key]];
838
398
  for (const refId of refIds) {
839
399
  if (refId) {
840
400
  const idStr = refId.toString();
841
- // 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);
401
+ let result = globalCache.objects[idStr]?.object;
402
+ if (!result) {
403
+ for (const manager of Object.values(this.parentManager.managers)) {
404
+ result = manager.getObject(idStr);
405
+ if (result)
406
+ break;
847
407
  }
848
- continue;
849
408
  }
850
- 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
- }
409
+ if (result && !alreadySeen.includes(idStr)) {
410
+ alreadySeen.push(idStr);
411
+ await result.loadForceReferences(undefined, undefined, alreadySeen);
857
412
  }
858
413
  }
859
414
  }
860
415
  }
861
416
  async createdWithParent(pointer, parent) {
862
- if (pointer.length !== 2) {
863
- 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());
417
+ if (pointer.length !== 2)
418
+ return;
419
+ const parentId = parent._id?.toString() ?? parent.toString();
420
+ const obj = this.parentManager.managers[pointer[0]]?.getObject(parentId);
876
421
  if (!obj)
877
422
  return;
878
423
  const val = obj.getValue(pointer[1]);
424
+ const myId = this.data._id.toString();
879
425
  if (Array.isArray(val)) {
880
- const 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");
426
+ const ids = val.map(v => v._id?.toString() ?? v.toString());
427
+ if (!ids.includes(myId)) {
428
+ await obj.setValue__(pointer[1], [...val, myId], true, false, true);
889
429
  }
890
- if (filtred.map((id) => id?._id?.toString()).includes(this.data._id))
430
+ else {
891
431
  await obj.contactChildren();
892
- else
893
- await obj.setValue__(pointer[1], [
894
- ...new Set([...filtred, this.data._id]),
895
- ], true, false, true);
432
+ }
896
433
  }
897
- else if (val?.toString() === this.data?._id?.toString())
434
+ else if ((val?._id?.toString() ?? val?.toString()) === myId) {
898
435
  await obj.contactChildren();
899
- else
900
- await obj.setValue__(pointer[1], this.data?._id?.toString(), true, false, true);
436
+ }
437
+ else {
438
+ await obj.setValue__(pointer[1], myId, true, false, true);
439
+ }
901
440
  }
902
441
  async destroy(once = false) {
903
- if (!once) {
442
+ if (!once)
904
443
  return await this.parentManager.deleteObject(this.data._id);
905
- }
906
- 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
- });
444
+ return new Promise((resolve) => {
445
+ this.socket.emit(EVENT_DELETE + this.className, this.data._id, (res) => {
446
+ resolve({ success: res.success, message: res.message ?? "" });
924
447
  });
925
448
  });
926
- return res;
927
449
  }
928
450
  async checkForMissingRefs() {
929
451
  for (const prop of this.properties) {
930
- let pointer = getMetadataRecursive("refsTo", this, prop.toString());
452
+ const pointer = getMetadataRecursive("refsTo", this, prop.toString());
931
453
  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);
454
+ const parts = pointer.split(":");
455
+ if (parts.length === 2)
456
+ await this.findMissingObjectReference(prop, parts);
936
457
  }
937
458
  }
938
459
  }
939
460
  async findMissingObjectReference(prop, pointer) {
940
- if (this.checkedMissingRefs && this.isLoadingReferences)
461
+ if (this.checkedMissingRefs)
941
462
  return;
942
463
  this.checkedMissingRefs = true;
943
464
  const ac = this.parentManager.managers[pointer[0]];
944
465
  if (!ac)
945
- throw new Error(`No AutoUpdateManager found for class ${pointer[0]}`);
466
+ return;
946
467
  const targetId = this.data._id.toString();
947
- const pointerKey = pointer[1];
948
- const isNested = pointerKey.includes(".");
949
- const pathParts = isNested ? pointerKey.split(".") : [];
950
468
  const allObjects = Object.values(ac.objects);
951
- const pendingObjects = allObjects.filter(obj => !obj.isLoaded);
952
- if (pendingObjects.length > 0) {
953
- await Promise.all(pendingObjects.map(obj => obj.waitForPreloaded()));
954
- }
955
469
  for (const obj of allObjects) {
956
- if (!obj.isLoaded) {
470
+ if (!obj.isLoaded)
957
471
  await obj.waitForPreloaded();
958
- }
959
- let val;
960
- if (isNested) {
961
- val = obj;
962
- for (const element of pathParts) {
963
- if (!val)
964
- break;
965
- val = val[element] ?? val.data?.[element];
966
- }
967
- }
968
- else {
969
- val = obj[pointerKey] ?? obj.data?.[pointerKey];
970
- }
472
+ const val = obj.getValue(pointer[1]);
971
473
  if (!val)
972
474
  continue;
973
- 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) {
475
+ const ids = Array.isArray(val) ? val.map(v => v._id?.toString() ?? v.toString()) : [val._id?.toString() ?? val.toString()];
476
+ if (ids.includes(targetId)) {
991
477
  this.data[prop] = obj._id;
992
478
  return;
993
479
  }
994
480
  }
995
481
  }
996
- async wipeSelf() {
997
- if (this.data.Wiped)
998
- return;
999
- const _id = this.data._id.toString();
1000
- for (const key of Object.keys(this.data)) {
1001
- delete this.data[key];
1002
- }
1003
- this.data = { Wiped: true };
1004
- this.loggers.info(`[${_id}] ${this.className} object wiped`);
1005
- }
1006
482
  async contactChildren() {
1007
- let childsManager = null;
1008
- const findMissingObjectWithoutKnownManager = async (o) => {
1009
- if (o instanceof AutoUpdatedClientObject)
1010
- return o;
1011
- if (childsManager)
1012
- try {
1013
- return await childsManager.handleGetMissingObject(o._id?.toString() ?? o.toString());
1014
- }
1015
- catch (error) {
1016
- this.loggers.error("This should fucking not happen wtffffff");
1017
- this.loggers.error(error.message);
1018
- childsManager = null;
1019
- }
1020
- else {
1021
- for (const manager of Object.values(this.parentManager.managers)) {
1022
- try {
1023
- const newO = await manager.handleGetMissingObject(o._id?.toString() ?? o.toString());
1024
- childsManager = manager;
1025
- return newO;
1026
- }
1027
- catch (e) {
1028
- const _ = e;
1029
- }
1030
- }
1031
- return undefined;
1032
- }
1033
- };
1034
483
  for (const prop of this.properties) {
1035
- const pointer = getMetadataRecursive("refsTo", this, prop.toString());
1036
484
  const isRef = getMetadataRecursive("isRef", this, prop.toString());
485
+ const pointer = getMetadataRecursive("refsTo", this, prop.toString());
1037
486
  if (!isRef || pointer)
1038
487
  continue;
1039
- 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
- }
488
+ const obj = this.getValue(prop);
1052
489
  if (!obj)
1053
490
  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}`);
491
+ const children = Array.isArray(obj) ? obj : [obj];
492
+ for (const child of children) {
493
+ if (child && typeof child.loadMissingReferences === "function") {
494
+ await child.loadMissingReferences();
1075
495
  }
1076
496
  }
1077
497
  }
1078
498
  }
1079
- getValueInternal(key) {
1080
- const keys = key.toString().split(".");
1081
- let value = this.data;
1082
- for (const k of keys) {
1083
- value = value[k];
1084
- }
1085
- return value;
1086
- }
1087
499
  }
1088
- export function processIsRefProperties(instance, target, prefix = null, allProps = [], newData = {}, loggers = console) {
500
+ export function processIsRefProperties(instance, target, prefix, allProps, newData, loggers) {
1089
501
  const props = Reflect.getMetadata("props", target) || [];
1090
502
  for (const prop of props) {
1091
503
  const path = prefix ? `${prefix}.${prop}` : prop;
@@ -1096,19 +508,17 @@ export function processIsRefProperties(instance, target, prefix = null, allProps
1096
508
  if (Reflect.getMetadata("isRef", target, prop)) {
1097
509
  if (Array.isArray(instance[prop]))
1098
510
  newData[prop] = instance[prop]
1099
- .map((item) => item?._id?.toString() ?? item?.toString() ?? undefined)
511
+ .map((item) => item?._id?.toString() ?? item?.toString())
1100
512
  .filter(Boolean);
1101
513
  else
1102
514
  newData[prop] =
1103
- instance[prop]?._id?.toString() ??
1104
- instance[prop]?.toString() ??
1105
- undefined;
515
+ instance[prop]?._id?.toString() ?? instance[prop]?.toString();
1106
516
  }
1107
517
  const type = Reflect.getMetadata("design:type", target, prop);
1108
518
  if (type?.prototype) {
1109
519
  const nestedProps = Reflect.getMetadata("props", type.prototype);
1110
520
  if (nestedProps && instance[prop]) {
1111
- newData[prop] = processIsRefProperties(instance[prop], type.prototype, path, allProps, undefined, loggers).newData;
521
+ newData[prop] = processIsRefProperties(instance[prop], type.prototype, path, allProps, {}, loggers).newData;
1112
522
  }
1113
523
  }
1114
524
  }