signalk-edge-link 2.0.0 → 2.1.1
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/LICENSE +1 -1
- package/lib/connection-config.js +14 -0
- package/lib/instance.js +2 -4
- package/lib/pipeline-v2-client.js +14 -6
- package/lib/pipeline-v2-server.js +1 -1
- package/lib/pipeline.js +1 -1
- package/lib/routes/monitoring.js +3 -9
- package/package.json +165 -161
package/LICENSE
CHANGED
package/lib/connection-config.js
CHANGED
|
@@ -235,6 +235,20 @@ function validateConnectionConfig(connection, prefix = "") {
|
|
|
235
235
|
}
|
|
236
236
|
}
|
|
237
237
|
}
|
|
238
|
+
// Validate that primary and backup links are different
|
|
239
|
+
const primaryLink = bonding.primary;
|
|
240
|
+
const backupLink = bonding.backup;
|
|
241
|
+
if (primaryLink && backupLink) {
|
|
242
|
+
const sameAddress = primaryLink.address !== undefined &&
|
|
243
|
+
backupLink.address !== undefined &&
|
|
244
|
+
primaryLink.address === backupLink.address;
|
|
245
|
+
const samePort = primaryLink.port !== undefined &&
|
|
246
|
+
backupLink.port !== undefined &&
|
|
247
|
+
primaryLink.port === backupLink.port;
|
|
248
|
+
if (sameAddress && samePort) {
|
|
249
|
+
return `${p}bonding primary and backup links must use different address:port combinations`;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
238
252
|
if (bonding.failover !== undefined) {
|
|
239
253
|
if (!bonding.failover ||
|
|
240
254
|
typeof bonding.failover !== "object" ||
|
package/lib/instance.js
CHANGED
|
@@ -93,7 +93,6 @@ function createInstance(app, options, instanceId, pluginId, onStatusChange) {
|
|
|
93
93
|
pipelineServer: null,
|
|
94
94
|
heartbeatHandle: null,
|
|
95
95
|
monitoring: null,
|
|
96
|
-
networkSimulator: null,
|
|
97
96
|
configDebounceTimers: {},
|
|
98
97
|
configContentHashes: {},
|
|
99
98
|
configWatcherObjects: [],
|
|
@@ -240,7 +239,7 @@ function createInstance(app, options, instanceId, pluginId, onStatusChange) {
|
|
|
240
239
|
state.timer = false;
|
|
241
240
|
return;
|
|
242
241
|
}
|
|
243
|
-
const actualBatchSize = Math.min(batchSize, state.deltas.length);
|
|
242
|
+
const actualBatchSize = Math.min(batchSize, state.deltas.length, state.maxDeltasPerBatch);
|
|
244
243
|
const batch = state.deltas.slice(0, actualBatchSize);
|
|
245
244
|
state.batchSendInFlight = true;
|
|
246
245
|
try {
|
|
@@ -269,7 +268,7 @@ function createInstance(app, options, instanceId, pluginId, onStatusChange) {
|
|
|
269
268
|
}
|
|
270
269
|
finally {
|
|
271
270
|
state.batchSendInFlight = false;
|
|
272
|
-
if (state.deltas.length > 0 && !state.pendingRetry) {
|
|
271
|
+
if (state.deltas.length > 0 && !state.pendingRetry && !state.stopped) {
|
|
273
272
|
setImmediate(() => {
|
|
274
273
|
flushDeltaBatch();
|
|
275
274
|
});
|
|
@@ -884,7 +883,6 @@ function createInstance(app, options, instanceId, pluginId, onStatusChange) {
|
|
|
884
883
|
}
|
|
885
884
|
state.monitoring = null;
|
|
886
885
|
}
|
|
887
|
-
state.networkSimulator = null;
|
|
888
886
|
// Stop ping monitor
|
|
889
887
|
if (state.pingMonitor) {
|
|
890
888
|
state.pingMonitor.stop();
|
|
@@ -140,8 +140,11 @@ function createPipelineV2Client(app, state, metricsApi) {
|
|
|
140
140
|
}
|
|
141
141
|
function _effectiveRetransmitAge() {
|
|
142
142
|
let maxAge = retransmitMaxAge;
|
|
143
|
-
|
|
144
|
-
|
|
143
|
+
// Use the congestion controller's smoothed RTT (EMA) instead of the raw
|
|
144
|
+
// latest sample to avoid volatile timeout swings from single RTT spikes.
|
|
145
|
+
const smoothedRtt = congestionControl.getAvgRTT();
|
|
146
|
+
if (smoothedRtt > 0) {
|
|
147
|
+
const rttBasedAge = Math.round(smoothedRtt * retransmitRttMultiplier);
|
|
145
148
|
maxAge = Math.min(maxAge, Math.max(retransmitMinAge, rttBasedAge));
|
|
146
149
|
}
|
|
147
150
|
const ackIdleMs = Date.now() - lastAckAt;
|
|
@@ -349,10 +352,12 @@ function createPipelineV2Client(app, state, metricsApi) {
|
|
|
349
352
|
const ackedSeq = packetParser.parseACKPayload(parsed.payload);
|
|
350
353
|
const now = Date.now();
|
|
351
354
|
let rttSample = null;
|
|
352
|
-
//
|
|
355
|
+
// Only sample RTT from packets that were NOT retransmitted (Karn's algorithm).
|
|
356
|
+
// When a retransmitted packet is ACKed, the measurement is ambiguous — the ACK
|
|
357
|
+
// could be for the original or the retransmit — so we skip it entirely.
|
|
353
358
|
const entry = retransmitQueue.get(ackedSeq);
|
|
354
|
-
if (entry &&
|
|
355
|
-
rttSample = Math.max(0, now -
|
|
359
|
+
if (entry && entry.attempts === 0) {
|
|
360
|
+
rttSample = Math.max(0, now - entry.originalTimestamp);
|
|
356
361
|
}
|
|
357
362
|
if (rttSample !== null) {
|
|
358
363
|
metrics.rtt = rttSample;
|
|
@@ -376,10 +381,13 @@ function createPipelineV2Client(app, state, metricsApi) {
|
|
|
376
381
|
lastAckAt = now;
|
|
377
382
|
lastAckRinfo = rinfo ? { address: rinfo.address, port: rinfo.port } : lastAckRinfo;
|
|
378
383
|
// Update congestion control with latest network metrics.
|
|
384
|
+
// Only feed RTT when we have a fresh sample; passing -1 causes the
|
|
385
|
+
// congestion controller's >= 0 guard to skip the RTT EMA update,
|
|
386
|
+
// preventing stale values from being repeatedly folded into the average.
|
|
379
387
|
// Clamp packetLoss to [0, 1] as a defensive measure against any future
|
|
380
388
|
// changes to _calculatePacketLoss that could produce out-of-range values.
|
|
381
389
|
congestionControl.updateMetrics({
|
|
382
|
-
rtt:
|
|
390
|
+
rtt: rttSample ?? -1,
|
|
383
391
|
packetLoss: Math.min(1, Math.max(0, _calculatePacketLoss()))
|
|
384
392
|
});
|
|
385
393
|
app.debug(`ACK received: seq=${ackedSeq}, removed=${removed}, queueDepth=${retransmitQueue.getSize()}, rtt=${metrics.rtt}ms`);
|
|
@@ -625,7 +625,7 @@ function createPipelineV2Server(app, state, metricsApi) {
|
|
|
625
625
|
if (!Array.isArray(deltaMessage.updates) || deltaMessage.updates.length === 0) {
|
|
626
626
|
continue;
|
|
627
627
|
}
|
|
628
|
-
trackPathStats(deltaMessage, decompressed.length /
|
|
628
|
+
trackPathStats(deltaMessage, decompressed.length / deltas.length);
|
|
629
629
|
app.handleMessage("", deltaMessage);
|
|
630
630
|
metrics.deltasReceived++;
|
|
631
631
|
}
|
package/lib/pipeline.js
CHANGED
|
@@ -203,7 +203,7 @@ function createPipeline(app, state, metricsApi) {
|
|
|
203
203
|
continue;
|
|
204
204
|
}
|
|
205
205
|
// Track path stats for server-side analytics
|
|
206
|
-
trackPathStats(deltaMessage, decompressed.length /
|
|
206
|
+
trackPathStats(deltaMessage, decompressed.length / deltas.length);
|
|
207
207
|
app.handleMessage("", deltaMessage);
|
|
208
208
|
// Log a compact summary only — never log full delta values which may
|
|
209
209
|
// contain sensitive data (position, fuel, MMSI) in plaintext logs.
|
package/lib/routes/monitoring.js
CHANGED
|
@@ -336,15 +336,9 @@ function register(router, ctx) {
|
|
|
336
336
|
if (!bundle) {
|
|
337
337
|
return res.status(503).json({ error: "Plugin not started" });
|
|
338
338
|
}
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
}
|
|
343
|
-
res.json({
|
|
344
|
-
enabled: true,
|
|
345
|
-
conditions: state.networkSimulator.getConditions(),
|
|
346
|
-
stats: state.networkSimulator.getStats()
|
|
347
|
-
});
|
|
339
|
+
// Network simulator is not currently implemented; endpoint kept for
|
|
340
|
+
// API compatibility and future use.
|
|
341
|
+
res.json({ enabled: false });
|
|
348
342
|
}
|
|
349
343
|
catch (err) {
|
|
350
344
|
res.status(500).json({ error: err instanceof Error ? err.message : String(err) });
|
package/package.json
CHANGED
|
@@ -1,161 +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
|
-
"schemas/"
|
|
10
|
-
],
|
|
11
|
-
"keywords": [
|
|
12
|
-
"signalk-node-server-plugin",
|
|
13
|
-
"signalk-category-network",
|
|
14
|
-
"signalk-webapp",
|
|
15
|
-
"signalk-category-utility",
|
|
16
|
-
"signalk-plugin-configurator"
|
|
17
|
-
],
|
|
18
|
-
"signalk": {
|
|
19
|
-
"appIcon": "./icons/icon-72x72.png",
|
|
20
|
-
"displayName": "Edge Link Configuration"
|
|
21
|
-
},
|
|
22
|
-
"signalk-plugin-enabled-by-default": false,
|
|
23
|
-
"scripts": {
|
|
24
|
-
"clean:lib": "node -e \"const fs=require('fs');if(fs.existsSync('lib'))fs.rmSync('lib',{recursive:true,force:true});\"",
|
|
25
|
-
"clean:public": "node -e \"const fs=require('fs');if(fs.existsSync('public'))fs.rmSync('public',{recursive:true,force:true});\"",
|
|
26
|
-
"build": "npm run build:ts && npm run build:web",
|
|
27
|
-
"build:web": "npm run clean:public && webpack --mode production",
|
|
28
|
-
"build:ts": "npm run clean:lib && tsc",
|
|
29
|
-
"check:ts": "tsc --noEmit",
|
|
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
|
-
"
|
|
87
|
-
"
|
|
88
|
-
"
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
"
|
|
98
|
-
"
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
"
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
"
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
"
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
"
|
|
135
|
-
"
|
|
136
|
-
],
|
|
137
|
-
"
|
|
138
|
-
"
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
"*.
|
|
155
|
-
"prettier --write"
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "signalk-edge-link",
|
|
3
|
+
"version": "2.1.1",
|
|
4
|
+
"description": "SignalK Edge Link. Secure UDP link for data exchange.",
|
|
5
|
+
"main": "lib/index.js",
|
|
6
|
+
"files": [
|
|
7
|
+
"lib/",
|
|
8
|
+
"public/",
|
|
9
|
+
"schemas/"
|
|
10
|
+
],
|
|
11
|
+
"keywords": [
|
|
12
|
+
"signalk-node-server-plugin",
|
|
13
|
+
"signalk-category-network",
|
|
14
|
+
"signalk-webapp",
|
|
15
|
+
"signalk-category-utility",
|
|
16
|
+
"signalk-plugin-configurator"
|
|
17
|
+
],
|
|
18
|
+
"signalk": {
|
|
19
|
+
"appIcon": "./icons/icon-72x72.png",
|
|
20
|
+
"displayName": "Edge Link Configuration"
|
|
21
|
+
},
|
|
22
|
+
"signalk-plugin-enabled-by-default": false,
|
|
23
|
+
"scripts": {
|
|
24
|
+
"clean:lib": "node -e \"const fs=require('fs');if(fs.existsSync('lib'))fs.rmSync('lib',{recursive:true,force:true});\"",
|
|
25
|
+
"clean:public": "node -e \"const fs=require('fs');if(fs.existsSync('public'))fs.rmSync('public',{recursive:true,force:true});\"",
|
|
26
|
+
"build": "npm run build:ts && npm run build:web",
|
|
27
|
+
"build:web": "npm run clean:public && webpack --mode production",
|
|
28
|
+
"build:ts": "npm run clean:lib && tsc",
|
|
29
|
+
"check:ts": "tsc --noEmit",
|
|
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
|
+
}
|