signalk-edge-link 2.5.1 → 2.6.0
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/lib/instance.js +21 -0
- package/lib/packet.js +20 -7
- package/lib/pipeline-v2-client.js +22 -0
- package/lib/pipeline-v2-server.js +37 -0
- package/lib/shared/connection-schema.js +9 -1
- package/package.json +165 -165
- package/public/982.078efbd502a09820e418.js +2 -0
- package/public/982.078efbd502a09820e418.js.map +1 -0
- package/public/remoteEntry.js +1 -1
- package/public/remoteEntry.js.map +1 -1
- package/public/982.8b63f75cddc5341d56cb.js +0 -2
- package/public/982.8b63f75cddc5341d56cb.js.map +0 -1
package/lib/instance.js
CHANGED
|
@@ -111,6 +111,7 @@ function createInstance(app, options, instanceId, pluginId, onStatusChange) {
|
|
|
111
111
|
metaDiffFlushTimer: null,
|
|
112
112
|
metaSnapshotTimers: [],
|
|
113
113
|
lastMetaRequestAt: 0,
|
|
114
|
+
lastFullStatusRequestAt: 0,
|
|
114
115
|
sourceRegistry: (0, source_replication_1.createSourceRegistry)(app)
|
|
115
116
|
};
|
|
116
117
|
const metricsApi = (0, metrics_1.default)();
|
|
@@ -384,6 +385,22 @@ function createInstance(app, options, instanceId, pluginId, onStatusChange) {
|
|
|
384
385
|
app.debug(`[${instanceId}] META_REQUEST snapshot failed: ${msg}`);
|
|
385
386
|
});
|
|
386
387
|
}
|
|
388
|
+
/** Minimum gap between server-initiated full-status replays. Prevents a
|
|
389
|
+
* restarting or misconfigured server from flooding the link. */
|
|
390
|
+
const FULL_STATUS_REQUEST_RATE_LIMIT_MS = 10000;
|
|
391
|
+
/** Server asked for a full values snapshot (FULL_STATUS_REQUEST control
|
|
392
|
+
* packet). Replays the entire current Signal K tree to the server.
|
|
393
|
+
* Rate-limited to prevent replay floods across rapid server restarts. */
|
|
394
|
+
function handleFullStatusRequest() {
|
|
395
|
+
const now = Date.now();
|
|
396
|
+
if (now - state.lastFullStatusRequestAt < FULL_STATUS_REQUEST_RATE_LIMIT_MS) {
|
|
397
|
+
app.debug(`[${instanceId}] FULL_STATUS_REQUEST rate-limited, skipping`);
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
state.lastFullStatusRequestAt = now;
|
|
401
|
+
app.debug(`[${instanceId}] FULL_STATUS_REQUEST received — replaying values snapshot`);
|
|
402
|
+
replayValuesSnapshot("full-status-request");
|
|
403
|
+
}
|
|
387
404
|
async function sendSourceSnapshot() {
|
|
388
405
|
if (state.stopped ||
|
|
389
406
|
!state.readyToSend ||
|
|
@@ -1128,6 +1145,9 @@ function createInstance(app, options, instanceId, pluginId, onStatusChange) {
|
|
|
1128
1145
|
if (typeof v2Pipeline.setMetaRequestHandler === "function") {
|
|
1129
1146
|
v2Pipeline.setMetaRequestHandler(handleMetaRequest);
|
|
1130
1147
|
}
|
|
1148
|
+
if (typeof v2Pipeline.setFullStatusRequestHandler === "function") {
|
|
1149
|
+
v2Pipeline.setFullStatusRequestHandler(handleFullStatusRequest);
|
|
1150
|
+
}
|
|
1131
1151
|
v2Pipeline.startMetricsPublishing();
|
|
1132
1152
|
if (options.congestionControl && options.congestionControl.enabled) {
|
|
1133
1153
|
v2Pipeline.startCongestionControl();
|
|
@@ -1210,6 +1230,7 @@ function createInstance(app, options, instanceId, pluginId, onStatusChange) {
|
|
|
1210
1230
|
Object.keys(state.configContentHashes).forEach((k) => delete state.configContentHashes[k]);
|
|
1211
1231
|
state.excludedSentences = ["GSV"];
|
|
1212
1232
|
state.lastPacketTime = 0;
|
|
1233
|
+
state.lastFullStatusRequestAt = 0;
|
|
1213
1234
|
// Reset metrics
|
|
1214
1235
|
resetMetrics();
|
|
1215
1236
|
// Clear timers
|
package/lib/packet.js
CHANGED
|
@@ -55,7 +55,9 @@ const PacketType = Object.freeze({
|
|
|
55
55
|
HEARTBEAT: 0x04,
|
|
56
56
|
HELLO: 0x05,
|
|
57
57
|
METADATA: 0x06,
|
|
58
|
-
META_REQUEST: 0x07
|
|
58
|
+
META_REQUEST: 0x07,
|
|
59
|
+
/** Server → client: request a full values snapshot replay. */
|
|
60
|
+
FULL_STATUS_REQUEST: 0x08
|
|
59
61
|
});
|
|
60
62
|
exports.PacketType = PacketType;
|
|
61
63
|
/**
|
|
@@ -176,6 +178,14 @@ class PacketBuilder {
|
|
|
176
178
|
buildMetaRequestPacket(options = {}) {
|
|
177
179
|
return this._buildPacket(PacketType.META_REQUEST, Buffer.alloc(0), {}, options);
|
|
178
180
|
}
|
|
181
|
+
/**
|
|
182
|
+
* Build a FULL_STATUS_REQUEST control packet (server → client).
|
|
183
|
+
* Payload is empty. Instructs the client to replay its full values snapshot
|
|
184
|
+
* so the server can rebuild state after a restart.
|
|
185
|
+
*/
|
|
186
|
+
buildFullStatusRequestPacket(options = {}) {
|
|
187
|
+
return this._buildPacket(PacketType.FULL_STATUS_REQUEST, Buffer.alloc(0), {}, options);
|
|
188
|
+
}
|
|
179
189
|
/**
|
|
180
190
|
* Build an ACK packet
|
|
181
191
|
* @param {number} ackedSequence - Sequence number being acknowledged
|
|
@@ -404,11 +414,13 @@ class PacketParser {
|
|
|
404
414
|
payload = payloadData;
|
|
405
415
|
}
|
|
406
416
|
else {
|
|
407
|
-
// HEARTBEAT and
|
|
408
|
-
// — accept as-is. ACK / NAK / HELLO must include
|
|
409
|
-
// reject undersized payloads so forged control
|
|
410
|
-
// through unverified.
|
|
411
|
-
if (type !== PacketType.HEARTBEAT &&
|
|
417
|
+
// HEARTBEAT, META_REQUEST, and FULL_STATUS_REQUEST carry a 0-byte
|
|
418
|
+
// payload with no CRC — accept as-is. ACK / NAK / HELLO must include
|
|
419
|
+
// a 2-byte CRC16 trailer; reject undersized payloads so forged control
|
|
420
|
+
// frames cannot slip through unverified.
|
|
421
|
+
if (type !== PacketType.HEARTBEAT &&
|
|
422
|
+
type !== PacketType.META_REQUEST &&
|
|
423
|
+
type !== PacketType.FULL_STATUS_REQUEST) {
|
|
412
424
|
if (payload.length < 2) {
|
|
413
425
|
throw new Error(`Control packet payload too short for CRC: ${payload.length} byte(s)`);
|
|
414
426
|
}
|
|
@@ -504,7 +516,8 @@ function getTypeName(type) {
|
|
|
504
516
|
[PacketType.HEARTBEAT]: "HEARTBEAT",
|
|
505
517
|
[PacketType.HELLO]: "HELLO",
|
|
506
518
|
[PacketType.METADATA]: "METADATA",
|
|
507
|
-
[PacketType.META_REQUEST]: "META_REQUEST"
|
|
519
|
+
[PacketType.META_REQUEST]: "META_REQUEST",
|
|
520
|
+
[PacketType.FULL_STATUS_REQUEST]: "FULL_STATUS_REQUEST"
|
|
508
521
|
};
|
|
509
522
|
return names[type] || "UNKNOWN";
|
|
510
523
|
}
|
|
@@ -106,6 +106,9 @@ function createPipelineV2Client(app, state, metricsApi) {
|
|
|
106
106
|
// (META_REQUEST control packet). Wired up by instance.ts, which is the only
|
|
107
107
|
// layer that knows how to build a snapshot from `app.signalk.retrieve()`.
|
|
108
108
|
let metaRequestHandler = null;
|
|
109
|
+
// Callback fired when the server sends FULL_STATUS_REQUEST, asking the client
|
|
110
|
+
// to replay its complete current values snapshot.
|
|
111
|
+
let fullStatusRequestHandler = null;
|
|
109
112
|
let metaEnvelopeSeq = 0;
|
|
110
113
|
let sourceEnvelopeSeq = 0;
|
|
111
114
|
// Seed all four meta bandwidth counters so downstream consumers (metrics
|
|
@@ -632,6 +635,9 @@ function createPipelineV2Client(app, state, metricsApi) {
|
|
|
632
635
|
function setMetaRequestHandler(handler) {
|
|
633
636
|
metaRequestHandler = handler;
|
|
634
637
|
}
|
|
638
|
+
function setFullStatusRequestHandler(handler) {
|
|
639
|
+
fullStatusRequestHandler = handler;
|
|
640
|
+
}
|
|
635
641
|
/**
|
|
636
642
|
* Handle incoming ACK packet from server.
|
|
637
643
|
* Removes acknowledged packets from the retransmit queue.
|
|
@@ -772,6 +778,21 @@ function createPipelineV2Client(app, state, metricsApi) {
|
|
|
772
778
|
}
|
|
773
779
|
}
|
|
774
780
|
}
|
|
781
|
+
else if (parsed.type === packet_1.PacketType.FULL_STATUS_REQUEST) {
|
|
782
|
+
// Server asks us to replay our full values snapshot (e.g. after a
|
|
783
|
+
// server restart). Rate-limited in instance.ts to prevent abuse.
|
|
784
|
+
if (fullStatusRequestHandler) {
|
|
785
|
+
try {
|
|
786
|
+
Promise.resolve(fullStatusRequestHandler()).catch((err) => {
|
|
787
|
+
app.debug(`FULL_STATUS_REQUEST handler rejected: ${err instanceof Error ? err.message : String(err)}`);
|
|
788
|
+
});
|
|
789
|
+
}
|
|
790
|
+
catch (err) {
|
|
791
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
792
|
+
app.debug(`FULL_STATUS_REQUEST handler error: ${errMsg}`);
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
}
|
|
775
796
|
// Ignore other packet types on client side
|
|
776
797
|
}
|
|
777
798
|
catch (err) {
|
|
@@ -1110,6 +1131,7 @@ function createPipelineV2Client(app, state, metricsApi) {
|
|
|
1110
1131
|
sendMetadata,
|
|
1111
1132
|
sendSourceSnapshot,
|
|
1112
1133
|
setMetaRequestHandler,
|
|
1134
|
+
setFullStatusRequestHandler,
|
|
1113
1135
|
getPacketBuilder,
|
|
1114
1136
|
getRetransmitQueue,
|
|
1115
1137
|
getMetricsPublisher,
|
|
@@ -178,6 +178,7 @@ function createPipelineV2Server(app, state, metricsApi) {
|
|
|
178
178
|
rateLimitCount: 0,
|
|
179
179
|
rateLimitWindowStart: Date.now(),
|
|
180
180
|
metaRequested: false,
|
|
181
|
+
statusRequested: false,
|
|
181
182
|
lastMetaEnvSeq: null,
|
|
182
183
|
seenMetaChunkIdx: new Set(),
|
|
183
184
|
lastSourceEnvSeq: null,
|
|
@@ -212,6 +213,8 @@ function createPipelineV2Server(app, state, metricsApi) {
|
|
|
212
213
|
rateLimitWindowStart: Date.now(),
|
|
213
214
|
// META_REQUEST bookkeeping
|
|
214
215
|
metaRequested: false,
|
|
216
|
+
// FULL_STATUS_REQUEST bookkeeping
|
|
217
|
+
statusRequested: false,
|
|
215
218
|
// Stale-envelope rejection for METADATA packets
|
|
216
219
|
lastMetaEnvSeq: null,
|
|
217
220
|
seenMetaChunkIdx: new Set(),
|
|
@@ -491,6 +494,7 @@ function createPipelineV2Server(app, state, metricsApi) {
|
|
|
491
494
|
seenChunkIdx.clear();
|
|
492
495
|
if (!isSource) {
|
|
493
496
|
session.metaRequested = false;
|
|
497
|
+
session.statusRequested = false;
|
|
494
498
|
}
|
|
495
499
|
}
|
|
496
500
|
if (lastEnvSeq !== null) {
|
|
@@ -610,6 +614,7 @@ function createPipelineV2Server(app, state, metricsApi) {
|
|
|
610
614
|
session.lastMetaEnvSeq = null;
|
|
611
615
|
session.seenMetaChunkIdx.clear();
|
|
612
616
|
session.metaRequested = false;
|
|
617
|
+
session.statusRequested = false;
|
|
613
618
|
}
|
|
614
619
|
if (session.lastMetaEnvSeq !== null) {
|
|
615
620
|
const distance = (envSeq - session.lastMetaEnvSeq) >>> 0;
|
|
@@ -697,6 +702,21 @@ function createPipelineV2Server(app, state, metricsApi) {
|
|
|
697
702
|
recordError("general", `v2 META decode error: ${msg}`);
|
|
698
703
|
}
|
|
699
704
|
}
|
|
705
|
+
/**
|
|
706
|
+
* Build and send a FULL_STATUS_REQUEST (0x08) control packet to a client.
|
|
707
|
+
* Instructs the client to replay its complete current values snapshot so the
|
|
708
|
+
* server can rebuild state immediately after a restart.
|
|
709
|
+
*/
|
|
710
|
+
async function _sendFullStatusRequest(session, secretKey) {
|
|
711
|
+
try {
|
|
712
|
+
const packet = packetBuilder.buildFullStatusRequestPacket({ secretKey });
|
|
713
|
+
await _sendUDP(packet, { address: session.address, port: session.port });
|
|
714
|
+
app.debug(`[v2-server] FULL_STATUS_REQUEST sent to ${session.key}`);
|
|
715
|
+
}
|
|
716
|
+
catch (err) {
|
|
717
|
+
throw err;
|
|
718
|
+
}
|
|
719
|
+
}
|
|
700
720
|
/**
|
|
701
721
|
* Build and send a META_REQUEST (0x07) control packet to a client.
|
|
702
722
|
* Instructs the client to emit a fresh metadata snapshot — used on first
|
|
@@ -819,6 +839,15 @@ function createPipelineV2Server(app, state, metricsApi) {
|
|
|
819
839
|
app.debug(`[v2-server] META_REQUEST send failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
820
840
|
});
|
|
821
841
|
}
|
|
842
|
+
// If the operator enabled full-status-on-restart, also request a values
|
|
843
|
+
// snapshot. Capped at one per session so rapid HELLOs (e.g. NAT churn)
|
|
844
|
+
// don't create repeated replay bursts.
|
|
845
|
+
if (session && !session.statusRequested && state.options?.requestFullStatusOnRestart) {
|
|
846
|
+
session.statusRequested = true;
|
|
847
|
+
_sendFullStatusRequest(session, secretKey).catch((err) => {
|
|
848
|
+
app.debug(`[v2-server] FULL_STATUS_REQUEST send failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
849
|
+
});
|
|
850
|
+
}
|
|
822
851
|
return;
|
|
823
852
|
}
|
|
824
853
|
if (parsed.type === packet_1.PacketType.METADATA) {
|
|
@@ -892,6 +921,14 @@ function createPipelineV2Server(app, state, metricsApi) {
|
|
|
892
921
|
if (session) {
|
|
893
922
|
session.hasReceivedData = true;
|
|
894
923
|
}
|
|
924
|
+
// On first DATA from a new session, request full-status replay if enabled.
|
|
925
|
+
// This covers the case where the client sends data before its next HELLO.
|
|
926
|
+
if (session && !session.statusRequested && state.options?.requestFullStatusOnRestart) {
|
|
927
|
+
session.statusRequested = true;
|
|
928
|
+
_sendFullStatusRequest(session, secretKey).catch((err) => {
|
|
929
|
+
app.debug(`[v2-server] FULL_STATUS_REQUEST (data trigger) send failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
930
|
+
});
|
|
931
|
+
}
|
|
895
932
|
const dataSeq = parsed.sequence >>> 0;
|
|
896
933
|
if (session) {
|
|
897
934
|
if (seqResult.resynced || session.lossBaseSeq === null) {
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* results to `RJSFSchema` at call sites.
|
|
16
16
|
*/
|
|
17
17
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
-
exports.alertThresholdsProperty = exports.skipOwnDataProperty = exports.enableNotificationsProperty = exports.bondingProperty = exports.congestionControlProperty = exports.serverReliabilityProperty = exports.clientReliabilityProperty = exports.clientTransportProperties = exports.v1ClientPingProperties = exports.commonConnectionProperties = void 0;
|
|
18
|
+
exports.alertThresholdsProperty = exports.skipOwnDataProperty = exports.enableNotificationsProperty = exports.bondingProperty = exports.congestionControlProperty = exports.serverReliabilityProperty = exports.requestFullStatusOnRestartProperty = exports.clientReliabilityProperty = exports.clientTransportProperties = exports.v1ClientPingProperties = exports.commonConnectionProperties = void 0;
|
|
19
19
|
exports.buildConnectionItemSchema = buildConnectionItemSchema;
|
|
20
20
|
exports.buildWebappConnectionSchema = buildWebappConnectionSchema;
|
|
21
21
|
const crypto_constants_1 = require("./crypto-constants");
|
|
@@ -246,6 +246,12 @@ exports.clientReliabilityProperty = {
|
|
|
246
246
|
}
|
|
247
247
|
};
|
|
248
248
|
// ── v2/v3 reliability (server pipeline — ACK/NAK timing) ──────────────────────
|
|
249
|
+
exports.requestFullStatusOnRestartProperty = {
|
|
250
|
+
type: "boolean",
|
|
251
|
+
title: "Request Full Status on Server Start (v2/v3 only)",
|
|
252
|
+
description: "When enabled, the server sends a request to each client on first contact asking it to replay its complete current values snapshot. This rebuilds the server's state immediately after a restart instead of waiting for incremental deltas to arrive.",
|
|
253
|
+
default: false
|
|
254
|
+
};
|
|
249
255
|
exports.serverReliabilityProperty = {
|
|
250
256
|
type: "object",
|
|
251
257
|
title: "Reliability Settings (v2/v3 only)",
|
|
@@ -511,6 +517,7 @@ function buildConnectionItemSchema() {
|
|
|
511
517
|
{
|
|
512
518
|
properties: {
|
|
513
519
|
serverType: { enum: ["server"] },
|
|
520
|
+
requestFullStatusOnRestart: exports.requestFullStatusOnRestartProperty,
|
|
514
521
|
reliability: exports.serverReliabilityProperty
|
|
515
522
|
}
|
|
516
523
|
},
|
|
@@ -567,6 +574,7 @@ function buildWebappConnectionSchema(isClient, protocolVersion) {
|
|
|
567
574
|
}
|
|
568
575
|
}
|
|
569
576
|
else if (isReliableProtocol) {
|
|
577
|
+
props.requestFullStatusOnRestart = exports.requestFullStatusOnRestartProperty;
|
|
570
578
|
props.reliability = exports.serverReliabilityProperty;
|
|
571
579
|
}
|
|
572
580
|
return { type: "object", required, properties: props };
|
package/package.json
CHANGED
|
@@ -1,165 +1,165 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "signalk-edge-link",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "SignalK Edge Link. Secure UDP link for data exchange.",
|
|
5
|
-
"main": "lib/index.js",
|
|
6
|
-
"files": [
|
|
7
|
-
"lib/",
|
|
8
|
-
"public/"
|
|
9
|
-
],
|
|
10
|
-
"keywords": [
|
|
11
|
-
"signalk-node-server-plugin",
|
|
12
|
-
"signalk-category-network",
|
|
13
|
-
"signalk-webapp",
|
|
14
|
-
"signalk-category-utility",
|
|
15
|
-
"signalk-plugin-configurator"
|
|
16
|
-
],
|
|
17
|
-
"signalk": {
|
|
18
|
-
"appIcon": "./icons/icon-72x72.png",
|
|
19
|
-
"displayName": "Edge Link Configuration"
|
|
20
|
-
},
|
|
21
|
-
"signalk-plugin-enabled-by-default": false,
|
|
22
|
-
"scripts": {
|
|
23
|
-
"clean:lib": "node -e \"const fs=require('fs');if(fs.existsSync('lib'))fs.rmSync('lib',{recursive:true,force:true});\"",
|
|
24
|
-
"clean:public": "node -e \"const fs=require('fs');if(fs.existsSync('public'))fs.rmSync('public',{recursive:true,force:true});\"",
|
|
25
|
-
"build": "npm run build:ts && npm run build:web",
|
|
26
|
-
"build:web": "npm run clean:public && webpack --mode production",
|
|
27
|
-
"build:ts": "npm run clean:lib && tsc",
|
|
28
|
-
"check:ts": "tsc --noEmit",
|
|
29
|
-
"check:release-docs": "node scripts/check-release-truth.js",
|
|
30
|
-
"dev": "webpack --mode development --watch",
|
|
31
|
-
"test": "jest --runInBand",
|
|
32
|
-
"test:v2": "jest __tests__/v2/",
|
|
33
|
-
"test:integration": "jest test/integration/",
|
|
34
|
-
"test:watch": "jest --watch",
|
|
35
|
-
"test:coverage": "jest --coverage",
|
|
36
|
-
"lint": "eslint .",
|
|
37
|
-
"lint:fix": "eslint . --fix",
|
|
38
|
-
"format": "prettier --write \"**/*.{js,ts,json,md}\"",
|
|
39
|
-
"migrate:config": "node lib/scripts/migrate-config.js",
|
|
40
|
-
"cli": "node lib/bin/edge-link-cli.js",
|
|
41
|
-
"prepare": "husky"
|
|
42
|
-
},
|
|
43
|
-
"dependencies": {
|
|
44
|
-
"@msgpack/msgpack": "^3.0.0",
|
|
45
|
-
"@rjsf/core": "^5.18.4",
|
|
46
|
-
"@rjsf/utils": "^5.18.4",
|
|
47
|
-
"@rjsf/validator-ajv8": "^5.18.4",
|
|
48
|
-
"ping-monitor": "^0.8.2"
|
|
49
|
-
},
|
|
50
|
-
"devDependencies": {
|
|
51
|
-
"@babel/core": "^7.22.0",
|
|
52
|
-
"@babel/preset-env": "^7.22.0",
|
|
53
|
-
"@babel/preset-react": "^7.22.0",
|
|
54
|
-
"@testing-library/jest-dom": "^5.17.0",
|
|
55
|
-
"@testing-library/react": "^12.1.5",
|
|
56
|
-
"@types/node": "^25.3.5",
|
|
57
|
-
"@types/react": "^16.14.0",
|
|
58
|
-
"@types/react-dom": "^16.9.0",
|
|
59
|
-
"babel-loader": "^9.1.2",
|
|
60
|
-
"copy-webpack-plugin": "^14.0.0",
|
|
61
|
-
"css-loader": "^6.8.1",
|
|
62
|
-
"eslint": "^8.57.1",
|
|
63
|
-
"eslint-plugin-react": "^7.37.5",
|
|
64
|
-
"html-webpack-plugin": "^5.5.3",
|
|
65
|
-
"husky": "^9.1.7",
|
|
66
|
-
"jest": "^29.7.0",
|
|
67
|
-
"jest-environment-jsdom": "^30.3.0",
|
|
68
|
-
"lint-staged": "^15.4.3",
|
|
69
|
-
"mini-css-extract-plugin": "^2.7.6",
|
|
70
|
-
"prettier": "^3.6.2",
|
|
71
|
-
"react": "^16.13.1",
|
|
72
|
-
"react-dom": "^16.13.1",
|
|
73
|
-
"react-test-renderer": "^16.14.0",
|
|
74
|
-
"style-loader": "^3.3.3",
|
|
75
|
-
"ts-jest": "^29.4.6",
|
|
76
|
-
"ts-loader": "^9.5.4",
|
|
77
|
-
"typescript": "^5.9.3",
|
|
78
|
-
"webpack": "^5.102.1",
|
|
79
|
-
"webpack-cli": "^5.1.4"
|
|
80
|
-
},
|
|
81
|
-
"engines": {
|
|
82
|
-
"node": ">=16"
|
|
83
|
-
},
|
|
84
|
-
"author": "Karl-Erik Gustafsson",
|
|
85
|
-
"repository": "https://github.com/KEGustafsson/signalk-edge-link",
|
|
86
|
-
"homepage": "https://github.com/KEGustafsson/signalk-edge-link#readme",
|
|
87
|
-
"bugs": {
|
|
88
|
-
"url": "https://github.com/KEGustafsson/signalk-edge-link/issues"
|
|
89
|
-
},
|
|
90
|
-
"license": "MIT",
|
|
91
|
-
"jest": {
|
|
92
|
-
"testEnvironment": "node",
|
|
93
|
-
"coverageDirectory": "coverage",
|
|
94
|
-
"collectCoverageFrom": [
|
|
95
|
-
"lib/**/*.js",
|
|
96
|
-
"!lib/webapp/**",
|
|
97
|
-
"!lib/components/**",
|
|
98
|
-
"!lib/utils/**"
|
|
99
|
-
],
|
|
100
|
-
"testMatch": [
|
|
101
|
-
"**/__tests__/**/*.js",
|
|
102
|
-
"**/*.test.js",
|
|
103
|
-
"**/*.spec.js"
|
|
104
|
-
],
|
|
105
|
-
"transform": {
|
|
106
|
-
"^.+\\.js$": [
|
|
107
|
-
"babel-jest",
|
|
108
|
-
{
|
|
109
|
-
"presets": [
|
|
110
|
-
[
|
|
111
|
-
"@babel/preset-env",
|
|
112
|
-
{
|
|
113
|
-
"targets": {
|
|
114
|
-
"node": "current"
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
]
|
|
118
|
-
]
|
|
119
|
-
}
|
|
120
|
-
],
|
|
121
|
-
".+\\.tsx$": [
|
|
122
|
-
"ts-jest",
|
|
123
|
-
{
|
|
124
|
-
"tsconfig": "tsconfig.webapp.json",
|
|
125
|
-
"diagnostics": false
|
|
126
|
-
}
|
|
127
|
-
],
|
|
128
|
-
"^.+\\.ts$": "ts-jest"
|
|
129
|
-
},
|
|
130
|
-
"moduleFileExtensions": [
|
|
131
|
-
"ts",
|
|
132
|
-
"tsx",
|
|
133
|
-
"js",
|
|
134
|
-
"json",
|
|
135
|
-
"node"
|
|
136
|
-
],
|
|
137
|
-
"testPathIgnorePatterns": [
|
|
138
|
-
"/node_modules/",
|
|
139
|
-
"/public/"
|
|
140
|
-
],
|
|
141
|
-
"coverageThreshold": {
|
|
142
|
-
"global": {
|
|
143
|
-
"branches": 60,
|
|
144
|
-
"functions": 65,
|
|
145
|
-
"lines": 65,
|
|
146
|
-
"statements": 65
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
},
|
|
150
|
-
"bin": {
|
|
151
|
-
"edge-link-cli": "lib/bin/edge-link-cli.js"
|
|
152
|
-
},
|
|
153
|
-
"lint-staged": {
|
|
154
|
-
"*.js": [
|
|
155
|
-
"prettier --write",
|
|
156
|
-
"eslint --fix"
|
|
157
|
-
],
|
|
158
|
-
"*.ts": [
|
|
159
|
-
"prettier --write"
|
|
160
|
-
],
|
|
161
|
-
"*.{json,md}": [
|
|
162
|
-
"prettier --write"
|
|
163
|
-
]
|
|
164
|
-
}
|
|
165
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "signalk-edge-link",
|
|
3
|
+
"version": "2.6.0",
|
|
4
|
+
"description": "SignalK Edge Link. Secure UDP link for data exchange.",
|
|
5
|
+
"main": "lib/index.js",
|
|
6
|
+
"files": [
|
|
7
|
+
"lib/",
|
|
8
|
+
"public/"
|
|
9
|
+
],
|
|
10
|
+
"keywords": [
|
|
11
|
+
"signalk-node-server-plugin",
|
|
12
|
+
"signalk-category-network",
|
|
13
|
+
"signalk-webapp",
|
|
14
|
+
"signalk-category-utility",
|
|
15
|
+
"signalk-plugin-configurator"
|
|
16
|
+
],
|
|
17
|
+
"signalk": {
|
|
18
|
+
"appIcon": "./icons/icon-72x72.png",
|
|
19
|
+
"displayName": "Edge Link Configuration"
|
|
20
|
+
},
|
|
21
|
+
"signalk-plugin-enabled-by-default": false,
|
|
22
|
+
"scripts": {
|
|
23
|
+
"clean:lib": "node -e \"const fs=require('fs');if(fs.existsSync('lib'))fs.rmSync('lib',{recursive:true,force:true});\"",
|
|
24
|
+
"clean:public": "node -e \"const fs=require('fs');if(fs.existsSync('public'))fs.rmSync('public',{recursive:true,force:true});\"",
|
|
25
|
+
"build": "npm run build:ts && npm run build:web",
|
|
26
|
+
"build:web": "npm run clean:public && webpack --mode production",
|
|
27
|
+
"build:ts": "npm run clean:lib && tsc",
|
|
28
|
+
"check:ts": "tsc --noEmit",
|
|
29
|
+
"check:release-docs": "node scripts/check-release-truth.js",
|
|
30
|
+
"dev": "webpack --mode development --watch",
|
|
31
|
+
"test": "jest --runInBand",
|
|
32
|
+
"test:v2": "jest __tests__/v2/",
|
|
33
|
+
"test:integration": "jest test/integration/",
|
|
34
|
+
"test:watch": "jest --watch",
|
|
35
|
+
"test:coverage": "jest --coverage",
|
|
36
|
+
"lint": "eslint .",
|
|
37
|
+
"lint:fix": "eslint . --fix",
|
|
38
|
+
"format": "prettier --write \"**/*.{js,ts,json,md}\"",
|
|
39
|
+
"migrate:config": "node lib/scripts/migrate-config.js",
|
|
40
|
+
"cli": "node lib/bin/edge-link-cli.js",
|
|
41
|
+
"prepare": "husky"
|
|
42
|
+
},
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"@msgpack/msgpack": "^3.0.0",
|
|
45
|
+
"@rjsf/core": "^5.18.4",
|
|
46
|
+
"@rjsf/utils": "^5.18.4",
|
|
47
|
+
"@rjsf/validator-ajv8": "^5.18.4",
|
|
48
|
+
"ping-monitor": "^0.8.2"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"@babel/core": "^7.22.0",
|
|
52
|
+
"@babel/preset-env": "^7.22.0",
|
|
53
|
+
"@babel/preset-react": "^7.22.0",
|
|
54
|
+
"@testing-library/jest-dom": "^5.17.0",
|
|
55
|
+
"@testing-library/react": "^12.1.5",
|
|
56
|
+
"@types/node": "^25.3.5",
|
|
57
|
+
"@types/react": "^16.14.0",
|
|
58
|
+
"@types/react-dom": "^16.9.0",
|
|
59
|
+
"babel-loader": "^9.1.2",
|
|
60
|
+
"copy-webpack-plugin": "^14.0.0",
|
|
61
|
+
"css-loader": "^6.8.1",
|
|
62
|
+
"eslint": "^8.57.1",
|
|
63
|
+
"eslint-plugin-react": "^7.37.5",
|
|
64
|
+
"html-webpack-plugin": "^5.5.3",
|
|
65
|
+
"husky": "^9.1.7",
|
|
66
|
+
"jest": "^29.7.0",
|
|
67
|
+
"jest-environment-jsdom": "^30.3.0",
|
|
68
|
+
"lint-staged": "^15.4.3",
|
|
69
|
+
"mini-css-extract-plugin": "^2.7.6",
|
|
70
|
+
"prettier": "^3.6.2",
|
|
71
|
+
"react": "^16.13.1",
|
|
72
|
+
"react-dom": "^16.13.1",
|
|
73
|
+
"react-test-renderer": "^16.14.0",
|
|
74
|
+
"style-loader": "^3.3.3",
|
|
75
|
+
"ts-jest": "^29.4.6",
|
|
76
|
+
"ts-loader": "^9.5.4",
|
|
77
|
+
"typescript": "^5.9.3",
|
|
78
|
+
"webpack": "^5.102.1",
|
|
79
|
+
"webpack-cli": "^5.1.4"
|
|
80
|
+
},
|
|
81
|
+
"engines": {
|
|
82
|
+
"node": ">=16"
|
|
83
|
+
},
|
|
84
|
+
"author": "Karl-Erik Gustafsson",
|
|
85
|
+
"repository": "https://github.com/KEGustafsson/signalk-edge-link",
|
|
86
|
+
"homepage": "https://github.com/KEGustafsson/signalk-edge-link#readme",
|
|
87
|
+
"bugs": {
|
|
88
|
+
"url": "https://github.com/KEGustafsson/signalk-edge-link/issues"
|
|
89
|
+
},
|
|
90
|
+
"license": "MIT",
|
|
91
|
+
"jest": {
|
|
92
|
+
"testEnvironment": "node",
|
|
93
|
+
"coverageDirectory": "coverage",
|
|
94
|
+
"collectCoverageFrom": [
|
|
95
|
+
"lib/**/*.js",
|
|
96
|
+
"!lib/webapp/**",
|
|
97
|
+
"!lib/components/**",
|
|
98
|
+
"!lib/utils/**"
|
|
99
|
+
],
|
|
100
|
+
"testMatch": [
|
|
101
|
+
"**/__tests__/**/*.js",
|
|
102
|
+
"**/*.test.js",
|
|
103
|
+
"**/*.spec.js"
|
|
104
|
+
],
|
|
105
|
+
"transform": {
|
|
106
|
+
"^.+\\.js$": [
|
|
107
|
+
"babel-jest",
|
|
108
|
+
{
|
|
109
|
+
"presets": [
|
|
110
|
+
[
|
|
111
|
+
"@babel/preset-env",
|
|
112
|
+
{
|
|
113
|
+
"targets": {
|
|
114
|
+
"node": "current"
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
]
|
|
118
|
+
]
|
|
119
|
+
}
|
|
120
|
+
],
|
|
121
|
+
".+\\.tsx$": [
|
|
122
|
+
"ts-jest",
|
|
123
|
+
{
|
|
124
|
+
"tsconfig": "tsconfig.webapp.json",
|
|
125
|
+
"diagnostics": false
|
|
126
|
+
}
|
|
127
|
+
],
|
|
128
|
+
"^.+\\.ts$": "ts-jest"
|
|
129
|
+
},
|
|
130
|
+
"moduleFileExtensions": [
|
|
131
|
+
"ts",
|
|
132
|
+
"tsx",
|
|
133
|
+
"js",
|
|
134
|
+
"json",
|
|
135
|
+
"node"
|
|
136
|
+
],
|
|
137
|
+
"testPathIgnorePatterns": [
|
|
138
|
+
"/node_modules/",
|
|
139
|
+
"/public/"
|
|
140
|
+
],
|
|
141
|
+
"coverageThreshold": {
|
|
142
|
+
"global": {
|
|
143
|
+
"branches": 60,
|
|
144
|
+
"functions": 65,
|
|
145
|
+
"lines": 65,
|
|
146
|
+
"statements": 65
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
},
|
|
150
|
+
"bin": {
|
|
151
|
+
"edge-link-cli": "lib/bin/edge-link-cli.js"
|
|
152
|
+
},
|
|
153
|
+
"lint-staged": {
|
|
154
|
+
"*.js": [
|
|
155
|
+
"prettier --write",
|
|
156
|
+
"eslint --fix"
|
|
157
|
+
],
|
|
158
|
+
"*.ts": [
|
|
159
|
+
"prettier --write"
|
|
160
|
+
],
|
|
161
|
+
"*.{json,md}": [
|
|
162
|
+
"prettier --write"
|
|
163
|
+
]
|
|
164
|
+
}
|
|
165
|
+
}
|