hsync 0.30.1 → 0.31.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.
@@ -0,0 +1,32 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main, master]
6
+ pull_request:
7
+ branches: [main, master]
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ matrix:
14
+ node-version: [22, 24]
15
+
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+
19
+ - name: Use Node.js ${{ matrix.node-version }}
20
+ uses: actions/setup-node@v4
21
+ with:
22
+ node-version: ${{ matrix.node-version }}
23
+ cache: 'npm'
24
+
25
+ - name: Install dependencies
26
+ run: npm ci
27
+
28
+ - name: Lint
29
+ run: npm run lint
30
+
31
+ - name: Test
32
+ run: npm run test:run
@@ -0,0 +1 @@
1
+ npx lint-staged
package/.prettierrc ADDED
@@ -0,0 +1,7 @@
1
+ {
2
+ "semi": true,
3
+ "singleQuote": true,
4
+ "tabWidth": 2,
5
+ "trailingComma": "es5",
6
+ "printWidth": 100
7
+ }
package/Readme.md CHANGED
@@ -1,5 +1,6 @@
1
1
  # hsync
2
2
 
3
+ [![CI](https://github.com/monteslu/hsync/actions/workflows/ci.yml/badge.svg)](https://github.com/monteslu/hsync/actions/workflows/ci.yml)
3
4
  [![NPM](https://nodei.co/npm/hsync.svg)](https://nodei.co/npm/hsync/)
4
5
 
5
6
  hsync is a [reverse-proxy](https://en.wikipedia.org/wiki/Reverse_proxy) client for node.js and browsers that connects to an [hsync-server](https://github.com/monteslu/hsync-server).
package/cli.js CHANGED
@@ -1,82 +1,129 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- const { program, Option } = require('commander');
3
+ import { program, Option } from 'commander';
4
+ import { createRequire } from 'module';
5
+ import config from './config.js';
6
+ import { createConnection } from './hsync.js';
7
+ import shell from './shell.js';
8
+
9
+ // For JSON imports (package.json)
10
+ const require = createRequire(import.meta.url);
4
11
  const pack = require('./package.json');
5
- const config = require('./config');
6
- const { createConnection } = require('./hsync');
7
- const shell = require('./shell');
8
12
 
9
13
  program
10
14
  .name(pack.name)
11
15
  .description(pack.description)
12
16
  .version(pack.version)
13
17
  .addOption(new Option('-p, --port <number>', 'port for local webserver', 3000).env('PORT'))
14
- .addOption(new Option('-d, --dynamic-host <url>', 'host to get a dynamic connection from').env('HSYNC_DYNAMIC_HOST'))
15
- .addOption(new Option('-s, --hsync-server <url>', 'hsync-server location ex: wss://sub.mydomain.com').env('HSYNC_SERVER'))
16
- .addOption(new Option('-hs, --hsync-secret <string>', 'password to connect to hsync-server').env('HSYNC_SECRET'))
17
- .addOption(new Option('-llp, --listener-local-port <number>', 'local port to open for listener').env('HSYNC_LLP'))
18
- .addOption(new Option('-lth, --listener-target-host <url>', 'target host for listener').env('HSYNC_LTH'))
19
- .addOption(new Option('-ltp, --listener-target-port <number>', 'target port for listener').env('HSYNC_LTP'))
20
- .addOption(new Option('-rip, --relay-inbound-port <number>', 'inbound port for remote relay requests').env('HSYNC_RIP'))
21
- .addOption(new Option('-rth, --relay-target-host <url>', 'target host for relay to open tcp connection on').env('HSYNC_RTH'))
22
- .addOption(new Option('-rtp, --relay-target-port <number>', 'target port for relay to open tcp connection on').env('HSYNC_RTP'))
23
- .addOption(new Option('-rwl, --relay-whitelist <string>', 'whitelist of domains that can access this relay').env('HSYNC_RWL'))
24
- .addOption(new Option('-rbl, --relay-blacklist <string>', 'blacklist of domains that should be blocked from this relay').env('HSYNC_RBL'))
25
- .addOption(new Option('-sh, --shell', 'shell to localhost and --port for piping data to a listener'));
18
+ .addOption(
19
+ new Option('-d, --dynamic-host <url>', 'host to get a dynamic connection from').env(
20
+ 'HSYNC_DYNAMIC_HOST'
21
+ )
22
+ )
23
+ .addOption(
24
+ new Option('-s, --hsync-server <url>', 'hsync-server location ex: wss://sub.mydomain.com').env(
25
+ 'HSYNC_SERVER'
26
+ )
27
+ )
28
+ .addOption(
29
+ new Option('-hs, --hsync-secret <string>', 'password to connect to hsync-server').env(
30
+ 'HSYNC_SECRET'
31
+ )
32
+ )
33
+ .addOption(
34
+ new Option('-llp, --listener-local-port <number>', 'local port to open for listener').env(
35
+ 'HSYNC_LLP'
36
+ )
37
+ )
38
+ .addOption(
39
+ new Option('-lth, --listener-target-host <url>', 'target host for listener').env('HSYNC_LTH')
40
+ )
41
+ .addOption(
42
+ new Option('-ltp, --listener-target-port <number>', 'target port for listener').env('HSYNC_LTP')
43
+ )
44
+ .addOption(
45
+ new Option('-rip, --relay-inbound-port <number>', 'inbound port for remote relay requests').env(
46
+ 'HSYNC_RIP'
47
+ )
48
+ )
49
+ .addOption(
50
+ new Option(
51
+ '-rth, --relay-target-host <url>',
52
+ 'target host for relay to open tcp connection on'
53
+ ).env('HSYNC_RTH')
54
+ )
55
+ .addOption(
56
+ new Option(
57
+ '-rtp, --relay-target-port <number>',
58
+ 'target port for relay to open tcp connection on'
59
+ ).env('HSYNC_RTP')
60
+ )
61
+ .addOption(
62
+ new Option(
63
+ '-rwl, --relay-whitelist <string>',
64
+ 'whitelist of domains that can access this relay'
65
+ ).env('HSYNC_RWL')
66
+ )
67
+ .addOption(
68
+ new Option(
69
+ '-rbl, --relay-blacklist <string>',
70
+ 'blacklist of domains that should be blocked from this relay'
71
+ ).env('HSYNC_RBL')
72
+ )
73
+ .addOption(
74
+ new Option('-sh, --shell', 'shell to localhost and --port for piping data to a listener')
75
+ );
26
76
 
27
77
  program.parse();
28
78
 
29
79
  const options = program.opts();
30
80
 
31
- if(options.port) {
81
+ if (options.port) {
32
82
  options.port = Number(options.port);
33
83
  }
34
84
 
35
85
  if (options.shell) {
36
86
  shell(options.port);
37
- return;
38
- }
87
+ } else {
88
+ if (options.listenerLocalPort) {
89
+ options.listenerLocalPort = options.listenerLocalPort.split(',').map((p) => Number(p));
90
+ }
91
+ if (options.listenerTargetHost) {
92
+ options.listenerTargetHost = options.listenerTargetHost.split(',');
93
+ }
94
+ if (options.listenerTargetPort) {
95
+ options.listenerTargetPort = options.listenerTargetPort.split(',').map((p) => Number(p));
96
+ }
39
97
 
40
- if(options.listenerLocalPort) {
41
- options.listenerLocalPort = options.listenerLocalPort.split(',').map((p) => Number(p));
42
- }
43
- if (options.listenerTargetHost) {
44
- options.listenerTargetHost = options.listenerTargetHost.split(',');
45
- }
46
- if (options.listenerTargetPort) {
47
- options.listenerTargetPort = options.listenerTargetPort.split(',').map((p) => Number(p));
48
- }
98
+ if (options.relayInboundPort) {
99
+ options.relayInboundPort = options.relayInboundPort.split(',').map((p) => Number(p));
100
+ }
101
+ if (options.relayTargetHost) {
102
+ options.relayTargetHost = options.relayTargetHost.split(',');
103
+ }
104
+ if (options.relayTargetPort) {
105
+ options.relayTargetPort = options.relayTargetPort.split(',').map((p) => Number(p));
106
+ }
49
107
 
108
+ // console.log('options', options);
50
109
 
51
- if (options.relayInboundPort) {
52
- options.relayInboundPort = options.relayInboundPort.split(',').map((p) => Number(p));
53
- }
54
- if (options.relayTargetHost) {
55
- options.relayTargetHost = options.relayTargetHost.split(',');
56
- }
57
- if (options.relayTargetPort) {
58
- options.relayTargetPort = options.relayTargetPort.split(',').map((p) => Number(p));
59
- }
60
-
61
- // console.log('options', options);
110
+ let [defaultCon] = config.connections;
111
+ defaultCon = { ...defaultCon, ...options };
62
112
 
113
+ if (!defaultCon.hsyncServer && !defaultCon.dynamicHost) {
114
+ defaultCon.dynamicHost = config.defaultDynamicHost;
115
+ }
63
116
 
64
- let [defaultCon] = config.connections;
65
- defaultCon = {...defaultCon, ...options};
117
+ config.connections[0] = defaultCon;
66
118
 
67
- if (!defaultCon.hsyncServer && !defaultCon.dynamicHost) {
68
- defaultCon.dynamicHost = config.defaultDynamicHost;
119
+ config.connections.forEach(async (conConfig) => {
120
+ const con = await createConnection(conConfig);
121
+ console.log();
122
+ console.log('Listening for requests on: ', con.webUrl);
123
+ console.log('And forwarding to: ', 'http://localhost:' + con.port);
124
+ console.log();
125
+ console.log('Admin ui at: ', con.webAdmin);
126
+ console.log('Secret: ', con.hsyncSecret);
127
+ console.log();
128
+ });
69
129
  }
70
-
71
- config.connections[0] = defaultCon;
72
-
73
- config.connections.forEach(async (conConfig) => {
74
- const con = await createConnection(conConfig);
75
- console.log();
76
- console.log('Listening for requests on: ', con.webUrl);
77
- console.log('And forwarding to: ', 'http://localhost:' + con.port);
78
- console.log();
79
- console.log('Admin ui at: ', con.webAdmin);
80
- console.log('Secret: ', con.hsyncSecret);
81
- console.log();
82
- });
package/config.js CHANGED
@@ -1,4 +1,4 @@
1
- const p = globalThis.process || {env: {}};
1
+ const p = globalThis.process || { env: {} };
2
2
  const { env } = p;
3
3
 
4
4
  const baseConfig = {
@@ -13,11 +13,10 @@ const baseConfig = {
13
13
  another: 'another',
14
14
  };
15
15
 
16
-
17
16
  const connections = [baseConfig];
18
17
  const keys = Object.keys(env);
19
18
  keys.forEach((k) => {
20
- if(k.startsWith('HSYNC_SERVER_')) {
19
+ if (k.startsWith('HSYNC_SERVER_')) {
21
20
  const name = k.substring(13);
22
21
  const value = env[k];
23
22
  if (name && value) {
@@ -33,8 +32,9 @@ keys.forEach((k) => {
33
32
  });
34
33
  }
35
34
  }
36
- })
35
+ });
37
36
 
38
- const config = Object.assign({}, baseConfig, {connections});
37
+ const config = Object.assign({}, baseConfig, { connections });
39
38
 
40
- module.exports = config;
39
+ export default config;
40
+ export { baseConfig, connections };
package/connection.js CHANGED
@@ -1,18 +1,14 @@
1
- const EventEmitter = require('events').EventEmitter;
2
- const debug = require('debug')('hsync:info');
3
- const debugVerbose = require('debug')('hsync:verbose');
4
- const debugError = require('debug')('hsync:error');
5
- const { initPeers } = require('./lib/peers');
6
- const { createWebHandler, setNet: webSetNet } = require('./lib/web-handler');
7
- const {
8
- setNet: listenSetNet,
9
- initListeners,
10
- } = require('./lib/socket-listeners');
11
- const {
12
- setNet: relaySetNet,
13
- initRelays,
14
- } = require('./lib/socket-relays');
15
- const fetch = require('./lib/fetch');
1
+ import { EventEmitter } from 'events';
2
+ import createDebug from 'debug';
3
+ import { initPeers } from './lib/peers.js';
4
+ import { createWebHandler, setNet as webSetNet } from './lib/web-handler.js';
5
+ import { setNet as listenSetNet, initListeners } from './lib/socket-listeners.js';
6
+ import { setNet as relaySetNet, initRelays } from './lib/socket-relays.js';
7
+ import fetch from './lib/fetch.js';
8
+
9
+ const debug = createDebug('hsync:info');
10
+ const debugVerbose = createDebug('hsync:verbose');
11
+ const debugError = createDebug('hsync:error');
16
12
 
17
13
  debug.color = 3;
18
14
  debugVerbose.color = 2;
@@ -20,25 +16,22 @@ debugError.color = 1;
20
16
 
21
17
  let mqtt;
22
18
 
23
- function setNet(netImpl) {
19
+ export function setNet(netImpl) {
24
20
  webSetNet(netImpl);
25
21
  listenSetNet(netImpl);
26
22
  relaySetNet(netImpl);
27
23
  }
28
24
 
29
- function setMqtt(mqttImpl) {
25
+ export function setMqtt(mqttImpl) {
30
26
  mqtt = mqttImpl;
31
27
  }
32
28
 
33
- async function createHsync(config) {
34
- let {
35
- hsyncServer,
36
- hsyncSecret,
29
+ export async function createHsync(config) {
30
+ const {
37
31
  localHost,
38
32
  port,
39
33
  hsyncBase,
40
34
  keepalive,
41
- dynamicHost,
42
35
  listenerLocalPort,
43
36
  listenerTargetHost,
44
37
  listenerTargetPort,
@@ -46,6 +39,8 @@ async function createHsync(config) {
46
39
  relayTargetHost,
47
40
  relayTargetPort,
48
41
  } = config;
42
+ const { dynamicHost } = config;
43
+ let { hsyncServer, hsyncSecret } = config;
49
44
 
50
45
  // console.log('config', config);
51
46
 
@@ -73,12 +68,12 @@ async function createHsync(config) {
73
68
  hsyncClient.relays = initRelays(hsyncClient);
74
69
 
75
70
  const events = new EventEmitter();
76
-
71
+
77
72
  hsyncClient.on = events.on.bind(events);
78
73
  hsyncClient.emit = events.emit.bind(events);
79
74
  hsyncClient.removeListener = events.removeListener.bind(events);
80
75
  hsyncClient.removeAllListeners = events.removeAllListeners.bind(events);
81
-
76
+
82
77
  let lastConnect;
83
78
  const hsu = new URL(hsyncServer.toLowerCase());
84
79
  // console.log(hsu);
@@ -93,18 +88,27 @@ async function createHsync(config) {
93
88
  // console.log('connectURL', connectURL);
94
89
  const myHostName = hsu.hostname;
95
90
  hsyncClient.myHostName = myHostName;
96
-
97
- debug('connecting to', connectURL, '…' );
98
- const mqConn = mqtt.connect(connectURL, { password: hsyncSecret, username: myHostName, keepalive });
91
+
92
+ debug('connecting to', connectURL, '…');
93
+ const mqConn = mqtt.connect(connectURL, {
94
+ password: hsyncSecret,
95
+ username: myHostName,
96
+ keepalive,
97
+ });
99
98
  mqConn.myHostName = myHostName;
100
99
  hsyncClient.mqConn = mqConn;
101
100
 
102
- const webHandler = config.webHandler || createWebHandler({myHostName, localHost, port, mqConn});
101
+ const webHandler = config.webHandler || createWebHandler({ myHostName, localHost, port, mqConn });
103
102
  hsyncClient.webHandler = webHandler;
104
103
 
105
104
  mqConn.on('connect', () => {
106
105
  const now = Date.now();
107
- debug('connected to', myHostName, lastConnect ? (now - lastConnect) : '', lastConnect ? 'since last connect' : '');
106
+ debug(
107
+ 'connected to',
108
+ myHostName,
109
+ lastConnect ? now - lastConnect : '',
110
+ lastConnect ? 'since last connect' : ''
111
+ );
108
112
  lastConnect = now;
109
113
  hsyncClient.emit('connected', config);
110
114
  hsyncClient.status = 'connected';
@@ -112,7 +116,7 @@ async function createHsync(config) {
112
116
 
113
117
  mqConn.on('error', (error) => {
114
118
  debugError('error on mqConn', myHostName, error.code, error);
115
- if ((error.code === 4) || (error.code === 5)) {
119
+ if (error.code === 4 || error.code === 5) {
116
120
  debug('ending');
117
121
  mqConn.end();
118
122
  // if (globalThis.process) {
@@ -127,8 +131,8 @@ async function createHsync(config) {
127
131
  return;
128
132
  }
129
133
  // message is Buffer
130
- const [name, hostName, segment3, action, segment5] = topic.split('/');
131
- debugVerbose('\n↓ MQTT' , topic);
134
+ const [name, hostName, segment3, action] = topic.split('/');
135
+ debugVerbose('\n↓ MQTT', topic);
132
136
  if (name === 'web') {
133
137
  webHandler.handleWebRequest(hostName, segment3, action, message);
134
138
  return;
@@ -139,15 +143,13 @@ async function createHsync(config) {
139
143
  const msg = JSON.parse(message.toString());
140
144
  msg.from = from;
141
145
  hsyncClient.emit('json', msg);
142
- } catch (e) {
146
+ } catch (_e) {
143
147
  debugError('error parsing json message');
144
148
  }
145
- }
146
- else if (!action && (segment3 === 'srpc')) {
149
+ } else if (!action && segment3 === 'srpc') {
147
150
  hsyncClient.serverPeer.transport.receiveData(message.toString());
148
151
  }
149
152
  }
150
-
151
153
  });
152
154
 
153
155
  function endClient(force, callback) {
@@ -167,7 +169,7 @@ async function createHsync(config) {
167
169
  if (callback) {
168
170
  callback(a, b);
169
171
  }
170
- })
172
+ });
171
173
  }
172
174
 
173
175
  const serverReplyMethods = {
@@ -182,7 +184,7 @@ async function createHsync(config) {
182
184
  requestInfo.hsyncClient = hsyncClient;
183
185
  const { msg } = requestInfo;
184
186
  debug('peerRpc handler', requestInfo.fromHost, msg.method);
185
- const peer = hsyncClient.peers.getRPCPeer({hostName: requestInfo.fromHost, hsyncClient});
187
+ const peer = hsyncClient.peers.getRPCPeer({ hostName: requestInfo.fromHost, hsyncClient });
186
188
  requestInfo.peer = peer;
187
189
  if (!msg.id) {
188
190
  // notification
@@ -190,9 +192,9 @@ async function createHsync(config) {
190
192
  msg.params.unshift(peer);
191
193
  }
192
194
  peer.transport.emit('rpc', msg);
193
- return { result: {}, id: msg.id};
195
+ return { result: {}, id: msg.id };
194
196
  }
195
- const reply = {id: msg.id, jsonrpc:'2.0'};
197
+ const reply = { id: msg.id, jsonrpc: '2.0' };
196
198
  try {
197
199
  if (!peer.localMethods[msg.method]) {
198
200
  const notFoundError = new Error('method not found');
@@ -210,7 +212,7 @@ async function createHsync(config) {
210
212
  };
211
213
  return reply;
212
214
  }
213
- }
215
+ },
214
216
  };
215
217
 
216
218
  const peerMethods = {
@@ -251,7 +253,7 @@ async function createHsync(config) {
251
253
  } else {
252
254
  hsyncClient.webUrl = hsyncServer;
253
255
  }
254
-
256
+
255
257
  debug('URL', hsu.host, hsu.protocol, hsyncClient.webUrl);
256
258
  hsyncClient.webAdmin = `${hsyncClient.webUrl}/${hsyncBase}/admin`;
257
259
  hsyncClient.webBase = `${hsyncClient.webUrl}/${hsyncBase}`;
@@ -289,9 +291,3 @@ async function createHsync(config) {
289
291
 
290
292
  return hsyncClient;
291
293
  }
292
-
293
- module.exports = {
294
- createHsync,
295
- setNet,
296
- setMqtt,
297
- };