@cero-base/core 1.7.1 → 1.8.1

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/core",
3
- "version": "1.7.1",
3
+ "version": "1.8.1",
4
4
  "description": "cero p2p primitives — identity, storage, network, database, blobs, rpc, pairing.",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -137,7 +137,7 @@
137
137
  "build:types": "rm -rf types && tsc -p .",
138
138
  "pretest": "npm run build:test",
139
139
  "prepublishOnly": "npm run build:types",
140
- "test": "npm run test:node",
140
+ "test": "npm run test:bare",
141
141
  "test:bare": "brittle-bare test/bare.js test/*.test.js",
142
142
  "pretest:bare": "npm run build:test",
143
143
  "test:node": "brittle-node test/*.test.js"
@@ -152,20 +152,20 @@
152
152
  "bare-path": "^3.1.1",
153
153
  "bip39-mnemonic": "^2.5.0",
154
154
  "blind-pairing": "^2.3.1",
155
- "blind-peering": "^2.5.0",
155
+ "blind-peering": "^2.5.3",
156
156
  "compact-encoding": "^3.3.0",
157
- "corestore": "^7.11.1",
157
+ "corestore": "^7.12.0",
158
158
  "framed-stream": "^1.0.1",
159
159
  "hrpc": "^4.3.0",
160
160
  "hyperblobs": "^2.12.1",
161
- "hypercore": "^11.34.1",
161
+ "hypercore": "^11.35.1",
162
162
  "hypercore-blob-server": "^1.15.0",
163
163
  "hypercore-crypto": "^3.7.0",
164
164
  "hypercore-id-encoding": "^1.3.0",
165
165
  "hypercore-storage": "^3.2.0",
166
- "hyperdb": "^6.7.0",
166
+ "hyperdb": "^6.8.0",
167
167
  "hyperdispatch": "^1.6.0",
168
- "hyperschema": "^1.21.0",
168
+ "hyperschema": "^1.22.0",
169
169
  "hyperswarm": "^4.17.0",
170
170
  "keet-identity-key": "^3.2.0",
171
171
  "protomux-wakeup": "^2.9.0",
@@ -182,7 +182,7 @@
182
182
  "bare-fetch": "^3.2.0",
183
183
  "bare-process": "^4.5.1",
184
184
  "bare-url": "^2.4.6",
185
- "blind-peer": "^3.12.3",
185
+ "blind-peer": "^3.12.5",
186
186
  "brittle": "^4.1.0",
187
187
  "typescript": "^5.9.3",
188
188
  "which-runtime": "^1.4.0"
@@ -23,6 +23,7 @@ const Response = getEncoding('@cero/confirm')
23
23
  * @property {import('../network/index.js').Network} network Network to host the BlindPairing member.
24
24
  * @property {import('../identity/index.js').Identity} identity Identity used to sign invites and derive the topic.
25
25
  * @property {Uint8Array} [topic] Optional override topic; defaults to `identity.publicKey`.
26
+ * @property {boolean} [host] Register the member listener that serves invites (default `true`). Join-only pairings pass `false` — a listener is one-per-topic, so a joiner registering one collides with any concurrent join.
26
27
  * @property {any} [inviteEncoding] compact-encoding type for the invite payload `data`.
27
28
  * @property {any} [joinerEncoding] compact-encoding type for the joiner-supplied `userData`.
28
29
  * @property {(err: Error) => void} [onerror] Called when a background candidate fails to process.
@@ -31,7 +32,7 @@ const Response = getEncoding('@cero/confirm')
31
32
  * @property {string} [role] Role tag bound into the invite.
32
33
  * @property {number} [expiresIn] TTL in ms; `0`/omitted = no expiry.
33
34
  * @property {any} [data] Arbitrary payload (encoded via `inviteEncoding` when set).
34
- * @property {boolean} [multiUse] Reusable until expiry/revoke; default `false` (consumed on first settle).
35
+ * @property {boolean} [reuse] Reusable until expiry/revoke; default `false` (consumed on first settle).
35
36
  *
36
37
  * @typedef {object} JoinOpts
37
38
  * @property {any} [userData] Payload sent with the candidate request.
