docpouch-client 0.8.9 → 0.8.11

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/index.d.ts CHANGED
@@ -106,7 +106,6 @@ export interface I_UserCreation {
106
106
  isAdmin: boolean;
107
107
  }
108
108
  export interface I_UserUpdate {
109
- _id: string;
110
109
  name?: string;
111
110
  password?: string;
112
111
  email?: string;
@@ -142,7 +141,6 @@ export interface I_DocumentCreationOwned extends I_DocumentCreation {
142
141
  owner: string;
143
142
  }
144
143
  export interface I_DocumentUpdate extends I_DocumentQuery {
145
- _id: string;
146
144
  content?: any;
147
145
  description?: string;
148
146
  }
@@ -178,7 +176,6 @@ export interface I_StructureCreation {
178
176
  fields: I_StructureField[];
179
177
  }
180
178
  export interface I_StructureUpdate {
181
- _id?: string;
182
179
  name?: string;
183
180
  description?: string;
184
181
  fields?: I_StructureField[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "docpouch-client",
3
- "version": "0.8.9",
3
+ "version": "0.8.11",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "exports": {
@@ -27,10 +27,10 @@
27
27
  },
28
28
  "devDependencies": {
29
29
  "@types/jest": "^30.0.0",
30
- "@types/node": "^24.0.7",
31
- "jest": "^30.0.3",
32
- "ts-jest": "^29.4.0",
33
- "typescript": "^5.8.3"
30
+ "@types/node": "^24.3.0",
31
+ "jest": "^30.0.5",
32
+ "ts-jest": "^29.4.1",
33
+ "typescript": "^5.9.2"
34
34
  },
35
35
  "dependencies": {
36
36
  "socket.io-client": "^4.8.1"
package/src/index.ts CHANGED
@@ -5,511 +5,509 @@ import packetJson from '../package.json'
5
5
  * Client for interacting with docPouch API.
6
6
  */
7
7
  export default class docPouchClient {
8
- /**
9
- * The base URL of the server.
10
- *
11
- * @type {string}
12
- */
13
- baseUrl: string;
14
- /**
15
- * Socket.IO socket instance for real-time communication with the server.
16
- *
17
- * @type {Socket}
18
- */
19
- socket: Socket;
20
- /**
21
- * Callback function to handle socket events.
22
- *
23
- * @type {(event: I_EventString, data: I_WsMessage) => void}
24
- */
25
- callbackFunction;
26
- /**
27
- * Flag indicating whether real-time synchronization is enabled.
28
- *
29
- * @type {boolean}
30
- */
31
- realTimeSync: boolean = false;
32
- /**
33
- * Authentication token used to authorize requests.
34
- *
35
- * @private
36
- * @type {string | null}
37
- */
38
- private authToken: string | null = null;
39
- /**
40
- * Flag indicating whether a connection attempt is in progress.
41
- *
42
- * @private
43
- * @type {boolean}
44
- */
45
- private connectionInProgress = false;
46
-
47
- /**
48
- * Creates an instance of docPouchClient.
49
- *
50
- * @param {string} host - The base URL for the server.
51
- * @param {number} [port=80] - The port number to connect to (default is 80).
52
- * @param {(event: I_EventString, data: I_WsMessage) => void} [callback] - Optional callback function for socket events.
53
- */
54
- constructor(host: string, port: number = 80, callback?: (event: I_EventString, data: I_WsMessage) => void) {
55
- this.baseUrl = host;
56
- const socketUrl = host.includes('://') ? host : `https://${host}`;
57
- const socketUrlWithPort = socketUrl.includes(':') && !socketUrl.endsWith(':')
58
- ? socketUrl
59
- : `${socketUrl}:${port}`;
60
-
61
- console.log(`Initializing Socket.IO with URL: ${socketUrlWithPort}, path: /socket.io`);
62
-
63
- this.socket = io(`${socketUrlWithPort}`, {
64
- autoConnect: false,
65
- transports: ['websocket'], // Try websocket only first
66
- reconnection: true,
67
- reconnectionAttempts: 5,
68
- reconnectionDelay: 1000,
69
- forceNew: true, // Force a new connection
70
- auth: {
71
- token: null // Will be set later
72
- },
73
- path: '/socket.io'
74
- });
75
-
76
- this.callbackFunction = callback;
77
-
78
- this.setupPermanentSocketListeners();
79
- }
80
-
81
- /**
82
- * Sets the real-time synchronization status.
83
- *
84
- * @param {boolean} newRealTimeSync - The new real-time sync setting (true/false).
85
- */
86
- setRealTimeSync(newRealTimeSync: boolean) {
87
- console.log(`Setting realtime sync to: ${newRealTimeSync}. Current setting: ${this.realTimeSync}`);
88
-
89
- // Skip if the setting isn't changing
90
- if (newRealTimeSync === this.realTimeSync) {
91
- console.log("Realtime sync setting unchanged, skipping");
92
- return;
8
+ /**
9
+ * The base URL of the server.
10
+ *
11
+ * @type {string}
12
+ */
13
+ baseUrl: string;
14
+ /**
15
+ * Socket.IO socket instance for real-time communication with the server.
16
+ *
17
+ * @type {Socket}
18
+ */
19
+ socket: Socket;
20
+ /**
21
+ * Callback function to handle socket events.
22
+ *
23
+ * @type {(event: I_EventString, data: I_WsMessage) => void}
24
+ */
25
+ callbackFunction;
26
+ /**
27
+ * Flag indicating whether real-time synchronization is enabled.
28
+ *
29
+ * @type {boolean}
30
+ */
31
+ realTimeSync: boolean = false;
32
+ /**
33
+ * Authentication token used to authorize requests.
34
+ *
35
+ * @private
36
+ * @type {string | null}
37
+ */
38
+ private authToken: string | null = null;
39
+ /**
40
+ * Flag indicating whether a connection attempt is in progress.
41
+ *
42
+ * @private
43
+ * @type {boolean}
44
+ */
45
+ private connectionInProgress = false;
46
+
47
+ /**
48
+ * Creates an instance of docPouchClient.
49
+ *
50
+ * @param {string} host - The base URL for the server.
51
+ * @param {number} [port=80] - The port number to connect to (default is 80).
52
+ * @param {(event: I_EventString, data: I_WsMessage) => void} [callback] - Optional callback function for socket events.
53
+ */
54
+ constructor(host: string, port: number = 80, callback?: (event: I_EventString, data: I_WsMessage) => void) {
55
+ this.baseUrl = host;
56
+ const socketUrl = host.includes('://') ? host : `https://${host}`;
57
+ const socketUrlWithPort = socketUrl.includes(':') && !socketUrl.endsWith(':')
58
+ ? socketUrl
59
+ : `${socketUrl}:${port}`;
60
+
61
+ console.log(`Initializing Socket.IO with URL: ${socketUrlWithPort}, path: /socket.io`);
62
+
63
+ this.socket = io(`${socketUrlWithPort}`, {
64
+ autoConnect: false,
65
+ transports: ['websocket'], // Try websocket only first
66
+ reconnection: true,
67
+ reconnectionAttempts: 5,
68
+ reconnectionDelay: 1000,
69
+ forceNew: true, // Force a new connection
70
+ auth: {
71
+ token: null // Will be set later
72
+ },
73
+ path: '/socket.io'
74
+ });
75
+
76
+ this.callbackFunction = callback;
77
+
78
+ this.setupPermanentSocketListeners();
93
79
  }
94
80
 
95
- this.realTimeSync = newRealTimeSync;
96
-
97
- if (newRealTimeSync && this.authToken) {
98
- console.log("Activating realtime updates");
99
-
100
- // Ensure we're not in the middle of another connection attempt
101
- if (this.connectionInProgress) {
102
- console.log("Connection already in progress, waiting before initializing");
103
- setTimeout(() => this.initWebSocket(), 500);
104
- } else {
105
- this.initWebSocket();
106
- }
107
- } else if (!newRealTimeSync) {
108
- console.log("Deactivating realtime updates");
109
- if (this.socket.connected) {
110
- console.log("Disconnecting socket");
111
- this.socket.disconnect();
112
- }
81
+ /**
82
+ * Sets the real-time synchronization status.
83
+ *
84
+ * @param {boolean} newRealTimeSync - The new real-time sync setting (true/false).
85
+ */
86
+ setRealTimeSync(newRealTimeSync: boolean) {
87
+ console.log(`Setting realtime sync to: ${newRealTimeSync}. Current setting: ${this.realTimeSync}`);
88
+
89
+ // Skip if the setting isn't changing
90
+ if (newRealTimeSync === this.realTimeSync) {
91
+ console.log("Realtime sync setting unchanged, skipping");
92
+ return;
93
+ }
94
+
95
+ this.realTimeSync = newRealTimeSync;
96
+
97
+ if (newRealTimeSync && this.authToken) {
98
+ console.log("Activating realtime updates");
99
+
100
+ // Ensure we're not in the middle of another connection attempt
101
+ if (this.connectionInProgress) {
102
+ console.log("Connection already in progress, waiting before initializing");
103
+ setTimeout(() => this.initWebSocket(), 500);
104
+ } else {
105
+ this.initWebSocket();
106
+ }
107
+ } else if (!newRealTimeSync) {
108
+ console.log("Deactivating realtime updates");
109
+ if (this.socket.connected) {
110
+ console.log("Disconnecting socket");
111
+ this.socket.disconnect();
112
+ }
113
+ }
113
114
  }
114
- }
115
115
 
116
- // User Administration Endpoints
117
- async login(credentials: I_UserLogin): Promise<I_LoginResponse | null> {
118
- const response = await this.request<I_LoginResponse>('/users/login', 'POST', credentials, false);
119
- if (response.token) {
120
- this.authToken = response.token;
116
+ // User Administration Endpoints
117
+ async login(credentials: I_UserLogin): Promise<I_LoginResponse | null> {
118
+ const response = await this.request<I_LoginResponse>('/users/login', 'POST', credentials, false);
119
+ if (response.token) {
120
+ this.authToken = response.token;
121
121
 
122
- // Reconnect websocket with new token if realtime sync is enabled
123
- if (this.realTimeSync) {
124
- this.initWebSocket();
125
- }
122
+ // Reconnect websocket with new token if realtime sync is enabled
123
+ if (this.realTimeSync) {
124
+ this.initWebSocket();
125
+ }
126
126
 
127
- return {token: response.token, isAdmin: response.isAdmin, userName: response.userName};
127
+ return {token: response.token, isAdmin: response.isAdmin, userName: response.userName};
128
+ }
129
+ return null;
128
130
  }
129
- return null;
130
- }
131
131
 
132
- async listUsers(): Promise<I_UserEntry[]> {
133
- return await this.request<I_UserEntry[]>('/users/list', 'GET');
134
- }
132
+ async listUsers(): Promise<I_UserEntry[]> {
133
+ return await this.request<I_UserEntry[]>('/users/list', 'GET');
134
+ }
135
135
 
136
- async updateUser(userID: string, userData: I_UserUpdate): Promise<void> {
137
- await this.request<void>(`/users/update/${userID}`, 'PATCH', userData);
138
- }
136
+ async updateUser(userID: string, userData: I_UserUpdate): Promise<void> {
137
+ await this.request<void>(`/users/update/${userID}`, 'PATCH', userData);
138
+ }
139
139
 
140
- async createUser(userData: I_UserCreation): Promise<I_UserDisplay> {
141
- return await this.request<I_UserDisplay>('/users/create', 'POST', userData);
142
- }
140
+ async createUser(userData: I_UserCreation): Promise<I_UserDisplay> {
141
+ return await this.request<I_UserDisplay>('/users/create', 'POST', userData);
142
+ }
143
143
 
144
- async removeUser(userID: string): Promise<void> {
145
- await this.request<void>(`/users/remove/${userID}`, 'DELETE');
146
- }
144
+ async removeUser(userID: string): Promise<void> {
145
+ await this.request<void>(`/users/remove/${userID}`, 'DELETE');
146
+ }
147
147
 
148
- // Document Management Endpoints
149
- async createDocument(document: I_DocumentEntry): Promise<I_DocumentEntry> {
150
- return await this.request<I_DocumentEntry>('/docs/create', 'POST', document);
151
- }
148
+ // Document Management Endpoints
149
+ async createDocument(document: I_DocumentEntry): Promise<I_DocumentEntry> {
150
+ return await this.request<I_DocumentEntry>('/docs/create', 'POST', document);
151
+ }
152
152
 
153
- async listDocuments(): Promise<I_DocumentEntry[]> {
154
- return await this.request<I_DocumentEntry[]>('/docs/list', 'GET');
155
- }
153
+ async listDocuments(): Promise<I_DocumentEntry[]> {
154
+ return await this.request<I_DocumentEntry[]>('/docs/list', 'GET');
155
+ }
156
156
 
157
- async fetchDocument(queryObject: I_DocumentQuery): Promise<I_DocumentEntry[]> {
158
- return await this.request<I_DocumentEntry[]>(`/docs/fetch/`, 'POST', queryObject);
159
- }
157
+ async fetchDocument(queryObject: I_DocumentQuery): Promise<I_DocumentEntry[]> {
158
+ return await this.request<I_DocumentEntry[]>(`/docs/fetch/`, 'POST', queryObject);
159
+ }
160
160
 
161
- async updateDocument(documentID: string, documentData: I_DocumentEntry): Promise<void> {
162
- await this.request<void>(`/docs/update/${documentID}`, 'PATCH', documentData);
163
- }
161
+ async updateDocument(documentID: string, documentData: I_DocumentEntry): Promise<void> {
162
+ await this.request<void>(`/docs/update/${documentID}`, 'PATCH', documentData);
163
+ }
164
164
 
165
- async removeDocument(documentID: string): Promise<void> {
166
- await this.request<void>(`/docs/remove/${documentID}`, 'DELETE');
167
- }
165
+ async removeDocument(documentID: string): Promise<void> {
166
+ await this.request<void>(`/docs/remove/${documentID}`, 'DELETE');
167
+ }
168
168
 
169
- // Data Structure Endpoints
170
- async createStructure(structure: I_StructureCreation): Promise<I_DataStructure> {
171
- return await this.request<I_DataStructure>('/structures/create', 'POST', structure);
172
- }
169
+ // Data Structure Endpoints
170
+ async createStructure(structure: I_StructureCreation): Promise<I_DataStructure> {
171
+ return await this.request<I_DataStructure>('/structures/create', 'POST', structure);
172
+ }
173
173
 
174
- async getStructures(): Promise<I_DataStructure[]> {
175
- return await this.request<I_DataStructure[]>('/structures/list', 'GET');
176
- }
174
+ async getStructures(): Promise<I_DataStructure[]> {
175
+ return await this.request<I_DataStructure[]>('/structures/list', 'GET');
176
+ }
177
177
 
178
- async updateStructure(structureID: string, structureData: I_DataStructure): Promise<void> {
179
- await this.request<void>(`/structures/update/${structureID}`, 'PATCH', structureData);
180
- }
178
+ async updateStructure(structureID: string, structureData: I_DataStructure): Promise<void> {
179
+ await this.request<void>(`/structures/update/${structureID}`, 'PATCH', structureData);
180
+ }
181
181
 
182
- async removeStructure(structureID: string): Promise<void> {
183
- await this.request<void>(`/structures/remove/${structureID}`, 'DELETE');
184
- }
182
+ async removeStructure(structureID: string): Promise<void> {
183
+ await this.request<void>(`/structures/remove/${structureID}`, 'DELETE');
184
+ }
185
185
 
186
- // Data Type Endpoints
187
- async createType(type: I_DocumentType): Promise<I_DocumentType> {
188
- return await this.request<I_DocumentType>('/types/write', 'POST', type);
189
- }
186
+ // Data Type Endpoints
187
+ async createType(type: I_DocumentType): Promise<I_DocumentType> {
188
+ return await this.request<I_DocumentType>('/types/write', 'POST', type);
189
+ }
190
190
 
191
- async removeType(typeID: string) {
192
- return await this.request<void>(`/types/remove/${typeID}`, 'DELETE');
193
- }
191
+ async removeType(typeID: string) {
192
+ return await this.request<void>(`/types/remove/${typeID}`, 'DELETE');
193
+ }
194
194
 
195
- async getTypes(): Promise<I_DocumentType[]> {
196
- return await this.request<I_DocumentType[]>('/types/list', 'GET');
197
- }
195
+ async getTypes(): Promise<I_DocumentType[]> {
196
+ return await this.request<I_DocumentType[]>('/types/list', 'GET');
197
+ }
198
198
 
199
- async updateType(updatedType: I_DocumentType): Promise<void> {
200
- await this.request<void>(`/types/write`, 'POST', updatedType);
201
- }
199
+ async updateType(updatedType: I_DocumentType): Promise<void> {
200
+ await this.request<void>(`/types/write`, 'POST', updatedType);
201
+ }
202
202
 
203
- setToken(token: string | null): void {
204
- console.log("Setting token to:", token ? "***token***" : "null");
203
+ setToken(token: string | null): void {
204
+ console.log("Setting token to:", token ? "***token***" : "null");
205
205
 
206
- const tokenChanged = this.authToken !== token;
207
- this.authToken = token;
206
+ const tokenChanged = this.authToken !== token;
207
+ this.authToken = token;
208
208
 
209
- if (!tokenChanged) {
210
- console.log("Token unchanged, no need to reconnect");
211
- return;
212
- }
209
+ if (!tokenChanged) {
210
+ console.log("Token unchanged, no need to reconnect");
211
+ return;
212
+ }
213
213
 
214
- // If we have a new token and realtime sync is enabled
215
- if (token && this.realTimeSync) {
216
- console.log("New token set, will initialize WebSocket");
217
-
218
- // Ensure any existing connection is closed first
219
- if (this.socket.connected) {
220
- console.log("Disconnecting existing socket before reconnecting with new token");
221
- this.socket.disconnect();
222
- }
223
-
224
- // Wait a moment for the disconnect to complete
225
- setTimeout(() => {
226
- console.log("Initializing WebSocket with new token");
227
- this.initWebSocket();
228
- }, 300);
229
- }
230
- // If token was cleared or realtime sync is disabled
231
- else if (this.socket.connected) {
232
- console.log("Token cleared or realtime sync disabled, disconnecting");
233
- this.socket.disconnect();
234
- }
235
- }
236
-
237
- getVersion() {
238
- return packetJson.version;
239
- }
240
-
241
- debugSocketConnection(): void {
242
- console.log("Socket connection debug info:");
243
- console.log("- Connected:", this.socket.connected);
244
- console.log("- Socket ID:", this.socket.id);
245
- console.log("- Auth token present:", !!this.authToken);
246
- console.log("- Connection in progress:", this.connectionInProgress);
247
- console.log("- Realtime sync enabled:", this.realTimeSync);
248
- console.log("- Socket options:", this.socket.io.opts);
249
-
250
- // Try to force reconnection
251
- if (!this.socket.connected && this.authToken && this.realTimeSync) {
252
- console.log("Attempting to force reconnection...");
253
- this.socket.auth = {token: this.authToken};
254
- this.socket.connect();
214
+ // If we have a new token and realtime sync is enabled
215
+ if (token && this.realTimeSync) {
216
+ console.log("New token set, will initialize WebSocket");
217
+
218
+ // Ensure any existing connection is closed first
219
+ if (this.socket.connected) {
220
+ console.log("Disconnecting existing socket before reconnecting with new token");
221
+ this.socket.disconnect();
222
+ }
223
+
224
+ // Wait a moment for the disconnect to complete
225
+ setTimeout(() => {
226
+ console.log("Initializing WebSocket with new token");
227
+ this.initWebSocket();
228
+ }, 300);
229
+ }
230
+ // If token was cleared or realtime sync is disabled
231
+ else if (this.socket.connected) {
232
+ console.log("Token cleared or realtime sync disabled, disconnecting");
233
+ this.socket.disconnect();
234
+ }
255
235
  }
256
- }
257
-
258
- /**
259
- * Sets up permanent socket listeners for the client.
260
- *
261
- * @private
262
- */
263
- private setupPermanentSocketListeners() {
264
- // These are permanent listeners that won't be removed
265
- this.socket.on('connect_error', (error) => {
266
- console.error('Socket connection error:', error.message);
267
- this.connectionInProgress = false;
268
- });
269
-
270
- this.socket.on('connect', () => {
271
- console.log('Socket connected successfully with ID:', this.socket.id);
272
- this.connectionInProgress = false;
273
- });
274
-
275
- this.socket.on('disconnect', (reason) => {
276
- console.log('Socket disconnected. Reason:', reason);
277
- this.connectionInProgress = false;
278
- });
279
-
280
- this.socket.on('error', (error) => {
281
- console.error('Socket error:', error);
282
- this.connectionInProgress = false;
283
- });
284
- }
285
-
286
- /**
287
- * Initializes the WebSocket connection with the server.
288
- *
289
- * @private
290
- */
291
- private initWebSocket() {
292
- console.log("initWebSocket called. Auth token present:", !!this.authToken,
293
- "Connection in progress:", this.connectionInProgress,
294
- "Socket connected:", this.socket.connected);
295
-
296
- if (!this.authToken) {
297
- console.log("Skipping WebSocket initialization: No auth token");
298
- return;
236
+
237
+ getVersion() {
238
+ return packetJson.version;
299
239
  }
300
240
 
301
- if (this.connectionInProgress) {
302
- console.log("Connection already in progress, skipping initialization");
303
- return;
241
+ debugSocketConnection(): void {
242
+ console.log("Socket connection debug info:");
243
+ console.log("- Connected:", this.socket.connected);
244
+ console.log("- Socket ID:", this.socket.id);
245
+ console.log("- Auth token present:", !!this.authToken);
246
+ console.log("- Connection in progress:", this.connectionInProgress);
247
+ console.log("- Realtime sync enabled:", this.realTimeSync);
248
+ console.log("- Socket options:", this.socket.io.opts);
249
+
250
+ // Try to force reconnection
251
+ if (!this.socket.connected && this.authToken && this.realTimeSync) {
252
+ console.log("Attempting to force reconnection...");
253
+ this.socket.auth = {token: this.authToken};
254
+ this.socket.connect();
255
+ }
304
256
  }
305
257
 
306
- if (this.socket.connected) {
307
- console.log("Socket already connected with ID:", this.socket.id);
308
- return;
258
+ /**
259
+ * Sets up permanent socket listeners for the client.
260
+ *
261
+ * @private
262
+ */
263
+ private setupPermanentSocketListeners() {
264
+ // These are permanent listeners that won't be removed
265
+ this.socket.on('connect_error', (error) => {
266
+ console.error('Socket connection error:', error.message);
267
+ this.connectionInProgress = false;
268
+ });
269
+
270
+ this.socket.on('connect', () => {
271
+ console.log('Socket connected successfully with ID:', this.socket.id);
272
+ this.connectionInProgress = false;
273
+ });
274
+
275
+ this.socket.on('disconnect', (reason) => {
276
+ console.log('Socket disconnected. Reason:', reason);
277
+ this.connectionInProgress = false;
278
+ });
279
+
280
+ this.socket.on('error', (error) => {
281
+ console.error('Socket error:', error);
282
+ this.connectionInProgress = false;
283
+ });
309
284
  }
310
285
 
311
- this.connectionInProgress = true;
286
+ /**
287
+ * Initializes the WebSocket connection with the server.
288
+ *
289
+ * @private
290
+ */
291
+ private initWebSocket() {
292
+ console.log("initWebSocket called. Auth token present:", !!this.authToken,
293
+ "Connection in progress:", this.connectionInProgress,
294
+ "Socket connected:", this.socket.connected);
295
+
296
+ if (!this.authToken) {
297
+ console.log("Skipping WebSocket initialization: No auth token");
298
+ return;
299
+ }
312
300
 
313
- try {
314
- console.log("Setting up WebSocket connection with token");
301
+ if (this.connectionInProgress) {
302
+ console.log("Connection already in progress, skipping initialization");
303
+ return;
304
+ }
315
305
 
316
- // Update the auth token
317
- this.socket.auth = {token: this.authToken};
306
+ if (this.socket.connected) {
307
+ console.log("Socket already connected with ID:", this.socket.id);
308
+ return;
309
+ }
318
310
 
319
- // Remove any dynamic event listeners that might have been added
320
- this.socket.offAny();
311
+ this.connectionInProgress = true;
321
312
 
322
- // Set up event handler for application events
323
- this.socket.onAny((event: I_EventString, data: I_WsMessage) => {
324
- if (event === "heartbeatPing") {
325
- console.log("Ping event received:", data);
326
- this.socket.emit("heartbeatPong", Date.now());
327
- } else if (this.callbackFunction) {
328
- this.callbackFunction(event, data);
329
- }
330
- });
313
+ try {
314
+ console.log("Setting up WebSocket connection with token");
331
315
 
332
- // Connect to the server
333
- console.log("Connecting socket with auth token");
334
- this.socket.connect();
316
+ // Update the auth token
317
+ this.socket.auth = {token: this.authToken};
335
318
 
336
- // Add a timeout to detect if connection is taking too long
337
- setTimeout(() => {
338
- if (this.connectionInProgress) {
339
- console.warn("Socket connection attempt timed out after 5 seconds");
340
- this.connectionInProgress = false;
319
+ // Remove any dynamic event listeners that might have been added
320
+ this.socket.offAny();
321
+
322
+ // Set up event handler for application events
323
+ this.socket.onAny((event: I_EventString, data: I_WsMessage) => {
324
+ if (event === "heartbeatPing") {
325
+ console.log("Ping event received:", data);
326
+ this.socket.emit("heartbeatPong", Date.now());
327
+ } else if (this.callbackFunction) {
328
+ this.callbackFunction(event, data);
329
+ }
330
+ });
341
331
 
342
- // If we're still not connected after the timeout, try again with polling
343
- if (!this.socket.connected) {
344
- console.log("Retrying connection with polling transport");
345
- this.socket.io.opts.transports = ['polling', 'websocket'];
332
+ // Connect to the server
333
+ console.log("Connecting socket with auth token");
346
334
  this.socket.connect();
347
- }
335
+
336
+ // Add a timeout to detect if connection is taking too long
337
+ setTimeout(() => {
338
+ if (this.connectionInProgress) {
339
+ console.warn("Socket connection attempt timed out after 5 seconds");
340
+ this.connectionInProgress = false;
341
+
342
+ // If we're still not connected after the timeout, try again with polling
343
+ if (!this.socket.connected) {
344
+ console.log("Retrying connection with polling transport");
345
+ this.socket.io.opts.transports = ['polling', 'websocket'];
346
+ this.socket.connect();
347
+ }
348
+ }
349
+ }, 5000);
350
+ } catch (error) {
351
+ console.error('Error in initWebSocket:', error);
352
+ this.connectionInProgress = false;
348
353
  }
349
- }, 5000);
350
- } catch (error) {
351
- console.error('Error in initWebSocket:', error);
352
- this.connectionInProgress = false;
353
- }
354
- }
355
-
356
- private async request<T>(endpoint: string, method: string, body?: any, requiresAuth: boolean = true): Promise<T> {
357
- const headers: HeadersInit = {
358
- 'Content-Type': 'application/json',
359
- };
360
-
361
- if (requiresAuth && this.authToken)
362
- headers['Authorization'] = `Bearer ${this.authToken}`;
363
- if (this.socket.id)
364
- headers['X-Socket-ID'] = this.socket.id;
365
-
366
- const options: RequestInit = {
367
- method,
368
- headers,
369
- body: body ? JSON.stringify(body) : undefined
370
- };
371
-
372
- const normalizedEndpoint = endpoint.startsWith('/') ? endpoint : `/${endpoint}`;
373
- const normalizedBaseUrl = this.baseUrl.endsWith('/') ? this.baseUrl.slice(0, -1) : this.baseUrl;
374
- const url = `${normalizedBaseUrl}${normalizedEndpoint}`;
375
- const response = await fetch(url, options);
376
-
377
- if (!response.ok) {
378
- if (response.status === 401 || response.status === 403) {
379
- this.authToken = null;
380
- }
381
- throw new Error(`API error: ${response.status} ${response.statusText}`);
382
354
  }
383
355
 
384
- return await response.json() as T;
385
- }
356
+ private async request<T>(endpoint: string, method: string, body?: any, requiresAuth: boolean = true): Promise<T> {
357
+ const headers: HeadersInit = {
358
+ 'Content-Type': 'application/json',
359
+ };
360
+
361
+ if (requiresAuth && this.authToken)
362
+ headers['Authorization'] = `Bearer ${this.authToken}`;
363
+ if (this.socket.id)
364
+ headers['X-Socket-ID'] = this.socket.id;
365
+
366
+ const options: RequestInit = {
367
+ method,
368
+ headers,
369
+ body: body ? JSON.stringify(body) : undefined
370
+ };
371
+
372
+ const normalizedEndpoint = endpoint.startsWith('/') ? endpoint : `/${endpoint}`;
373
+ const normalizedBaseUrl = this.baseUrl.endsWith('/') ? this.baseUrl.slice(0, -1) : this.baseUrl;
374
+ const url = `${normalizedBaseUrl}${normalizedEndpoint}`;
375
+ const response = await fetch(url, options);
376
+
377
+ if (!response.ok) {
378
+ if (response.status === 401 || response.status === 403) {
379
+ this.authToken = null;
380
+ }
381
+ throw new Error(`API error: ${response.status} ${response.statusText}`);
382
+ }
383
+
384
+ return await response.json() as T;
385
+ }
386
386
  }
387
387
 
388
388
  // Common type definitions for both frontend and backend
389
389
 
390
390
  // User related types
391
391
  export interface I_UserEntry extends I_UserCreation {
392
- _id: string;
392
+ _id: string;
393
393
  }
394
394
 
395
395
  export interface I_UserLogin {
396
- name: string;
397
- password: string;
396
+ name: string;
397
+ password: string;
398
398
  }
399
399
 
400
400
  export interface I_UserCreation {
401
- name: string;
402
- password: string;
403
- email?: string;
404
- department: string;
405
- group: string;
406
- isAdmin: boolean;
401
+ name: string;
402
+ password: string;
403
+ email?: string;
404
+ department: string;
405
+ group: string;
406
+ isAdmin: boolean;
407
407
  }
408
408
 
409
409
  export interface I_UserUpdate {
410
- _id: string;
411
- name?: string;
412
- password?: string;
413
- email?: string;
414
- department?: string;
415
- group?: string;
416
- isAdmin?: boolean;
410
+ name?: string;
411
+ password?: string;
412
+ email?: string;
413
+ department?: string;
414
+ group?: string;
415
+ isAdmin?: boolean;
417
416
  }
418
417
 
419
418
  export interface I_UserDisplay {
420
- _id: string;
421
- username: string;
422
- department: string;
423
- group: string;
424
- email?: string;
419
+ _id: string;
420
+ username: string;
421
+ department: string;
422
+ group: string;
423
+ email?: string;
425
424
  }
426
425
 
427
426
  export interface I_LoginResponse {
428
- token: string;
429
- isAdmin: boolean;
430
- userName: string;
427
+ token: string;
428
+ isAdmin: boolean;
429
+ userName: string;
431
430
  }
432
431
 
433
432
  // Document related types
434
433
  export interface I_DocumentEntry extends I_DocumentCreationOwned {
435
- _id: string;
434
+ _id: string;
436
435
  }
437
436
 
438
437
  export interface I_DocumentCreation {
439
- title: string;
440
- description?: string;
441
- type: number;
442
- subType: number;
443
- content: any;
444
- shareWithGroup: boolean;
445
- shareWithDepartment: boolean;
438
+ title: string;
439
+ description?: string;
440
+ type: number;
441
+ subType: number;
442
+ content: any;
443
+ shareWithGroup: boolean;
444
+ shareWithDepartment: boolean;
446
445
  }
447
446
 
448
447
 
449
448
  export interface I_DocumentCreationOwned extends I_DocumentCreation {
450
- owner: string;
449
+ owner: string;
451
450
  }
452
451
 
453
452
  export interface I_DocumentUpdate extends I_DocumentQuery {
454
- _id: string;
455
- content?: any;
456
- description?: string;
453
+ content?: any;
454
+ description?: string;
457
455
  }
458
456
 
459
457
  export interface I_DocumentQuery {
460
- _id?: string;
461
- owner?: string;
462
- title?: string;
463
- type?: number;
464
- subType?: number;
465
- shareWithGroup?: boolean;
466
- shareWithDepartment?: boolean;
458
+ _id?: string;
459
+ owner?: string;
460
+ title?: string;
461
+ type?: number;
462
+ subType?: number;
463
+ shareWithGroup?: boolean;
464
+ shareWithDepartment?: boolean;
467
465
  }
468
466
 
469
467
 
470
468
  // Structure related types
471
469
  export interface I_DataStructure {
472
- _id?: string | undefined;
473
- name: string;
474
- description: string;
475
- fields: I_StructureField[];
470
+ _id?: string | undefined;
471
+ name: string;
472
+ description: string;
473
+ fields: I_StructureField[];
476
474
  }
477
475
 
478
476
  export interface I_StructureField {
479
- name: string;
480
- type: string;
481
- items?: string;
477
+ name: string;
478
+ type: string;
479
+ items?: string;
482
480
  }
483
481
 
484
482
  export interface I_StructureEntry {
485
- _id?: string;
486
- name: string;
487
- description: string;
488
- fields: I_StructureField[];
483
+ _id?: string;
484
+ name: string;
485
+ description: string;
486
+ fields: I_StructureField[];
489
487
  }
490
488
 
491
489
 
492
490
  export interface I_StructureCreation {
493
- name: string;
494
- description?: string;
495
- fields: I_StructureField[];
491
+ name: string;
492
+ description?: string;
493
+ fields: I_StructureField[];
496
494
  }
497
495
 
498
496
  export interface I_StructureUpdate {
499
- _id?: string;
500
- name?: string;
501
- description?: string;
502
- fields?: I_StructureField[];
497
+ _id?: string
498
+ name?: string;
499
+ description?: string;
500
+ fields?: I_StructureField[];
503
501
  }
504
502
 
505
503
  // Document type related types
506
504
  export interface I_DocumentType {
507
- _id?: string;
508
- type: number;
509
- subType: number;
510
- name: string;
511
- description?: string;
512
- defaultStructureID?: string;
505
+ _id?: string;
506
+ type: number;
507
+ subType: number;
508
+ name: string;
509
+ description?: string;
510
+ defaultStructureID?: string;
513
511
  }
514
512
 
515
513
  // WebSocket-related types
@@ -518,16 +516,16 @@ export type I_EventString = 'heartbeatPong' | "heartbeatPing" | "newDocument" |
518
516
  "removedUser" | "removedStructure" | "removedDocument" | "removedType";
519
517
 
520
518
  export interface I_WsMessage {
521
- newDocument?: I_DocumentEntry;
522
- newStructure?: I_StructureEntry;
523
- newUser?: I_UserEntry;
524
- removedID?: string;
525
- changedDocument?: I_DocumentUpdate;
526
- changedStructure?: I_StructureUpdate;
527
- changedUser?: I_UserUpdate;
528
- confirmSubscription?: boolean;
529
- confirmUnsubscription?: boolean;
530
- heartbeatPing?: number;
531
- heartbeatPong?: number;
532
- newType?: I_DocumentType;
519
+ newDocument?: I_DocumentEntry;
520
+ newStructure?: I_StructureEntry;
521
+ newUser?: I_UserEntry;
522
+ removedID?: string;
523
+ changedDocument?: I_DocumentUpdate;
524
+ changedStructure?: I_StructureUpdate;
525
+ changedUser?: I_UserUpdate;
526
+ confirmSubscription?: boolean;
527
+ confirmUnsubscription?: boolean;
528
+ heartbeatPing?: number;
529
+ heartbeatPong?: number;
530
+ newType?: I_DocumentType;
533
531
  }