@derec-alliance/react-native 0.0.2 → 0.0.4

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/lib/types.js CHANGED
@@ -3,6 +3,113 @@
3
3
  // Copyright (c) 2026 DeRec Alliance. All rights reserved.
4
4
  Object.defineProperty(exports, "__esModule", { value: true });
5
5
  exports.FlowKind = exports.ContactMode = exports.SenderKind = void 0;
6
+ exports.channelFilterMatches = channelFilterMatches;
7
+ exports.advertisedEndpoints = advertisedEndpoints;
8
+ exports.sequentialFailover = sequentialFailover;
9
+ exports.singleEndpointTransport = singleEndpointTransport;
10
+ /**
11
+ * Whether a channel or member with these attributes survives `filter`.
12
+ *
13
+ * Every empty field means "do not restrict", `exclude` is applied after `ids`,
14
+ * and the restrictions combine with AND — the same contract the core states on
15
+ * `ChannelFilter`. A store whose backing cannot express the filter as a query
16
+ * can list and call this; that is correct but transfers the rows the filter
17
+ * exists to leave behind.
18
+ *
19
+ * `id` is a decimal string, as ids are everywhere on this bridge. `role` is the
20
+ * peer's `SenderKind` name for `listHelpers` and the member's `ReplicaRole`
21
+ * name for `listReplicas`.
22
+ */
23
+ function channelFilterMatches(filter, id, status, role) {
24
+ if (!filter)
25
+ return true;
26
+ const ids = filter.ids ?? [];
27
+ const statuses = filter.status ?? [];
28
+ const exclude = filter.exclude ?? [];
29
+ if (ids.length > 0 && !ids.includes(id))
30
+ return false;
31
+ if (statuses.length > 0 && !statuses.includes(status))
32
+ return false;
33
+ if (filter.role != null && filter.role !== role)
34
+ return false;
35
+ return !exclude.includes(id);
36
+ }
37
+ /**
38
+ * The endpoints a peer-supplied message advertises, in the peer's own order.
39
+ *
40
+ * Yields `supported_transports` when it is non-empty, and otherwise the
41
+ * singular `transport_protocol` — which is how every implementation predating
42
+ * the offer list advertises, and the reason this is a function rather than a
43
+ * field read. Reading `transport_protocol` directly is a bug: its meaning
44
+ * narrowed to "one entry of a list, and possibly absent", so a peer that has
45
+ * moved past it looks unreachable to a reader that was correct before 0.0.3.
46
+ *
47
+ * Reports what was advertised, not what is acceptable — nothing here is
48
+ * validated, and the protocol still applies its own transport policy to
49
+ * whatever it records.
50
+ */
51
+ function advertisedEndpoints(message) {
52
+ if (!message)
53
+ return [];
54
+ const offers = message.supported_transports ?? [];
55
+ if (offers.length > 0)
56
+ return offers;
57
+ return message.transport_protocol ? [message.transport_protocol] : [];
58
+ }
59
+ /**
60
+ * Builds a {@link Transport} that tries each endpoint in the order the peer
61
+ * offered it and stops at the first success.
62
+ *
63
+ * An error is thrown only when every endpoint failed. The message is delivered
64
+ * at most once.
65
+ *
66
+ * This is the right default. A peer advertising several endpoints is saying it
67
+ * can be reached at any of them, and the reason 0.0.3 records the whole list is
68
+ * so one being down does not end the conversation.
69
+ */
70
+ function sequentialFailover(dialer) {
71
+ return {
72
+ async send(endpoints, message) {
73
+ let last;
74
+ for (const endpoint of endpoints) {
75
+ try {
76
+ await dialer(endpoint, message);
77
+ return;
78
+ }
79
+ catch (e) {
80
+ last = e;
81
+ }
82
+ }
83
+ // `endpoints` is never empty — the library refuses to record a peer
84
+ // whose endpoints were all filtered away — so reaching here means at
85
+ // least one attempt was made and `last` is populated.
86
+ throw last ?? new Error('send was called with no endpoints');
87
+ },
88
+ };
89
+ }
90
+ /**
91
+ * Builds a {@link Transport} that uses the first endpoint only.
92
+ *
93
+ * Reproduces the pre-0.0.3 behaviour exactly, for an application that genuinely
94
+ * serves one endpoint or has a reason not to fail over.
95
+ *
96
+ * It exists so that choosing it is visible. `endpoints[0]` written inline looks
97
+ * like an implementation detail and reads as finished; naming this records that
98
+ * failover was considered and declined, which is a claim a reviewer can
99
+ * disagree with. If the peers this application talks to advertise more than one
100
+ * endpoint, prefer {@link sequentialFailover} — every endpoint after the first
101
+ * is reachability being thrown away.
102
+ */
103
+ function singleEndpointTransport(dialer) {
104
+ return {
105
+ async send(endpoints, message) {
106
+ if (endpoints.length === 0) {
107
+ throw new Error('send was called with no endpoints');
108
+ }
109
+ await dialer(endpoints[0], message);
110
+ },
111
+ };
112
+ }
6
113
  var SenderKind;
