iobroker.al-ko 0.3.1 → 0.3.3
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/README.md +4 -20
- package/docs/de/README.md +15 -0
- package/docs/en/README.md +4 -4
- package/io-package.json +9 -13
- package/main.js +214 -24
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -53,28 +53,12 @@ Do **not** contact AL-KO customer service regarding this project.
|
|
|
53
53
|
|
|
54
54
|
## Changelog
|
|
55
55
|
|
|
56
|
-
### 0.3.
|
|
56
|
+
### 0.3.3 (2026-03-13)
|
|
57
57
|
|
|
58
|
-
-
|
|
59
|
-
-
|
|
58
|
+
- Improved WebSocket reconnect handling
|
|
59
|
+
- Fixed processing of AL-KO WebSocket reportedState messages
|
|
60
|
+
- Updated Dependabot and workflow configuration
|
|
60
61
|
- Updated development dependencies
|
|
61
|
-
- Minor CI / workflow cleanup
|
|
62
|
-
- No functional changes
|
|
63
|
-
|
|
64
|
-
### 0.3.0 (2026-03-09)
|
|
65
|
-
|
|
66
|
-
- Major maintenance release
|
|
67
|
-
- Updated ESLint 9, Prettier 3 and TypeScript tooling
|
|
68
|
-
- Updated development dependencies
|
|
69
|
-
- Improved CI pipeline and adapter structure
|
|
70
|
-
- No functional changes
|
|
71
|
-
|
|
72
|
-
### 0.2.15 (2025-11-02)
|
|
73
|
-
|
|
74
|
-
- Cleaned up admin/jsonConfig structure for adapter-check
|
|
75
|
-
- Added missing `size` attributes
|
|
76
|
-
- Added `.commitinfo` to `.gitignore`
|
|
77
|
-
- No functional changes
|
|
78
62
|
|
|
79
63
|
➡ Full changelog here:
|
|
80
64
|
[CHANGELOG.md](./CHANGELOG.md)
|
package/docs/de/README.md
CHANGED
|
@@ -48,6 +48,21 @@ AL-KO bietet **keinen offiziellen Support** hierfür.
|
|
|
48
48
|
|
|
49
49
|
## Änderungen (Auszug)
|
|
50
50
|
|
|
51
|
+
### 0.3.3 (2026-03-13)
|
|
52
|
+
|
|
53
|
+
- WebSocket-Verarbeitung verbessert
|
|
54
|
+
- Verarbeitung der AL-KO `reportedState` WebSocket-Nachrichten korrigiert
|
|
55
|
+
- Stabileres Wiederverbinden der WebSocket-Verbindung
|
|
56
|
+
- GitHub-Workflows aktualisiert (Dependabot / Automerge)
|
|
57
|
+
- Entwicklungsabhängigkeiten aktualisiert
|
|
58
|
+
|
|
59
|
+
### 0.3.2 (2026-03-12)
|
|
60
|
+
|
|
61
|
+
- WebSocket-Reconnect nach Token-Aktualisierung verbessert
|
|
62
|
+
- Reconnect-Schleifen bei absichtlich geschlossenen WebSocket-Verbindungen verhindert
|
|
63
|
+
- API-Fehlerlogging für Push-Requests verbessert
|
|
64
|
+
- Logging für WebSocket-Close-Code und Reason ergänzt
|
|
65
|
+
|
|
51
66
|
### 0.3.1 (2026-03-09)
|
|
52
67
|
|
|
53
68
|
- Verbesserte Dokumentation
|
package/docs/en/README.md
CHANGED
|
@@ -48,12 +48,12 @@ It is a **community-developed project**.
|
|
|
48
48
|
|
|
49
49
|
## Changes (Summary)
|
|
50
50
|
|
|
51
|
-
### 0.3.
|
|
51
|
+
### 0.3.3 (2026-03-13)
|
|
52
52
|
|
|
53
|
-
-
|
|
54
|
-
-
|
|
53
|
+
- Improved WebSocket reconnect handling
|
|
54
|
+
- Fixed processing of AL-KO WebSocket reportedState messages
|
|
55
|
+
- Updated Dependabot and workflow configuration
|
|
55
56
|
- Updated development dependencies
|
|
56
|
-
- No functional changes
|
|
57
57
|
|
|
58
58
|
See full changelog here:
|
|
59
59
|
➡ [CHANGELOG.md](../../CHANGELOG.md)
|
package/io-package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"common": {
|
|
3
3
|
"name": "al-ko",
|
|
4
|
-
"version": "0.3.
|
|
4
|
+
"version": "0.3.3",
|
|
5
5
|
"tier": 3,
|
|
6
6
|
"titleLang": {
|
|
7
7
|
"en": "AL-KO",
|
|
@@ -40,6 +40,14 @@
|
|
|
40
40
|
"de": "docs/de/README.md"
|
|
41
41
|
},
|
|
42
42
|
"news": {
|
|
43
|
+
"0.3.3": {
|
|
44
|
+
"en": "Improved WebSocket handling and message processing, updated repository workflows, and updated development dependencies.",
|
|
45
|
+
"de": "WebSocket-Handling und Nachrichtenverarbeitung verbessert, Repository-Workflows aktualisiert und Entwicklungsabhängigkeiten aktualisiert."
|
|
46
|
+
},
|
|
47
|
+
"0.3.2": {
|
|
48
|
+
"en": "Improved WebSocket reconnect handling, prevented reconnect loops on intentional closes, and improved API error logging for push requests.",
|
|
49
|
+
"de": "WebSocket-Reconnect-Verhalten verbessert, Reconnect-Schleifen bei absichtlich geschlossenen Verbindungen verhindert und API-Fehlerlogging für Push-Requests verbessert."
|
|
50
|
+
},
|
|
43
51
|
"0.3.1": {
|
|
44
52
|
"en": "Documentation improvements, corrected license information and updated development dependencies. No functional changes.",
|
|
45
53
|
"de": "Dokumentation verbessert, Lizenzangaben korrigiert und Entwicklungsabhängigkeiten aktualisiert. Keine funktionalen Änderungen."
|
|
@@ -47,18 +55,6 @@
|
|
|
47
55
|
"0.3.0": {
|
|
48
56
|
"en": "Major maintenance release. Updated ESLint 9, Prettier 3, TypeScript tooling and development dependencies. Improved adapter structure and CI pipeline. No functional changes.",
|
|
49
57
|
"de": "Größeres Wartungsrelease. Aktualisierung von ESLint 9, Prettier 3, TypeScript-Tooling und Entwicklungsabhängigkeiten. Verbesserte Adapterstruktur und CI-Pipeline. Keine funktionalen Änderungen."
|
|
50
|
-
},
|
|
51
|
-
"0.2.15": {
|
|
52
|
-
"en": "Admin config cleanup for adapter-check: removed $schema, corrected structure, added missing size attributes.",
|
|
53
|
-
"de": "Admin-Konfiguration für adapter-check bereinigt: $schema entfernt, Struktur korrigiert, fehlende size-Attribute ergänzt."
|
|
54
|
-
},
|
|
55
|
-
"0.2.14": {
|
|
56
|
-
"en": "Updated development tooling: release-script v5, plugins v4, ESLint 9, TypeScript 5.9, Prettier 3.",
|
|
57
|
-
"de": "Entwicklungswerkzeuge aktualisiert: release-script v5, Plugins v4, ESLint 9, TypeScript 5.9, Prettier 3."
|
|
58
|
-
},
|
|
59
|
-
"0.2.13": {
|
|
60
|
-
"en": "Fixed JSON syntax error in io-package.json. No functional changes.",
|
|
61
|
-
"de": "JSON-Syntaxfehler in io-package.json behoben. Keine funktionalen Änderungen."
|
|
62
58
|
}
|
|
63
59
|
},
|
|
64
60
|
"keywords": ["al-ko", "Robolinho", "mower", "garden", "smart-garden"],
|
package/main.js
CHANGED
|
@@ -83,7 +83,9 @@ class AlKoAdapter extends utils.Adapter {
|
|
|
83
83
|
this.log.info("Adapter is ready.");
|
|
84
84
|
} catch (err) {
|
|
85
85
|
this.log.error(
|
|
86
|
-
`Startup error: ${err.response?.
|
|
86
|
+
`Startup error: ${err.response?.status || ""} ${
|
|
87
|
+
JSON.stringify(err.response?.data, null, 2) || err.message
|
|
88
|
+
}`,
|
|
87
89
|
);
|
|
88
90
|
}
|
|
89
91
|
}
|
|
@@ -139,6 +141,34 @@ class AlKoAdapter extends utils.Adapter {
|
|
|
139
141
|
this.tokenExpiresAt = Date.now() + res.data.expires_in * 1000;
|
|
140
142
|
|
|
141
143
|
this.log.debug("Access token refreshed successfully.");
|
|
144
|
+
|
|
145
|
+
for (const deviceId of Object.keys(this.webSockets)) {
|
|
146
|
+
try {
|
|
147
|
+
this.log.debug(
|
|
148
|
+
`Reconnecting WebSocket for ${deviceId} after token refresh`,
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
// const ws = this.webSockets[deviceId];
|
|
152
|
+
// if (ws) {
|
|
153
|
+
// ws.terminate();
|
|
154
|
+
//}
|
|
155
|
+
|
|
156
|
+
const ws = this.webSockets[deviceId];
|
|
157
|
+
if (ws) {
|
|
158
|
+
ws._intentionalClose = true;
|
|
159
|
+
ws.terminate();
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
this.clearInterval(this.pingIntervals[deviceId]);
|
|
163
|
+
this.clearTimeout(this.pongTimeouts[deviceId]);
|
|
164
|
+
|
|
165
|
+
this.connectWebSocket(deviceId);
|
|
166
|
+
} catch (err) {
|
|
167
|
+
this.log.warn(
|
|
168
|
+
`Failed to reconnect WebSocket for ${deviceId}: ${err.message}`,
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
142
172
|
}
|
|
143
173
|
}
|
|
144
174
|
|
|
@@ -227,24 +257,137 @@ class AlKoAdapter extends utils.Adapter {
|
|
|
227
257
|
return res.data;
|
|
228
258
|
}
|
|
229
259
|
|
|
260
|
+
// ---------------- WebSocket Handling ----------------
|
|
261
|
+
// alte Version
|
|
262
|
+
|
|
263
|
+
//connectWebSocket(deviceId) {
|
|
264
|
+
// if (!this.accessToken) {
|
|
265
|
+
// return;
|
|
266
|
+
// }
|
|
267
|
+
//
|
|
268
|
+
// const url = `wss://socket.al-ko.com/v1?Authorization=${this.accessToken}&thingName=${deviceId}`;
|
|
269
|
+
// const ws = new WebSocket(url);
|
|
270
|
+
//
|
|
271
|
+
// ws.isAlive = true;
|
|
272
|
+
//
|
|
273
|
+
// ws.on("open", () => {
|
|
274
|
+
// this.log.debug(`WebSocket connected for device ${deviceId}`);
|
|
275
|
+
//
|
|
276
|
+
// this.pingIntervals[deviceId] = this.setInterval(() => {
|
|
277
|
+
// if (ws.readyState === WebSocket.OPEN) {
|
|
278
|
+
// ws.ping();
|
|
279
|
+
// ws.isAlive = false;
|
|
280
|
+
//
|
|
281
|
+
// this.pongTimeouts[deviceId] = this.setTimeout(() => {
|
|
282
|
+
// if (!ws.isAlive) {
|
|
283
|
+
// this.log.warn(
|
|
284
|
+
// `WebSocket ping timeout for ${deviceId}, closing connection.`,
|
|
285
|
+
// );
|
|
286
|
+
// ws.terminate();
|
|
287
|
+
// }
|
|
288
|
+
// }, 30000);
|
|
289
|
+
// }
|
|
290
|
+
// }, 120000);
|
|
291
|
+
// });
|
|
292
|
+
//
|
|
293
|
+
// ws.on("pong", () => {
|
|
294
|
+
// ws.isAlive = true;
|
|
295
|
+
// this.clearTimeout(this.pongTimeouts[deviceId]);
|
|
296
|
+
// });
|
|
297
|
+
//
|
|
298
|
+
// ws.on("message", async (msg) => {
|
|
299
|
+
// if (this.config.wsDebug) {
|
|
300
|
+
// this.log.debug(`WebSocket message (${deviceId}): ${msg}`);
|
|
301
|
+
// }
|
|
302
|
+
// try {
|
|
303
|
+
// const data = JSON.parse(msg.toString());
|
|
304
|
+
// if (data && data.state) {
|
|
305
|
+
// const newState = data.state.reported || data.state;
|
|
306
|
+
//
|
|
307
|
+
// this.deviceStates[deviceId] = this.deepMerge(
|
|
308
|
+
// this.deviceStates[deviceId] || {},
|
|
309
|
+
// newState,
|
|
310
|
+
// );
|
|
311
|
+
//
|
|
312
|
+
// await this.createStatesRecursive(
|
|
313
|
+
// `${this.namespace}.${deviceId}.state`,
|
|
314
|
+
// this.deviceStates[deviceId],
|
|
315
|
+
// "",
|
|
316
|
+
// );
|
|
317
|
+
// }
|
|
318
|
+
// } catch (e) {
|
|
319
|
+
// this.log.error(
|
|
320
|
+
// `Error processing WebSocket message for device ${deviceId}: ${e.message}`,
|
|
321
|
+
// );
|
|
322
|
+
// }
|
|
323
|
+
// });
|
|
324
|
+
//
|
|
325
|
+
// ws.on("close", () => {
|
|
326
|
+
// this.log.warn(
|
|
327
|
+
// `WebSocket closed for device ${deviceId}. Retrying in 10 seconds.`,
|
|
328
|
+
// );
|
|
329
|
+
// this.clearInterval(this.pingIntervals[deviceId]);
|
|
330
|
+
// this.clearTimeout(this.pongTimeouts[deviceId]);
|
|
331
|
+
//
|
|
332
|
+
// this.reconnectTimeouts[deviceId] = this.setTimeout(
|
|
333
|
+
// () => this.connectWebSocket(deviceId),
|
|
334
|
+
// 10000,
|
|
335
|
+
// );
|
|
336
|
+
// });
|
|
337
|
+
//
|
|
338
|
+
// ws.on("error", (err) => {
|
|
339
|
+
// this.log.error(`WebSocket error for ${deviceId}: ${err.message}`);
|
|
340
|
+
// try {
|
|
341
|
+
// ws.terminate();
|
|
342
|
+
// } catch {}
|
|
343
|
+
// });
|
|
344
|
+
//
|
|
345
|
+
// this.webSockets[deviceId] = ws;
|
|
346
|
+
//}
|
|
347
|
+
|
|
230
348
|
// ---------------- WebSocket Handling ----------------
|
|
231
349
|
connectWebSocket(deviceId) {
|
|
232
|
-
if (!this.accessToken) {
|
|
350
|
+
if (!this.accessToken || this._stopRequested) {
|
|
233
351
|
return;
|
|
234
352
|
}
|
|
235
353
|
|
|
354
|
+
if (this.reconnectTimeouts[deviceId]) {
|
|
355
|
+
this.clearTimeout(this.reconnectTimeouts[deviceId]);
|
|
356
|
+
delete this.reconnectTimeouts[deviceId];
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
const existingWs = this.webSockets[deviceId];
|
|
360
|
+
if (existingWs) {
|
|
361
|
+
try {
|
|
362
|
+
existingWs._intentionalClose = true;
|
|
363
|
+
existingWs.terminate();
|
|
364
|
+
} catch {}
|
|
365
|
+
}
|
|
366
|
+
|
|
236
367
|
const url = `wss://socket.al-ko.com/v1?Authorization=${this.accessToken}&thingName=${deviceId}`;
|
|
237
368
|
const ws = new WebSocket(url);
|
|
238
369
|
|
|
239
370
|
ws.isAlive = true;
|
|
371
|
+
ws._intentionalClose = false;
|
|
240
372
|
|
|
241
373
|
ws.on("open", () => {
|
|
242
374
|
this.log.debug(`WebSocket connected for device ${deviceId}`);
|
|
243
375
|
|
|
376
|
+
if (this.pingIntervals[deviceId]) {
|
|
377
|
+
this.clearInterval(this.pingIntervals[deviceId]);
|
|
378
|
+
}
|
|
379
|
+
if (this.pongTimeouts[deviceId]) {
|
|
380
|
+
this.clearTimeout(this.pongTimeouts[deviceId]);
|
|
381
|
+
}
|
|
382
|
+
|
|
244
383
|
this.pingIntervals[deviceId] = this.setInterval(() => {
|
|
245
384
|
if (ws.readyState === WebSocket.OPEN) {
|
|
246
|
-
ws.ping();
|
|
247
385
|
ws.isAlive = false;
|
|
386
|
+
ws.ping();
|
|
387
|
+
|
|
388
|
+
if (this.pongTimeouts[deviceId]) {
|
|
389
|
+
this.clearTimeout(this.pongTimeouts[deviceId]);
|
|
390
|
+
}
|
|
248
391
|
|
|
249
392
|
this.pongTimeouts[deviceId] = this.setTimeout(() => {
|
|
250
393
|
if (!ws.isAlive) {
|
|
@@ -260,29 +403,51 @@ class AlKoAdapter extends utils.Adapter {
|
|
|
260
403
|
|
|
261
404
|
ws.on("pong", () => {
|
|
262
405
|
ws.isAlive = true;
|
|
263
|
-
|
|
406
|
+
if (this.pongTimeouts[deviceId]) {
|
|
407
|
+
this.clearTimeout(this.pongTimeouts[deviceId]);
|
|
408
|
+
delete this.pongTimeouts[deviceId];
|
|
409
|
+
}
|
|
264
410
|
});
|
|
265
411
|
|
|
266
412
|
ws.on("message", async (msg) => {
|
|
267
413
|
if (this.config.wsDebug) {
|
|
268
414
|
this.log.debug(`WebSocket message (${deviceId}): ${msg}`);
|
|
269
415
|
}
|
|
416
|
+
|
|
270
417
|
try {
|
|
271
418
|
const data = JSON.parse(msg.toString());
|
|
272
|
-
if (data && data.state) {
|
|
273
|
-
const newState = data.state.reported || data.state;
|
|
274
419
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
420
|
+
let newState = null;
|
|
421
|
+
|
|
422
|
+
if (data?.reportedState?.state?.reported) {
|
|
423
|
+
newState = data.reportedState.state.reported;
|
|
424
|
+
} else if (
|
|
425
|
+
data?.reportedState &&
|
|
426
|
+
typeof data.reportedState === "object" &&
|
|
427
|
+
!data.reportedState.state &&
|
|
428
|
+
!data.reportedState.previous &&
|
|
429
|
+
!data.reportedState.current &&
|
|
430
|
+
!data.reportedState.metadata
|
|
431
|
+
) {
|
|
432
|
+
newState = data.reportedState;
|
|
433
|
+
} else if (data?.state?.reported) {
|
|
434
|
+
newState = data.state.reported;
|
|
435
|
+
}
|
|
279
436
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
this.deviceStates[deviceId],
|
|
283
|
-
"",
|
|
284
|
-
);
|
|
437
|
+
if (!newState || typeof newState !== "object") {
|
|
438
|
+
return;
|
|
285
439
|
}
|
|
440
|
+
|
|
441
|
+
this.deviceStates[deviceId] = this.deepMerge(
|
|
442
|
+
this.deviceStates[deviceId] || {},
|
|
443
|
+
newState,
|
|
444
|
+
);
|
|
445
|
+
|
|
446
|
+
await this.createStatesRecursive(
|
|
447
|
+
`${this.namespace}.${deviceId}.state`,
|
|
448
|
+
this.deviceStates[deviceId],
|
|
449
|
+
"",
|
|
450
|
+
);
|
|
286
451
|
} catch (e) {
|
|
287
452
|
this.log.error(
|
|
288
453
|
`Error processing WebSocket message for device ${deviceId}: ${e.message}`,
|
|
@@ -290,17 +455,36 @@ class AlKoAdapter extends utils.Adapter {
|
|
|
290
455
|
}
|
|
291
456
|
});
|
|
292
457
|
|
|
293
|
-
ws.on("close", () => {
|
|
458
|
+
ws.on("close", (code, reason) => {
|
|
459
|
+
if (this.pingIntervals[deviceId]) {
|
|
460
|
+
this.clearInterval(this.pingIntervals[deviceId]);
|
|
461
|
+
delete this.pingIntervals[deviceId];
|
|
462
|
+
}
|
|
463
|
+
if (this.pongTimeouts[deviceId]) {
|
|
464
|
+
this.clearTimeout(this.pongTimeouts[deviceId]);
|
|
465
|
+
delete this.pongTimeouts[deviceId];
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
if (this.webSockets[deviceId] === ws) {
|
|
469
|
+
delete this.webSockets[deviceId];
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
if (this._stopRequested || ws._intentionalClose) {
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
|
|
294
476
|
this.log.warn(
|
|
295
|
-
`WebSocket closed for device ${deviceId}. Retrying in 10 seconds.`,
|
|
477
|
+
`WebSocket closed for device ${deviceId}. Code: ${code}, Reason: ${reason?.toString() || "none"}. Retrying in 10 seconds.`,
|
|
296
478
|
);
|
|
297
|
-
this.clearInterval(this.pingIntervals[deviceId]);
|
|
298
|
-
this.clearTimeout(this.pongTimeouts[deviceId]);
|
|
299
479
|
|
|
300
|
-
this.reconnectTimeouts[deviceId]
|
|
301
|
-
(
|
|
302
|
-
|
|
303
|
-
|
|
480
|
+
if (this.reconnectTimeouts[deviceId]) {
|
|
481
|
+
this.clearTimeout(this.reconnectTimeouts[deviceId]);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
this.reconnectTimeouts[deviceId] = this.setTimeout(() => {
|
|
485
|
+
delete this.reconnectTimeouts[deviceId];
|
|
486
|
+
this.connectWebSocket(deviceId);
|
|
487
|
+
}, 10000);
|
|
304
488
|
});
|
|
305
489
|
|
|
306
490
|
ws.on("error", (err) => {
|
|
@@ -536,8 +720,14 @@ class AlKoAdapter extends utils.Adapter {
|
|
|
536
720
|
this.log.info(`Push successful: ${id}`);
|
|
537
721
|
this.updateDeviceStateCache(deviceId, relPathArr, state.val);
|
|
538
722
|
} catch (err) {
|
|
723
|
+
const status = err.response?.status;
|
|
724
|
+
const data =
|
|
725
|
+
typeof err.response?.data === "object"
|
|
726
|
+
? JSON.stringify(err.response.data, null, 2)
|
|
727
|
+
: err.response?.data;
|
|
728
|
+
|
|
539
729
|
this.log.error(
|
|
540
|
-
`Error pushing state ${id}: ${
|
|
730
|
+
`Error pushing state ${id}: ${status || ""} ${data || err.message}`,
|
|
541
731
|
);
|
|
542
732
|
} finally {
|
|
543
733
|
this.pendingPushes.delete(id);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "iobroker.al-ko",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.3",
|
|
4
4
|
"description": "Adapter for communication with AL-KO smart garden devices (Robolinho, mowing windows, operationState, etc.)",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Hubert Zechner",
|
|
@@ -64,9 +64,9 @@
|
|
|
64
64
|
"@iobroker/eslint-config": "^2.2.0",
|
|
65
65
|
"@iobroker/testing": "^5.2.2",
|
|
66
66
|
"@tsconfig/node20": "^20.1.8",
|
|
67
|
-
"@types/node": "^25.
|
|
68
|
-
"@typescript-eslint/eslint-plugin": "^8.
|
|
69
|
-
"@typescript-eslint/parser": "^8.
|
|
67
|
+
"@types/node": "^25.5.0",
|
|
68
|
+
"@typescript-eslint/eslint-plugin": "^8.57.0",
|
|
69
|
+
"@typescript-eslint/parser": "^8.57.0",
|
|
70
70
|
"prettier": "^3.8.1",
|
|
71
71
|
"proxyquire": "^2.1.3"
|
|
72
72
|
},
|