@@ -65,6 +66,7 @@ export class Pairing extends ReadyResource {
65
66
  network,
66
67
  identity,
67
68
  topic,
69
+ host = true,
68
70
  inviteEncoding = null,
69
71
  joinerEncoding = null,
70
72
  onerror = safetyCatch
@@ -76,6 +78,7 @@ export class Pairing extends ReadyResource {
76
78
  this.network = network
77
79
  this.identity = identity
78
80
  this.topic = topic || identity.publicKey
81
+ this.host = host
79
82
  this.inviteEncoding = inviteEncoding
80
83
  this.joinerEncoding = joinerEncoding
81
84
  this._onerror = onerror
@@ -91,10 +94,15 @@ export class Pairing extends ReadyResource {
91
94
  // shared instance — one set of swarm/DHT listeners for every handle
92
95
  this._blind = await this.network.blind()
93
96
 
94
- this._member = this._blind.addMember({
95
- discoveryKey: discoveryKey(this.topic),
96
- onadd: (req) => this._onCandidate(req).catch(this._onerror)
97
- })
97
+ // blind-pairing allows ONE member listener per discovery key — a join-only
98
+ // pairing must not register one, or concurrent joins under the same
99
+ // identity throw 'Active member already exist'
100
+ if (this.host) {
101
+ this._member = this._blind.addMember({
102
+ discoveryKey: discoveryKey(this.topic),
103
+ onadd: (req) => this._onCandidate(req).catch(this._onerror)
104
+ })
105
+ }
98
106
  await this.network.refreshInjected()
99
107
  }
100
108
 
@@ -133,8 +141,11 @@ export class Pairing extends ReadyResource {
133
141
  * @param {CreateInviteOpts} [opts]
134
142
  * @returns {Promise<string>}
135
143
  */
136
- async createInvite({ role = '', expiresIn = 0, data = null, multiUse = false } = {}) {
144
+ async createInvite({ role = '', expiresIn = 0, data = null, reuse = false } = {}) {
137
145
  if (this.closing || this.closed) throw CeroError.CLOSED('Pairing')
146
+ // a join-only pairing has no member listener — an invite it mints would
147
+ // never be served, so fail loudly instead of handing out a dead invite
148
+ if (!this.host) throw CeroError.INVALID('cannot create invites on a join-only Pairing')
138
149
  if (!this.opened) await this.ready()
139
150
 
140
151
  // Rendezvous key matches the Member topic so candidates land on the right
@@ -157,7 +168,7 @@ export class Pairing extends ReadyResource {
157
168
  seed: blind.seed,
158
169
  publicKey: blind.publicKey,
159
170
  invite,
160
- multiUse
171
+ reuse
161
172
  })
162
173
 
163
174
  return invite.toString()
@@ -281,7 +292,7 @@ export class Pairing extends ReadyResource {
281
292
  seed: record.seed,
282
293
  userData,
283
294
  onsettle: () => {
284
- if (!record.multiUse) this._invites.delete(id)
295
+ if (!record.reuse) this._invites.delete(id)
285
296
  }
286
297
  })
287
298
 
@@ -3,6 +3,7 @@
3
3
  * @property {import('../network/index.js').Network} network Network to host the BlindPairing member.
4
4
  * @property {import('../identity/index.js').Identity} identity Identity used to sign invites and derive the topic.
5
5
  * @property {Uint8Array} [topic] Optional override topic; defaults to `identity.publicKey`.
6
+ * @property {boolean} [host] Register the member listener that serves invites (default `true`). Join-only pairings pass `false` — a listener is one-per-topic, so a joiner registering one collides with any concurrent join.
6
7
  * @property {any} [inviteEncoding] compact-encoding type for the invite payload `data`.
7
8
  * @property {any} [joinerEncoding] compact-encoding type for the joiner-supplied `userData`.
8
9
  * @property {(err: Error) => void} [onerror] Called when a background candidate fails to process.
@@ -11,7 +12,7 @@
11
12
  * @property {string} [role] Role tag bound into the invite.
12
13
  * @property {number} [expiresIn] TTL in ms; `0`/omitted = no expiry.
13
14
  * @property {any} [data] Arbitrary payload (encoded via `inviteEncoding` when set).
14
- * @property {boolean} [multiUse] Reusable until expiry/revoke; default `false` (consumed on first settle).
15
+ * @property {boolean} [reuse] Reusable until expiry/revoke; default `false` (consumed on first settle).
15
16
  *
16
17
  * @typedef {object} JoinOpts
17
18
  * @property {any} [userData] Payload sent with the candidate request.
@@ -56,10 +57,11 @@ export class Pairing extends ReadyResource {
56
57
  */
57
58
  static isInvite(str: unknown): boolean;
58
59
  /** @param {PairingOpts} [opts] */
59
- constructor({ network, identity, topic, inviteEncoding, joinerEncoding, onerror }?: PairingOpts);
60
+ constructor({ network, identity, topic, host, inviteEncoding, joinerEncoding, onerror }?: PairingOpts);
60
61
  network: import("../index.js").Network;
61
62
  identity: import("../index.js").Identity;
62
63
  topic: Uint8Array<ArrayBufferLike>;
64
+ host: boolean;
63
65
  inviteEncoding: any;
64
66
  joinerEncoding: any;
65
67
  _onerror: (err: Error) => void;
@@ -73,7 +75,7 @@ export class Pairing extends ReadyResource {
73
75
  * @param {CreateInviteOpts} [opts]
74
76
  * @returns {Promise<string>}
75
77
  */
76
- createInvite({ role, expiresIn, data, multiUse }?: CreateInviteOpts): Promise<string>;
78
+ createInvite({ role, expiresIn, data, reuse }?: CreateInviteOpts): Promise<string>;
77
79
  /**
78
80
  * Forget a previously-minted invite. New candidates carrying it will be
79
81
  * dropped silently.
@@ -124,6 +126,10 @@ export type PairingOpts = {
124
126
  * Optional override topic; defaults to `identity.publicKey`.
125
127
  */
126
128
  topic?: Uint8Array;
129
+ /**
130
+ * Register the member listener that serves invites (default `true`). Join-only pairings pass `false` — a listener is one-per-topic, so a joiner registering one collides with any concurrent join.
131
+ */
132
+ host?: boolean;
127
133
  /**
128
134
  * compact-encoding type for the invite payload `data`.
129
135
  */
@@ -153,7 +159,7 @@ export type CreateInviteOpts = {
153
159
  /**
154
160
  * Reusable until expiry/revoke; default `false` (consumed on first settle).
155
161
  */
156
- multiUse?: boolean;
162
+ reuse?: boolean;
157
163
  };
158
164
  export type JoinOpts = {
159
165
  /**