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