@cero-base/cero 0.5.0 → 0.5.2

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": "@cero-base/cero",
3
- "version": "0.5.0",
3
+ "version": "0.5.2",
4
4
  "description": "The ideal p2p API — everything is a handle, handles contain refs, refs contain rows.",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
@@ -67,7 +67,7 @@
67
67
  "test": "ls test/*.test.js | xargs -P2 -n1 brittle-node"
68
68
  },
69
69
  "dependencies": {
70
- "@cero-base/core": "^0.4.1",
70
+ "@cero-base/core": "^0.5.2",
71
71
  "b4a": "^1.8.1",
72
72
  "bare-crypto": "^1.13.7",
73
73
  "bare-fs": "^4.7.1",
@@ -1,5 +1,6 @@
1
1
  import b4a from 'b4a'
2
2
  import Hypercore from 'hypercore'
3
+ import { discoveryKey } from 'hypercore-crypto'
3
4
  import ReadyResource from 'ready-resource'
4
5
  import safetyCatch from 'safety-catch'
5
6
  import z32 from 'z32'
@@ -372,6 +373,18 @@ export class Handle extends ReadyResource {
372
373
  */
373
374
  async _join(invite, type, { routes, timeout } = {}) {
374
375
  const deadline = timeout || 30000
376
+
377
+ // Idempotent: if the invite targets a handle we already have, return it —
378
+ // without pairing, so there's no waiting on a peer to confirm.
379
+ const target = Pairing.inviteTopic(invite)
380
+ if (target) {
381
+ const { data: joined } = await this.store.get('handles')
382
+ const existing = joined.find(
383
+ (h) => h.type === type && b4a.equals(discoveryKey(h.key), target)
384
+ )
385
+ if (existing) return this._load(type, existing.id)
386
+ }
387
+
375
388
  const child = /** @type {Child} */ (
376
389
  await Handle.join(invite, {
377
390
  parent: this,
package/src/index.js CHANGED
@@ -132,6 +132,10 @@ export async function restore(me, phrase) {
132
132
  if (!me?._dir) throw CeroError.INVALID('me must be a cero instance')
133
133
  if (!phrase || typeof phrase !== 'string') throw CeroError.INVALID('phrase must be a string')
134
134
 
135
+ // Already this identity? Nothing to restore — return the running instance.
136
+ const current = await Identity.fromSeed(Identity.toSeed(phrase))
137
+ if (current.id === me.identity.id) return me
138
+
135
139
  const { _dir: dir, spec, _opts: opts } = me
136
140
  const { name, bootstrap, isMobile, onerror, routes, recoveryTimeout } = opts
137
141