@derec-alliance/react-native 0.0.4 → 0.0.6

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/src/errors.ts CHANGED
@@ -42,4 +42,11 @@ export interface DeRecError {
42
42
  memo?: string;
43
43
  expected?: number;
44
44
  got?: number;
45
+ /**
46
+ * Present only when `code` is `"restore_conflict"`: the channels already
47
+ * living at the recovered canonical ids, as decimal strings — the same
48
+ * field `@derec-alliance/nodejs` carries on its restore conflict error.
49
+ * Clear exactly these and retry `restore`.
50
+ */
51
+ channel_ids?: string[];
45
52
  }
package/src/native.ts CHANGED
@@ -6,7 +6,8 @@
6
6
  * surface passes `ArrayBuffer`s that TurboModule specs cannot express.
7
7
  */
8
8
  export interface DeRecNative {
9
- version(): string;
9
+ version(): { major: number; minor: number };
10
+ generate_replica_id(): bigint;
10
11
  }
11
12
 
12
13
  /**
package/src/primitives.ts CHANGED
@@ -55,6 +55,7 @@ import type {
55
55
  PrePairResponseExtractResult,
56
56
  PrePairResponseMessage,
57
57
  ProcessPrePairResult,
58
+ ProducePrePairNoKeysResult,
58
59
  ProducePrePairResult,
59
60
  ProduceResult,
60
61
  RecoverResult,
@@ -94,22 +95,17 @@ function replyTo(value: TransportProtocol[] | undefined): Uint8Array {
94
95
  }
95
96
 
96
97
  /**
97
- * `ContactMessage` keeps its own dedicated codec rather than going through
98
- * the generic message codec: `encode_contact_message` /
99
- * `decode_contact_message` also enforce the mode/field invariant — an
100
- * `InlineKeys` contact must carry keys, a `HashedKeys` one must carry only
101
- * the commitment — so a malformed contact is rejected here rather than
102
- * travelling.
98
+ * Every contact crossing the native seam goes through the core's contact
99
+ * codec, which also refuses a contact that violates the invariants of its
100
+ * contact mode. Delegates to `pairing.request.encode_contact` /
101
+ * `decode_contact`, defined below.
103
102
  */
104
103
  function encodeContact(contact_message: ContactMessage): Uint8Array {
105
- return bytes(
106
- call('encode_contact_message', jsonToBytes(plainMessage(contact_message))),
107
- );
104
+ return pairing.request.encode_contact(contact_message);
108
105
  }
109
106
 
110
107
  function decodeContact(wire: Uint8Array | ArrayBuffer): ContactMessage {
111
- const json = call('decode_contact_message', wire) as ArrayBuffer;
112
- return reviveMessage(jsonFromBytes(json)) as ContactMessage;
108
+ return pairing.request.decode_contact(wire);
113
109
  }
114
110
 
