@prestizni-software/server-dem 0.4.94 → 0.4.96
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 +67 -50
- package/dist/AutoUpdateServerManagerClass.js +138 -331
- package/dist/AutoUpdateServerManagerClass.js.map +1 -1
- package/dist/AutoUpdatedClientObjectClass.d.ts +26 -28
- package/dist/AutoUpdatedClientObjectClass.js +154 -97
- 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 +33 -21
- package/dist/CommonTypes.js.map +1 -1
- package/dist/CommonTypes_server.d.ts +0 -4
- package/dist/CommonTypes_server.js +1 -1
- package/dist/server.js +0 -2
- package/dist/server.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
|
@@ -1,29 +1,26 @@
|
|
|
1
1
|
import "reflect-metadata";
|
|
2
|
-
import { Constructor, EventEmitter3, IsData, LoggersType, PathValueOf, ServerUpdateRequest,
|
|
3
|
-
import { ObjectId } from "bson";
|
|
4
|
-
import { Socket } from "socket.io-client";
|
|
2
|
+
import { Constructor, EventEmitter3, IsData, LoggersType, PathValueOf, ServerUpdateRequest, MongoId, IDEMSocket } from "./CommonTypes.js";
|
|
5
3
|
import { AutoUpdateManager } from "./AutoUpdateManagerClass.js";
|
|
6
|
-
export type DEMClientCallbacks<T extends AutoUpdatedClientObject<T>> = {
|
|
4
|
+
export type DEMClientCallbacks<T extends AutoUpdatedClientObject<T, any>> = {
|
|
7
5
|
new: (obj: T) => Promise<void> | void;
|
|
8
6
|
update: (obj: T, key: string) => Promise<void> | void;
|
|
9
7
|
delete: (obj: T) => Promise<void> | void;
|
|
10
8
|
progress: (percent: number) => void;
|
|
11
|
-
onUpdate?: (obj: T, set: <K extends
|
|
9
|
+
onUpdate?: (obj: T, set: <K extends keyof IsData<T> & string>(key: K, val: PathValueOf<IsData<T>, K>) => Promise<{
|
|
12
10
|
success: boolean;
|
|
13
11
|
msg: string;
|
|
14
12
|
}>) => Promise<void>;
|
|
15
13
|
};
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
protected readonly socket: SocketType;
|
|
14
|
+
export declare abstract class AutoUpdatedClientObject<T extends AutoUpdatedClientObject<T, any>, M extends Record<string, any> = any> {
|
|
15
|
+
protected entry: unknown;
|
|
16
|
+
preLoad: unknown;
|
|
17
|
+
registerSocket: unknown;
|
|
18
|
+
readyLoggers: unknown;
|
|
19
|
+
loadFromDB(a: unknown): unknown;
|
|
20
|
+
protected readonly socket: IDEMSocket;
|
|
24
21
|
protected data: IsData<T>;
|
|
25
22
|
protected readonly isServer: boolean;
|
|
26
|
-
abstract readonly _id:
|
|
23
|
+
abstract readonly _id: MongoId;
|
|
27
24
|
protected readonly loggers: LoggersType;
|
|
28
25
|
protected isLoading: boolean;
|
|
29
26
|
protected isLoadingReferences: boolean;
|
|
@@ -32,29 +29,31 @@ export declare abstract class AutoUpdatedClientObject<T extends AutoUpdatedClien
|
|
|
32
29
|
readonly properties: (keyof IsData<T> & string)[];
|
|
33
30
|
readonly classParam: Constructor<T>;
|
|
34
31
|
readonly className: string;
|
|
35
|
-
parentManager: AutoUpdateManager<T>;
|
|
32
|
+
parentManager: AutoUpdateManager<T, M>;
|
|
36
33
|
private readonly EmitterID;
|
|
37
34
|
protected readonly toChangeOnParents: {
|
|
38
35
|
key: string;
|
|
39
|
-
value:
|
|
36
|
+
value: unknown;
|
|
40
37
|
}[];
|
|
41
38
|
callbacks: DEMClientCallbacks<T>;
|
|
42
39
|
private readonly loadReferencesAsync;
|
|
43
40
|
/** @deprecated Use loadReferencesAsync instead */
|
|
44
41
|
private readonly loadShit;
|
|
45
|
-
constructor(classParam?: Constructor<T>, socket?:
|
|
42
|
+
constructor(classParam?: Constructor<T>, socket?: IDEMSocket, data?: string | IsData<T>, loggers?: LoggersType, className?: string, parentManager?: AutoUpdateManager<T, M>, callback?: DEMClientCallbacks<T>, emitter?: EventEmitter3, isServer?: boolean);
|
|
46
43
|
waitForPreloaded(): Promise<void>;
|
|
47
44
|
protected handleNewObject(data: IsData<T>): void;
|
|
48
|
-
get extractedData():
|
|
45
|
+
get extractedData(): {
|
|
46
|
+
[K in keyof IsData<T>]: unknown;
|
|
47
|
+
};
|
|
49
48
|
get isLoaded(): boolean;
|
|
50
49
|
isPreLoadedAsync(): Promise<boolean>;
|
|
51
50
|
loadMissingReferences(): Promise<void>;
|
|
52
51
|
private openSockets;
|
|
53
52
|
private handleUpdateRequest;
|
|
54
53
|
protected generateSettersAndGetters(): void;
|
|
55
|
-
getValue(key_:
|
|
56
|
-
protected findReference(id: string |
|
|
57
|
-
setValue<K extends
|
|
54
|
+
getValue(key_: keyof IsData<T> & string): unknown;
|
|
55
|
+
protected findReference(id: string | MongoId, key: string): unknown;
|
|
56
|
+
setValue<K extends keyof IsData<T> & string>(key: K, val: PathValueOf<IsData<T>, K>, options?: {
|
|
58
57
|
silent?: boolean;
|
|
59
58
|
noGet?: boolean;
|
|
60
59
|
noUpdate?: boolean;
|
|
@@ -63,12 +62,12 @@ export declare abstract class AutoUpdatedClientObject<T extends AutoUpdatedClien
|
|
|
63
62
|
success: boolean;
|
|
64
63
|
msg: string;
|
|
65
64
|
}>;
|
|
66
|
-
protected setValueInternal(key: string, value:
|
|
65
|
+
protected setValueInternal(key: string, value: unknown, silent?: boolean, noUpdate?: boolean): Promise<{
|
|
67
66
|
success: boolean;
|
|
68
67
|
msg: string;
|
|
69
68
|
}>;
|
|
70
|
-
protected makeUpdate(key: string, value:
|
|
71
|
-
protected resolveReference(id: string): Promise<AutoUpdatedClientObject<any> | null>;
|
|
69
|
+
protected makeUpdate(key: string, value: unknown): ServerUpdateRequest<T>;
|
|
70
|
+
protected resolveReference(id: string): Promise<AutoUpdatedClientObject<any, any> | null>;
|
|
72
71
|
private findAndLoadReferences;
|
|
73
72
|
protected wipeSelf(): Promise<void>;
|
|
74
73
|
private loadForceReferences;
|
|
@@ -83,9 +82,8 @@ export declare abstract class AutoUpdatedClientObject<T extends AutoUpdatedClien
|
|
|
83
82
|
private findMissingObjectReference;
|
|
84
83
|
contactChildren(): Promise<void>;
|
|
85
84
|
}
|
|
86
|
-
export declare function processIsRefProperties(instance:
|
|
85
|
+
export declare function processIsRefProperties(instance: Record<string, any>, target: object, prefix: string | null, allProps: string[], newData: Record<string, unknown>, loggers: LoggersType): {
|
|
87
86
|
allProps: string[];
|
|
88
|
-
newData:
|
|
87
|
+
newData: Record<string, unknown>;
|
|
89
88
|
};
|
|
90
|
-
export declare function getMetadataRecursive(metaKey: string,
|
|
91
|
-
export {};
|
|
89
|
+
export declare function getMetadataRecursive(metaKey: string, target: object, prop: string): unknown;
|
|
@@ -7,7 +7,7 @@ export class AutoUpdatedClientObject {
|
|
|
7
7
|
preLoad;
|
|
8
8
|
registerSocket;
|
|
9
9
|
readyLoggers;
|
|
10
|
-
loadFromDB(a) { }
|
|
10
|
+
loadFromDB(a) { return a; }
|
|
11
11
|
socket;
|
|
12
12
|
data;
|
|
13
13
|
isServer = false;
|
|
@@ -35,7 +35,7 @@ export class AutoUpdatedClientObject {
|
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
37
|
catch (error) {
|
|
38
|
-
this.loggers
|
|
38
|
+
// this.loggers?.error?.("Error loading references: " + error.message);
|
|
39
39
|
}
|
|
40
40
|
finally {
|
|
41
41
|
this.isLoadingReferences = false;
|
|
@@ -59,18 +59,8 @@ export class AutoUpdatedClientObject {
|
|
|
59
59
|
this.parentManager = parentManager;
|
|
60
60
|
this.callbacks = callback;
|
|
61
61
|
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???");
|
|
62
|
+
this.properties = [];
|
|
63
|
+
return;
|
|
74
64
|
}
|
|
75
65
|
this.classParam = classParam;
|
|
76
66
|
this.socket = socket;
|
|
@@ -83,31 +73,44 @@ export class AutoUpdatedClientObject {
|
|
|
83
73
|
const allProps = new Set();
|
|
84
74
|
let proto = classParam.prototype;
|
|
85
75
|
while (proto && proto !== Object.prototype) {
|
|
86
|
-
const props = Reflect.getOwnMetadata("props", proto)
|
|
87
|
-
|
|
88
|
-
|
|
76
|
+
const props = Reflect.getOwnMetadata("props", proto);
|
|
77
|
+
if (props) {
|
|
78
|
+
for (const p of props)
|
|
79
|
+
allProps.add(p);
|
|
80
|
+
}
|
|
89
81
|
proto = Object.getPrototypeOf(proto);
|
|
90
82
|
}
|
|
91
83
|
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}`),
|
|
84
|
+
this.callbacks = callback || {
|
|
85
|
+
new: () => { },
|
|
86
|
+
update: () => { },
|
|
87
|
+
delete: () => { },
|
|
88
|
+
progress: () => { },
|
|
98
89
|
};
|
|
99
90
|
if (typeof data === "string") {
|
|
100
91
|
this.data = { _id: data };
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
this.data = data;
|
|
95
|
+
}
|
|
96
|
+
const currentId = this.data?._id?.toString() ?? "not loaded";
|
|
97
|
+
this.loggers = {
|
|
98
|
+
debug: (s) => loggers.debug?.(`[${this.className}: ${currentId}] ${s}`),
|
|
99
|
+
info: (s) => loggers.info?.(`[${this.className}: ${currentId}] ${s}`),
|
|
100
|
+
warn: (s) => loggers.warn?.(`[${this.className}: ${currentId}] ${s}`),
|
|
101
|
+
error: (s) => loggers.error?.(`[${this.className}: ${currentId}] ${s}`),
|
|
102
|
+
};
|
|
103
|
+
if (typeof data === "string") {
|
|
101
104
|
if (this.isServer) {
|
|
102
105
|
this.isLoading = false;
|
|
103
106
|
this.generateSettersAndGetters();
|
|
107
|
+
this.emitter.emit(EVENT_INTERNAL_PRE_LOADED + this.EmitterID);
|
|
104
108
|
return;
|
|
105
109
|
}
|
|
106
110
|
this.socket.emit(EVENT_GET + this.className + data, null, async (res) => {
|
|
107
111
|
if (!res.success) {
|
|
108
112
|
this.isLoading = false;
|
|
109
|
-
this.
|
|
110
|
-
this.emitter.emit(EVENT_INTERNAL_PRE_LOADED + this.EmitterID);
|
|
113
|
+
this.emitter.emit(EVENT_INTERNAL_PRE_LOADED + this.EmitterID, true, res.message);
|
|
111
114
|
return;
|
|
112
115
|
}
|
|
113
116
|
this.data = res.data;
|
|
@@ -119,44 +122,46 @@ export class AutoUpdatedClientObject {
|
|
|
119
122
|
});
|
|
120
123
|
}
|
|
121
124
|
else {
|
|
122
|
-
|
|
123
|
-
this.
|
|
124
|
-
for (const key of this.properties || []) {
|
|
125
|
+
const currentDataRec = this.data;
|
|
126
|
+
for (const key of this.properties) {
|
|
125
127
|
const isRef = getMetadataRecursive("isRef", this, key);
|
|
126
|
-
if (isRef &&
|
|
127
|
-
if (Array.isArray(
|
|
128
|
-
|
|
128
|
+
if (isRef && currentDataRec[key]) {
|
|
129
|
+
if (Array.isArray(currentDataRec[key])) {
|
|
130
|
+
currentDataRec[key] = currentDataRec[key].map((obj) => obj._id?.toString() ?? obj?.toString());
|
|
129
131
|
}
|
|
130
132
|
else {
|
|
131
|
-
|
|
133
|
+
currentDataRec[key] = currentDataRec[key]?._id?.toString() ?? currentDataRec[key]?.toString();
|
|
132
134
|
}
|
|
133
135
|
}
|
|
134
136
|
}
|
|
135
|
-
if ((!
|
|
137
|
+
if ((!currentDataRec["_id"] || currentDataRec["_id"] === "") && !this.isServer) {
|
|
138
|
+
this.isLoading = true;
|
|
136
139
|
this.handleNewObject(this.data);
|
|
137
140
|
}
|
|
138
141
|
else {
|
|
139
142
|
this.isLoading = false;
|
|
143
|
+
this.emitter.emit(EVENT_INTERNAL_PRE_LOADED + this.EmitterID);
|
|
140
144
|
if (!this.isServer) {
|
|
141
145
|
this.openSockets();
|
|
142
|
-
this.onUpdate();
|
|
146
|
+
this.onUpdate();
|
|
143
147
|
}
|
|
144
148
|
}
|
|
145
149
|
}
|
|
146
150
|
this.generateSettersAndGetters();
|
|
147
|
-
//
|
|
151
|
+
// Ensure setters/getters are set after child constructor
|
|
148
152
|
Promise.resolve().then(() => {
|
|
149
153
|
this.generateSettersAndGetters();
|
|
150
154
|
});
|
|
155
|
+
if (!this.isServer) {
|
|
156
|
+
this.loadShit();
|
|
157
|
+
}
|
|
151
158
|
}
|
|
152
159
|
async waitForPreloaded() {
|
|
153
160
|
if (this.isLoaded)
|
|
154
161
|
return;
|
|
155
|
-
// console.log(`[DEBUG] ${this.className} Waiting for preloaded - ID: ${this.data?._id ?? (this as any)._id}`);
|
|
156
162
|
await new Promise((resolve, reject) => {
|
|
157
163
|
const timer = setTimeout(() => {
|
|
158
|
-
|
|
159
|
-
reject(new Error("Timeout waiting for preloaded"));
|
|
164
|
+
reject(new Error(`Timeout waiting for preloaded: ${this.className}`));
|
|
160
165
|
}, 10000);
|
|
161
166
|
this.emitter.once(EVENT_INTERNAL_PRE_LOADED + this.EmitterID, (failed, reason) => {
|
|
162
167
|
clearTimeout(timer);
|
|
@@ -169,10 +174,14 @@ export class AutoUpdatedClientObject {
|
|
|
169
174
|
}
|
|
170
175
|
handleNewObject(data) {
|
|
171
176
|
this.isLoading = true;
|
|
177
|
+
if (!this.socket || typeof this.socket.emit !== 'function') {
|
|
178
|
+
this.isLoading = false;
|
|
179
|
+
this.emitter.emit(EVENT_INTERNAL_PRE_LOADED + this.EmitterID, true, "Socket not available");
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
172
182
|
this.socket.emit(EVENT_NEW + this.className, data, (res) => {
|
|
173
183
|
if (!res.success) {
|
|
174
184
|
this.isLoading = false;
|
|
175
|
-
this.loggers.error("Could not create data on server: " + res.message);
|
|
176
185
|
this.emitter.emit(EVENT_INTERNAL_PRE_LOADED + this.EmitterID, true, res.message);
|
|
177
186
|
return;
|
|
178
187
|
}
|
|
@@ -186,13 +195,13 @@ export class AutoUpdatedClientObject {
|
|
|
186
195
|
}
|
|
187
196
|
get extractedData() {
|
|
188
197
|
const extracted = processIsRefProperties(this.data, this, null, [], {}, this.loggers).newData;
|
|
189
|
-
return
|
|
198
|
+
return extracted;
|
|
190
199
|
}
|
|
191
200
|
get isLoaded() {
|
|
192
201
|
return !this.isLoading;
|
|
193
202
|
}
|
|
194
203
|
async isPreLoadedAsync() {
|
|
195
|
-
await this.
|
|
204
|
+
await this.waitForPreloaded();
|
|
196
205
|
return true;
|
|
197
206
|
}
|
|
198
207
|
async loadMissingReferences() {
|
|
@@ -200,11 +209,14 @@ export class AutoUpdatedClientObject {
|
|
|
200
209
|
this.generateSettersAndGetters();
|
|
201
210
|
}
|
|
202
211
|
openSockets() {
|
|
203
|
-
const
|
|
212
|
+
const dataRec = this.data;
|
|
213
|
+
const id = dataRec["_id"];
|
|
214
|
+
if (!id || !this.socket || typeof this.socket.on !== 'function')
|
|
215
|
+
return;
|
|
204
216
|
const event = EVENT_UPDATE + this.className + id.toString();
|
|
205
217
|
this.socket.on(event, async (update, ack) => {
|
|
206
218
|
const res = await this.handleUpdateRequest(update);
|
|
207
|
-
if (ack
|
|
219
|
+
if (ack)
|
|
208
220
|
ack(res);
|
|
209
221
|
return res;
|
|
210
222
|
});
|
|
@@ -217,7 +229,8 @@ export class AutoUpdatedClientObject {
|
|
|
217
229
|
return { success: true, data: undefined, message: "" };
|
|
218
230
|
}
|
|
219
231
|
catch (error) {
|
|
220
|
-
|
|
232
|
+
const dataRec = this.data;
|
|
233
|
+
this.loggers.error?.(`[${dataRec["_id"]?.toString()}] Error applying patch: ${error.message}`);
|
|
221
234
|
return { success: false, message: "Error applying update: " + error.message };
|
|
222
235
|
}
|
|
223
236
|
}
|
|
@@ -230,7 +243,8 @@ export class AutoUpdatedClientObject {
|
|
|
230
243
|
const isRef = getMetadataRecursive("isRef", this, key);
|
|
231
244
|
Object.defineProperty(this, key, {
|
|
232
245
|
get: () => {
|
|
233
|
-
|
|
246
|
+
const dataRec = this.data;
|
|
247
|
+
let val = dataRec ? dataRec[key] : undefined;
|
|
234
248
|
if (isRef && val) {
|
|
235
249
|
if (Array.isArray(val)) {
|
|
236
250
|
return val.map((id) => this.findReference(id, key)).filter(Boolean);
|
|
@@ -242,7 +256,8 @@ export class AutoUpdatedClientObject {
|
|
|
242
256
|
return val;
|
|
243
257
|
},
|
|
244
258
|
set: (v) => {
|
|
245
|
-
|
|
259
|
+
if (this.data)
|
|
260
|
+
this.data[key] = v;
|
|
246
261
|
},
|
|
247
262
|
enumerable: true,
|
|
248
263
|
configurable: true,
|
|
@@ -251,19 +266,20 @@ export class AutoUpdatedClientObject {
|
|
|
251
266
|
}
|
|
252
267
|
getValue(key_) {
|
|
253
268
|
const key = key_;
|
|
254
|
-
|
|
255
|
-
return this[key]
|
|
269
|
+
const dataRec = this.data;
|
|
270
|
+
return (this)[key] === undefined ? (dataRec ? dataRec[key_] : undefined) : this[key_];
|
|
256
271
|
}
|
|
257
272
|
findReference(id, key) {
|
|
258
|
-
if (!id)
|
|
273
|
+
if (!id || !this.parentManager)
|
|
259
274
|
return undefined;
|
|
260
275
|
const idStr = id.toString();
|
|
261
|
-
|
|
262
|
-
|
|
276
|
+
const cacheRec = this.parentManager.cache.references;
|
|
277
|
+
if (cacheRec[key])
|
|
278
|
+
return cacheRec[key].getObject(idStr);
|
|
263
279
|
for (const manager of Object.values(this.parentManager.managers)) {
|
|
264
280
|
const result = manager.getObject(idStr);
|
|
265
281
|
if (result) {
|
|
266
|
-
|
|
282
|
+
cacheRec[key] = manager;
|
|
267
283
|
return result;
|
|
268
284
|
}
|
|
269
285
|
}
|
|
@@ -281,16 +297,18 @@ export class AutoUpdatedClientObject {
|
|
|
281
297
|
if (isRef) {
|
|
282
298
|
valueToStore = Array.isArray(val)
|
|
283
299
|
? val.map((v) => v._id?.toString() ?? v.toString())
|
|
284
|
-
: (val?._id ?? val?.toString() ?? val);
|
|
300
|
+
: (val?._id?.toString() ?? val?.toString() ?? val);
|
|
285
301
|
}
|
|
286
|
-
const
|
|
302
|
+
const dataRec = this.data;
|
|
303
|
+
const currentVal = dataRec ? dataRec[key] : undefined;
|
|
287
304
|
if (_.isEqual(currentVal, valueToStore))
|
|
288
305
|
return { success: true, msg: "Successfully set " + key + " to " + val };
|
|
289
306
|
const res = await this.setValueInternal(key, valueToStore, silent, noUpdate);
|
|
290
307
|
if (res.success) {
|
|
291
|
-
this.data
|
|
308
|
+
if (this.data)
|
|
309
|
+
this.data[key] = valueToStore;
|
|
292
310
|
await this.findAndLoadReferences(key, valueToStore);
|
|
293
|
-
if (isRef && this.parentManager.isLoaded)
|
|
311
|
+
if (isRef && this.parentManager && this.parentManager.isLoaded)
|
|
294
312
|
await this.contactChildren();
|
|
295
313
|
if (this.isLoaded)
|
|
296
314
|
this.callbacks.update(this, key);
|
|
@@ -298,7 +316,7 @@ export class AutoUpdatedClientObject {
|
|
|
298
316
|
return { ...res, msg: res.msg ?? "Successfully set " + key + " to " + val };
|
|
299
317
|
}
|
|
300
318
|
catch (error) {
|
|
301
|
-
this.loggers.error(`Error setting value ${key}: ${error.message}`);
|
|
319
|
+
this.loggers.error?.(`Error setting value ${key}: ${error.message}`);
|
|
302
320
|
return { success: false, msg: error.message };
|
|
303
321
|
}
|
|
304
322
|
}
|
|
@@ -306,21 +324,34 @@ export class AutoUpdatedClientObject {
|
|
|
306
324
|
if (silent || noUpdate)
|
|
307
325
|
return { success: true, msg: "Silent or no update" };
|
|
308
326
|
return new Promise((resolve) => {
|
|
309
|
-
const
|
|
310
|
-
|
|
327
|
+
const dataRec = this.data;
|
|
328
|
+
const id = dataRec ? dataRec["_id"] : undefined;
|
|
329
|
+
if (!id)
|
|
330
|
+
return resolve({ success: false, msg: "Missing _id" });
|
|
331
|
+
if (!this.socket || typeof this.socket.emit !== 'function') {
|
|
332
|
+
return resolve({ success: false, msg: "Socket not available" });
|
|
333
|
+
}
|
|
334
|
+
const timeout = setTimeout(() => {
|
|
335
|
+
resolve({ success: false, msg: "Timeout waiting for server response" });
|
|
336
|
+
}, 5000);
|
|
337
|
+
this.socket.emit(EVENT_UPDATE + this.className + id.toString(), { _id: id.toString(), key, value }, (res) => {
|
|
338
|
+
clearTimeout(timeout);
|
|
311
339
|
resolve({ success: res.success, msg: res.message ?? (res.success ? "Success" : "Error") });
|
|
312
340
|
});
|
|
313
341
|
});
|
|
314
342
|
}
|
|
315
343
|
makeUpdate(key, value) {
|
|
316
|
-
const
|
|
344
|
+
const dataRec = this.data;
|
|
345
|
+
const id = dataRec ? dataRec["_id"] : undefined;
|
|
317
346
|
if (!id) {
|
|
318
|
-
this.loggers.error(`Probably missing the identifier ['_id'] again: ${key} = ${value}`);
|
|
347
|
+
this.loggers.error?.(`Probably missing the identifier ['_id'] again: ${key} = ${value}`);
|
|
319
348
|
throw new Error(`Cannot make update for ${this.className} because _id is missing.`);
|
|
320
349
|
}
|
|
321
|
-
return { _id: id
|
|
350
|
+
return { _id: id, key, value };
|
|
322
351
|
}
|
|
323
352
|
async resolveReference(id) {
|
|
353
|
+
if (!this.parentManager)
|
|
354
|
+
return null;
|
|
324
355
|
for (const manager of Object.values(this.parentManager.managers)) {
|
|
325
356
|
const obj = manager.getObject(id);
|
|
326
357
|
if (obj)
|
|
@@ -330,7 +361,7 @@ export class AutoUpdatedClientObject {
|
|
|
330
361
|
}
|
|
331
362
|
async findAndLoadReferences(lastPath, value) {
|
|
332
363
|
const isRef = getMetadataRecursive("isRef", this, lastPath);
|
|
333
|
-
if (isRef) {
|
|
364
|
+
if (isRef && this.parentManager) {
|
|
334
365
|
for (const id of Array.isArray(value) ? value : [value]) {
|
|
335
366
|
if (!id)
|
|
336
367
|
continue;
|
|
@@ -347,51 +378,57 @@ export class AutoUpdatedClientObject {
|
|
|
347
378
|
}
|
|
348
379
|
}
|
|
349
380
|
async wipeSelf() {
|
|
350
|
-
|
|
381
|
+
const dataRec = this.data;
|
|
382
|
+
if (!dataRec || dataRec["Wiped"])
|
|
351
383
|
return;
|
|
352
|
-
const id =
|
|
384
|
+
const id = dataRec["_id"];
|
|
353
385
|
const _id = id ? id.toString() : "unknown";
|
|
354
|
-
for (const key of Object.keys(
|
|
355
|
-
delete
|
|
386
|
+
for (const key of Object.keys(dataRec)) {
|
|
387
|
+
delete dataRec[key];
|
|
356
388
|
}
|
|
357
|
-
|
|
358
|
-
this.loggers.info(`[${_id}] ${this.className} object wiped`);
|
|
389
|
+
dataRec["Wiped"] = true;
|
|
390
|
+
this.loggers.info?.(`[${_id}] ${this.className} object wiped`);
|
|
359
391
|
}
|
|
360
392
|
async loadForceReferences(obj = this.data, proto = this, alreadySeen = []) {
|
|
393
|
+
if (!obj)
|
|
394
|
+
return;
|
|
361
395
|
if (obj === this.data) {
|
|
362
|
-
const
|
|
396
|
+
const dataRec = this.data;
|
|
397
|
+
const myId = dataRec["_id"]?.toString();
|
|
363
398
|
if (myId && !alreadySeen.includes(myId))
|
|
364
399
|
alreadySeen.push(myId);
|
|
365
400
|
}
|
|
366
401
|
const props = Reflect.getMetadata("props", proto) || [];
|
|
367
402
|
for (const key of props) {
|
|
368
|
-
if (typeof key !== "string")
|
|
369
|
-
continue;
|
|
370
403
|
const isRef = Reflect.getMetadata("isRef", proto, key);
|
|
371
404
|
const pointer = Reflect.getMetadata("refsTo", proto, key);
|
|
372
|
-
|
|
373
|
-
|
|
405
|
+
const objRec = obj;
|
|
406
|
+
if (pointer && obj === this.data && objRec[key] && !alreadySeen.includes(JSON.stringify(obj))) {
|
|
407
|
+
await this.createdWithParent(pointer.split(":"), objRec[key]);
|
|
374
408
|
}
|
|
375
|
-
if (
|
|
376
|
-
alreadySeen.push(
|
|
409
|
+
if (objRec[key] && !alreadySeen.includes(objRec[key]))
|
|
410
|
+
alreadySeen.push(objRec[key]);
|
|
377
411
|
if (isRef)
|
|
378
412
|
await this.handleLoad(obj, key, alreadySeen);
|
|
379
|
-
const val =
|
|
413
|
+
const val = objRec[key];
|
|
380
414
|
if (val && typeof val === "object") {
|
|
381
415
|
const nestedProto = Object.getPrototypeOf(val);
|
|
382
|
-
if (nestedProto && !alreadySeen.includes(val)) {
|
|
383
|
-
alreadySeen.push(val);
|
|
416
|
+
if (nestedProto && nestedProto !== Object.prototype && !alreadySeen.includes(JSON.stringify(val))) {
|
|
417
|
+
alreadySeen.push(JSON.stringify(val));
|
|
384
418
|
await this.loadForceReferences(val, nestedProto, alreadySeen);
|
|
385
419
|
}
|
|
386
420
|
}
|
|
387
421
|
}
|
|
388
422
|
}
|
|
389
423
|
async handleLoad(obj, key, alreadySeen) {
|
|
390
|
-
|
|
424
|
+
if (!this.parentManager)
|
|
425
|
+
return;
|
|
426
|
+
const objRec = obj;
|
|
427
|
+
const refIds = Array.isArray(objRec[key]) ? objRec[key] : [objRec[key]];
|
|
391
428
|
for (const refId of refIds) {
|
|
392
429
|
if (refId) {
|
|
393
430
|
const idStr = refId.toString();
|
|
394
|
-
let result = globalCache.objects[idStr]?.object;
|
|
431
|
+
let result = (globalCache.objects[idStr]?.object);
|
|
395
432
|
if (!result) {
|
|
396
433
|
for (const manager of Object.values(this.parentManager.managers)) {
|
|
397
434
|
result = manager.getObject(idStr);
|
|
@@ -414,14 +451,20 @@ export class AutoUpdatedClientObject {
|
|
|
414
451
|
});
|
|
415
452
|
}
|
|
416
453
|
async createdWithParent(pointer, parent) {
|
|
417
|
-
if (pointer.length !== 2)
|
|
454
|
+
if (pointer.length !== 2 || !this.parentManager)
|
|
418
455
|
return;
|
|
419
456
|
const parentId = parent._id?.toString() ?? parent.toString();
|
|
420
|
-
const
|
|
457
|
+
const ac = this.parentManager.managers[pointer[0]];
|
|
458
|
+
if (!ac)
|
|
459
|
+
return;
|
|
460
|
+
const obj = ac.getObject(parentId);
|
|
421
461
|
if (!obj)
|
|
422
462
|
return;
|
|
423
463
|
const val = obj.getValue(pointer[1]);
|
|
424
|
-
const
|
|
464
|
+
const dataRec = this.data;
|
|
465
|
+
const myId = dataRec["_id"]?.toString();
|
|
466
|
+
if (!myId)
|
|
467
|
+
return;
|
|
425
468
|
if (Array.isArray(val)) {
|
|
426
469
|
const ids = val.map(v => v._id?.toString() ?? v.toString());
|
|
427
470
|
if (!ids.includes(myId)) {
|
|
@@ -433,10 +476,21 @@ export class AutoUpdatedClientObject {
|
|
|
433
476
|
}
|
|
434
477
|
}
|
|
435
478
|
async destroy(once = false) {
|
|
436
|
-
|
|
437
|
-
|
|
479
|
+
const dataRec = this.data;
|
|
480
|
+
const id = dataRec ? dataRec["_id"] : undefined;
|
|
481
|
+
if (!id)
|
|
482
|
+
return { success: false, message: "Missing _id" };
|
|
483
|
+
if (!once && this.parentManager)
|
|
484
|
+
return await this.parentManager.deleteObject(id);
|
|
438
485
|
return new Promise((resolve) => {
|
|
439
|
-
this.socket
|
|
486
|
+
if (!this.socket || typeof this.socket.emit !== 'function') {
|
|
487
|
+
return resolve({ success: false, message: "Socket not available" });
|
|
488
|
+
}
|
|
489
|
+
const timeout = setTimeout(() => {
|
|
490
|
+
resolve({ success: false, message: "Timeout waiting for server deletion" });
|
|
491
|
+
}, 5000);
|
|
492
|
+
this.socket.emit(EVENT_DELETE + this.className, id.toString(), (res) => {
|
|
493
|
+
clearTimeout(timeout);
|
|
440
494
|
resolve({ success: res.success, message: res.message ?? "" });
|
|
441
495
|
});
|
|
442
496
|
});
|
|
@@ -444,7 +498,7 @@ export class AutoUpdatedClientObject {
|
|
|
444
498
|
async checkForMissingRefs() {
|
|
445
499
|
for (const prop of this.properties) {
|
|
446
500
|
const pointer = getMetadataRecursive("refsTo", this, prop.toString());
|
|
447
|
-
if (pointer) {
|
|
501
|
+
if (typeof pointer === "string") {
|
|
448
502
|
const parts = pointer.split(":");
|
|
449
503
|
if (parts.length === 2)
|
|
450
504
|
await this.findMissingObjectReference(prop, parts);
|
|
@@ -452,13 +506,16 @@ export class AutoUpdatedClientObject {
|
|
|
452
506
|
}
|
|
453
507
|
}
|
|
454
508
|
async findMissingObjectReference(prop, pointer) {
|
|
455
|
-
if (this.checkedMissingRefs)
|
|
509
|
+
if (this.checkedMissingRefs || !this.parentManager)
|
|
456
510
|
return;
|
|
457
511
|
this.checkedMissingRefs = true;
|
|
458
512
|
const ac = this.parentManager.managers[pointer[0]];
|
|
459
513
|
if (!ac)
|
|
460
514
|
return;
|
|
461
|
-
const
|
|
515
|
+
const dataRec = this.data;
|
|
516
|
+
const targetId = dataRec ? dataRec["_id"]?.toString() : undefined;
|
|
517
|
+
if (!targetId)
|
|
518
|
+
return;
|
|
462
519
|
const allObjects = Object.values(ac.objects);
|
|
463
520
|
for (const obj of allObjects) {
|
|
464
521
|
if (!obj.isLoaded)
|
|
@@ -468,7 +525,7 @@ export class AutoUpdatedClientObject {
|
|
|
468
525
|
continue;
|
|
469
526
|
const ids = Array.isArray(val) ? val.map(v => v._id?.toString() ?? v.toString()) : [val._id?.toString() ?? val.toString()];
|
|
470
527
|
if (ids.includes(targetId)) {
|
|
471
|
-
|
|
528
|
+
dataRec[prop] = obj._id;
|
|
472
529
|
return;
|
|
473
530
|
}
|
|
474
531
|
}
|
|
@@ -497,6 +554,8 @@ export function processIsRefProperties(instance, target, prefix, allProps, newDa
|
|
|
497
554
|
for (const prop of props) {
|
|
498
555
|
const path = prefix ? `${prefix}.${prop}` : prop;
|
|
499
556
|
allProps.push(path);
|
|
557
|
+
if (!instance)
|
|
558
|
+
continue;
|
|
500
559
|
newData[prop] = ObjectId.isValid(instance[prop])
|
|
501
560
|
? instance[prop]?.toString()
|
|
502
561
|
: instance[prop];
|
|
@@ -511,18 +570,16 @@ export function processIsRefProperties(instance, target, prefix, allProps, newDa
|
|
|
511
570
|
}
|
|
512
571
|
const type = Reflect.getMetadata("design:type", target, prop);
|
|
513
572
|
if (type?.prototype) {
|
|
514
|
-
|
|
515
|
-
if (nestedProps && instance[prop]) {
|
|
516
|
-
newData[prop] = processIsRefProperties(instance[prop], type.prototype, path, allProps, {}, loggers).newData;
|
|
517
|
-
}
|
|
573
|
+
// DO NOT RECURSE into nested prototypes to avoid circularity issues
|
|
518
574
|
}
|
|
519
575
|
}
|
|
520
576
|
return { allProps, newData };
|
|
521
577
|
}
|
|
522
|
-
export function getMetadataRecursive(metaKey,
|
|
578
|
+
export function getMetadataRecursive(metaKey, target, prop) {
|
|
579
|
+
let proto = target;
|
|
523
580
|
while (proto) {
|
|
524
581
|
const meta = Reflect.getMetadata(metaKey, proto, prop);
|
|
525
|
-
if (meta)
|
|
582
|
+
if (meta !== undefined)
|
|
526
583
|
return meta;
|
|
527
584
|
proto = Object.getPrototypeOf(proto);
|
|
528
585
|
}
|