hsync 0.18.3 → 0.20.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/hsync-web.js CHANGED
@@ -14,20 +14,14 @@ setRTC(rtc);
14
14
  setNet(net);
15
15
  setMqtt(mqtt);
16
16
 
17
- async function dynamicConnect(dynamicHost, useLocalStorage, configObj = {}) {
18
- if (typeof useLocalStorage === 'object') {
19
- configObj = useLocalStorage;
20
- }
21
- else {
22
- configObj.useLocalStorage = useLocalStorage;
23
- }
24
-
17
+ async function dynamicConnect(configObj = { useLocalStorage: true }) {
25
18
  const fullConfig = {...config, ...configObj};
19
+ fullConfig.dynamicHost = fullConfig.dynamicHost || fullConfig.defaultDynamicHost;
26
20
  if (fullConfig.net) {
27
21
  setNet(fullConfig.net);
28
22
  }
29
23
  let con;
30
- if (useLocalStorage) {
24
+ if (configObj.useLocalStorage) {
31
25
  const localConfigStr = localStorage.getItem('hsyncConfig');
32
26
  if (localConfigStr) {
33
27
  const localConfig = JSON.parse(localConfigStr);
@@ -39,13 +33,9 @@ async function dynamicConnect(dynamicHost, useLocalStorage, configObj = {}) {
39
33
  }
40
34
  }
41
35
 
42
- if (!fullConfig.hsyncSecret) {
43
- fullConfig.dynamicHost = dynamicHost || fullConfig.defaultDynamicHost;
44
- }
45
-
46
36
  con = await createHsync(fullConfig);
47
37
 
48
- if (fullConfig.dynamicHost) {
38
+ if (!fullConfig.hsyncSecret) {
49
39
  const storeConfig = {
50
40
  hsyncSecret: con.hsyncSecret,
51
41
  hsyncServer: con.hsyncServer,
@@ -58,7 +48,6 @@ async function dynamicConnect(dynamicHost, useLocalStorage, configObj = {}) {
58
48
  return con;
59
49
  }
60
50
 
61
- fullConfig.dynamicHost = dynamicHost || fullConfig.defaultDynamicHost;
62
51
  con = await createHsync(fullConfig);
63
52
 
64
53
  return con;
package/hsync.js CHANGED
@@ -1,5 +1,6 @@
1
1
  const net = require('net');
2
2
  const mqtt = require('mqtt');
3
+ const debugError = require('debug')('errors');
3
4
  const { createHsync, setNet, setMqtt } = require('./connection');
4
5
  const config = require('./config');
5
6
  const { setRTC } = require('./lib/peers');
@@ -9,11 +10,18 @@ setRTC(rtc);
9
10
  setNet(net);
10
11
  setMqtt(mqtt);
11
12
 
12
- async function dynamicConnect(dynamicHost, configObj = {}) {
13
+ process.on('unhandledRejection', (reason, p) => {
14
+ debugError(reason, 'Unhandled Rejection at Promise', p, reason.stack, p.stack);
15
+ });
16
+ process.on('uncaughtException', err => {
17
+ debugError(err, 'Uncaught Exception thrown', err.stack);
18
+ });
19
+
20
+ async function dynamicConnect(configObj = {}) {
13
21
  const fullConfig = {...config, ...configObj};
14
22
  let con;
15
23
 
16
- fullConfig.dynamicHost = dynamicHost || fullConfig.defaultDynamicHost;
24
+ fullConfig.dynamicHost = fullConfig.dynamicHost || fullConfig.defaultDynamicHost;
17
25
  con = await createHsync(fullConfig);
18
26
 
19
27
  return con;
package/lib/fetch.js CHANGED
@@ -11,20 +11,16 @@ function getContent(res) {
11
11
  return res.blob();
12
12
  }
13
13
 
14
- function handledFetch(path, options) {
15
- return fetch(path, options)
16
- .then((res) => {
17
- if (res.status >= 400) {
18
- const err = new Error('Bad response from server');
19
- err.status = res.status;
20
- return getContent(res)
21
- .then((content) => {
22
- err.content = content;
23
- throw err;
24
- });
25
- }
26
- return res;
27
- });
14
+ async function handledFetch(path, options) {
15
+ const rawRes = await fetch(path, options);
16
+ const content = await getContent(rawRes);
17
+ if (rawRes.status >= 400) {
18
+ const err = new Error('Bad response from server');
19
+ err.status = rawRes.status;
20
+ err.content = content;
21
+ throw err;
22
+ }
23
+ return content;
28
24
  }
29
25
 
30
26
  function apiFetch(path, options = {}) {
@@ -42,8 +38,7 @@ function apiFetch(path, options = {}) {
42
38
  'Content-Type': 'application/json',
43
39
  ...options.headers,
44
40
  };
45
- return handledFetch(`${path}${qs}`, options)
46
- .then(getContent);
41
+ return handledFetch(`${path}${qs}`, options);
47
42
  }
48
43
 
49
44
  apiFetch.post = (path, body) => {
package/lib/peers.js CHANGED
@@ -10,17 +10,9 @@ globalThis.Buffer = buffer.Buffer;
10
10
  const { handleSocketPacket } = require('./socket-map');
11
11
  const fetch = require('./fetch');
12
12
 
13
- const peers = {};
14
-
15
- let rtc;
16
-
17
- function setRTC(rtcImpl) {
18
- rtc = rtcImpl;
19
- }
20
-
21
13
  function createPacket(topic, payload) {
22
14
  let payloadStr = payload;
23
- // console.log('mqttPacket', { topic, payload });
15
+ console.log('mqttPacket', { topic, payload });
24
16
  // if (payload instanceof Uint8Array) {
25
17
  // console.log('str payload', new TextDecoder().decode(payload));
26
18
  // }
@@ -43,207 +35,237 @@ function parsePacket(packet) {
43
35
  });
44
36
  }
45
37
 
46
- function getRPCPeer(options = {}) {
47
- const { hostName, temporary, timeout = 10000, hsyncClient } = options;
48
- let peer = peers[hostName];
49
- if (!peer) {
50
- debug('CREATING peer', hostName);
51
- peer = createRPCPeer({hostName, hsyncClient, timeout});
52
- if (temporary) {
53
- peer.rpcTemporary = true;
54
- }
55
- peers[hostName] = peer;
56
- }
57
- return peer;
58
- }
59
38
 
60
- function createRPCPeer(options = {}) {
61
- const { hostName, hsyncClient, timeout = 10000, useRTC = true } = options;
62
- if (!hostName) {
63
- throw new Error('No hostname specified');
64
- }
65
- if (hostName === hsyncClient.myHostName) {
66
- throw new Error('Peer must be a different host');
67
- }
68
- const myAuth = b64id.generateId();
69
- const transport = new EventEmitter();
70
- const peer = rawr({transport, methods: Object.assign({}, hsyncClient.peerMethods), timeout, idGenerator: b64id.generateId});
71
- peer.hostName = hostName;
72
- peer.rtcEvents = new EventEmitter();
73
- peer.localMethods = Object.assign({}, hsyncClient.peerMethods);
74
- peer.sockets = {};
39
+ let rtc;
75
40
 
76
- peer.localMethods.rtcSignal = (peerInfo, signal) => {
77
- debug('rtcSignal', signal.type);
78
- if (signal.type === 'offer' && !peer.rtcCon && !signal.alreadySent) {
79
- rtc.answerPeer(peer, signal);
80
- } else if (signal.type === 'answer') {
81
- peer.handleRtcAnswer(signal);
82
- }
83
- return 'rtcSignal ok';
84
- }
41
+ function setRTC(rtcImpl) {
42
+ rtc = rtcImpl;
43
+ }
85
44
 
86
- peer.rtcEvents.on('packet', async (packet) => {
87
- debug('↓ on packet', packet);
88
- let toParse = packet;
89
- try {
90
- if (packet instanceof Blob) {
91
- toParse = await packet.arrayBuffer();
92
- }
93
- const msg = await parsePacket(toParse);
94
- const [p1, p2, p3] = msg.topic.split('/');
95
- if (p1 === 'rpc') {
96
- transport.receiveData(JSON.parse(msg.payload.toString()));
97
- } else if (p1 === 'socketData') {
98
- handleSocketPacket(msg);
99
- } else if (p1 === 'test') {
100
- debug('test topic', msg.payload);
101
- } else {
102
- debug('other topic', msg.topic);
45
+ function initPeers(hsyncClient) {
46
+ const cachedPeers = {};
47
+ function getRPCPeer(options = {}) {
48
+ const { hostName, temporary, timeout = 10000, hsyncClient } = options;
49
+ let peer = cachedPeers[hostName];
50
+ if (!peer) {
51
+ debug('CREATING peer', hostName);
52
+ peer = createRPCPeer({hostName, hsyncClient, timeout});
53
+ if (temporary) {
54
+ peer.rpcTemporary = true;
103
55
  }
104
- } catch (e) {
105
- debug('bad packet', e, packet);
56
+ cachedPeers[hostName] = peer;
106
57
  }
107
- });
108
-
109
- peer.rtcEvents.on('dcOpen', () => {
110
- debug('dcOpen');
111
- peer.packAndSend = (topic, payload) => {
112
- const packet = createPacket(topic, payload);
113
- if (topic === 'test') {
114
- debug('sending test packet', packet);
115
- }
116
- peer.rtcSend(packet);
58
+ return peer;
59
+ }
60
+
61
+ function createRPCPeer(options = {}) {
62
+ const { hostName, timeout = 10000, useRTC = true } = options;
63
+ if (!hostName) {
64
+ throw new Error('No hostname specified');
117
65
  }
118
- // firefox is weird about the first bit of data, so send a test packet
119
- peer.packAndSend('test', 'test');
120
- });
121
-
122
- peer.rtcEvents.on('closed', () => {
123
- peer.dcOpen = false;
124
- delete peer.packAndSend;
125
- debug('rtc closed');
126
- for (s in peer.sockets) {
127
- try {
128
- debug('closing socket', s);
129
- peer.sockets[s].destroy();
130
- delete peer.sockets[s];
131
- } catch (e) {
132
- debug('error closing socket', e);
133
- }
66
+ if (hostName === hsyncClient.myHostName) {
67
+ throw new Error('Peer must be a different host');
134
68
  }
135
- });
136
-
137
- peer.rtcEvents.on('disconnected', () => {
138
- peer.dcOpen = false;
139
- delete peer.packAndSend;
140
- debug('rtc disconnected');
141
- for (s in peer.sockets) {
142
- try {
143
- debug('closing socket', s);
144
- peer.sockets[s].destroy();
145
- delete peer.sockets[s];
146
- } catch (e) {
147
- debug('error closing socket', e);
69
+ const myAuth = b64id.generateId();
70
+ const transport = new EventEmitter();
71
+ const peer = rawr({transport, methods: Object.assign({}, hsyncClient.peerMethods), timeout, idGenerator: b64id.generateId});
72
+ peer.hostName = hostName;
73
+ peer.rtcEvents = new EventEmitter();
74
+ peer.localMethods = Object.assign({}, hsyncClient.peerMethods);
75
+ peer.sockets = {};
76
+
77
+ peer.localMethods.rtcSignal = (peerInfo, signal) => {
78
+ debug('rtcSignal', signal.type);
79
+ if (signal.type === 'offer' && !peer.rtcCon && !signal.alreadySent) {
80
+ rtc.answerPeer(peer, signal);
81
+ } else if (signal.type === 'answer') {
82
+ peer.handleRtcAnswer(signal);
148
83
  }
84
+ return 'rtcSignal ok';
149
85
  }
150
- });
151
-
152
- peer.connectRTC = async () => {
153
- debug('connectRTC');
154
-
155
- return new Promise(async (resolve, reject) => {
86
+
87
+ peer.rtcEvents.on('packet', async (packet) => {
88
+ debug('↓ on packet', packet);
89
+ let toParse = packet;
156
90
  try {
157
- const offer = await rtc.offerPeer(peer);
158
- peer.rtcEvents.once('dcOpen', () => {
159
- debug('dcOpen!');
160
- resolve(offer);
161
- });
91
+ if (packet instanceof Blob) {
92
+ toParse = await packet.arrayBuffer();
93
+ }
94
+ const msg = await parsePacket(toParse);
95
+ const [p1, p2, p3] = msg.topic.split('/');
96
+ if (p1 === 'rpc') {
97
+ const rpcMsg = JSON.parse(msg.payload.toString());
98
+ debug('↓ peer RTC rpc', rpcMsg);
99
+ // if (rpcMsg.method) {
100
+ transport.receiveData(rpcMsg);
101
+ // return;
102
+ // }
103
+ } else if (p1 === 'socketData') {
104
+ handleSocketPacket(msg);
105
+ } else if (p1 === 'test') {
106
+ debug('test topic', msg.payload);
107
+ } else {
108
+ debug('other topic', msg.topic);
109
+ }
162
110
  } catch (e) {
163
- reject(e);
111
+ debug('bad packet', e, packet);
164
112
  }
165
113
  });
166
- };
167
-
168
- transport.send = async (msg) => {
169
- const fullMsg = {
170
- msg,
171
- myAuth,
172
- toHost: hostName,
173
- fromHost: hsyncClient.webUrl,
114
+
115
+ peer.rtcEvents.on('dcOpen', () => {
116
+ debug('dcOpen');
117
+ peer.packAndSend = (topic, payload) => {
118
+ const packet = createPacket(topic, payload);
119
+ if (topic === 'test') {
120
+ debug('sending test packet', packet);
121
+ }
122
+ peer.rtcSend(packet);
123
+ }
124
+ // firefox is weird about the first bit of data, so send a test packet
125
+ peer.packAndSend('test', 'test');
126
+ });
127
+
128
+ peer.rtcEvents.on('closed', () => {
129
+ peer.dcOpen = false;
130
+ delete peer.packAndSend;
131
+ debug('rtc closed');
132
+ for (s in peer.sockets) {
133
+ try {
134
+ debug('closing socket', s);
135
+ peer.sockets[s].destroy();
136
+ delete peer.sockets[s];
137
+ } catch (e) {
138
+ debug('error closing socket', e);
139
+ }
140
+ }
141
+ });
142
+
143
+ peer.rtcEvents.on('disconnected', () => {
144
+ peer.dcOpen = false;
145
+ delete peer.packAndSend;
146
+ debug('rtc disconnected');
147
+ for (s in peer.sockets) {
148
+ try {
149
+ debug('closing socket', s);
150
+ peer.sockets[s].destroy();
151
+ delete peer.sockets[s];
152
+ } catch (e) {
153
+ debug('error closing socket', e);
154
+ }
155
+ }
156
+ });
157
+
158
+ peer.connectRTC = async () => {
159
+ debug('connectRTC');
160
+ return new Promise(async (resolve, reject) => {
161
+ try {
162
+ const offer = await rtc.offerPeer(peer);
163
+ debug('offer', offer);
164
+ peer.rtcEvents.once('dcOpen', () => {
165
+ debug('dcOpen!');
166
+ resolve(offer);
167
+ });
168
+ } catch (e) {
169
+ debug('error connecting to rtc', e);
170
+ reject(e);
171
+ }
172
+ });
174
173
  };
175
-
176
- debug('↑ peer rpc', peer.dcOpen ? 'RTC' : 'REST', `${hostName}/_hs/rpc`, msg.method);
177
-
178
- if (peer.dcOpen) {
179
- let payload = msg;
180
- if (typeof msg === 'object') {
181
- payload = JSON.stringify(payload);
174
+
175
+ transport.send = async (msg) => {
176
+ const fullMsg = {
177
+ msg,
178
+ myAuth,
179
+ toHost: hostName,
180
+ fromHost: hsyncClient.webUrl,
181
+ };
182
+
183
+ debug('↑ peer rpc', peer.dcOpen ? 'RTC' : 'REST', `${hostName}/_hs/rpc`, msg.method);
184
+
185
+ if (peer.dcOpen) {
186
+ let payload = msg;
187
+ if (typeof msg === 'object') {
188
+ payload = JSON.stringify(payload);
189
+ }
190
+ const packet = createPacket('rpc', payload);
191
+ peer.rtcSend(packet);
192
+ return;
182
193
  }
183
- const packet = createPacket('rpc', payload);
184
- peer.rtcSend(packet);
185
- return;
186
- }
187
-
188
- try {
189
- debug('fetching', fullMsg, useRTC);
190
- const result = await fetch.post(`${hostName}/_hs/rpc`, fullMsg);
191
- debug('fetch result', result);
192
- if (msg.id) {
193
- transport.receiveData({id: msg.id, result, jsonrpc: msg.jsonrpc});
194
+
195
+ try {
196
+ const path = `${hostName}/_hs/rpc`;
197
+ debug('fetching', path, fullMsg, useRTC);
198
+ const result = await fetch.post(path, fullMsg);
199
+ debug('fetch result', result);
200
+ if (msg.id) {
201
+ transport.receiveData({id: msg.id, result, jsonrpc: msg.jsonrpc});
202
+ }
203
+ } catch(e) {
204
+ debug('error sending peer RPC request', e);
205
+ if (msg.id) { // only send error if it's a request, not a notification
206
+ transport.receiveData({
207
+ id: msg.id,
208
+ error: e.message,
209
+ method: msg.method,
210
+ jsonrpc: msg.jsonrpc
211
+ });
212
+ }
194
213
  }
195
- // if (!peer.rtcCon && useRTC) {
196
- // debug('STARTING rtc creation');
197
- // rtc.offerPeer(peer);
198
- // }
199
- } catch(e) {
200
- debug('error sending peer RPC request', e);
201
- transport.receiveData({id: msg.id, error: e, method: msg.method, jsonrpc: msg.jsonrpc});
202
- }
214
+
215
+ };
203
216
 
204
- };
205
-
206
- transport.receiveData = (msg) => {
207
- if(typeof msg === 'string') {
208
- msg = JSON.parse(msg);
209
- }
210
- debug('↓ peer rpc receivedData', msg, msg.params);
211
- if (msg.params && Array.isArray(msg.params)) {
212
- debug('unshifting', msg.params);
213
- msg.params.unshift(peer);
214
- }
215
- transport.emit('rpc', msg);
216
- };
217
+ transport.receiveData = (msg) => {
218
+ debug('↓ transport.receiveData', msg);
219
+ if(typeof msg === 'string') {
220
+ msg = JSON.parse(msg);
221
+ }
222
+ debug('↓ peer rpc receivedData', msg);
223
+ if (msg.params && Array.isArray(msg.params)) {
224
+ debug('unshifting', msg.params);
225
+ msg.params.unshift(peer);
226
+ }
227
+ transport.emit('rpc', msg);
228
+ // debug('transport emitted', msg);
229
+ };
230
+
231
+ peer.myAuth = myAuth;
232
+ peer.hostName = hostName;
233
+ return peer;
234
+ }
235
+
236
+ function createServerPeer(hsyncClient, methods) {
237
+ const transport = new EventEmitter();
238
+ transport.send = (msg) => {
239
+ if(typeof msg === 'object') {
240
+ msg = JSON.stringify(msg);
241
+ }
242
+ const topic = `srpc/${hsyncClient.myHostName}`;
243
+ debug('↑ server rpc request', msg);
244
+ hsyncClient.mqConn.publish(topic, Buffer.from(msg));
245
+ };
246
+ transport.receiveData = (msg) => {
247
+ if(msg) {
248
+ msg = JSON.parse(msg);
249
+ }
250
+ debug('↓ server rpc reply', msg.method, msg.id);
251
+ transport.emit('rpc', msg);
252
+ };
253
+ const peer = rawr({transport, methods, timeout: 5000});
254
+ return peer;
255
+ }
217
256
 
218
- peer.myAuth = myAuth;
219
- peer.hostName = hostName;
220
- return peer;
221
- }
257
+ hsyncClient.cachedPeers = cachedPeers;
258
+ hsyncClient.getRPCPeer = getRPCPeer;
259
+ hsyncClient.createServerPeer = createServerPeer;
222
260
 
223
- function createServerPeer(hsyncClient, methods) {
224
- const transport = new EventEmitter();
225
- transport.send = (msg) => {
226
- if(typeof msg === 'object') {
227
- msg = JSON.stringify(msg);
228
- }
229
- const topic = `srpc/${hsyncClient.myHostName}`;
230
- debug('↑ server rpc request', msg);
231
- hsyncClient.mqConn.publish(topic, Buffer.from(msg));
232
- };
233
- transport.receiveData = (msg) => {
234
- if(msg) {
235
- msg = JSON.parse(msg);
236
- }
237
- debug('↓ server rpc reply', msg.method, msg.id);
238
- transport.emit('rpc', msg);
261
+ return {
262
+ getRPCPeer,
263
+ createRPCPeer,
264
+ createServerPeer,
239
265
  };
240
- const peer = rawr({transport, methods, timeout: 5000});
241
- return peer;
242
266
  }
243
267
 
244
268
  module.exports = {
245
- createRPCPeer,
246
- createServerPeer,
247
- getRPCPeer,
269
+ initPeers,
248
270
  setRTC,
249
271
  };