@prestizni-software/server-dem 0.5.6 → 0.5.8

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.
@@ -1,23 +1,17 @@
1
1
  import "reflect-metadata";
2
2
  import _ from "lodash";
3
- import { EVENT_INTERNAL_PRE_LOADED, EVENT_DELETE, EVENT_GET, EVENT_NEW, EVENT_UPDATE, globalCache, } from "./CommonTypes.js";
3
+ import { EVENT_INTERNAL_PRE_LOADED, EVENT_DELETE, EVENT_GET, EVENT_NEW, EVENT_UPDATE, globalCache, } from "./CommonTypes";
4
4
  import { ObjectId } from "bson";
5
5
  export class AutoUpdatedClientObject {
6
6
  entry;
7
- preLoad;
8
- registerSocket;
9
- readyLoggers;
10
- loadFromDB(a) {
11
- return a;
12
- }
13
7
  socket;
14
8
  data;
15
9
  isServer = false;
16
10
  loggers;
17
11
  isLoading = true;
12
+ loadError;
18
13
  isLoadingReferences = true;
19
14
  checkedMissingRefs = false;
20
- isDestroyed = false;
21
15
  emitter;
22
16
  properties;
23
17
  classParam;
@@ -26,7 +20,6 @@ export class AutoUpdatedClientObject {
26
20
  EmitterID = new ObjectId().toHexString();
27
21
  toChangeOnParents = [];
28
22
  callbacks;
29
- preloadTimers = new Set();
30
23
  loadReferencesAsync = async () => {
31
24
  try {
32
25
  if (!this.isLoaded) {
@@ -35,14 +28,11 @@ export class AutoUpdatedClientObject {
35
28
  this.generateSettersAndGetters();
36
29
  await this.loadForceReferences();
37
30
  for (const thing of this.toChangeOnParents) {
38
- await this.setValue(thing.key, thing.value, {
39
- silent: true,
40
- isParentUpdate: true,
41
- });
31
+ await this.setValue__(thing.key, thing.value, true, false, false, true);
42
32
  }
43
33
  }
44
34
  catch (error) {
45
- // this.loggers?.error?.("Error loading references: " + error.message);
35
+ this.loggers.error?.("Error loading references: " + (error instanceof Error ? error.message : String(error)));
46
36
  }
47
37
  finally {
48
38
  this.isLoadingReferences = false;
@@ -66,8 +56,18 @@ export class AutoUpdatedClientObject {
66
56
  this.parentManager = parentManager;
67
57
  this.callbacks = callback;
68
58
  this.emitter = emitter;
69
- this.properties = [];
70
- return;
59
+ this.properties = undefined;
60
+ if (!classParam &&
61
+ !socket &&
62
+ !data &&
63
+ !loggers &&
64
+ !className &&
65
+ !parentManager &&
66
+ !callback &&
67
+ !emitter)
68
+ return;
69
+ else
70
+ throw new Error("Missing arguments???");
71
71
  }
72
72
  this.classParam = classParam;
73
73
  this.socket = socket;
@@ -78,126 +78,96 @@ export class AutoUpdatedClientObject {
78
78
  this.parentManager = parentManager;
79
79
  this.className = className;
80
80
  const allProps = new Set();
81
- let proto = classParam.prototype;
82
- while (proto && proto !== Object.prototype) {
83
- const props = Reflect.getOwnMetadata("props", proto);
84
- if (props) {
85
- for (const p of props)
86
- allProps.add(p);
87
- }
88
- proto = Object.getPrototypeOf(proto);
81
+ let proto_ = classParam.prototype;
82
+ while (proto_ && proto_ !== Object.prototype) {
83
+ const props = Reflect.getOwnMetadata("props", proto_) || [];
84
+ for (const p of props)
85
+ allProps.add(p);
86
+ proto_ = Object.getPrototypeOf(proto_);
89
87
  }
90
88
  this.properties = Array.from(allProps);
91
- this.callbacks = callback || {
92
- new: () => { },
93
- update: () => { },
94
- delete: () => { },
95
- progress: () => { },
89
+ this.callbacks = callback;
90
+ this.loggers = {
91
+ debug: (s) => loggers.debug?.(`[DEM - ${this.className}: ${this.data?._id ?? this._id ?? "not loaded"}] ${s}`),
92
+ info: (s) => loggers.info?.(`[DEM - ${this.className}: ${this.data?._id ?? this._id ?? "not loaded"}] ${s}`),
93
+ warn: (s) => loggers.warn?.(`[DEM - ${this.className}: ${this.data?._id ?? this._id ?? "not loaded"}] ${s}`),
94
+ error: (s) => loggers.error?.(`[DEM - ${this.className}: ${this.data?._id ?? this._id ?? "not loaded"}] ${s}`),
96
95
  };
97
- try {
98
- if (typeof data === "string") {
99
- this.data = { _id: data };
100
- }
101
- else {
102
- this.data = data;
96
+ if (typeof data === "string") {
97
+ this.data = { _id: data };
98
+ if (this.isServer) {
99
+ this.isLoading = false;
100
+ this.generateSettersAndGetters();
101
+ return;
103
102
  }
104
- const currentId = this.data?._id?.toString() ?? "not loaded";
105
- this.loggers = {
106
- debug: (s) => loggers.debug?.(`[${this.className}: ${currentId}] ${s}`),
107
- info: (s) => loggers.info?.(`[${this.className}: ${currentId}] ${s}`),
108
- warn: (s) => loggers.warn?.(`[${this.className}: ${currentId}] ${s}`),
109
- error: (s) => loggers.error?.(`[${this.className}: ${currentId}] ${s}`),
110
- };
111
- if (typeof data === "string") {
112
- if (this.isServer) {
103
+ this.socket.emit(EVENT_GET + this.className + data, null, (res) => {
104
+ if (!res.success) {
113
105
  this.isLoading = false;
114
- this.generateSettersAndGetters();
115
- this.emitter.emit(EVENT_INTERNAL_PRE_LOADED + this.EmitterID);
106
+ this.loadError = res.message;
107
+ this.loggers.error?.("Could not load data from server: " + res.message);
108
+ this.emitter.emit(EVENT_INTERNAL_PRE_LOADED + this.EmitterID, true, res.message);
116
109
  return;
117
110
  }
118
- this.socket.emit(EVENT_GET + this.className + data, null, async (res) => {
119
- if (this.isDestroyed)
120
- return;
121
- if (!res.success) {
122
- this.isLoading = false;
123
- this.emitter.emit(EVENT_INTERNAL_PRE_LOADED + this.EmitterID, true, res.message);
124
- return;
125
- }
126
- this.data = res.data;
127
- this.generateSettersAndGetters();
128
- this.isLoading = false;
129
- await this.onUpdate();
130
- this.emitter.emit(EVENT_INTERNAL_PRE_LOADED + this.EmitterID);
131
- this.openSockets();
132
- });
133
- }
134
- else {
135
- const currentDataRec = this.data;
136
- for (const key of this.properties) {
137
- const isRef = getMetadataRecursive("isRef", this, key);
138
- if (isRef && currentDataRec[key]) {
139
- if (Array.isArray(currentDataRec[key])) {
140
- currentDataRec[key] = currentDataRec[key].map((obj) => obj._id?.toString() ?? obj?.toString());
141
- }
142
- else {
143
- currentDataRec[key] =
144
- currentDataRec[key]?._id?.toString() ??
145
- currentDataRec[key]?.toString();
146
- }
111
+ this.data = res.data;
112
+ this.generateSettersAndGetters();
113
+ this.isLoading = false;
114
+ this.emitter.emit(EVENT_INTERNAL_PRE_LOADED + this.EmitterID);
115
+ this.openSockets();
116
+ });
117
+ }
118
+ else {
119
+ this.isLoading = true;
120
+ this.data = data;
121
+ for (const key of this.properties || []) {
122
+ const isRef = getMetadataRecursive("isRef", this, key);
123
+ const dataAsRecord = this.data;
124
+ if (isRef && dataAsRecord[key]) {
125
+ if (Array.isArray(dataAsRecord[key])) {
126
+ dataAsRecord[key] = dataAsRecord[key].map((obj) => obj?._id?.toString() ?? obj?.toString());
147
127
  }
148
- }
149
- if ((!currentDataRec["_id"] || currentDataRec["_id"] === "") &&
150
- !this.isServer) {
151
- this.isLoading = true;
152
- this.handleNewObject(this.data);
153
- }
154
- else {
155
- this.isLoading = false;
156
- this.emitter.emit(EVENT_INTERNAL_PRE_LOADED + this.EmitterID);
157
- if (!this.isServer) {
158
- this.openSockets();
159
- this.onUpdate();
128
+ else {
129
+ dataAsRecord[key] =
130
+ dataAsRecord[key]?._id?.toString() ??
131
+ dataAsRecord[key]?.toString();
160
132
  }
161
133
  }
162
134
  }
163
- this.generateSettersAndGetters();
164
- // Ensure setters/getters are set after child constructor
165
- Promise.resolve().then(() => {
166
- if (!this.isDestroyed)
167
- this.generateSettersAndGetters();
168
- });
169
- if (!this.isServer && !this.isLoaded) {
170
- this.loadShit();
135
+ if ((!this.data._id || this.data._id === "") &&
136
+ !this.isServer) {
137
+ this.handleNewObject(data);
138
+ }
139
+ else {
140
+ this.isLoading = false;
141
+ if (!this.isServer)
142
+ this.openSockets();
171
143
  }
172
144
  }
173
- catch (e) {
174
- this.destroyImmediate();
175
- throw e;
176
- }
145
+ this.generateSettersAndGetters();
146
+ Promise.resolve().then(() => {
147
+ this.generateSettersAndGetters();
148
+ });
177
149
  }
178
150
  async waitForPreloaded() {
151
+ if (this.loadError)
152
+ throw new Error(this.loadError);
179
153
  if (this.isLoaded)
180
154
  return;
181
- return new Promise((resolve, reject) => {
182
- const onPreloaded = (failed, reason) => {
155
+ await new Promise((resolve, reject) => {
156
+ this.emitter.once(EVENT_INTERNAL_PRE_LOADED + this.EmitterID, (failed, reason) => {
183
157
  if (failed)
184
158
  reject(new Error(reason));
185
159
  else
186
160
  resolve();
187
- };
188
- this.emitter.once(EVENT_INTERNAL_PRE_LOADED + this.EmitterID, onPreloaded);
161
+ });
189
162
  });
190
163
  }
191
164
  handleNewObject(data) {
192
165
  this.isLoading = true;
193
- if (!this.socket || typeof this.socket.emit !== "function") {
194
- this.isLoading = false;
195
- this.emitter.emit(EVENT_INTERNAL_PRE_LOADED + this.EmitterID, true, "Socket not available");
196
- return;
197
- }
198
166
  this.socket.emit(EVENT_NEW + this.className, data, (res) => {
199
167
  if (!res.success) {
200
168
  this.isLoading = false;
169
+ this.loadError = res.message;
170
+ this.loggers.error?.("Could not create data on server: " + res.message);
201
171
  this.emitter.emit(EVENT_INTERNAL_PRE_LOADED + this.EmitterID, true, res.message);
202
172
  return;
203
173
  }
@@ -211,13 +181,13 @@ export class AutoUpdatedClientObject {
211
181
  }
212
182
  get extractedData() {
213
183
  const extracted = processIsRefProperties(this.data, this, null, [], {}, this.loggers).newData;
214
- return extracted;
184
+ return _.cloneDeep(extracted);
215
185
  }
216
186
  get isLoaded() {
217
187
  return !this.isLoading;
218
188
  }
219
189
  async isPreLoadedAsync() {
220
- await this.waitForPreloaded();
190
+ await this.loadShit();
221
191
  return true;
222
192
  }
223
193
  async loadMissingReferences() {
@@ -225,33 +195,27 @@ export class AutoUpdatedClientObject {
225
195
  this.generateSettersAndGetters();
226
196
  }
227
197
  openSockets() {
228
- const dataRec = this.data;
229
- const id = dataRec["_id"];
230
- if (!id || !this.socket || typeof this.socket.on !== "function")
231
- return;
198
+ const id = this.data?._id ?? this._id;
232
199
  const event = EVENT_UPDATE + this.className + id.toString();
233
200
  this.socket.on(event, async (update, ack) => {
234
201
  const res = await this.handleUpdateRequest(update);
235
- if (ack)
202
+ if (ack && typeof ack === "function")
236
203
  ack(res);
237
204
  return res;
238
205
  });
239
206
  }
240
207
  async handleUpdateRequest(update) {
241
208
  try {
242
- await this.setValue(update.key, update.value, {
243
- silent: true,
244
- });
209
+ await this.setValue__(update.key, update.value, true);
245
210
  if (this.isLoaded)
246
211
  this.callbacks.update(this, update.key);
247
212
  return { success: true, data: undefined, message: "" };
248
213
  }
249
214
  catch (error) {
250
- const dataRec = this.data;
251
- this.loggers.error?.(`[${dataRec["_id"]?.toString()}] Error applying patch: ${error.message}`);
215
+ this.loggers.error?.(`[${this.data._id}] Error applying patch: ${error instanceof Error ? error.message : String(error)}`);
252
216
  return {
253
217
  success: false,
254
- message: "Error applying update: " + error.message,
218
+ message: "Error applying update: " + (error instanceof Error ? error.message : String(error)),
255
219
  };
256
220
  }
257
221
  }
@@ -262,10 +226,14 @@ export class AutoUpdatedClientObject {
262
226
  if (typeof key !== "string")
263
227
  continue;
264
228
  const isRef = getMetadataRecursive("isRef", this, key);
229
+ delete this[key];
265
230
  Object.defineProperty(this, key, {
266
231
  get: () => {
267
- const dataRec = this.data;
268
- let val = dataRec ? dataRec[key] : undefined;
232
+ if (!this.data)
233
+ return undefined;
234
+ let val = this.data[key];
235
+ if (val === null)
236
+ val = undefined;
269
237
  if (isRef && val) {
270
238
  if (Array.isArray(val)) {
271
239
  return val
@@ -289,31 +257,43 @@ export class AutoUpdatedClientObject {
289
257
  }
290
258
  getValue(key_) {
291
259
  const key = key_;
292
- const dataRec = this.data;
293
- return this[key] === undefined
294
- ? dataRec
295
- ? dataRec[key_]
296
- : undefined
297
- : this[key_];
260
+ const parts = key.split(".");
261
+ let value = this;
262
+ for (const part of parts) {
263
+ if (value === undefined || value === null)
264
+ return undefined;
265
+ const nextValue = value[part];
266
+ if (nextValue !== undefined) {
267
+ value = nextValue;
268
+ }
269
+ else if (value.data && value.data[part] !== undefined) {
270
+ value = value.data[part];
271
+ }
272
+ else {
273
+ return undefined;
274
+ }
275
+ }
276
+ return value;
298
277
  }
299
278
  findReference(id, key) {
300
- if (!id || !this.parentManager)
279
+ if (!id)
301
280
  return undefined;
302
281
  const idStr = id.toString();
303
- const cacheRec = this.parentManager.cache.references;
304
- if (cacheRec[key])
305
- return cacheRec[key].getObject(idStr);
282
+ if (this.parentManager.cache.references[key])
283
+ return this.parentManager.cache.references[key]?.getObject(idStr) ?? undefined;
306
284
  for (const manager of Object.values(this.parentManager.managers)) {
307
285
  const result = manager.getObject(idStr);
308
286
  if (result) {
309
- cacheRec[key] = manager;
287
+ this.parentManager.cache.references[key] = manager;
310
288
  return result;
311
289
  }
312
290
  }
313
291
  return undefined;
314
292
  }
315
- async setValue(key, val, options = {}) {
316
- const { silent = false, noUpdate = false, isParentUpdate = false, } = options;
293
+ async setValue(key, val) {
294
+ return await this.setValue__(key, val);
295
+ }
296
+ async setValue__(key, val, silent = false, noGet = false, noUpdate = false, isParentUpdate = false) {
317
297
  try {
318
298
  const isRef = getMetadataRecursive("isRef", this, key);
319
299
  const pointer = getMetadataRecursive("refsTo", this, key);
@@ -323,19 +303,41 @@ export class AutoUpdatedClientObject {
323
303
  let valueToStore = val;
324
304
  if (isRef) {
325
305
  valueToStore = Array.isArray(val)
326
- ? val.map((v) => v._id?.toString() ?? v.toString())
327
- : (val?._id?.toString() ?? val?.toString() ?? val);
306
+ ? val.map((v) => v?._id?.toString() ?? v?.toString() ?? v)
307
+ : val?._id?.toString() ?? val?.toString() ?? val;
328
308
  }
329
- const dataRec = this.data;
330
- const currentVal = dataRec ? dataRec[key] : undefined;
331
- if (_.isEqual(currentVal, valueToStore))
309
+ const currentVal = this.getValue(key);
310
+ const currentValId = Array.isArray(currentVal)
311
+ ? currentVal.map((v) => v?._id?.toString() ?? v?.toString() ?? v)
312
+ : currentVal?._id?.toString() ?? currentVal?.toString();
313
+ if (_.isEqual(currentValId, valueToStore))
332
314
  return { success: true, msg: "Successfully set " + key + " to " + val };
315
+ const path = key.split(".");
316
+ if (path.length > 1) {
317
+ let obj = this.data;
318
+ for (let i = 0; i < path.length - 1; i++) {
319
+ const currentKey = path[i];
320
+ const valAtKey = obj[currentKey];
321
+ if (typeof valAtKey === "string" ||
322
+ (valAtKey && ObjectId.isValid(valAtKey))) {
323
+ const ref = await this.resolveReference(String(valAtKey));
324
+ if (!ref)
325
+ throw new Error("Could not resolve reference on path: " + key);
326
+ return await ref.setValue(path.slice(i + 1).join("."), val);
327
+ }
328
+ obj = obj[currentKey];
329
+ }
330
+ }
333
331
  const res = await this.setValueInternal(key, valueToStore, silent, noUpdate);
334
332
  if (res.success) {
335
- if (this.data)
336
- this.data[key] = valueToStore;
333
+ const pathArr = key.split(".");
334
+ let obj = this.data;
335
+ for (let i = 0; i < pathArr.length - 1; i++) {
336
+ obj = obj[pathArr[i]];
337
+ }
338
+ obj[pathArr[pathArr.length - 1]] = valueToStore;
337
339
  await this.findAndLoadReferences(key, valueToStore);
338
- if (isRef && this.parentManager && this.parentManager.isLoaded)
340
+ if (isRef && this.parentManager.isLoaded)
339
341
  await this.contactChildren();
340
342
  if (this.isLoaded)
341
343
  this.callbacks.update(this, key);
@@ -346,22 +348,16 @@ export class AutoUpdatedClientObject {
346
348
  };
347
349
  }
348
350
  catch (error) {
349
- this.loggers.error?.(`Error setting value ${key}: ${error.message}`);
350
- return { success: false, msg: error.message };
351
+ this.loggers.error?.(`Error setting value ${key}: ${error instanceof Error ? error.message : String(error)}`);
352
+ return { success: false, msg: error instanceof Error ? error.message : String(error) };
351
353
  }
352
354
  }
353
355
  async setValueInternal(key, value, silent = false, noUpdate = false) {
354
- if (silent || noUpdate)
355
- return { success: true, msg: "Silent or no update" };
356
+ if (silent)
357
+ return { success: true, msg: "Silent" };
356
358
  return new Promise((resolve) => {
357
- const dataRec = this.data;
358
- const id = dataRec ? dataRec["_id"] : undefined;
359
- if (!id)
360
- return resolve({ success: false, msg: "Missing _id" });
361
- if (!this.socket || typeof this.socket.emit !== "function") {
362
- return resolve({ success: false, msg: "Socket not available" });
363
- }
364
- this.socket.emit(EVENT_UPDATE + this.className + id.toString(), { _id: id.toString(), key, value }, (res) => {
359
+ const id = this.data?._id ?? this._id;
360
+ this.socket.emit(EVENT_UPDATE + this.className + id, { _id: id.toString(), key, value }, (res) => {
365
361
  resolve({
366
362
  success: res.success,
367
363
  msg: res.message ?? (res.success ? "Success" : "Error"),
@@ -370,17 +366,14 @@ export class AutoUpdatedClientObject {
370
366
  });
371
367
  }
372
368
  makeUpdate(key, value) {
373
- const dataRec = this.data;
374
- const id = dataRec ? dataRec["_id"] : undefined;
369
+ const id = this.data?._id ?? this._id;
375
370
  if (!id) {
376
371
  this.loggers.error?.(`Probably missing the identifier ['_id'] again: ${key} = ${value}`);
377
372
  throw new Error(`Cannot make update for ${this.className} because _id is missing.`);
378
373
  }
379
- return { _id: id, key, value };
374
+ return { _id: id.toString(), key, value };
380
375
  }
381
376
  async resolveReference(id) {
382
- if (!this.parentManager)
383
- return null;
384
377
  for (const manager of Object.values(this.parentManager.managers)) {
385
378
  const obj = manager.getObject(id);
386
379
  if (obj)
@@ -390,7 +383,7 @@ export class AutoUpdatedClientObject {
390
383
  }
391
384
  async findAndLoadReferences(lastPath, value) {
392
385
  const isRef = getMetadataRecursive("isRef", this, lastPath);
393
- if (isRef && this.parentManager) {
386
+ if (isRef) {
394
387
  for (const id of Array.isArray(value) ? value : [value]) {
395
388
  if (!id)
396
389
  continue;
@@ -407,60 +400,48 @@ export class AutoUpdatedClientObject {
407
400
  }
408
401
  }
409
402
  async wipeSelf() {
410
- const dataRec = this.data;
411
- if (!dataRec || dataRec["Wiped"])
403
+ if (this.data.Wiped)
412
404
  return;
413
- const id = dataRec["_id"];
405
+ const id = this.data?._id ?? this._id;
414
406
  const _id = id ? id.toString() : "unknown";
415
- for (const key of Object.keys(dataRec)) {
416
- delete dataRec[key];
407
+ for (const key of Object.keys(this.data)) {
408
+ delete this.data[key];
417
409
  }
418
- dataRec["Wiped"] = true;
410
+ this.data = { Wiped: true };
419
411
  this.loggers.info?.(`[${_id}] ${this.className} object wiped`);
420
412
  }
413
+ destroyImmediate() {
414
+ this.wipeSelf();
415
+ }
421
416
  async loadForceReferences(obj = this.data, proto = this, alreadySeen = []) {
422
- if (!obj)
423
- return;
424
- if (obj === this.data) {
425
- const dataRec = this.data;
426
- const myId = dataRec["_id"]?.toString();
427
- if (myId && !alreadySeen.includes(myId))
428
- alreadySeen.push(myId);
429
- }
430
417
  const props = Reflect.getMetadata("props", proto) || [];
431
418
  for (const key of props) {
419
+ if (typeof key !== "string")
420
+ continue;
432
421
  const isRef = Reflect.getMetadata("isRef", proto, key);
433
422
  const pointer = Reflect.getMetadata("refsTo", proto, key);
434
- const objRec = obj;
435
423
  if (pointer &&
436
424
  obj === this.data &&
437
- objRec[key] &&
438
- !alreadySeen.includes(JSON.stringify(obj))) {
439
- await this.createdWithParent(pointer.split(":"), objRec[key]);
425
+ obj[key] &&
426
+ !alreadySeen.includes(obj)) {
427
+ await this.createdWithParent(pointer.split(":"), obj[key]);
440
428
  }
441
- if (objRec[key] && !alreadySeen.includes(objRec[key]))
442
- alreadySeen.push(objRec[key]);
429
+ if (obj[key] && !alreadySeen.includes(obj[key]))
430
+ alreadySeen.push(obj[key]);
443
431
  if (isRef)
444
432
  await this.handleLoad(obj, key, alreadySeen);
445
- const val = objRec[key];
433
+ const val = obj[key];
446
434
  if (val && typeof val === "object") {
447
435
  const nestedProto = Object.getPrototypeOf(val);
448
- if (nestedProto &&
449
- nestedProto !== Object.prototype &&
450
- !alreadySeen.includes(JSON.stringify(val))) {
451
- alreadySeen.push(JSON.stringify(val));
436
+ if (nestedProto && !alreadySeen.includes(val)) {
437
+ alreadySeen.push(val);
452
438
  await this.loadForceReferences(val, nestedProto, alreadySeen);
453
439
  }
454
440
  }
455
441
  }
456
442
  }
457
443
  async handleLoad(obj, key, alreadySeen) {
458
- if (!this.parentManager)
459
- return;
460
- const objRec = obj;
461
- const refIds = Array.isArray(objRec[key])
462
- ? objRec[key]
463
- : [objRec[key]];
444
+ const refIds = Array.isArray(obj[key]) ? obj[key] : [obj[key]];
464
445
  for (const refId of refIds) {
465
446
  if (refId) {
466
447
  const idStr = refId.toString();
@@ -479,82 +460,38 @@ export class AutoUpdatedClientObject {
479
460
  }
480
461
  }
481
462
  }
482
- async onUpdate(noUpdate = false) {
483
- if (noUpdate)
484
- return;
485
- try {
486
- await this.callbacks?.onUpdate?.(this, (key, val) => {
487
- return this.setValue(key, val, {
488
- silent: false,
489
- noGet: true,
490
- noUpdate: true,
491
- });
492
- });
493
- }
494
- catch (error) {
495
- this.loggers.error(`[onUpdate] ${error}`);
496
- }
497
- }
498
463
  async createdWithParent(pointer, parent) {
499
- if (pointer.length !== 2 || !this.parentManager)
464
+ if (pointer.length !== 2)
500
465
  return;
501
466
  const parentId = parent._id?.toString() ?? parent.toString();
502
- const ac = this.parentManager.managers[pointer[0]];
503
- if (!ac)
504
- return;
505
- const obj = ac.getObject(parentId);
467
+ const obj = this.parentManager.managers[pointer[0]]?.getObject(parentId);
506
468
  if (!obj)
507
469
  return;
508
470
  const val = obj.getValue(pointer[1]);
509
- const dataRec = this.data;
510
- const myId = dataRec["_id"]?.toString();
511
- if (!myId)
512
- return;
471
+ const myId = this.data._id.toString();
513
472
  if (Array.isArray(val)) {
514
- const ids = val.map((v) => v._id?.toString() ?? v.toString());
473
+ const ids = val.map((v) => v?._id?.toString() ?? v?.toString());
515
474
  if (!ids.includes(myId)) {
516
- await obj.setValue(pointer[1], [...val, myId], { silent: true, isParentUpdate: true });
475
+ await obj.setValue__(pointer[1], [...val, myId], true, false, false, true);
517
476
  }
518
477
  }
519
478
  else if ((val?._id?.toString() ?? val?.toString()) !== myId) {
520
- await obj.setValue(pointer[1], myId, {
521
- silent: true,
522
- isParentUpdate: true,
523
- });
479
+ await obj.setValue__(pointer[1], myId, true, false, false, true);
524
480
  }
525
481
  }
526
482
  async destroy(once = false) {
527
- for (const timer of this.preloadTimers) {
528
- clearTimeout(timer);
529
- }
530
- this.preloadTimers.clear();
531
- const dataRec = this.data;
532
- const id = dataRec ? dataRec["_id"] : undefined;
533
- if (!id)
534
- return { success: false, message: "Missing _id" };
535
- if (!once && this.parentManager)
536
- return await this.parentManager.deleteObject(id);
483
+ if (!once)
484
+ return await this.parentManager.deleteObject(this.data._id.toString());
537
485
  return new Promise((resolve) => {
538
- if (!this.socket || typeof this.socket.emit !== "function") {
539
- return resolve({ success: false, message: "Socket not available" });
540
- }
541
- this.socket.emit(EVENT_DELETE + this.className, id.toString(), (res) => {
486
+ this.socket.emit(EVENT_DELETE + this.className, this.data._id, (res) => {
542
487
  resolve({ success: res.success, message: res.message ?? "" });
543
488
  });
544
489
  });
545
490
  }
546
- destroyImmediate() {
547
- this.isDestroyed = true;
548
- for (const timer of this.preloadTimers) {
549
- clearTimeout(timer);
550
- }
551
- this.preloadTimers.clear();
552
- this.isLoading = false;
553
- }
554
491
  async checkForMissingRefs() {
555
492
  for (const prop of this.properties) {
556
493
  const pointer = getMetadataRecursive("refsTo", this, prop.toString());
557
- if (typeof pointer === "string") {
494
+ if (pointer) {
558
495
  const parts = pointer.split(":");
559
496
  if (parts.length === 2)
560
497
  await this.findMissingObjectReference(prop, parts);
@@ -562,19 +499,14 @@ export class AutoUpdatedClientObject {
562
499
  }
563
500
  }
564
501
  async findMissingObjectReference(prop, pointer) {
565
- if (this.checkedMissingRefs || !this.parentManager)
502
+ if (this.checkedMissingRefs)
566
503
  return;
567
504
  this.checkedMissingRefs = true;
568
505
  const ac = this.parentManager.managers[pointer[0]];
569
506
  if (!ac)
570
507
  return;
571
- const dataRec = this.data;
572
- const targetId = dataRec
573
- ? dataRec["_id"]?.toString()
574
- : undefined;
575
- if (!targetId)
576
- return;
577
- const allObjects = Object.values(ac.objects);
508
+ const targetId = this.data._id.toString();
509
+ const allObjects = Object.values(ac.objectsAsArray);
578
510
  for (const obj of allObjects) {
579
511
  if (!obj.isLoaded)
580
512
  await obj.waitForPreloaded();
@@ -582,49 +514,33 @@ export class AutoUpdatedClientObject {
582
514
  if (!val)
583
515
  continue;
584
516
  const ids = Array.isArray(val)
585
- ? val.map((v) => v._id?.toString() ?? v.toString())
517
+ ? val.map((v) => v?._id?.toString() ?? v.toString())
586
518
  : [val._id?.toString() ?? val.toString()];
587
519
  if (ids.includes(targetId)) {
588
- dataRec[prop] = obj._id;
520
+ this.data[prop] = obj._id;
589
521
  return;
590
522
  }
591
523
  }
592
524
  }
593
- async resolveReferences() {
594
- await this.loadMissingReferences();
595
- await this.contactChildren();
596
- }
597
525
  async contactChildren() {
598
- if (!this.parentManager)
599
- return;
600
526
  for (const prop of this.properties) {
601
527
  const isRef = getMetadataRecursive("isRef", this, prop.toString());
602
528
  const pointer = getMetadataRecursive("refsTo", this, prop.toString());
603
529
  if (!isRef || pointer)
604
530
  continue;
605
- const val = this.getValue(prop);
606
- if (!val)
531
+ const obj = this.getValue(prop);
532
+ if (!obj)
607
533
  continue;
608
- const idsOrObjs = Array.isArray(val) ? val : [val];
609
- for (const item of idsOrObjs) {
610
- if (!item)
611
- continue;
612
- let childObj = item;
613
- if (typeof item === "string" || item instanceof ObjectId) {
614
- const idStr = item.toString();
615
- for (const manager of Object.values(this.parentManager.managers)) {
616
- childObj = manager.getObject(idStr);
617
- if (childObj)
618
- break;
619
- }
620
- }
621
- if (childObj &&
622
- typeof childObj.loadMissingReferences === "function") {
623
- await childObj.loadMissingReferences();
534
+ const children = Array.isArray(obj) ? obj : [obj];
535
+ for (const child of children) {
536
+ if (child && typeof child.loadMissingReferences === "function") {
537
+ await child.loadMissingReferences();
624
538
  }
625
539
  }
626
540
  }
627
- this.generateSettersAndGetters();
541
+ }
542
+ async onUpdate() {
543
+ // Placeholder for server-side override
628
544
  }
629
545
  }
630
546
  export function processIsRefProperties(instance, target, prefix, allProps, newData, loggers) {
@@ -632,9 +548,7 @@ export function processIsRefProperties(instance, target, prefix, allProps, newDa
632
548
  for (const prop of props) {
633
549
  const path = prefix ? `${prefix}.${prop}` : prop;
634
550
  allProps.push(path);
635
- if (!instance)
636
- continue;
637
- newData[prop] = ObjectId.isValid(instance[prop])
551
+ newData[prop] = (instance[prop] && ObjectId.isValid(instance[prop]))
638
552
  ? instance[prop]?.toString()
639
553
  : instance[prop];
640
554
  if (Reflect.getMetadata("isRef", target, prop)) {
@@ -648,16 +562,18 @@ export function processIsRefProperties(instance, target, prefix, allProps, newDa
648
562
  }
649
563
  const type = Reflect.getMetadata("design:type", target, prop);
650
564
  if (type?.prototype) {
651
- // DO NOT RECURSE into nested prototypes to avoid circularity issues
565
+ const nestedProps = Reflect.getMetadata("props", type.prototype);
566
+ if (nestedProps && instance[prop]) {
567
+ newData[prop] = processIsRefProperties(instance[prop], type.prototype, path, allProps, {}, loggers).newData;
568
+ }
652
569
  }
653
570
  }
654
571
  return { allProps, newData };
655
572
  }
656
- export function getMetadataRecursive(metaKey, target, prop) {
657
- let proto = target;
573
+ export function getMetadataRecursive(metaKey, proto, prop) {
658
574
  while (proto) {
659
575
  const meta = Reflect.getMetadata(metaKey, proto, prop);
660
- if (meta !== undefined)
576
+ if (meta)
661
577
  return meta;
662
578
  proto = Object.getPrototypeOf(proto);
663
579
  }