115
111
  const discovery = {
@@ -247,22 +243,24 @@ function decodeTransportList(framed: Uint8Array | ArrayBuffer): TransportProtoco
247
243
 
248
244
  const pairing = {
249
245
  request: {
246
+ /** Creates an out-of-band `ContactMessage`. `nonce` omitted or `null`
247
+ * lets the library draw a random one; `ContactMode.NoKeys` requires the
248
+ * caller to supply it. */
250
249
  create_contact(
251
250
  channel_id: bigint,
252
251
  contact_mode: ContactMode | number,
253
252
  transport_protocols: TransportProtocol[],
253
+ nonce?: bigint | number | null,
254
254
  ): CreateContactResult {
255
- // The C entry point also accepts a caller-supplied nonce behind a
256
- // presence flag. `@derec-alliance/nodejs` has no such parameter, so the
257
- // flag is 0 and the library generates one — the same behaviour, not a
258
- // default invented here.
255
+ // The C entry point carries `Option<u64>` as a presence flag plus a
256
+ // value; an absent nonce clears the flag and the library draws one.
259
257
  const result = call(
260
258
  'create_contact_message',
261
259
  channel_id,
262
260
  contact_mode,
263
261
  encodeTransportList(transport_protocols),
264
- 0,
265
- 0n,
262
+ nonce == null ? 0 : 1,
263
+ nonce ?? 0n,
266
264
  ) as { contact_wire_bytes: ArrayBuffer; secret_key_material: ArrayBuffer };
267
265
  return {
268
266
  contact_message: decodeContact(result.contact_wire_bytes),
@@ -270,13 +268,22 @@ const pairing = {
270
268
  };
271
269
  },
272
270
 
273
- /** Proto-encodes a `ContactMessage`. Rejects a contact that violates the
274
- * mode/field invariant rather than serializing it. */
275
- encode_contact: encodeContact,
271
+ /** Serializes a `ContactMessage` to the bytes delivered out of band.
272
+ * Refuses a contact that violates the invariants of its contact mode or
273
+ * advertises no endpoint. */
274
+ encode_contact(contact_message: ContactMessage): Uint8Array {
275
+ return bytes(
276
+ call('encode_contact_message', jsonToBytes(plainMessage(contact_message))),
277
+ );
278
+ },
276
279
 
277
- /** Decodes proto `ContactMessage` bytes. Rejects a contact that violates
278
- * the mode/field invariant rather than returning it. */
279
- decode_contact: decodeContact,
280
+ /** Parses out-of-band contact bytes back into a `ContactMessage`.
281
+ * Refuses bytes that are not a contact, or a contact that violates the
282
+ * invariants of its contact mode. */
283
+ decode_contact(bytes: Uint8Array | ArrayBuffer): ContactMessage {
284
+ const json = call('decode_contact_message', bytes) as ArrayBuffer;
285
+ return reviveMessage(jsonFromBytes(json)) as ContactMessage;
286
+ },
280
287
 
281
288
  produce(
282
289
  kind: SenderKind,
@@ -306,8 +313,17 @@ const pairing = {
306
313
  };
307
314
  },
308
315
 
309
- extract(envelope_bytes: Uint8Array, secret_key: Uint8Array): { request: PairRequestMessage } {
310
- const result = call('extract_pair_request', envelope_bytes, secret_key) as {
316
+ extract(
317
+ envelope_bytes: Uint8Array,
318
+ secret_key: Uint8Array,
319
+ parameter_range: ParameterRange | null,
320
+ ): { request: PairRequestMessage } {
321
+ const result = call(
322
+ 'extract_pair_request',
323
+ envelope_bytes,
324
+ secret_key,
325
+ encodeOptionalMessage(MessageKind.ParameterRange, parameter_range),
326
+ ) as {
311
327
  request_proto_bytes: ArrayBuffer;
312
328
  };
313
329
  return {
@@ -393,12 +409,14 @@ const pairing = {
393
409
  contact_message: ContactMessage,
394
410
  response: PairResponseMessage,
395
411
  secret_key: Uint8Array,
412
+ parameter_range: ParameterRange | null,
396
413
  ): PairingProcessResult {
397
414
  const result = call(
398
415
  'process_pair_response_message',
399
416
  encodeContact(contact_message),
400
417
  encodeMessage(MessageKind.PairResponse, response),
401
418
  secret_key,
419
+ encodeOptionalMessage(MessageKind.ParameterRange, parameter_range),
402
420
  ) as { shared_key: ArrayBuffer; channel_id: bigint };
403
421
  return { shared_key: bytes(result.shared_key), channel_id: result.channel_id };
404
422
  },
@@ -448,6 +466,45 @@ const pairing = {
448
466
  nonce: result.nonce,
449
467
  };
450
468
  },
469
+
470
+ produce_pre_pair_no_keys(
471
+ channel_id: bigint,
472
+ request: PrePairRequestMessage,
473
+ ): ProducePrePairNoKeysResult {
474
+ const result = call(
475
+ 'produce_pre_pair_no_keys_response_message',
476
+ channel_id,
477
+ encodeMessage(MessageKind.PrePairRequest, request),
478
+ ) as { envelope_wire_bytes: ArrayBuffer; secret_key_material: ArrayBuffer };
479
+ return {
480
+ envelope: bytes(result.envelope_wire_bytes),
481
+ secret_key_material: bytes(result.secret_key_material),
482
+ };
483
+ },
484
+
485
+ process_pre_pair_no_keys(
486
+ contact_message: ContactMessage,
487
+ response: PrePairResponseMessage,
488
+ ): ProcessPrePairResult {
489
+ const result = call(
490
+ 'process_pre_pair_no_keys_response_message',
491
+ encodeContact(contact_message),
492
+ encodeMessage(MessageKind.PrePairResponse, response),
493
+ ) as {
494
+ mlkem_encapsulation_key: ArrayBuffer;
495
+ ecies_public_key: ArrayBuffer;
496
+ nonce: bigint;
497
+ };
498
+ return {
499
+ mlkem_encapsulation_key: bytes(result.mlkem_encapsulation_key),
500
+ ecies_public_key: bytes(result.ecies_public_key),
501
+ nonce: result.nonce,
502
+ };
503
+ },
504
+ },
505
+
506
+ fingerprint(shared_key: Uint8Array): string {
507
+ return call('pairing_fingerprint', shared_key) as string;
451
508
  },
452
509
  };
453
510
 
package/src/protocol.ts CHANGED
@@ -11,6 +11,7 @@ import type {
11
11
  ContactMessage,
12
12
  ContactMode,
13
13
  DeRecEvent,
14
+ Endpoint,
14
15
  DiscoveryParams,
15
16
  PairingParams,
16
17
  ParameterRange,
@@ -20,6 +21,7 @@ import type {
20
21
  SecretStore,
21
22
  ShareStore,
22
23
  StateStore,
24
+ StatusEnum,
23
25
  ReplicaDiscoveryParams,
24
26
  Target,
25
27
  Timeouts,
@@ -81,25 +83,6 @@ function decodeEvents(buffer: ArrayBuffer): DeRecEvent[] {
81
83
  return reviveEventBytes(jsonFromBytes(buffer)) as DeRecEvent[];
82
84
  }
83
85
 
84
- /**
85
- * Maps a transport protocol name to the `derec_proto::Protocol` discriminant.
86
- * The shared enum fixture every other numeric mapping in this SDK is checked
87
- * against does not carry this enum (it
88
- * only covers the Rust-internal serde enums listed under its `"enums"` key);
89
- * the transport `Protocol` enum lives in `protobufs/transportprotocol.proto`
90
- * instead, where `HTTPS = 0` and `GRPC = 1` are defined.
91
- */
92
- export function protocolDiscriminant(protocol: string): number {
93
- switch (protocol.toLowerCase()) {
94
- case 'https':
95
- return 0;
96
- case 'grpc':
97
- return 1;
98
- default:
99
- throw new Error(`DeRec: unknown transport protocol "${protocol}"`);
100
- }
101
- }
102
-
103
86
  function encodeTarget(target: Target): unknown {
104
87
  if (target === null) {
105
88
  return null;
@@ -261,13 +244,8 @@ function buildStartParams(flowKind: FlowKind, params: unknown): Uint8Array {
261
244
  if (p.communication_info !== undefined) {
262
245
  out.communication_info = p.communication_info;
263
246
  }
264
- if (p.own_transports !== undefined && p.own_transports.length > 0) {
247
+ if (p.own_transports !== undefined) {
265
248
  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) {
270
- out.transport_protocol = p.transport_protocol;
271
249
  }
272
250
  return jsonToBytes(out);
273
251
  }
@@ -313,7 +291,6 @@ export class DeRecProtocolBuilder {
313
291
  private readonly secretIdValue: bigint;
314
292
  private config: Record<string, unknown> = {};
315
293
  private stores: Record<string, unknown> = {};
316
- private communicationInfo: Record<string, string> | undefined;
317
294
 
318
295
  constructor(secretId: bigint | number) {
319
296
  this.secretIdValue = BigInt(secretId);
@@ -349,23 +326,10 @@ export class DeRecProtocolBuilder {
349
326
  return this;
350
327
  }
351
328
 
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
- */
357
- withOwnTransport(endpoint: { uri: string; protocol: string }): this {
358
- this.config.own_transport_uri = endpoint.uri;
359
- this.config.own_transport_protocol = protocolDiscriminant(endpoint.protocol);
360
- return this;
361
- }
362
-
363
329
  /**
364
330
  * 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`.
331
+ * order. Written to the config's `own_transports` array — see
332
+ * `ProtocolConfig` in `library/src/interop/ffi/protocol/handle/mod.rs`.
369
333
  *
370
334
  * The order given is forwarded verbatim: it is not sorted, deduplicated,
371
335
  * or reordered here. It is this application's own preference and
@@ -374,15 +338,11 @@ export class DeRecProtocolBuilder {
374
338
  * listing an endpoint this application does not serve makes pairing
375
339
  * succeed and replies vanish.
376
340
  *
377
- * Supersedes {@link withOwnTransport} for applications serving more
378
- * than one transport; the single-endpoint setter remains fully
379
- * supported.
341
+ * Required: `build()` with no own transport is refused by the library,
342
+ * since a node with no endpoint cannot be reached by any peer.
380
343
  */
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
- }));
344
+ withOwnTransports(transports: Endpoint[]): this {
345
+ this.config.own_transports = transports.map(({ uri, protocol }) => ({ uri, protocol }));
386
346
  return this;
387
347
  }
388
348
 
@@ -410,24 +370,9 @@ export class DeRecProtocolBuilder {
410
370
  return this;
411
371
  }
412
372
 
413
- /**
414
- * @deprecated Use {@link withUnsafeConnection}, which names both gated
415
- * schemes. Removed at 0.0.5. Default: false.
416
- */
417
- withUnsafeHttp(allow: boolean): this {
418
- this.config.unsafe_http = allow;
419
- return this;
420
- }
421
-
422
373
  /**
423
374
  * 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.
375
+ * **Development only.** Default: false.
431
376
  */
432
377
  withUnsafeConnection(allow: boolean): this {
433
378
  this.config.unsafe_connection = allow;
@@ -435,24 +380,11 @@ export class DeRecProtocolBuilder {
435
380
  }
436
381
 
437
382
  /**
438
- * Default: empty. `derec_protocol_new`'s own `communication_info` argument
439
- * is a pre-encoded `CommunicationInfo` protobuf message, and this SDK has
440
- * no encoder for that message — the C ABI exposes one only for
441
- * `ContactMessage`. Hand-rolling one here would be SDK-side protocol logic
442
- * with no test coverage — exactly what this codebase's "Rust decides, the
443
- * SDK only marshals" rule forbids. Instead,
444
- * `build()` constructs the protocol with an empty `communication_info`
445
- * (a legitimate, always-accepted value — see `decode_communication_info`
446
- * in `library/src/interop/ffi/protocol/handle/mod.rs`, which treats a zero-length
447
- * buffer as "no entries") and then calls the already-bound
448
- * {@link DeRecProtocol.setCommunicationInfo}, which takes the same map as
449
- * plain JSON (`derec_protocol_set_communication_info` in
450
- * `library/src/interop/ffi/protocol/handle/config.rs`). That setter is queued on
451
- * the protocol's serial worker before `build()` returns, so it is ordered
452
- * ahead of every call the caller can subsequently make.
383
+ * Default: empty. Written to the config's `communication_info` key, so the
384
+ * map is part of the protocol from construction.
453
385
  */
454
386
  withCommunicationInfo(info: Record<string, string>): this {
455
- this.communicationInfo = info;
387
+ this.config.communication_info = info;
456
388
  return this;
457
389
  }
458
390
 
@@ -464,13 +396,7 @@ export class DeRecProtocolBuilder {
464
396
 
465
397
  /** Default: "required". */
466
398
  withUnpairAck(ack: UnpairAck): this {
467
- if (ack === 'required') {
468
- this.config.unpair_ack = 0;
469
- } else if (ack === 'not_required') {
470
- this.config.unpair_ack = 1;
471
- } else {
472
- throw new Error(`DeRec: unknown unpair ack "${ack as string}"`);
473
- }
399
+ this.config.unpair_ack = ack;
474
400
  return this;
475
401
  }
476
402
 
@@ -480,17 +406,20 @@ export class DeRecProtocolBuilder {
480
406
  return this;
481
407
  }
482
408
 
483
- /** Default: empty policy (every flow off). */
409
+ /**
410
+ * Default: empty policy (every flow off). A flow left out of `policy` is
411
+ * left out of the config, and the library applies its own default to it.
412
+ */
484
413
  withAutoAccept(policy: AutoAcceptPolicy): this {
485
414
  this.config.auto_accept = {
486
- pairing: policy.pairing ?? false,
487
- pre_pair: policy.prePair ?? false,
488
- store_share: policy.storeShare ?? false,
489
- verify_share: policy.verifyShare ?? false,
490
- discovery: policy.discovery ?? false,
491
- get_share: policy.getShare ?? false,
492
- unpair: policy.unpair ?? false,
493
- update_channel_info: policy.updateChannelInfo ?? false,
415
+ pairing: policy.pairing,
416
+ pre_pair: policy.prePair,
417
+ store_share: policy.storeShare,
418
+ verify_share: policy.verifyShare,
419
+ discovery: policy.discovery,
420
+ get_share: policy.getShare,
421
+ unpair: policy.unpair,
422
+ update_channel_info: policy.updateChannelInfo,
494
423
  };
495
424
  return this;
496
425
  }
@@ -508,30 +437,26 @@ export class DeRecProtocolBuilder {
508
437
  * against the peer's range on inbound ones: a range that fails to
509
438
  * intersect rejects the pairing. Every bound is optional and defaults to
510
439
  * `0`, which the protocol reads as "no constraint on this dimension".
440
+ * Bounds cross to the library as decimal strings, so the full `i64` range
441
+ * survives; a bound left out is left out of the config, and the library
442
+ * applies its default.
511
443
  *
512
444
  * Default: unset — no constraints advertised, every peer range accepted.
513
445
  */
514
446
  withParameterRange(range: Partial<ParameterRange>): this {
515
- const n = (v: bigint | undefined) => Number(v ?? 0n);
516
447
  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
- ),
448
+ min_share_size: range.min_share_size?.toString(),
449
+ max_share_size: range.max_share_size?.toString(),
450
+ min_time_between_verifications: range.min_time_between_verifications?.toString(),
451
+ max_time_between_verifications: range.max_time_between_verifications?.toString(),
452
+ min_time_between_share_updates: range.min_time_between_share_updates?.toString(),
453
+ max_time_between_share_updates: range.max_time_between_share_updates?.toString(),
454
+ min_unresponsive_deletion_timeout: range.min_unresponsive_deletion_timeout?.toString(),
455
+ max_unresponsive_deletion_timeout: range.max_unresponsive_deletion_timeout?.toString(),
456
+ min_unresponsive_deactivation_timeout:
457
+ range.min_unresponsive_deactivation_timeout?.toString(),
458
+ max_unresponsive_deactivation_timeout:
459
+ range.max_unresponsive_deactivation_timeout?.toString(),
535
460
  };
536
461
  return this;
537
462
  }
@@ -540,8 +465,6 @@ export class DeRecProtocolBuilder {
540
465
  const config = {
541
466
  secret_id: this.secretIdValue.toString(),
542
467
  replica_id: null,
543
- own_transport_uri: '',
544
- own_transport_protocol: 0,
545
468
  ...this.config,
546
469
  };
547
470
  const host = (getNative() as unknown as NativeHost).protocol_new(
@@ -549,29 +472,15 @@ export class DeRecProtocolBuilder {
549
472
  new Uint8Array(0),
550
473
  this.stores,
551
474
  ) as NativeHost;
552
- const protocol = DeRecProtocol.fromHost(host);
553
- if (this.communicationInfo !== undefined) {
554
- // `setCommunicationInfo` is asynchronous, but `build()` stays
555
- // synchronous: the guarantee callers need is not that the map has been
556
- // applied by the time `build()` returns, it is that no flow can run
557
- // before it. Posting it here puts it on the protocol's serial worker
558
- // queue ahead of anything the caller has had the chance to start, so
559
- // every subsequent call observes it. Left unhandled deliberately — a
560
- // failure reaches the runtime's unhandled-rejection reporting rather
561
- // than being silently discarded, and `build()` has no synchronous
562
- // channel to raise it on.
563
- void protocol.setCommunicationInfo(this.communicationInfo);
564
- }
565
- return protocol;
475
+ return DeRecProtocol.fromHost(host);
566
476
  }
567
477
  }
568
478
 
569
479
  /**
570
480
  * Thin wrapper around the `ProtocolHost` JSI host object, mirroring
571
481
  * `@derec-alliance/nodejs`'s `DeRecProtocol` — same methods, and the same
572
- * sync/async split except for `setCommunicationInfo` and `setOwnTransport`,
573
- * which return a `Promise` here (see their own doc comments for why);
574
- * `secretId` is synchronous and everything else returns a `Promise`. Every method encodes its
482
+ * sync/async split: `secretId` is synchronous and everything else returns a
483
+ * `Promise`. Every method encodes its
575
484
  * arguments into the wire shape the host function expects and decodes its
576
485
  * result back into the typed value declared here; no protocol decision is
577
486
  * made in this class.
@@ -601,68 +510,39 @@ export class DeRecProtocol {
601
510
  }
602
511
 
603
512
  /**
604
- * Returns a `Promise`, where `@derec-alliance/nodejs` declares this
605
- * `void`. The native setter takes the same handle mutex a running flow
606
- * holds across its store callbacks, so calling it on the JavaScript thread
607
- * would block that thread until the flow released the mutex — which it
608
- * cannot do while it is waiting on the JavaScript thread. The binding
609
- * therefore queues the setter onto the same serial worker every flow runs
610
- * on, which orders it after any call already in flight. The nodejs SDK is
611
- * WASM and single-threaded, so it has no such hazard.
513
+ * Replaces this node's local communication info.
612
514
  *
613
- * Callers that ignore the result behave exactly as before, so this stays
614
- * source-compatible with nodejs-shaped code; awaiting it additionally
615
- * surfaces a failure that would otherwise be swallowed.
515
+ * The native setter takes the same handle mutex a running flow holds
516
+ * across its store callbacks, so calling it on the JavaScript thread would
517
+ * block that thread until the flow released the mutex — which it cannot do
518
+ * while it is waiting on the JavaScript thread. The binding therefore
519
+ * queues the setter onto the same serial worker every flow runs on, which
520
+ * orders it after any call already in flight. Awaiting the returned
521
+ * `Promise` surfaces a failure that would otherwise be swallowed.
616
522
  */
617
523
  setCommunicationInfo(info: Record<string, string>): Promise<void> {
618
524
  return this.host.setCommunicationInfo(jsonToBytes(info)) as Promise<void>;
619
525
  }
620
526
 
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
- */
634
- setOwnTransport(uri: string, protocol: string): Promise<void> {
635
- return this.host.setOwnTransport(
636
- uri,
637
- protocolDiscriminant(protocol),
638
- ) as Promise<void>;
639
- }
640
-
641
527
  /**
642
528
  * 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).
529
+ * the runtime counterpart to `withOwnTransports`.
646
530
  *
647
531
  * Every entry is validated before any is stored, so a malformed URI
648
532
  * leaves the previous set intact. An empty array is rejected.
649
533
  *
650
- * Returns a `Promise` for the same reason {@link setCommunicationInfo} does.
534
+ * Queued onto the serial worker for the same reason as
535
+ * {@link setCommunicationInfo}.
651
536
  */
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
- }));
537
+ setOwnTransports(transports: Endpoint[]): Promise<void> {
538
+ const wire = transports.map(({ uri, protocol }) => ({ uri, protocol }));
659
539
  return this.host.setOwnTransports(jsonToBytes(wire)) as Promise<void>;
660
540
  }
661
541
 
662
542
  /**
663
543
  * Decodes the `contact_wire_bytes` `derec_protocol_create_contact`
664
544
  * produces into a typed `ContactMessage`, so an application can read the
665
- * contact's `nonce`, `transport_protocol` and mode without a protobuf
545
+ * contact's `nonce`, `supported_transports` and mode without a protobuf
666
546
  * codec of its own. Feed the returned object straight back into
667
547
  * `start(FlowKind.Pairing, { contact })`, which re-encodes it.
668
548
  */
@@ -713,7 +593,7 @@ export class DeRecProtocol {
713
593
  return decodeEvents(buffer);
714
594
  }
715
595
 
716
- async reject(actionBytes: Uint8Array, status: number, memo: string): Promise<void> {
596
+ async reject(actionBytes: Uint8Array, status: StatusEnum, memo: string): Promise<void> {
717
597
  await this.host.reject(actionBytes, status, memo);
718
598
  }
719
599
 
@@ -730,6 +610,21 @@ export class DeRecProtocol {
730
610
  return jsonFromBytes(buffer) as string[];
731
611
  }
732
612
 
613
+ /**
614
+ * Rebuild this protocol's `secret_id` namespace from a recovered `Secret`.
615
+ * Pass the `secret` carried by the `SecretRecovered` event verbatim.
616
+ * A helper or member whose `transports` is empty gets no channel: it is
617
+ * reported as a `PeerNotRestored` event in the returned array and the rest
618
+ * of the roster is restored.
619
+ *
620
+ * Rejects with a `DeRecError`; among its codes:
621
+ *
622
+ * | code | meaning |
623
+ * |--------------------|-------------------------------------------------------------|
624
+ * | `already_restored` | A user-secret snapshot already exists for this `secret_id`. |
625
+ * | `restore_conflict` | Channels live at ids restore is about to write. The error |
626
+ * | | carries `channel_ids: string[]` listing the collisions. |
627
+ */
733
628
  async restore(
734
629
  recoveredSecret: Extract<DeRecEvent, { type: 'SecretRecovered' }>['secret'],
735
630
  version: number,
@@ -743,4 +638,9 @@ export class DeRecProtocol {
743
638
  free(): void {
744
639
  this.host.free();
745
640
  }
641
+
642
+ /** Same as {@link DeRecProtocol.free}, for `using`. */
643
+ [Symbol.dispose](): void {
644
+ this.free();
645
+ }
746
646
  }