autopass 2.2.0 → 3.0.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/.gitattributes ADDED
@@ -0,0 +1 @@
1
+ * text=auto eol=lf
@@ -14,10 +14,10 @@ jobs:
14
14
  os: [ubuntu-latest, macos-latest, windows-latest]
15
15
  runs-on: ${{ matrix.os }}
16
16
  steps:
17
- - uses: actions/checkout@v3
18
- - name: Use Node.js ${{ matrix.node-version }}
19
- uses: actions/setup-node@v3
20
- with:
21
- node-version: ${{ matrix.node-version }}
22
- - run: npm install
23
- - run: npm test
17
+ - uses: actions/checkout@v3
18
+ - name: Use Node.js ${{ matrix.node-version }}
19
+ uses: actions/setup-node@v3
20
+ with:
21
+ node-version: ${{ matrix.node-version }}
22
+ - run: npm install
23
+ - run: npm test
package/.prettierrc ADDED
@@ -0,0 +1 @@
1
+ "prettier-config-holepunch"
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Distributed notes/password manager
4
4
 
5
- ``` sh
5
+ ```sh
6
6
  npm install autopass
7
7
  ```
8
8
 
@@ -13,7 +13,7 @@ npm install autopass
13
13
 
14
14
  First choose if you wanna pair or make a new instance.
15
15
 
16
- ``` js
16
+ ```js
17
17
  import Autopass from 'autopass'
18
18
  import Corestore from 'corestore'
19
19
 
@@ -25,7 +25,7 @@ console.log('share to add', inv)
25
25
 
26
26
  Then invite another instance
27
27
 
28
- ``` js
28
+ ```js
29
29
  const pair = Autopass.pair(new Corestore('./another-pass'), inv)
30
30
 
31
31
  const anotherPass = await pair.finished()
@@ -34,13 +34,13 @@ await anotherPass.ready()
34
34
 
35
35
  When paired you can simply start the instance again with the normal constructor.
36
36
 
37
- ``` js
37
+ ```js
38
38
  await pass.add('a-note', 'hello this is a note')
39
39
  ```
40
40
 
41
41
  Then on the other node you get it out with
42
42
 
43
- ``` js
43
+ ```js
44
44
  const note = await pass.get('a-note')
45
45
  console.log({ note })