7
114
  (function (SenderKind) {
8
115
  SenderKind[SenderKind["Owner"] = 0] = "Owner";
@@ -54,10 +161,10 @@ var FlowKind;
54
161
  /** Ask the replica group whether this device is behind, and catch up if it
55
162
  * is. Replica-only, and takes no parameters — the group and this device's
56
163
  * own version both come from the stores. */
57
- FlowKind[FlowKind["SyncCheck"] = 7] = "SyncCheck";
164
+ FlowKind[FlowKind["ReplicaDiscovery"] = 7] = "ReplicaDiscovery";
58
165
  /** Remove a member from the replica group. Replica-only. Naming this device
59
166
  * is a voluntary departure; naming another is an eviction. Params:
60
167
  * `{ replica_id: string; memo?: string }` — `replica_id` is a decimal
61
168
  * string so ids above 2^53 survive JS number handling. */
62
- FlowKind[FlowKind["RemoveReplica"] = 8] = "RemoveReplica";
169
+ FlowKind[FlowKind["UnpairReplica"] = 8] = "UnpairReplica";
63
170
  })(FlowKind || (exports.FlowKind = FlowKind = {}));
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "@derec-alliance/react-native",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "description": "React Native bindings for derec-library, the Rust SDK for the DeRec protocol.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
7
7
  "type": "git",
8
8
  "url": "git+https://github.com/derecalliance/lib-derec"
9
9
  },
10
+ "homepage": "https://derec.org",
10
11
  "main": "lib/index.js",
11
12
  "types": "lib/index.d.ts",
