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