@mblaney/holster 1.0.1 → 1.0.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mblaney/holster",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "Holster is a service that allows synchronising data between devices using Node, Deno, Bun or the browser.",
5
5
  "type": "module",
6
6
  "main": "index.js",
package/src/holster.js CHANGED
@@ -229,7 +229,7 @@ const Holster = opt => {
229
229
  // The context has been resolved but it does not include the requested
230
230
  // node, which requires one more lookup.
231
231
  ctx.chain.push({item: null, soul: null})
232
- api(ctxid).next(null, request.get, cb)
232
+ api(ctxid).next(null, request.get, cb, request._opt)
233
233
  return false
234
234
  }
235
235
 
@@ -361,7 +361,8 @@ const Holster = opt => {
361
361
  return
362
362
  }
363
363
 
364
- // Resolve the current context before putting data.
364
+ // Resolve the current context before putting data. (Note that set is
365
+ // not passed to resolve because it's already been applied above.)
365
366
  const {item, soul} = resolve({put: data}, _ack)
366
367
  if (!soul) return
367
368
 
@@ -639,7 +640,7 @@ const Holster = opt => {
639
640
 
640
641
  // When there's a callback need to resolve the context first.
641
642
  const _done = done(ctxid)
642
- const {soul} = resolve({get: lex}, _done)
643
+ const {soul} = resolve({get: lex, _opt: _opt}, _done)
643
644
  if (soul) get(lex, soul, _done, _opt)
644
645
  }
645
646
  }
package/src/store.js CHANGED
@@ -35,11 +35,11 @@ const fileSystem = opt => {
35
35
  })
36
36
  },
