@prestizni-software/server-dem 0.4.95 → 0.4.97
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/AutoUpdateManagerClass.d.ts +16 -17
- package/dist/AutoUpdateManagerClass.js +22 -15
- package/dist/AutoUpdateManagerClass.js.map +1 -1
- package/dist/AutoUpdateServerManagerClass.d.ts +66 -48
- package/dist/AutoUpdateServerManagerClass.js +139 -331
- package/dist/AutoUpdateServerManagerClass.js.map +1 -1
- package/dist/AutoUpdatedClientObjectClass.d.ts +24 -26
- package/dist/AutoUpdatedClientObjectClass.js +217 -111
- package/dist/AutoUpdatedClientObjectClass.js.map +1 -1
- package/dist/AutoUpdatedServerObjectClass.d.ts +11 -10
- package/dist/AutoUpdatedServerObjectClass.js +54 -43
- package/dist/AutoUpdatedServerObjectClass.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,60 +127,71 @@ 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 && !this.isLoaded) {
|
|
164
|
+
this.loadShit();
|
|
165
|
+
}
|
|
151
166
|
}
|
|
152
167
|
async waitForPreloaded() {
|
|
153
168
|
if (this.isLoaded)
|
|
154
169
|
return;
|
|
155
|
-
|
|
156
|
-
await new Promise((resolve, reject) => {
|
|
170
|
+
return new Promise((resolve, reject) => {
|
|
157
171
|
const timer = setTimeout(() => {
|
|
158
|
-
|
|
159
|
-
reject(new Error(
|
|
172
|
+
this.emitter.off(EVENT_INTERNAL_PRE_LOADED + this.EmitterID, onPreloaded);
|
|
173
|
+
reject(new Error(`Timeout waiting for preloaded: ${this.className}`));
|
|
160
174
|
}, 10000);
|
|
161
|
-
|
|
175
|
+
const onPreloaded = (failed, reason) => {
|
|
162
176
|
clearTimeout(timer);
|
|
163
177
|
if (failed)
|
|
164
178
|
reject(new Error(reason));
|
|
165
179
|
else
|
|
166
180
|
resolve();
|
|
167
|
-
}
|
|
181
|
+
};
|
|
182
|
+
this.emitter.once(EVENT_INTERNAL_PRE_LOADED + this.EmitterID, onPreloaded);
|
|
168
183
|
});
|
|
169
184
|
}
|
|
170
185
|
handleNewObject(data) {
|
|
171
186
|
this.isLoading = true;
|
|
187
|
+
if (!this.socket || typeof this.socket.emit !== "function") {
|
|
188
|
+
this.isLoading = false;
|
|
189
|
+
this.emitter.emit(EVENT_INTERNAL_PRE_LOADED + this.EmitterID, true, "Socket not available");
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
172
192
|
this.socket.emit(EVENT_NEW + this.className, data, (res) => {
|
|
173
193
|
if (!res.success) {
|
|
174
194
|
this.isLoading = false;
|
|
175
|
-
this.loggers.error("Could not create data on server: " + res.message);
|
|
176
195
|
this.emitter.emit(EVENT_INTERNAL_PRE_LOADED + this.EmitterID, true, res.message);
|
|
177
196
|
return;
|
|
178
197
|
}
|
|
@@ -186,13 +205,13 @@ export class AutoUpdatedClientObject {
|
|
|
186
205
|
}
|
|
187
206
|
get extractedData() {
|
|
188
207
|
const extracted = processIsRefProperties(this.data, this, null, [], {}, this.loggers).newData;
|
|
189
|
-
return
|
|
208
|
+
return extracted;
|
|
190
209
|
}
|
|
191
210
|
get isLoaded() {
|
|
192
211
|
return !this.isLoading;
|
|
193
212
|
}
|
|
194
213
|
async isPreLoadedAsync() {
|
|
195
|
-
await this.
|
|
214
|
+
await this.waitForPreloaded();
|
|
196
215
|
return true;
|
|
197
216
|
}
|
|
198
217
|
async loadMissingReferences() {
|
|
@@ -200,25 +219,34 @@ export class AutoUpdatedClientObject {
|
|
|
200
219
|
this.generateSettersAndGetters();
|
|
201
220
|
}
|
|
202
221
|
openSockets() {
|
|
203
|
-
const
|
|
222
|
+
const dataRec = this.data;
|
|
223
|
+
const id = dataRec["_id"];
|
|
224
|
+
if (!id || !this.socket || typeof this.socket.on !== "function")
|
|
225
|
+
return;
|
|
204
226
|
const event = EVENT_UPDATE + this.className + id.toString();
|
|
205
227
|
this.socket.on(event, async (update, ack) => {
|
|
206
228
|
const res = await this.handleUpdateRequest(update);
|
|
207
|
-
if (ack
|
|
229
|
+
if (ack)
|
|
208
230
|
ack(res);
|
|
209
231
|
return res;
|
|
210
232
|
});
|
|
211
233
|
}
|
|
212
234
|
async handleUpdateRequest(update) {
|
|
213
235
|
try {
|
|
214
|
-
await this.setValue(update.key, update.value, {
|
|
236
|
+
await this.setValue(update.key, update.value, {
|
|
237
|
+
silent: true,
|
|
238
|
+
});
|
|
215
239
|
if (this.isLoaded)
|
|
216
240
|
this.callbacks.update(this, update.key);
|
|
217
241
|
return { success: true, data: undefined, message: "" };
|
|
218
242
|
}
|
|
219
243
|
catch (error) {
|
|
220
|
-
|
|
221
|
-
|
|
244
|
+
const dataRec = this.data;
|
|
245
|
+
this.loggers.error?.(`[${dataRec["_id"]?.toString()}] Error applying patch: ${error.message}`);
|
|
246
|
+
return {
|
|
247
|
+
success: false,
|
|
248
|
+
message: "Error applying update: " + error.message,
|
|
249
|
+
};
|
|
222
250
|
}
|
|
223
251
|
}
|
|
224
252
|
generateSettersAndGetters() {
|
|
@@ -230,10 +258,13 @@ export class AutoUpdatedClientObject {
|
|
|
230
258
|
const isRef = getMetadataRecursive("isRef", this, key);
|
|
231
259
|
Object.defineProperty(this, key, {
|
|
232
260
|
get: () => {
|
|
233
|
-
|
|
261
|
+
const dataRec = this.data;
|
|
262
|
+
let val = dataRec ? dataRec[key] : undefined;
|
|
234
263
|
if (isRef && val) {
|
|
235
264
|
if (Array.isArray(val)) {
|
|
236
|
-
return val
|
|
265
|
+
return val
|
|
266
|
+
.map((id) => this.findReference(id, key))
|
|
267
|
+
.filter(Boolean);
|
|
237
268
|
}
|
|
238
269
|
else {
|
|
239
270
|
return this.findReference(val, key);
|
|
@@ -242,7 +273,8 @@ export class AutoUpdatedClientObject {
|
|
|
242
273
|
return val;
|
|
243
274
|
},
|
|
244
275
|
set: (v) => {
|
|
245
|
-
|
|
276
|
+
if (this.data)
|
|
277
|
+
this.data[key] = v;
|
|
246
278
|
},
|
|
247
279
|
enumerable: true,
|
|
248
280
|
configurable: true,
|
|
@@ -251,26 +283,31 @@ export class AutoUpdatedClientObject {
|
|
|
251
283
|
}
|
|
252
284
|
getValue(key_) {
|
|
253
285
|
const key = key_;
|
|
254
|
-
|
|
255
|
-
return
|
|
286
|
+
const dataRec = this.data;
|
|
287
|
+
return this[key] === undefined
|
|
288
|
+
? dataRec
|
|
289
|
+
? dataRec[key_]
|
|
290
|
+
: undefined
|
|
291
|
+
: this[key_];
|
|
256
292
|
}
|
|
257
293
|
findReference(id, key) {
|
|
258
|
-
if (!id)
|
|
294
|
+
if (!id || !this.parentManager)
|
|
259
295
|
return undefined;
|
|
260
296
|
const idStr = id.toString();
|
|
261
|
-
|
|
262
|
-
|
|
297
|
+
const cacheRec = this.parentManager.cache.references;
|
|
298
|
+
if (cacheRec[key])
|
|
299
|
+
return cacheRec[key].getObject(idStr);
|
|
263
300
|
for (const manager of Object.values(this.parentManager.managers)) {
|
|
264
301
|
const result = manager.getObject(idStr);
|
|
265
302
|
if (result) {
|
|
266
|
-
|
|
303
|
+
cacheRec[key] = manager;
|
|
267
304
|
return result;
|
|
268
305
|
}
|
|
269
306
|
}
|
|
270
307
|
return undefined;
|
|
271
308
|
}
|
|
272
309
|
async setValue(key, val, options = {}) {
|
|
273
|
-
const { silent = false, noUpdate = false, isParentUpdate = false } = options;
|
|
310
|
+
const { silent = false, noUpdate = false, isParentUpdate = false, } = options;
|
|
274
311
|
try {
|
|
275
312
|
const isRef = getMetadataRecursive("isRef", this, key);
|
|
276
313
|
const pointer = getMetadataRecursive("refsTo", this, key);
|
|
@@ -281,24 +318,29 @@ export class AutoUpdatedClientObject {
|
|
|
281
318
|
if (isRef) {
|
|
282
319
|
valueToStore = Array.isArray(val)
|
|
283
320
|
? val.map((v) => v._id?.toString() ?? v.toString())
|
|
284
|
-
: (val?._id ?? val?.toString() ?? val);
|
|
321
|
+
: (val?._id?.toString() ?? val?.toString() ?? val);
|
|
285
322
|
}
|
|
286
|
-
const
|
|
323
|
+
const dataRec = this.data;
|
|
324
|
+
const currentVal = dataRec ? dataRec[key] : undefined;
|
|
287
325
|
if (_.isEqual(currentVal, valueToStore))
|
|
288
326
|
return { success: true, msg: "Successfully set " + key + " to " + val };
|
|
289
327
|
const res = await this.setValueInternal(key, valueToStore, silent, noUpdate);
|
|
290
328
|
if (res.success) {
|
|
291
|
-
this.data
|
|
329
|
+
if (this.data)
|
|
330
|
+
this.data[key] = valueToStore;
|
|
292
331
|
await this.findAndLoadReferences(key, valueToStore);
|
|
293
|
-
if (isRef && this.parentManager.isLoaded)
|
|
332
|
+
if (isRef && this.parentManager && this.parentManager.isLoaded)
|
|
294
333
|
await this.contactChildren();
|
|
295
334
|
if (this.isLoaded)
|
|
296
335
|
this.callbacks.update(this, key);
|
|
297
336
|
}
|
|
298
|
-
return {
|
|
337
|
+
return {
|
|
338
|
+
...res,
|
|
339
|
+
msg: res.msg ?? "Successfully set " + key + " to " + val,
|
|
340
|
+
};
|
|
299
341
|
}
|
|
300
342
|
catch (error) {
|
|
301
|
-
this.loggers.error(`Error setting value ${key}: ${error.message}`);
|
|
343
|
+
this.loggers.error?.(`Error setting value ${key}: ${error.message}`);
|
|
302
344
|
return { success: false, msg: error.message };
|
|
303
345
|
}
|
|
304
346
|
}
|
|
@@ -306,21 +348,37 @@ export class AutoUpdatedClientObject {
|
|
|
306
348
|
if (silent || noUpdate)
|
|
307
349
|
return { success: true, msg: "Silent or no update" };
|
|
308
350
|
return new Promise((resolve) => {
|
|
309
|
-
const
|
|
310
|
-
|
|
311
|
-
|
|
351
|
+
const dataRec = this.data;
|
|
352
|
+
const id = dataRec ? dataRec["_id"] : undefined;
|
|
353
|
+
if (!id)
|
|
354
|
+
return resolve({ success: false, msg: "Missing _id" });
|
|
355
|
+
if (!this.socket || typeof this.socket.emit !== "function") {
|
|
356
|
+
return resolve({ success: false, msg: "Socket not available" });
|
|
357
|
+
}
|
|
358
|
+
const timeout = setTimeout(() => {
|
|
359
|
+
resolve({ success: false, msg: "Timeout waiting for server response" });
|
|
360
|
+
}, 5000);
|
|
361
|
+
this.socket.emit(EVENT_UPDATE + this.className + id.toString(), { _id: id.toString(), key, value }, (res) => {
|
|
362
|
+
clearTimeout(timeout);
|
|
363
|
+
resolve({
|
|
364
|
+
success: res.success,
|
|
365
|
+
msg: res.message ?? (res.success ? "Success" : "Error"),
|
|
366
|
+
});
|
|
312
367
|
});
|
|
313
368
|
});
|
|
314
369
|
}
|
|
315
370
|
makeUpdate(key, value) {
|
|
316
|
-
const
|
|
371
|
+
const dataRec = this.data;
|
|
372
|
+
const id = dataRec ? dataRec["_id"] : undefined;
|
|
317
373
|
if (!id) {
|
|
318
|
-
this.loggers.error(`Probably missing the identifier ['_id'] again: ${key} = ${value}`);
|
|
374
|
+
this.loggers.error?.(`Probably missing the identifier ['_id'] again: ${key} = ${value}`);
|
|
319
375
|
throw new Error(`Cannot make update for ${this.className} because _id is missing.`);
|
|
320
376
|
}
|
|
321
|
-
return { _id: id
|
|
377
|
+
return { _id: id, key, value };
|
|
322
378
|
}
|
|
323
379
|
async resolveReference(id) {
|
|
380
|
+
if (!this.parentManager)
|
|
381
|
+
return null;
|
|
324
382
|
for (const manager of Object.values(this.parentManager.managers)) {
|
|
325
383
|
const obj = manager.getObject(id);
|
|
326
384
|
if (obj)
|
|
@@ -330,7 +388,7 @@ export class AutoUpdatedClientObject {
|
|
|
330
388
|
}
|
|
331
389
|
async findAndLoadReferences(lastPath, value) {
|
|
332
390
|
const isRef = getMetadataRecursive("isRef", this, lastPath);
|
|
333
|
-
if (isRef) {
|
|
391
|
+
if (isRef && this.parentManager) {
|
|
334
392
|
for (const id of Array.isArray(value) ? value : [value]) {
|
|
335
393
|
if (!id)
|
|
336
394
|
continue;
|
|
@@ -347,47 +405,60 @@ export class AutoUpdatedClientObject {
|
|
|
347
405
|
}
|
|
348
406
|
}
|
|
349
407
|
async wipeSelf() {
|
|
350
|
-
|
|
408
|
+
const dataRec = this.data;
|
|
409
|
+
if (!dataRec || dataRec["Wiped"])
|
|
351
410
|
return;
|
|
352
|
-
const id =
|
|
411
|
+
const id = dataRec["_id"];
|
|
353
412
|
const _id = id ? id.toString() : "unknown";
|
|
354
|
-
for (const key of Object.keys(
|
|
355
|
-
delete
|
|
413
|
+
for (const key of Object.keys(dataRec)) {
|
|
414
|
+
delete dataRec[key];
|
|
356
415
|
}
|
|
357
|
-
|
|
358
|
-
this.loggers.info(`[${_id}] ${this.className} object wiped`);
|
|
416
|
+
dataRec["Wiped"] = true;
|
|
417
|
+
this.loggers.info?.(`[${_id}] ${this.className} object wiped`);
|
|
359
418
|
}
|
|
360
419
|
async loadForceReferences(obj = this.data, proto = this, alreadySeen = []) {
|
|
420
|
+
if (!obj)
|
|
421
|
+
return;
|
|
361
422
|
if (obj === this.data) {
|
|
362
|
-
const
|
|
423
|
+
const dataRec = this.data;
|
|
424
|
+
const myId = dataRec["_id"]?.toString();
|
|
363
425
|
if (myId && !alreadySeen.includes(myId))
|
|
364
426
|
alreadySeen.push(myId);
|
|
365
427
|
}
|
|
366
428
|
const props = Reflect.getMetadata("props", proto) || [];
|
|
367
429
|
for (const key of props) {
|
|
368
|
-
if (typeof key !== "string")
|
|
369
|
-
continue;
|
|
370
430
|
const isRef = Reflect.getMetadata("isRef", proto, key);
|
|
371
431
|
const pointer = Reflect.getMetadata("refsTo", proto, key);
|
|
372
|
-
|
|
373
|
-
|
|
432
|
+
const objRec = obj;
|
|
433
|
+
if (pointer &&
|
|
434
|
+
obj === this.data &&
|
|
435
|
+
objRec[key] &&
|
|
436
|
+
!alreadySeen.includes(JSON.stringify(obj))) {
|
|
437
|
+
await this.createdWithParent(pointer.split(":"), objRec[key]);
|
|
374
438
|
}
|
|
375
|
-
if (
|
|
376
|
-
alreadySeen.push(
|
|
439
|
+
if (objRec[key] && !alreadySeen.includes(objRec[key]))
|
|
440
|
+
alreadySeen.push(objRec[key]);
|
|
377
441
|
if (isRef)
|
|
378
442
|
await this.handleLoad(obj, key, alreadySeen);
|
|
379
|
-
const val =
|
|
443
|
+
const val = objRec[key];
|
|
380
444
|
if (val && typeof val === "object") {
|
|
381
445
|
const nestedProto = Object.getPrototypeOf(val);
|
|
382
|
-
if (nestedProto &&
|
|
383
|
-
|
|
446
|
+
if (nestedProto &&
|
|
447
|
+
nestedProto !== Object.prototype &&
|
|
448
|
+
!alreadySeen.includes(JSON.stringify(val))) {
|
|
449
|
+
alreadySeen.push(JSON.stringify(val));
|
|
384
450
|
await this.loadForceReferences(val, nestedProto, alreadySeen);
|
|
385
451
|
}
|
|
386
452
|
}
|
|
387
453
|
}
|
|
388
454
|
}
|
|
389
455
|
async handleLoad(obj, key, alreadySeen) {
|
|
390
|
-
|
|
456
|
+
if (!this.parentManager)
|
|
457
|
+
return;
|
|
458
|
+
const objRec = obj;
|
|
459
|
+
const refIds = Array.isArray(objRec[key])
|
|
460
|
+
? objRec[key]
|
|
461
|
+
: [objRec[key]];
|
|
391
462
|
for (const refId of refIds) {
|
|
392
463
|
if (refId) {
|
|
393
464
|
const idStr = refId.toString();
|
|
@@ -410,33 +481,60 @@ export class AutoUpdatedClientObject {
|
|
|
410
481
|
if (noUpdate)
|
|
411
482
|
return;
|
|
412
483
|
await this.callbacks?.onUpdate?.(this, (key, val) => {
|
|
413
|
-
return this.setValue(key, val, {
|
|
484
|
+
return this.setValue(key, val, {
|
|
485
|
+
silent: false,
|
|
486
|
+
noGet: true,
|
|
487
|
+
noUpdate: true,
|
|
488
|
+
});
|
|
414
489
|
});
|
|
415
490
|
}
|
|
416
491
|
async createdWithParent(pointer, parent) {
|
|
417
|
-
if (pointer.length !== 2)
|
|
492
|
+
if (pointer.length !== 2 || !this.parentManager)
|
|
418
493
|
return;
|
|
419
494
|
const parentId = parent._id?.toString() ?? parent.toString();
|
|
420
|
-
const
|
|
495
|
+
const ac = this.parentManager.managers[pointer[0]];
|
|
496
|
+
if (!ac)
|
|
497
|
+
return;
|
|
498
|
+
const obj = ac.getObject(parentId);
|
|
421
499
|
if (!obj)
|
|
422
500
|
return;
|
|
423
501
|
const val = obj.getValue(pointer[1]);
|
|
424
|
-
const
|
|
502
|
+
const dataRec = this.data;
|
|
503
|
+
const myId = dataRec["_id"]?.toString();
|
|
504
|
+
if (!myId)
|
|
505
|
+
return;
|
|
425
506
|
if (Array.isArray(val)) {
|
|
426
|
-
const ids = val.map(v => v._id?.toString() ?? v.toString());
|
|
507
|
+
const ids = val.map((v) => v._id?.toString() ?? v.toString());
|
|
427
508
|
if (!ids.includes(myId)) {
|
|
428
509
|
await obj.setValue(pointer[1], [...val, myId], { silent: true, isParentUpdate: true });
|
|
429
510
|
}
|
|
430
511
|
}
|
|
431
512
|
else if ((val?._id?.toString() ?? val?.toString()) !== myId) {
|
|
432
|
-
await obj.setValue(pointer[1], myId, {
|
|
513
|
+
await obj.setValue(pointer[1], myId, {
|
|
514
|
+
silent: true,
|
|
515
|
+
isParentUpdate: true,
|
|
516
|
+
});
|
|
433
517
|
}
|
|
434
518
|
}
|
|
435
519
|
async destroy(once = false) {
|
|
436
|
-
|
|
437
|
-
|
|
520
|
+
const dataRec = this.data;
|
|
521
|
+
const id = dataRec ? dataRec["_id"] : undefined;
|
|
522
|
+
if (!id)
|
|
523
|
+
return { success: false, message: "Missing _id" };
|
|
524
|
+
if (!once && this.parentManager)
|
|
525
|
+
return await this.parentManager.deleteObject(id);
|
|
438
526
|
return new Promise((resolve) => {
|
|
439
|
-
this.socket
|
|
527
|
+
if (!this.socket || typeof this.socket.emit !== "function") {
|
|
528
|
+
return resolve({ success: false, message: "Socket not available" });
|
|
529
|
+
}
|
|
530
|
+
const timeout = setTimeout(() => {
|
|
531
|
+
resolve({
|
|
532
|
+
success: false,
|
|
533
|
+
message: "Timeout waiting for server deletion",
|
|
534
|
+
});
|
|
535
|
+
}, 5000);
|
|
536
|
+
this.socket.emit(EVENT_DELETE + this.className, id.toString(), (res) => {
|
|
537
|
+
clearTimeout(timeout);
|
|
440
538
|
resolve({ success: res.success, message: res.message ?? "" });
|
|
441
539
|
});
|
|
442
540
|
});
|
|
@@ -444,7 +542,7 @@ export class AutoUpdatedClientObject {
|
|
|
444
542
|
async checkForMissingRefs() {
|
|
445
543
|
for (const prop of this.properties) {
|
|
446
544
|
const pointer = getMetadataRecursive("refsTo", this, prop.toString());
|
|
447
|
-
if (pointer) {
|
|
545
|
+
if (typeof pointer === "string") {
|
|
448
546
|
const parts = pointer.split(":");
|
|
449
547
|
if (parts.length === 2)
|
|
450
548
|
await this.findMissingObjectReference(prop, parts);
|
|
@@ -452,13 +550,18 @@ export class AutoUpdatedClientObject {
|
|
|
452
550
|
}
|
|
453
551
|
}
|
|
454
552
|
async findMissingObjectReference(prop, pointer) {
|
|
455
|
-
if (this.checkedMissingRefs)
|
|
553
|
+
if (this.checkedMissingRefs || !this.parentManager)
|
|
456
554
|
return;
|
|
457
555
|
this.checkedMissingRefs = true;
|
|
458
556
|
const ac = this.parentManager.managers[pointer[0]];
|
|
459
557
|
if (!ac)
|
|
460
558
|
return;
|
|
461
|
-
const
|
|
559
|
+
const dataRec = this.data;
|
|
560
|
+
const targetId = dataRec
|
|
561
|
+
? dataRec["_id"]?.toString()
|
|
562
|
+
: undefined;
|
|
563
|
+
if (!targetId)
|
|
564
|
+
return;
|
|
462
565
|
const allObjects = Object.values(ac.objects);
|
|
463
566
|
for (const obj of allObjects) {
|
|
464
567
|
if (!obj.isLoaded)
|
|
@@ -466,9 +569,11 @@ export class AutoUpdatedClientObject {
|
|
|
466
569
|
const val = obj.getValue(pointer[1]);
|
|
467
570
|
if (!val)
|
|
468
571
|
continue;
|
|
469
|
-
const ids = Array.isArray(val)
|
|
572
|
+
const ids = Array.isArray(val)
|
|
573
|
+
? val.map((v) => v._id?.toString() ?? v.toString())
|
|
574
|
+
: [val._id?.toString() ?? val.toString()];
|
|
470
575
|
if (ids.includes(targetId)) {
|
|
471
|
-
|
|
576
|
+
dataRec[prop] = obj._id;
|
|
472
577
|
return;
|
|
473
578
|
}
|
|
474
579
|
}
|
|
@@ -484,7 +589,8 @@ export class AutoUpdatedClientObject {
|
|
|
484
589
|
continue;
|
|
485
590
|
const children = Array.isArray(obj) ? obj : [obj];
|
|
486
591
|
for (const child of children) {
|
|
487
|
-
if (child &&
|
|
592
|
+
if (child &&
|
|
593
|
+
typeof child.loadMissingReferences === "function") {
|
|
488
594
|
await child.loadMissingReferences();
|
|
489
595
|
}
|
|
490
596
|
}
|
|
@@ -497,6 +603,8 @@ export function processIsRefProperties(instance, target, prefix, allProps, newDa
|
|
|
497
603
|
for (const prop of props) {
|
|
498
604
|
const path = prefix ? `${prefix}.${prop}` : prop;
|
|
499
605
|
allProps.push(path);
|
|
606
|
+
if (!instance)
|
|
607
|
+
continue;
|
|
500
608
|
newData[prop] = ObjectId.isValid(instance[prop])
|
|
501
609
|
? instance[prop]?.toString()
|
|
502
610
|
: instance[prop];
|
|
@@ -511,18 +619,16 @@ export function processIsRefProperties(instance, target, prefix, allProps, newDa
|
|
|
511
619
|
}
|
|
512
620
|
const type = Reflect.getMetadata("design:type", target, prop);
|
|
513
621
|
if (type?.prototype) {
|
|
514
|
-
|
|
515
|
-
if (nestedProps && instance[prop]) {
|
|
516
|
-
newData[prop] = processIsRefProperties(instance[prop], type.prototype, path, allProps, {}, loggers).newData;
|
|
517
|
-
}
|
|
622
|
+
// DO NOT RECURSE into nested prototypes to avoid circularity issues
|
|
518
623
|
}
|
|
519
624
|
}
|
|
520
625
|
return { allProps, newData };
|
|
521
626
|
}
|
|
522
|
-
export function getMetadataRecursive(metaKey,
|
|
627
|
+
export function getMetadataRecursive(metaKey, target, prop) {
|
|
628
|
+
let proto = target;
|
|
523
629
|
while (proto) {
|
|
524
630
|
const meta = Reflect.getMetadata(metaKey, proto, prop);
|
|
525
|
-
if (meta)
|
|
631
|
+
if (meta !== undefined)
|
|
526
632
|
return meta;
|
|
527
633
|
proto = Object.getPrototypeOf(proto);
|
|
528
634
|
}
|