12
13
  "files": [
package/src/primitives.ts CHANGED
@@ -86,11 +86,11 @@ function bytes(value: unknown): Uint8Array {
86
86
  return new Uint8Array(value as ArrayBuffer);
87
87
  }
88
88
 
89
- /** `null` and `undefined` both mean "no override" for every optional
90
- * `TransportProtocol` argument; the C ABI reads a zero-length buffer as
91
- * absent. */
92
- function replyTo(value: TransportProtocol | null | undefined): Uint8Array {
93
- return encodeOptionalMessage(MessageKind.TransportProtocol, value);
89
+ /** An omitted or empty list means "no override" for every reply-to argument;
90
+ * the C ABI reads a zero-length buffer as absent, and the responder then
91
+ * routes to the endpoints already recorded for the channel. */
92
+ function replyTo(value: TransportProtocol[] | undefined): Uint8Array {
93
+ return value && value.length > 0 ? encodeTransportList(value) : new Uint8Array(0);
94
94
  }
95
95
 
96
96
  /**
@@ -117,7 +117,7 @@ const discovery = {
117
117
  produce(
118
118
  channel_id: bigint,
119
119
  shared_key: Uint8Array,
120
- reply_to?: TransportProtocol | null,
120
+ reply_to?: TransportProtocol[],
121
121
  ): ProduceResult {
122
122
  const envelope = call(
123
123
  'produce_get_secret_ids_versions_request_message',
@@ -188,12 +188,69 @@ const discovery = {
188
188
  },
189
189
  };
190
190
 
191
+ /**
192
+ * Frames a preference-ordered endpoint list for the native seam: each entry
193
+ * preceded by its protobuf varint byte length. Order is preserved exactly.
194
+ */
195
+ function encodeTransportList(transports: TransportProtocol[]): Uint8Array {
196
+ const entries = transports.map((t) =>
197
+ new Uint8Array(encodeMessage(MessageKind.TransportProtocol, t)),
198
+ );
199
+ const out: number[] = [];
200
+ for (const entry of entries) {
201
+ let len = entry.length;
202
+ do {
203
+ let b = len & 0x7f;
204
+ len >>>= 7;
205
+ if (len !== 0) b |= 0x80;
206
+ out.push(b);
207
+ } while (len !== 0);
208
+ out.push(...entry);
209
+ }
210
+ return new Uint8Array(out);
211
+ }
212
+
213
+ /**
214
+ * Reads the framing `encodeTransportList` writes. Order is the peer's and is
215
+ * preserved exactly — the library filters a peer's endpoints but never ranks
216
+ * them, so this order is the peer's own preference, not a recommendation.
217
+ */
218
+ function decodeTransportList(framed: Uint8Array | ArrayBuffer): TransportProtocol[] {
219
+ const buf = framed instanceof Uint8Array ? framed : new Uint8Array(framed);
220
+ const out: TransportProtocol[] = [];
221
+ let i = 0;
222
+ while (i < buf.length) {
223
+ let len = 0;
224
+ let shift = 0;
225
+ for (;;) {
226
+ if (i >= buf.length) {
227
+ throw new Error('truncated transport list: length prefix runs past the end');
228
+ }
229
+ const b = buf[i++]!;
230
+ len |= (b & 0x7f) << shift;
231
+ if ((b & 0x80) === 0) break;
232
+ shift += 7;
233
+ }
234
+ if (i + len > buf.length) {
235
+ throw new Error('truncated transport list: entry runs past the end');
236
+ }
237
+ out.push(
238
+ decodeMessage<TransportProtocol>(
239
+ MessageKind.TransportProtocol,
240
+ buf.subarray(i, i + len),
241
+ ),
242
+ );
243
+ i += len;
244
+ }
245
+ return out;
246
+ }
247
+
191
248
  const pairing = {
192
249
  request: {
193
250
  create_contact(
194
251
  channel_id: bigint,
195
252
  contact_mode: ContactMode | number,
196
- transport_protocol: TransportProtocol,
253
+ transport_protocols: TransportProtocol[],
197
254
  ): CreateContactResult {
198
255
  // The C entry point also accepts a caller-supplied nonce behind a
199
256
  // presence flag. `@derec-alliance/nodejs` has no such parameter, so the
@@ -203,7 +260,7 @@ const pairing = {
203
260
  'create_contact_message',
204
261
  channel_id,
205
262
  contact_mode,
206
- encodeMessage(MessageKind.TransportProtocol, transport_protocol),
263
+ encodeTransportList(transport_protocols),
207
264
  0,
208
265
  0n,
209
266
  ) as { contact_wire_bytes: ArrayBuffer; secret_key_material: ArrayBuffer };
@@ -223,7 +280,7 @@ const pairing = {
223
280
 
224
281
  produce(
225
282
  kind: SenderKind,
226
- transport_protocol: TransportProtocol,
283
+ transport_protocols: TransportProtocol[],
227
284
  contact_message: ContactMessage,
228
285
  communication_info: CommunicationInfo | null,
229
286
  parameter_range: ParameterRange | null,
@@ -231,7 +288,7 @@ const pairing = {
231
288
  const result = call(
232
289
  'produce_pair_request_message',
233
290
  kind,
234
- encodeMessage(MessageKind.TransportProtocol, transport_protocol),
291
+ encodeTransportList(transport_protocols),
235
292
  encodeContact(contact_message),
236
293
  encodeOptionalMessage(MessageKind.CommunicationInfo, communication_info),
237
294
  encodeOptionalMessage(MessageKind.ParameterRange, parameter_range),
@@ -262,12 +319,12 @@ const pairing = {
262
319
  },
263
320
 
264
321
  produce_pre_pair(
265
- transport_protocol: TransportProtocol,
322
+ own_transports: TransportProtocol[],
266
323
  contact_message: ContactMessage,
267
324
  ): ProducePrePairResult {
268
325
  const envelope = call(
269
326
  'produce_pre_pair_request_message',
270
- encodeMessage(MessageKind.TransportProtocol, transport_protocol),
327
+ encodeTransportList(own_transports),
271
328
  encodeContact(contact_message),
272
329
  );
273
330
  return { envelope: bytes(envelope) };
@@ -293,6 +350,7 @@ const pairing = {
293
350
  secret_key: Uint8Array,
294
351
  communication_info: CommunicationInfo | null,
295
352
  parameter_range: ParameterRange | null,
353
+ unsafe_connection = false,
296
354
  ): PairingResponseProduceResult {
297
355
  const result = call(
298
356
  'produce_pair_response_message',
@@ -301,18 +359,16 @@ const pairing = {
301
359
  secret_key,
302
360
  encodeOptionalMessage(MessageKind.CommunicationInfo, communication_info),
303
361
  encodeOptionalMessage(MessageKind.ParameterRange, parameter_range),
362
+ unsafe_connection,
304
363
  ) as {
305
364
  response_wire_bytes: ArrayBuffer;
306
- peer_transport_protocol: ArrayBuffer;
365
+ peer_transports: ArrayBuffer;
307
366
  shared_key: ArrayBuffer;
308
367
  channel_id: bigint;
309
368
  };
310
369
  return {
311
370
  envelope: bytes(result.response_wire_bytes),
312
- peer_transport_protocol: decodeMessage<TransportProtocol>(
313
- MessageKind.TransportProtocol,
314
- result.peer_transport_protocol,
315
- ),
371
+ peer_transports: decodeTransportList(result.peer_transports),
316
372
  shared_key: bytes(result.shared_key),
317
373
  channel_id: result.channel_id,
318
374
  };
@@ -402,7 +458,7 @@ const recovery = {
402
458
  secret_id: bigint,
403
459
  version: number,
404
460
  shared_key: Uint8Array,
405
- reply_to?: TransportProtocol | null,
461
+ reply_to?: TransportProtocol[],
406
462
  ): ProduceResult {
407
463
  const envelope = call(
408
464
  'produce_get_share_request_message',
@@ -505,7 +561,7 @@ const sharing = {
505
561
  keep_list: number[],
506
562
  description: string,
507
563
  shared_key: Uint8Array,
508
- reply_to?: TransportProtocol | null,
564
+ reply_to?: TransportProtocol[],
509
565
  ): ProduceResult {
510
566
  const envelope = call(
511
567
  'produce_store_share_request_message',
@@ -596,7 +652,7 @@ const unpairing = {
596
652
  channel_id: bigint,
597
653
  memo: string,
598
654
  shared_key: Uint8Array,
599
- reply_to?: TransportProtocol | null,
655
+ reply_to?: TransportProtocol[],
600
656
  ): ProduceResult {
601
657
  const envelope = call(
602
658
  'produce_unpair_request_message',
@@ -668,7 +724,7 @@ const verification = {
668
724
  secret_id: bigint,
669
725
  version: number,
670
726
  shared_key: Uint8Array,
671
- reply_to?: TransportProtocol | null,
727
+ reply_to?: TransportProtocol[],
672
728
  ): ProduceResult {
673
729
  const envelope = call(
674
730
  'produce_verify_share_request_message',
package/src/protocol.ts CHANGED
@@ -13,13 +13,14 @@ import type {
13
13
  DeRecEvent,
14
14
  DiscoveryParams,
15
15
  PairingParams,
16
+ ParameterRange,
16
17
  ProtectSecretParams,
17
18
  RecoverSecretParams,
18
- RemoveReplicaParams,
19
+ UnpairReplicaParams,
19
20
  SecretStore,
20
21
  ShareStore,
21
22
  StateStore,
22
- SyncCheckParams,
23
+ ReplicaDiscoveryParams,
23
24
  Target,
24
25
  Timeouts,
25
26
  Transport,
@@ -86,12 +87,14 @@ function decodeEvents(buffer: ArrayBuffer): DeRecEvent[] {
86
87
  * against does not carry this enum (it
87
88
  * only covers the Rust-internal serde enums listed under its `"enums"` key);
88
89
  * the transport `Protocol` enum lives in `protobufs/transportprotocol.proto`
89
- * instead, where only `HTTPS = 0` is currently defined.
90
+ * instead, where `HTTPS = 0` and `GRPC = 1` are defined.
90
91
  */
91
92
  export function protocolDiscriminant(protocol: string): number {
92
93
  switch (protocol.toLowerCase()) {
93
94
  case 'https':
94
95
  return 0;
96
+ case 'grpc':
97
+ return 1;
95
98
  default:
96
99
  throw new Error(`DeRec: unknown transport protocol "${protocol}"`);
97
100
  }
@@ -156,7 +159,7 @@ export function buildRestoreParams(
156
159
  helpers: recoveredSecret.helpers.map((helper) => {
157
160
  const out: Record<string, unknown> = {
158
161
  channel_id: helper.channel_id,
159
- transport_uri: helper.transport_uri,
162
+ transports: helper.transports,
160
163
  shared_key: Array.from(helper.shared_key),
161
164
  };
162
165
  const info = communicationInfoOrOmit(helper.communication_info);
@@ -177,7 +180,7 @@ export function buildRestoreParams(
177
180
  members: recoveredSecret.replicas.members.map((member) => {
178
181
  const out: Record<string, unknown> = {
179
182
  replica_id: member.replica_id,
180
- transport_uri: member.transport_uri,
183
+ transports: member.transports,
181
184
  role: member.role,
182
185
  };
183
186
  const info = communicationInfoOrOmit(member.communication_info);
@@ -258,17 +261,22 @@ function buildStartParams(flowKind: FlowKind, params: unknown): Uint8Array {
258
261
  if (p.communication_info !== undefined) {
259
262
  out.communication_info = p.communication_info;
260
263
  }
261
- if (p.transport_protocol !== undefined) {
264
+ if (p.own_transports !== undefined && p.own_transports.length > 0) {
265
+ out.own_transports = p.own_transports;
266
+ // The first entry also fills the deprecated singular field so a peer
267
+ // predating `supported_transports` still learns the new address.
268
+ out.transport_protocol = p.own_transports[0];
269
+ } else if (p.transport_protocol !== undefined) {
262
270
  out.transport_protocol = p.transport_protocol;
263
271
  }
264
272
  return jsonToBytes(out);
265
273
  }
266
- case FlowKind.SyncCheck:
274
+ case FlowKind.ReplicaDiscovery:
267
275
  // No parameters: the group and this device's own version both come
268
276
  // from the stores.
269
277
  return jsonToBytes({});
270
- case FlowKind.RemoveReplica: {
271
- const p = params as RemoveReplicaParams;
278
+ case FlowKind.UnpairReplica: {
279
+ const p = params as UnpairReplicaParams;
272
280
  const out: Record<string, unknown> = { replica_id: p.replica_id };
273
281
  if (p.memo !== undefined) {
274
282
  out.memo = p.memo;
@@ -341,12 +349,43 @@ export class DeRecProtocolBuilder {
341
349
  return this;
342
350
  }
343
351
 
352
+ /**
353
+ * @deprecated Use {@link withOwnTransports}, which takes the whole
354
+ * preference list — `withOwnTransports([endpoint])` is the direct
355
+ * replacement. Removed at 0.0.5.
356
+ */
344
357
  withOwnTransport(endpoint: { uri: string; protocol: string }): this {
345
358
  this.config.own_transport_uri = endpoint.uri;
346
359
  this.config.own_transport_protocol = protocolDiscriminant(endpoint.protocol);
347
360
  return this;
348
361
  }
349
362
 
363
+ /**
364
+ * Set every transport endpoint this application serves, in preference
365
+ * order. Written to the config's `own_transports` array, which takes
366
+ * precedence over `own_transport_uri` / `own_transport_protocol` on the
367
+ * Rust side when non-empty — see `ProtocolConfig` in
368
+ * `library/src/interop/ffi/protocol/handle/mod.rs`.
369
+ *
370
+ * The order given is forwarded verbatim: it is not sorted, deduplicated,
371
+ * or reordered here. It is this application's own preference and
372
+ * decides which of a peer's offered endpoints is used. Every listed
373
+ * transport must actually be served, because delivery is push-only —
374
+ * listing an endpoint this application does not serve makes pairing
375
+ * succeed and replies vanish.
376
+ *
377
+ * Supersedes {@link withOwnTransport} for applications serving more
378
+ * than one transport; the single-endpoint setter remains fully
379
+ * supported.
380
+ */
381
+ withOwnTransports(transports: { uri: string; protocol: string }[]): this {
382
+ this.config.own_transports = transports.map((t) => ({
383
+ uri: t.uri,
384
+ protocol: protocolDiscriminant(t.protocol),
385
+ }));
386
+ return this;
387
+ }
388
+
350
389
  /** Default: 3. */
351
390
  withThreshold(threshold: number): this {
352
391
  this.config.threshold = threshold;
@@ -371,12 +410,30 @@ export class DeRecProtocolBuilder {
371
410
  return this;
372
411
  }
373
412
 
374
- /** Default: false. */
413
+ /**
414
+ * @deprecated Use {@link withUnsafeConnection}, which names both gated
415
+ * schemes. Removed at 0.0.5. Default: false.
416
+ */
375
417
  withUnsafeHttp(allow: boolean): this {
376
418
  this.config.unsafe_http = allow;
377
419
  return this;
378
420
  }
379
421
 
422
+ /**
423
+ * Accept plaintext `http://` and `grpc://` transport endpoints.
424
+ * **Development only.** Default: false. Supersedes
425
+ * {@link withUnsafeHttp}, which names only the HTTP scheme.
426
+ *
427
+ * Either flag alone is honored. Setting both to disagreeing values fails
428
+ * construction with the error code `CONFLICTING_PLAINTEXT_OPT_IN` rather
429
+ * than resolving silently, because precedence would hand the decision to
430
+ * the flag being removed.
431
+ */
432
+ withUnsafeConnection(allow: boolean): this {
433
+ this.config.unsafe_connection = allow;
434
+ return this;
435
+ }
436
+
380
437
  /**
381
438
  * Default: empty. `derec_protocol_new`'s own `communication_info` argument
382
439
  * is a pre-encoded `CommunicationInfo` protobuf message, and this SDK has
@@ -444,6 +501,41 @@ export class DeRecProtocolBuilder {
444
501
  return this;
445
502
  }
446
503
 
504
+ /**
505
+ * Declare the bounds this node advertises during pair negotiation.
506
+ *
507
+ * Embedded in outbound `PairRequest`/`PairResponse` envelopes and checked
508
+ * against the peer's range on inbound ones: a range that fails to
509
+ * intersect rejects the pairing. Every bound is optional and defaults to
510
+ * `0`, which the protocol reads as "no constraint on this dimension".
511
+ *
512
+ * Default: unset — no constraints advertised, every peer range accepted.
513
+ */
514
+ withParameterRange(range: Partial<ParameterRange>): this {
515
+ const n = (v: bigint | undefined) => Number(v ?? 0n);
516
+ this.config.parameter_range = {
517
+ min_share_size: n(range.min_share_size),
518
+ max_share_size: n(range.max_share_size),
519
+ min_time_between_verifications: n(range.min_time_between_verifications),
520
+ max_time_between_verifications: n(range.max_time_between_verifications),
521
+ min_time_between_share_updates: n(range.min_time_between_share_updates),
522
+ max_time_between_share_updates: n(range.max_time_between_share_updates),
523
+ min_unresponsive_deletion_timeout: n(
524
+ range.min_unresponsive_deletion_timeout,
525
+ ),
526
+ max_unresponsive_deletion_timeout: n(
527
+ range.max_unresponsive_deletion_timeout,
528
+ ),
529
+ min_unresponsive_deactivation_timeout: n(
530
+ range.min_unresponsive_deactivation_timeout,
531
+ ),
532
+ max_unresponsive_deactivation_timeout: n(
533
+ range.max_unresponsive_deactivation_timeout,
534
+ ),
535
+ };
536
+ return this;
537
+ }
538
+
447
539
  build(): DeRecProtocol {
448
540
  const config = {
449
541
  secret_id: this.secretIdValue.toString(),
@@ -526,7 +618,19 @@ export class DeRecProtocol {
526
618
  return this.host.setCommunicationInfo(jsonToBytes(info)) as Promise<void>;
527
619
  }
528
620
 
529
- /** Returns a `Promise` for the same reason {@link setCommunicationInfo} does. */
621
+ /**
622
+ * Replace this node's endpoint for one protocol, leaving the others
623
+ * alone. A node serves at most one endpoint per protocol, so the
624
+ * `(uri, protocol)` pair identifies the entry it replaces; an entry for a
625
+ * protocol not yet served is appended, and a replaced one keeps its
626
+ * position in the preference order.
627
+ *
628
+ * Returns a `Promise` for the same reason {@link setCommunicationInfo} does.
629
+ *
630
+ * @deprecated Use {@link setOwnTransports}, which takes the whole
631
+ * preference list and is the only way to change which protocols this node
632
+ * serves, or their order. Removed at 0.0.5.
633
+ */
530
634
  setOwnTransport(uri: string, protocol: string): Promise<void> {
531
635
  return this.host.setOwnTransport(
532
636
  uri,
@@ -534,6 +638,27 @@ export class DeRecProtocol {
534
638
  ) as Promise<void>;
535
639
  }
536
640
 
641
+ /**
642
+ * Replaces every endpoint this node advertises, in preference order —
643
+ * the runtime counterpart to `withOwnTransports`, and the only way to
644
+ * change a multi-endpoint node's set ({@link setOwnTransport} collapses
645
+ * it to the one endpoint it is given).
646
+ *
647
+ * Every entry is validated before any is stored, so a malformed URI
648
+ * leaves the previous set intact. An empty array is rejected.
649
+ *
650
+ * Returns a `Promise` for the same reason {@link setCommunicationInfo} does.
651
+ */
652
+ setOwnTransports(
653
+ transports: { uri: string; protocol: string }[],
654
+ ): Promise<void> {
655
+ const wire = transports.map(({ uri, protocol }) => ({
656
+ uri,
657
+ protocol: protocolDiscriminant(protocol),
658
+ }));
659
+ return this.host.setOwnTransports(jsonToBytes(wire)) as Promise<void>;
660
+ }
661
+
537
662
  /**
538
663
  * Decodes the `contact_wire_bytes` `derec_protocol_create_contact`
539
664
  * produces into a typed `ContactMessage`, so an application can read the
@@ -565,8 +690,8 @@ export class DeRecProtocol {
565
690
  start(flowKind: FlowKind.RecoverSecret, params: RecoverSecretParams): Promise<DeRecEvent[]>;
566
691
  start(flowKind: FlowKind.Unpair, params: UnpairParams): Promise<DeRecEvent[]>;
567
692
  start(flowKind: FlowKind.UpdateChannelInfo, params: UpdateChannelInfoParams): Promise<DeRecEvent[]>;
568
- start(flowKind: FlowKind.SyncCheck, params?: SyncCheckParams): Promise<DeRecEvent[]>;
569
- start(flowKind: FlowKind.RemoveReplica, params: RemoveReplicaParams): Promise<DeRecEvent[]>;
693
+ start(flowKind: FlowKind.ReplicaDiscovery, params?: ReplicaDiscoveryParams): Promise<DeRecEvent[]>;
694
+ start(flowKind: FlowKind.UnpairReplica, params: UnpairReplicaParams): Promise<DeRecEvent[]>;
570
695
  async start(flowKind: FlowKind, params?: unknown): Promise<DeRecEvent[]> {
571
696
  const paramsBytes = buildStartParams(flowKind, params);
572
697
  const buffer = (await this.host.start(flowKind, paramsBytes)) as ArrayBuffer;