46
46
  ```
@@ -59,23 +59,14 @@ Triggered when it updates, ie something added/removed an entry
59
59
 
60
60
  Get an entry.
61
61
 
62
- #### `value = await pass.getFile(name)`
63
-
64
- Get a file.
65
-
66
62
  #### `stream = pass.list()`
67
63
 
68
64
  Get all entries.
69
65
 
70
- #### `await pass.add(key, value)`
66
+ #### `await pass.add(key, value, file)`
71
67
 
72
68
  Add new entry
73
69
 
74
- #### `await pass.addFIle(name, buffer)`
75
-
76
- Add new file
77
-
78
-
79
70
  #### `await pass.remove(key)`
80
71
 
81
72
  Remove an entry.
@@ -112,6 +103,18 @@ Pair with another instance.
112
103
 
113
104
  Wait for the pair to finish.
114
105
 
106
+ #### `await pass.addMirror(key)`
107
+
108
+ Add a blind mirror.
109
+
110
+ #### `await getMirror()`
111
+
112
+ Returns an array of blind mirrors
113
+
114
+ #### `await removeMirror(key)`
115
+
116
+ Remove a blind mirror
117
+
115
118
  #### `await pair.close()`
116
119
 
117
120
  Force close the pair instance. Only need to call this if you dont wait for it to finish.get
@@ -120,6 +123,14 @@ Force close the pair instance. Only need to call this if you dont wait for it to
120
123
 
121
124
  Fully close the pass instance.
122
125
 
126
+ #### `await pass.suspend()`
127
+
128
+ Suspend the swarm and discovery
129
+
130
+ #### `await pass.resume`
131
+
132
+ Resume the swarm is suspended
133
+
123
134
  ## Contributors
124
135
 
125
136
  Written with big contributions from [@supersu](https://github.com/supersuryaansh)
package/example.mjs CHANGED
@@ -20,7 +20,7 @@ if (pass.base.writable) {
20
20
  onupdate()
21
21
  pass.on('update', onupdate)
22
22
 
23
- function onupdate () {
23
+ function onupdate() {
24
24
  console.log('db changed, all entries:')
25
25
  pass.list().on('data', console.log)
26
26
  }
package/index.js CHANGED
@@ -7,16 +7,19 @@ const Hyperswarm = require('hyperswarm')
7
7
  const ReadyResource = require('ready-resource')
8
8
  const z32 = require('z32')
9
9
  const b4a = require('b4a')
10
- const { Router, dispatch } = require('./spec/hyperdispatch')
10
+ const { Router, encode, decode } = require('./spec/hyperdispatch')
11
+ const BlindPeering = require('blind-peering')
11
12
  const db = require('./spec/db/index.js')
13
+ const enc = require('hypercore-id-encoding')
12
14
 
13
15
  class AutopassPairer extends ReadyResource {
14
- constructor (store, invite, opts = {}) {
16
+ constructor(store, invite, opts = {}) {
15
17
  super()
16
18
  this.store = store
17
19
  this.invite = invite
18
20
  this.swarm = null
19
21
  this.pairing = null
22
+ this.peering = null
20
23
  this.candidate = null
21
24
  this.bootstrap = opts.bootstrap || null
22
25
  this.onresolve = null
@@ -26,7 +29,7 @@ class AutopassPairer extends ReadyResource {
26
29
  this.ready().catch(noop)
27
30
  }
28
31
 
29
- async _open () {
32
+ async _open() {
30
33
  await this.store.ready()
31
34
  this.swarm = new Hyperswarm({
32
35
  keyPair: await this.store.createKeyPair('hyperswarm'),
@@ -54,6 +57,8 @@ class AutopassPairer extends ReadyResource {
54
57
  encryptionKey: result.encryptionKey,
55
58
  bootstrap: this.bootstrap
56
59
  })
60
+
61
+ await this.pass.deleteInvite()
57
62
  }
58
63
  this.swarm = null
59
64
  this.store = null
@@ -63,7 +68,7 @@ class AutopassPairer extends ReadyResource {
63
68
  })
64
69
  }
65
70
 
66
- _whenWritable () {
71
+ _whenWritable() {
67
72
  if (this.pass.base.writable) return
68
73
  const check = () => {
69
74
  if (this.pass.base.writable) {
@@ -74,7 +79,7 @@ class AutopassPairer extends ReadyResource {
74
79
  this.pass.base.on('update', check)
75
80
  }
76
81
 
77
- async _close () {
82
+ async _close() {
78
83
  if (this.candidate !== null) {
79
84
  await this.candidate.close()
80
85
  }
@@ -94,7 +99,7 @@ class AutopassPairer extends ReadyResource {
94
99
  }
95
100
  }
96
101
 
97
- finished () {
102
+ finished() {
98
103
  return new Promise((resolve, reject) => {
99
104
  this.onresolve = resolve
100
105
  this.onreject = reject
@@ -103,7 +108,7 @@ class AutopassPairer extends ReadyResource {
103
108
  }
104
109
 
105
110
  class Autopass extends ReadyResource {
106
- constructor (corestore, opts = {}) {
111
+ constructor(corestore, opts = {}) {
107
112
  super()
108
113
  this.router = new Router()
109
114
  this.store = corestore
@@ -127,10 +132,18 @@ class Autopass extends ReadyResource {
127
132
  await context.view.insert('@autopass/records', data)
128
133
  })
129
134
 
135
+ this.router.add('@autopass/add-mirror', async (data, context) => {
136
+ await context.view.insert('@autopass/mirrors', data)
137
+ })
138
+
130
139
  this.router.add('@autopass/del', async (data, context) => {
131
140
  await context.view.delete('@autopass/records', { key: data.key })
132
141
  })
133
142
 
143
+ this.router.add('@autopass/del-mirror', async (data, context) => {
144
+ await context.view.delete('@autopass/mirrors', { key: data.key })
145
+ })
146
+
134
147
  this.router.add('@autopass/add-invite', async (data, context) => {
135
148
  await context.view.insert('@autopass/invite', data)
136
149
  })
@@ -144,13 +157,13 @@ class Autopass extends ReadyResource {
144
157
  }
145
158
 
146
159
  // Initialize autobase
147
- _boot (opts = {}) {
160
+ _boot(opts = {}) {
148
161
  const { encryptionKey, key } = opts
149
162
 
150
163
  this.base = new Autobase(this.store, key, {
151
164
  encrypt: true,
152
165
  encryptionKey,
153
- open (store) {
166
+ open(store) {
154
167
  return HyperDB.bee(store.get('view'), db, {
155
168
  extension: false,
156
169
  autoUpdate: true
@@ -165,19 +178,19 @@ class Autopass extends ReadyResource {
165
178
  })
166
179
  }
167
180
 
168
- async _apply (nodes, view, base) {
181
+ async _apply(nodes, view, base) {
169
182
  for (const node of nodes) {
170
183
  await this.router.dispatch(node.value, { view, base })
171
184
  }
172
185
  await view.flush()
173
186
  }
174
187
 
175
- async _open () {
188
+ async _open() {
176
189
  await this.base.ready()
177
190
  if (this.replicate) await this._replicate()
178
191
  }
179
192
 
180
- async _close () {
193
+ async _close() {
181
194
  if (this.swarm) {
182
195
  await this.member.close()
183
196
  await this.pairing.close()
@@ -186,73 +199,83 @@ class Autopass extends ReadyResource {
186
199
  await this.base.close()
187
200
  }
188
201
 
189
- get writerKey () {
202
+ get writerKey() {
190
203
  return this.base.local.key
191
204
  }
192
205
 
193
- get key () {
206
+ get key() {
194
207
  return this.base.key
195
208
  }
196
209
 
197
- get discoveryKey () {
210
+ get discoveryKey() {
198
211
  return this.base.discoveryKey
199
212
  }
200
213
 
201
- get encryptionKey () {
214
+ get encryptionKey() {
202
215
  return this.base.encryptionKey
203
216
  }
204
217
 
205
- static pair (store, invite, opts) {
218
+ static pair(store, invite, opts) {
206
219
  return new AutopassPairer(store, invite, opts)
207
220
  }
208
221
 
209
- async createInvite (opts) {
222
+ async createInvite(opts) {
210
223
  if (this.opened === false) await this.ready()
211
224
  const existing = await this.base.view.findOne('@autopass/invite', {})
212
225
  if (existing) {
213
226
  return z32.encode(existing.invite)
214
227
  }
215
- const { id, invite, publicKey, expires } = BlindPairing.createInvite(this.base.key)
228
+ const { id, invite, publicKey, expires } = BlindPairing.createInvite(
229
+ this.base.key
230
+ )
216
231
 
217
232
  const record = { id, invite, publicKey, expires }
218
- await this.base.append(dispatch('@autopass/add-invite', record))
233
+ await this.base.append(encode('@autopass/add-invite', record))
219
234
  return z32.encode(record.invite)
220
235
  }
221
236
 
222
- async deleteInvite () {
237
+ async deleteInvite() {
223
238
  if (this.opened === false) await this.ready()
224
239
  const existing = await this.base.view.findOne('@autopass/invite', {})
225
240
  if (existing) {
226
- await this.base.append(dispatch('@autopass/del-invite', existing))
241
+ await this.base.append(encode('@autopass/del-invite', existing))
227
242
  }
228
243
  }
229
244
 
230
- list (opts) {
245
+ list(opts) {
231
246
  return this.base.view.find('@autopass/records', {})
232
247
  }
233
248
 
234
- async get (key) {
249
+ async get(key) {
235
250
  const data = await this.base.view.get('@autopass/records', { key })
236
251
  if (data === null) {
237
252
  return null
238
253
  }
239
- return data.value
254
+ return { value: data.value, file: data.file }
240
255
  }
241
256
 
242
- async addWriter (key) {
243
- await this.base.append(dispatch('@autopass/add-writer', { key: b4a.isBuffer(key) ? key : b4a.from(key) }))
257
+ async addWriter(key) {
258
+ await this.base.append(
259
+ encode('@autopass/add-writer', {
260
+ key: b4a.isBuffer(key) ? key : b4a.from(key)
261
+ })
262
+ )
244
263
  return true
245
264
  }
246
265
 
247
- async removeWriter (key) {
248
- await this.base.append(dispatch('@autopass/remove-writer', { key: b4a.isBuffer(key) ? key : b4a.from(key) }))
266
+ async removeWriter(key) {
267
+ await this.base.append(
268
+ encode('@autopass/remove-writer', {
269
+ key: b4a.isBuffer(key) ? key : b4a.from(key)
270
+ })
271
+ )
249
272
  }
250
273
 
251
- get writable () {
274
+ get writable() {
252
275
  return this.base.writable
253
276
  }
254
277
 
255
- async _replicate () {
278
+ async _replicate() {
256
279
  await this.base.ready()
257
280
  if (this.swarm === null) {
258
281
  this.swarm = new Hyperswarm({
@@ -269,7 +292,7 @@ class Autopass extends ReadyResource {
269
292
  onadd: async (candidate) => {
270
293
  const id = candidate.inviteId
271
294
  const inv = await this.base.view.findOne('@autopass/invite', {})
272
- if (!b4a.equals(inv.id, id)) {
295
+ if (inv === null || !b4a.equals(inv.id, id)) {
273
296
  return
274
297
  }
275
298
  candidate.open(inv.publicKey)
@@ -278,32 +301,63 @@ class Autopass extends ReadyResource {
278
301
  key: this.base.key,
279
302
  encryptionKey: this.base.encryptionKey
280
303
  })
304
+ await this.deleteInvite()
281
305
  }
282
306
  })
283
307
  this.swarm.join(this.base.discoveryKey)
308
+
309
+ const mirrorList = await this.getMirror()
310
+ const mirrors = mirrorList.map((item) => item.key)
311
+ this.peering = new BlindPeering(this.swarm, this.store, {
312
+ autobaseMirrors: mirrors
313
+ })
314
+ this.peering.addAutobaseBackground(this.base)
284
315
  }
285
316
 
286
- async add (key, value) {
287
- await this.base.append(dispatch('@autopass/put', { key, value }))
317
+ async add(key, value, file) {
318
+ await this.base.append(encode('@autopass/put', { key, value, file }))
288
319
  }
289
320
 
290
- async addFile (key, file) {
291
- await this.base.append(dispatch('@autopass/put', { key, file }))
321
+ async remove(key) {
322
+ await this.base.append(encode('@autopass/del', { key }))
292
323
  }
293
324
 
294
- async getFile (key) {
295
- const data = await this.base.view.get('@autopass/records', { key })
296
- if (data === null) {
297
- return null
325
+ async addMirror(key) {
326
+ const keyBuffer = enc.decode(enc.normalize(key))
327
+ await this.base.append(encode('@autopass/add-mirror', { key: keyBuffer }))
328
+ }
329
+
330
+ async getMirror() {
331
+ const queryStream = this.base.view.find('@autopass/mirrors', {})
332
+ const results = await queryStream.toArray()
333
+ return results.map((r) => ({
334
+ ...r,
335
+ key: enc.encode(r.key)
336
+ }))
337
+ }
338
+
339
+ async removeMirror(key) {
340
+ const keyBuffer = enc.decode(enc.normalize(key))
341
+ await this.base.append(encode('@autopass/del-mirror', { key: keyBuffer }))
342
+ }
343
+
344
+ async suspend() {
345
+ if (this.swarm) {
346
+ await this.pairing.suspend()
347
+ await this.swarm.suspend()
348
+ await this.store.suspend()
298
349
  }
299
- return data.file
300
350
  }
301
351
 
302
- async remove (key) {
303
- await this.base.append(dispatch('@autopass/del', { key }))
352
+ async resume() {
353
+ if (this.swarm) {
354
+ await this.store.resume()
355
+ await this.swarm.resume()
356
+ await this.pairing.resume()
357
+ }
304
358
  }
305
359
  } // end class
306
360
 
307
- function noop () {}
361
+ function noop() {}
308
362
 
309
363
  module.exports = Autopass
package/package.json CHANGED
@@ -1,9 +1,11 @@
1
1
  {
2
2
  "name": "autopass",
3
- "version": "2.2.0",
3
+ "version": "3.0.0",
4
4
  "main": "index.js",
5
5
  "scripts": {
6
- "test": "standard && brittle test.js"
6
+ "brittle": "npx brittle test.js",
7
+ "lint": "npx prettier . --check",
8
+ "test": "npm run lint && npm run brittle"
7
9
  },
8
10
  "author": "Holepunch Inc",
9
11
  "license": "Apache-2.0",
@@ -19,25 +21,27 @@
19
21
  }
20
22
  },
21
23
  "devDependencies": {
22
- "brittle": "^3.7.0",
23
- "hyperdht": "^6.20.1",
24
- "standard": "^17.1.2",
25
- "test-tmp": "^1.3.0"
24
+ "bare-fs": "^4.4.4",
25
+ "bare-process": "^4.2.1",
26
+ "brittle": "^3.19.0",
27
+ "hyperdht": "^6.23.0",
28
+ "prettier": "^3.6.2",
29
+ "prettier-config-holepunch": "^1.0.0",
30
+ "test-tmp": "^1.4.0"
26
31
  },
27
32
  "dependencies": {
28
- "autobase": "^7.0.34",
29
- "b4a": "^1.6.7",
30
- "bare-fs": "^4.1.2",
31
- "bare-process": "^4.2.1",
33
+ "autobase": "^7.19.4",
34
+ "b4a": "^1.7.1",
32
35
  "blind-pairing": "^2.3.1",
33
- "compact-encoding": "^2.16.0",
34
- "corestore": "^7.0.7",
35
- "hyperbee": "^2.22.2",
36
- "hypercore": "^11.0.9",
37
- "hyperdb": "^4.9.4",
38
- "hyperdispatch": "^1.0.2",
39
- "hyperschema": "^1.10.4",
40
- "hyperswarm": "^4.8.4",
41
- "ready-resource": "^1.1.1"
36
+ "blind-peering": "^1.13.0",
37
+ "corestore": "^7.4.7",
38
+ "hyperbee": "^2.26.5",
39
+ "hypercore": "^11.16.2",
40
+ "hypercore-id-encoding": "^1.3.0",
41
+ "hyperdb": "^4.16.1",
42
+ "hyperdispatch": "^1.4.2",
43
+ "hyperschema": "^1.15.0",
44
+ "hyperswarm": "^4.14.0",
45
+ "ready-resource": "^1.2.0"
42
46
  }
43
47
  }