37
37
  put: (file, data, cb) => {
38
- var random = Math.random().toString(36).slice(-9)
39
38
  // Don't put tmp files under dir so that they're not listed.
40
- var tmp = file + "." + random + ".tmp"
39
+ var tmp = file + "." + utils.text.random(9) + ".tmp"
41
40
  fs.writeFile(tmp, data, err => {
42
41
  if (err) {
42
+ console.log("fs.writeFile error:", err)
43
43
  cb(err)
44
44
  return
45
45
  }
@@ -90,7 +90,7 @@ const fileSystem = opt => {
90
90
 
91
91
  return {
92
92
  get: (file, cb) => {
93
- if (db) {
93
+ const _get = (file, cb) => {
94
94
  const tx = db.transaction([dir], "readonly")
95
95
  const req = tx.objectStore(dir).get(file)
96
96
  req.onerror = () => {
@@ -99,12 +99,28 @@ const fileSystem = opt => {
99
99
  req.onsuccess = () => {
100
100
  cb(null, req.result)
101
101
  }
102
- } else {
103
- cb("error indexedDB not available")
104
102
  }
103
+ if (db) {
104
+ _get(file, cb)
105
+ return
106
+ }
107
+
108
+ let retry = 0
109
+ const interval = setInterval(() => {
110
+ if (db) {
111
+ clearInterval(interval)
112
+ _get(file, cb)
113
+ return
114
+ }
115
+
116
+ if (retry++ > 5) {
117
+ clearInterval(interval)
118
+ cb("error indexedDB not available")
119
+ }
120
+ }, 1000)
105
121
  },
106
122
  put: (file, data, cb) => {
107
- if (db) {
123
+ const _put = (file, data, cb) => {
108
124
  const tx = db.transaction([dir], "readwrite")
109
125
  const req = tx.objectStore(dir).put(data, file)
110
126
  req.onerror = () => {
@@ -113,12 +129,28 @@ const fileSystem = opt => {
113
129
  req.onsuccess = () => {
114
130
  cb(null)
115
131
  }
116
- } else {
117
- cb("error indexedDB not available")
118
132
  }
133
+ if (db) {
134
+ _put(file, data, cb)
135
+ return
136
+ }
137
+
138
+ let retry = 0
139
+ const interval = setInterval(() => {
140
+ if (db) {
141
+ clearInterval(interval)
142
+ _put(file, data, cb)
143
+ return
144
+ }
145
+
146
+ if (retry++ > 5) {
147
+ clearInterval(interval)
148
+ cb("error indexedDB not available")
149
+ }
150
+ }, 1000)
119
151
  },
120
152
  list: cb => {
121
- if (db) {
153
+ const _list = cb => {
122
154
  const tx = db.transaction([dir], "readonly")
123
155
  const req = tx.objectStore(dir).getAllKeys()
124
156
  req.onerror = () => console.log("error getting keys for", dir)
@@ -126,10 +158,26 @@ const fileSystem = opt => {
126
158
  req.result.forEach(cb)
127
159
  cb()
128
160
  }
129
- } else {
130
- console.log("error indexedDB not available")
131
- cb()
132
161
  }
162
+ if (db) {
163
+ _list(cb)
164
+ return
165
+ }
166
+
167
+ let retry = 0
168
+ const interval = setInterval(() => {
169
+ if (db) {
170
+ clearInterval(interval)
171
+ _list(cb)
172
+ return
173
+ }
174
+
175
+ if (retry++ > 5) {
176
+ clearInterval(interval)
177
+ console.log("error indexedDB not available")
178
+ cb()
179
+ }
180
+ }, 1000)
133
181
  },
134
182
  }
135
183
  }
package/src/user.js CHANGED
@@ -27,53 +27,57 @@ const User = (opt, wire) => {
27
27
  }
28
28
 
29
29
  const pub = pubs.shift()
30
- wire.get({"#": pub}, async msg => {
31
- if (msg.err) {
32
- done(`error getting ${pub}: ${msg.err}`)
33
- return
34
- }
30
+ wire.get(
31
+ {"#": pub},
32
+ async msg => {
33
+ if (msg.err) {
34
+ done(`error getting ${pub}: ${msg.err}`)
35
+ return
36
+ }
35
37
 
36
- const data = msg.put && msg.put[pub]
37
- if (!data || !data.auth) return next()
38
+ const data = msg.put && msg.put[pub]
39
+ if (!data || !data.auth) return next()
38
40
 
39
- const auth = JSON.parse(data.auth)
40
- const work = await SEA.work(password, auth.salt)
41
- const dec = await SEA.decrypt(auth.enc, work)
42
- if (!dec) return next()
41
+ const auth = JSON.parse(data.auth)
42
+ const work = await SEA.work(password, auth.salt)
43
+ const dec = await SEA.decrypt(auth.enc, work)
44
+ if (!dec) return next()
43
45
 
44
- user.is = {
45
- username: username,
46
- pub: data.pub,
47
- epub: data.epub,
48
- priv: dec.priv,
49
- epriv: dec.epriv,
50
- }
51
- if (newPassword !== "") {
52
- // Encrypt private key using new password and a new salt.
53
- const salt = utils.text.random(64)
54
- const work = await SEA.work(newPassword, salt)
55
- const enc = await SEA.encrypt(dec, work)
56
- // Put all fields so that verification applies to the whole node.
57
- const update = {
46
+ user.is = {
58
47
  username: username,
59
48
  pub: data.pub,
60
49
  epub: data.epub,
61
- auth: JSON.stringify({enc: enc, salt: salt}),
50
+ priv: dec.priv,
51
+ epriv: dec.epriv,
62
52
  }
63
- const signed = await SEA.sign(update, user.is)
64
- const graph = utils.graph(pub, signed.m, signed.s, data.pub)
65
- wire.put(graph, err => {
66
- if (err) {
67
- done(`error putting ${update} on ${pub}: ${err}`)
68
- } else {
69
- done(null)
53
+ if (newPassword !== "") {
54
+ // Encrypt private key using new password and a new salt.
55
+ const salt = utils.text.random(64)
56
+ const work = await SEA.work(newPassword, salt)
57
+ const enc = await SEA.encrypt(dec, work)
58
+ // Put all fields so that verification applies to the whole node.
59
+ const update = {
60
+ username: username,
61
+ pub: data.pub,
62
+ epub: data.epub,
63
+ auth: JSON.stringify({enc: enc, salt: salt}),
70
64
  }
71
- })
72
- return
73
- }
65
+ const signed = await SEA.sign(update, user.is)
66
+ const graph = utils.graph(pub, signed.m, signed.s, data.pub)
67
+ wire.put(graph, err => {
68
+ if (err) {
69
+ done(`error putting ${update} on ${pub}: ${err}`)
70
+ } else {
71
+ done(null)
72
+ }
73
+ })
74
+ return
75
+ }
74
76
 
75
- return done(null)
76
- })
77
+ return done(null)
78
+ },
79
+ {wait: 1000},
80
+ )
77
81
  }
78
82
 
79
83
  if (retries > 9) {
@@ -82,21 +86,25 @@ const User = (opt, wire) => {
82
86
  }
83
87
 
84
88
  const soul = "~@" + username
85
- wire.get({"#": soul}, async msg => {
86
- if (msg.err) {
87
- done(`error getting ${soul}: ${msg.err}`)
88
- return
89
- }
90
-
91
- const data = msg.put && msg.put[soul]
92
- if (!data) return retry(username)
89
+ wire.get(
90
+ {"#": soul},
91
+ async msg => {
92
+ if (msg.err) {
93
+ done(`error getting ${soul}: ${msg.err}`)
94
+ return
95
+ }
93
96
 
94
- delete msg.put[soul]._
95
- // Usernames aren't guaranteed to be unique, so store the list and then
96
- // try each of them until one is successful with the given password.
97
- pubs = Object.keys(data)
98
- next()
99
- })
97
+ const data = msg.put && msg.put[soul]
98
+ if (!data) return retry(username)
99
+
100
+ delete msg.put[soul]._
101
+ // Usernames aren't guaranteed to be unique, so store the list and then
102
+ // try each of them until one is successful with the given password.
103
+ pubs = Object.keys(data)
104
+ next()
105
+ },
106
+ {wait: 1000},
107
+ )
100
108
  }
101
109
 
102
110
  const user = {
@@ -124,53 +132,57 @@ const User = (opt, wire) => {
124
132
  creating = true
125
133
 
126
134
  const soul = "~@" + username
127
- wire.get({"#": soul}, async msg => {
128
- if (msg.err) {
129
- creating = false
130
- ack(`error getting ${soul}: ${msg.err}`)
131
- return
132
- }
133
-
134
- if (msg.put && msg.put[soul]) {
135
- creating = false
136
- ack("Username already exists")
137
- return
138
- }
139
-
140
- const salt = utils.text.random(64)
141
- const work = await SEA.work(password, salt)
142
- const pair = await SEA.pair()
143
- const priv = {priv: pair.priv, epriv: pair.epriv}
144
- const enc = await SEA.encrypt(priv, work)
145
- const data = {
146
- username: username,
147
- pub: pair.pub,
148
- epub: pair.epub,
149
- auth: JSON.stringify({enc: enc, salt: salt}),
150
- }
135
+ wire.get(
136
+ {"#": soul},
137
+ async msg => {
138
+ if (msg.err) {
139
+ creating = false
140
+ ack(`error getting ${soul}: ${msg.err}`)
141
+ return
142
+ }
151
143
 
152
- const pub = "~" + pair.pub
153
- const signed = await SEA.sign(data, pair)
154
- const graph = utils.graph(pub, signed.m, signed.s, pair.pub)
155
- wire.put(graph, err => {
156
- creating = false
157
- if (err) {
158
- ack(`error putting ${data} on ${pub}: ${err}`)
144
+ if (msg.put && msg.put[soul]) {
145
+ creating = false
146
+ ack("Username already exists")
159
147
  return
160
148
  }
161
149
 
162
- const rel = {[pub]: {"#": pub}}
163
- wire.put(utils.graph(soul, rel), err => {
150
+ const salt = utils.text.random(64)
151
+ const work = await SEA.work(password, salt)
152
+ const pair = await SEA.pair()
153
+ const priv = {priv: pair.priv, epriv: pair.epriv}
154
+ const enc = await SEA.encrypt(priv, work)
155
+ const data = {
156
+ username: username,
157
+ pub: pair.pub,
158
+ epub: pair.epub,
159
+ auth: JSON.stringify({enc: enc, salt: salt}),
160
+ }
161
+
162
+ const pub = "~" + pair.pub
163
+ const signed = await SEA.sign(data, pair)
164
+ const graph = utils.graph(pub, signed.m, signed.s, pair.pub)
165
+ wire.put(graph, err => {
166
+ creating = false
164
167
  if (err) {
165
- ack(`error putting ${rel} on ${soul}: ${err}`)
168
+ ack(`error putting ${data} on ${pub}: ${err}`)
166
169
  return
167
170
  }
168
171
 
169
- // Return null on success.
170
- if (cb) cb(null)
172
+ const rel = {[pub]: {"#": pub}}
173
+ wire.put(utils.graph(soul, rel), err => {
174
+ if (err) {
175
+ ack(`error putting ${rel} on ${soul}: ${err}`)
176
+ return
177
+ }
178
+
179
+ // Return null on success.
180
+ if (cb) cb(null)
181
+ })
171
182
  })
172
- })
173
- })
183
+ },
184
+ {wait: 1000},
185
+ )
174
186
  },
175
187
  auth: (username, password, cb) => {
176
188
  const ack = err => {
package/src/wire.js CHANGED
@@ -220,8 +220,22 @@ const Wire = opt => {
220
220
 
221
221
  const send = (data, isBinary) => {
222
222
  clients().forEach(client => {
223
- if (client.readyState === WebSocket.OPEN) {
223
+ if (client && client.readyState === WebSocket.OPEN) {
224
224
  client.send(data, {binary: isBinary})
225
+ } else {
226
+ let retry = 0
227
+ const interval = setInterval(() => {
228
+ if (client && client.readyState === WebSocket.OPEN) {
229
+ clearInterval(interval)
230
+ client.send(data, {binary: isBinary})
231
+ return
232
+ }
233
+
234
+ if (retry++ > 5) {
235
+ clearInterval(interval)
236
+ console.log("websocket not available")
237
+ }
238
+ }, 1000)
225
239
  }
226
240
  })
227
241
  }
@@ -257,7 +271,19 @@ const Wire = opt => {
257
271
  if (peer && peer.readyState === WebSocket.OPEN) {
258
272
  peer.send(data)
259
273
  } else {
260
- console.log("websocket not available")
274
+ let retry = 0
275
+ const interval = setInterval(() => {
276
+ if (peer && peer.readyState === WebSocket.OPEN) {
277
+ clearInterval(interval)
278
+ peer.send(data)
279
+ return
280
+ }
281
+
282
+ if (retry++ > 5) {
283
+ clearInterval(interval)
284
+ console.log("websocket not available")
285
+ }
286
+ }, 1000)
261
287
  }
262
288
  })
263
289
  }
@@ -265,10 +291,9 @@ const Wire = opt => {
265
291
  opt.peers = [`ws://localhost:${opt.port || 8765}`]
266
292
  }
267
293
  opt.peers.forEach(peer => {
268
- let ws = new WebSocket(peer)
269
- peers.push(ws)
270
294
  const start = () => {
271
- if (!ws) ws = new WebSocket(peer)
295
+ let ws = new WebSocket(peer)
296
+ peers.push(ws)
272
297
  ws.onclose = c => {
273
298
  if (peers.indexOf(ws) !== -1) {
274
299
  peers.splice(peers.indexOf(ws), 1)