@derec-alliance/react-native 0.0.5 → 0.0.7

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/protocol.js CHANGED
@@ -3,7 +3,6 @@
3
3
  // Copyright (c) 2026 DeRec Alliance. All rights reserved.
4
4
  Object.defineProperty(exports, "__esModule", { value: true });
5
5
  exports.DeRecProtocol = exports.DeRecProtocolBuilder = void 0;
6
- exports.protocolDiscriminant = protocolDiscriminant;
7
6
  exports.buildProtectSecretParams = buildProtectSecretParams;
8
7
  exports.buildRestoreParams = buildRestoreParams;
9
8
  const codec_1 = require("./codec");
@@ -50,24 +49,6 @@ function reviveEventBytes(value) {
50
49
  function decodeEvents(buffer) {
51
50
  return reviveEventBytes((0, codec_1.jsonFromBytes)(buffer));
52
51
  }
53
- /**
54
- * Maps a transport protocol name to the `derec_proto::Protocol` discriminant.
55
- * The shared enum fixture every other numeric mapping in this SDK is checked
56
- * against does not carry this enum (it
57
- * only covers the Rust-internal serde enums listed under its `"enums"` key);
58
- * the transport `Protocol` enum lives in `protobufs/transportprotocol.proto`
59
- * instead, where `HTTPS = 0` and `GRPC = 1` are defined.
60
- */
61
- function protocolDiscriminant(protocol) {
62
- switch (protocol.toLowerCase()) {
63
- case 'https':
64
- return 0;
65
- case 'grpc':
66
- return 1;
67
- default:
68
- throw new Error(`DeRec: unknown transport protocol "${protocol}"`);
69
- }
70
- }
71
52
  function encodeTarget(target) {
72
53
  if (target === null) {
73
54
  return null;
@@ -219,14 +200,8 @@ function buildStartParams(flowKind, params) {
219
200
  if (p.communication_info !== undefined) {
220
201
  out.communication_info = p.communication_info;
221
202
  }
222
- if (p.own_transports !== undefined && p.own_transports.length > 0) {
203
+ if (p.own_transports !== undefined) {
223
204
  out.own_transports = p.own_transports;
224
- // The first entry also fills the deprecated singular field so a peer
225
- // predating `supported_transports` still learns the new address.
226
- out.transport_protocol = p.own_transports[0];
227
- }
228
- else if (p.transport_protocol !== undefined) {
229
- out.transport_protocol = p.transport_protocol;
230
205
  }
231
206
  return (0, codec_1.jsonToBytes)(out);
232
207
  }
@@ -259,9 +234,9 @@ function toBigInt(id) {
259
234
  *
260
235
  * Every value the caller supplies is forwarded verbatim — defaults, clamping
261
236
  * and the meaning of a disabled cleanup policy are library decisions.
262
- * `threshold`, `keep_versions_count`, `auto_respond_on_failure`,
263
- * `unpair_ack`, `auto_reply_to` and `auto_accept` are omitted entirely from
264
- * the emitted config unless their setter was called: `ProtocolConfig` has a
237
+ * `threshold`, `auto_respond_on_failure`, `unpair_ack`, `auto_reply_to`
238
+ * and `auto_accept` are omitted entirely from the emitted config unless
239
+ * their setter was called: `ProtocolConfig` has a
265
240
  * `#[serde(default = "...")]` on each of them, reading the same constants
266
241
  * `DeRecProtocolBuilder::new` uses, so an absent key resolves to the
267
242
  * library's own default rather than one frozen into this shim.
@@ -296,22 +271,10 @@ class DeRecProtocolBuilder {
296
271
  this.stores.transport = transport;
297
272
  return this;
298
273
  }
299
- /**
300
- * @deprecated Use {@link withOwnTransports}, which takes the whole
301
- * preference list — `withOwnTransports([endpoint])` is the direct
302
- * replacement. Removed at 0.0.5.
303
- */
304
- withOwnTransport(endpoint) {
305
- this.config.own_transport_uri = endpoint.uri;
306
- this.config.own_transport_protocol = protocolDiscriminant(endpoint.protocol);
307
- return this;
308
- }
309
274
  /**
310
275
  * Set every transport endpoint this application serves, in preference
311
- * order. Written to the config's `own_transports` array, which takes
312
- * precedence over `own_transport_uri` / `own_transport_protocol` on the
313
- * Rust side when non-empty — see `ProtocolConfig` in
314
- * `library/src/interop/ffi/protocol/handle/mod.rs`.
276
+ * order. Written to the config's `own_transports` array — see
277
+ * `ProtocolConfig` in `library/src/interop/ffi/protocol/handle/mod.rs`.
315
278
  *
316
279
  * The order given is forwarded verbatim: it is not sorted, deduplicated,
317
280
  * or reordered here. It is this application's own preference and
@@ -320,15 +283,11 @@ class DeRecProtocolBuilder {
320
283
  * listing an endpoint this application does not serve makes pairing
321
284
  * succeed and replies vanish.
322
285
  *
323
- * Supersedes {@link withOwnTransport} for applications serving more
324
- * than one transport; the single-endpoint setter remains fully
325
- * supported.
286
+ * Required: `build()` with no own transport is refused by the library,
287
+ * since a node with no endpoint cannot be reached by any peer.
326
288
  */
327
289
  withOwnTransports(transports) {
328
- this.config.own_transports = transports.map((t) => ({
329
- uri: t.uri,
330
- protocol: protocolDiscriminant(t.protocol),
331
- }));
290
+ this.config.own_transports = transports.map(({ uri, protocol }) => ({ uri, protocol }));
332
291
  return this;
333
292
  }
334
293
  /** Default: 3. */
@@ -336,11 +295,6 @@ class DeRecProtocolBuilder {
336
295
  this.config.threshold = threshold;
337
296
  return this;
338
297
  }
339
- /** Default: 3. */
340
- withKeepVersionsCount(count) {
341
- this.config.keep_versions_count = count;
342
- return this;
343
- }
344
298
  /**
345
299
  * Every field is optional; not calling this at all leaves all four at the
346
300
  * library's defaults — `timeouts` is genuinely absent from the JSON in
@@ -352,47 +306,20 @@ class DeRecProtocolBuilder {
352
306
  this.config.timeouts = { ...timeouts };
353
307
  return this;
354
308
  }
355
- /**
356
- * @deprecated Use {@link withUnsafeConnection}, which names both gated
357
- * schemes. Removed at 0.0.5. Default: false.
358
- */
359
- withUnsafeHttp(allow) {
360
- this.config.unsafe_http = allow;
361
- return this;
362
- }
363
309
  /**
364
310
  * Accept plaintext `http://` and `grpc://` transport endpoints.
365
- * **Development only.** Default: false. Supersedes
366
- * {@link withUnsafeHttp}, which names only the HTTP scheme.
367
- *
368
- * Either flag alone is honored. Setting both to disagreeing values fails
369
- * construction with the error code `CONFLICTING_PLAINTEXT_OPT_IN` rather
370
- * than resolving silently, because precedence would hand the decision to
371
- * the flag being removed.
311
+ * **Development only.** Default: false.
372
312
  */
373
313
  withUnsafeConnection(allow) {
374
314
  this.config.unsafe_connection = allow;
375
315
  return this;
376
316
  }
377
317
  /**
378
- * Default: empty. `derec_protocol_new`'s own `communication_info` argument
379
- * is a pre-encoded `CommunicationInfo` protobuf message, and this SDK has
380
- * no encoder for that message — the C ABI exposes one only for
381
- * `ContactMessage`. Hand-rolling one here would be SDK-side protocol logic
382
- * with no test coverage — exactly what this codebase's "Rust decides, the
383
- * SDK only marshals" rule forbids. Instead,
384
- * `build()` constructs the protocol with an empty `communication_info`
385
- * (a legitimate, always-accepted value — see `decode_communication_info`
386
- * in `library/src/interop/ffi/protocol/handle/mod.rs`, which treats a zero-length
387
- * buffer as "no entries") and then calls the already-bound
388
- * {@link DeRecProtocol.setCommunicationInfo}, which takes the same map as
389
- * plain JSON (`derec_protocol_set_communication_info` in
390
- * `library/src/interop/ffi/protocol/handle/config.rs`). That setter is queued on
391
- * the protocol's serial worker before `build()` returns, so it is ordered
392
- * ahead of every call the caller can subsequently make.
318
+ * Default: empty. Written to the config's `communication_info` key, so the
319
+ * map is part of the protocol from construction.
393
320
  */
394
321
  withCommunicationInfo(info) {
395
- this.communicationInfo = info;
322
+ this.config.communication_info = info;
396
323
  return this;
397
324
  }
398
325
  /** Default: false. */
@@ -402,15 +329,7 @@ class DeRecProtocolBuilder {
402
329
  }
403
330
  /** Default: "required". */
404
331
  withUnpairAck(ack) {
405
- if (ack === 'required') {
406
- this.config.unpair_ack = 0;
407
- }
408
- else if (ack === 'not_required') {
409
- this.config.unpair_ack = 1;
410
- }
411
- else {
412
- throw new Error(`DeRec: unknown unpair ack "${ack}"`);
413
- }
332
+ this.config.unpair_ack = ack;
414
333
  return this;
415
334
  }
416
335
  /** Default: false. */
@@ -418,17 +337,20 @@ class DeRecProtocolBuilder {
418
337
  this.config.auto_reply_to = enabled;
419
338
  return this;
420
339
  }
421
- /** Default: empty policy (every flow off). */
340
+ /**
341
+ * Default: empty policy (every flow off). A flow left out of `policy` is
342
+ * left out of the config, and the library applies its own default to it.
343
+ */
422
344
  withAutoAccept(policy) {
423
345
  this.config.auto_accept = {
424
- pairing: policy.pairing ?? false,
425
- pre_pair: policy.prePair ?? false,
426
- store_share: policy.storeShare ?? false,
427
- verify_share: policy.verifyShare ?? false,
428
- discovery: policy.discovery ?? false,
429
- get_share: policy.getShare ?? false,
430
- unpair: policy.unpair ?? false,
431
- update_channel_info: policy.updateChannelInfo ?? false,
346
+ pairing: policy.pairing,
347
+ pre_pair: policy.prePair,
348
+ store_share: policy.storeShare,
349
+ verify_share: policy.verifyShare,
350
+ discovery: policy.discovery,
351
+ get_share: policy.getShare,
352
+ unpair: policy.unpair,
353
+ update_channel_info: policy.updateChannelInfo,
432
354
  };
433
355
  return this;
434
356
  }
@@ -444,22 +366,24 @@ class DeRecProtocolBuilder {
444
366
  * against the peer's range on inbound ones: a range that fails to
445
367
  * intersect rejects the pairing. Every bound is optional and defaults to
446
368
  * `0`, which the protocol reads as "no constraint on this dimension".
369
+ * Bounds cross to the library as decimal strings, so the full `i64` range
370
+ * survives; a bound left out is left out of the config, and the library
371
+ * applies its default.
447
372
  *
448
373
  * Default: unset — no constraints advertised, every peer range accepted.
449
374
  */
450
375
  withParameterRange(range) {
451
- const n = (v) => Number(v ?? 0n);
452
376
  this.config.parameter_range = {
453
- min_share_size: n(range.min_share_size),
454
- max_share_size: n(range.max_share_size),
455
- min_time_between_verifications: n(range.min_time_between_verifications),
456
- max_time_between_verifications: n(range.max_time_between_verifications),
457
- min_time_between_share_updates: n(range.min_time_between_share_updates),
458
- max_time_between_share_updates: n(range.max_time_between_share_updates),
459
- min_unresponsive_deletion_timeout: n(range.min_unresponsive_deletion_timeout),
460
- max_unresponsive_deletion_timeout: n(range.max_unresponsive_deletion_timeout),
461
- min_unresponsive_deactivation_timeout: n(range.min_unresponsive_deactivation_timeout),
462
- max_unresponsive_deactivation_timeout: n(range.max_unresponsive_deactivation_timeout),
377
+ min_share_size: range.min_share_size?.toString(),
378
+ max_share_size: range.max_share_size?.toString(),
379
+ min_time_between_verifications: range.min_time_between_verifications?.toString(),
380
+ max_time_between_verifications: range.max_time_between_verifications?.toString(),
381
+ min_time_between_share_updates: range.min_time_between_share_updates?.toString(),
382
+ max_time_between_share_updates: range.max_time_between_share_updates?.toString(),
383
+ min_unresponsive_deletion_timeout: range.min_unresponsive_deletion_timeout?.toString(),
384
+ max_unresponsive_deletion_timeout: range.max_unresponsive_deletion_timeout?.toString(),
385
+ min_unresponsive_deactivation_timeout: range.min_unresponsive_deactivation_timeout?.toString(),
386
+ max_unresponsive_deactivation_timeout: range.max_unresponsive_deactivation_timeout?.toString(),
463
387
  };
464
388
  return this;
465
389
  }
@@ -467,34 +391,18 @@ class DeRecProtocolBuilder {
467
391
  const config = {
468
392
  secret_id: this.secretIdValue.toString(),
469
393
  replica_id: null,
470
- own_transport_uri: '',
471
- own_transport_protocol: 0,
472
394
  ...this.config,
473
395
  };
474
396
  const host = (0, native_1.getNative)().protocol_new(JSON.stringify(config), new Uint8Array(0), this.stores);
475
- const protocol = DeRecProtocol.fromHost(host);
476
- if (this.communicationInfo !== undefined) {
477
- // `setCommunicationInfo` is asynchronous, but `build()` stays
478
- // synchronous: the guarantee callers need is not that the map has been
479
- // applied by the time `build()` returns, it is that no flow can run
480
- // before it. Posting it here puts it on the protocol's serial worker
481
- // queue ahead of anything the caller has had the chance to start, so
482
- // every subsequent call observes it. Left unhandled deliberately — a
483
- // failure reaches the runtime's unhandled-rejection reporting rather
484
- // than being silently discarded, and `build()` has no synchronous
485
- // channel to raise it on.
486
- void protocol.setCommunicationInfo(this.communicationInfo);
487
- }
488
- return protocol;
397
+ return DeRecProtocol.fromHost(host);
489
398
  }
490
399
  }
491
400
  exports.DeRecProtocolBuilder = DeRecProtocolBuilder;
492
401
  /**
493
402
  * Thin wrapper around the `ProtocolHost` JSI host object, mirroring
494
403
  * `@derec-alliance/nodejs`'s `DeRecProtocol` — same methods, and the same
495
- * sync/async split except for `setCommunicationInfo` and `setOwnTransport`,
496
- * which return a `Promise` here (see their own doc comments for why);
497
- * `secretId` is synchronous and everything else returns a `Promise`. Every method encodes its
404
+ * sync/async split: `secretId` is synchronous and everything else returns a
405
+ * `Promise`. Every method encodes its
498
406
  * arguments into the wire shape the host function expects and decodes its
499
407
  * result back into the typed value declared here; no protocol decision is
500
408
  * made in this class.
@@ -519,60 +427,37 @@ class DeRecProtocol {
519
427
  return this.host.secretId();
520
428
  }
521
429
  /**
522
- * Returns a `Promise`, where `@derec-alliance/nodejs` declares this
523
- * `void`. The native setter takes the same handle mutex a running flow
524
- * holds across its store callbacks, so calling it on the JavaScript thread
525
- * would block that thread until the flow released the mutex — which it
526
- * cannot do while it is waiting on the JavaScript thread. The binding
527
- * therefore queues the setter onto the same serial worker every flow runs
528
- * on, which orders it after any call already in flight. The nodejs SDK is
529
- * WASM and single-threaded, so it has no such hazard.
430
+ * Replaces this node's local communication info.
530
431
  *
531
- * Callers that ignore the result behave exactly as before, so this stays
532
- * source-compatible with nodejs-shaped code; awaiting it additionally
533
- * surfaces a failure that would otherwise be swallowed.
432
+ * The native setter takes the same handle mutex a running flow holds
433
+ * across its store callbacks, so calling it on the JavaScript thread would
434
+ * block that thread until the flow released the mutex — which it cannot do
435
+ * while it is waiting on the JavaScript thread. The binding therefore
436
+ * queues the setter onto the same serial worker every flow runs on, which
437
+ * orders it after any call already in flight. Awaiting the returned
438
+ * `Promise` surfaces a failure that would otherwise be swallowed.
534
439
  */
535
440
  setCommunicationInfo(info) {
536
441
  return this.host.setCommunicationInfo((0, codec_1.jsonToBytes)(info));
537
442
  }
538
- /**
539
- * Replace this node's endpoint for one protocol, leaving the others
540
- * alone. A node serves at most one endpoint per protocol, so the
541
- * `(uri, protocol)` pair identifies the entry it replaces; an entry for a
542
- * protocol not yet served is appended, and a replaced one keeps its
543
- * position in the preference order.
544
- *
545
- * Returns a `Promise` for the same reason {@link setCommunicationInfo} does.
546
- *
547
- * @deprecated Use {@link setOwnTransports}, which takes the whole
548
- * preference list and is the only way to change which protocols this node
549
- * serves, or their order. Removed at 0.0.5.
550
- */
551
- setOwnTransport(uri, protocol) {
552
- return this.host.setOwnTransport(uri, protocolDiscriminant(protocol));
553
- }
554
443
  /**
555
444
  * Replaces every endpoint this node advertises, in preference order —
556
- * the runtime counterpart to `withOwnTransports`, and the only way to
557
- * change a multi-endpoint node's set ({@link setOwnTransport} collapses
558
- * it to the one endpoint it is given).
445
+ * the runtime counterpart to `withOwnTransports`.
559
446
  *
560
447
  * Every entry is validated before any is stored, so a malformed URI
561
448
  * leaves the previous set intact. An empty array is rejected.
562
449
  *
563
- * Returns a `Promise` for the same reason {@link setCommunicationInfo} does.
450
+ * Queued onto the serial worker for the same reason as
451
+ * {@link setCommunicationInfo}.
564
452
  */
565
453
  setOwnTransports(transports) {
566
- const wire = transports.map(({ uri, protocol }) => ({
567
- uri,
568
- protocol: protocolDiscriminant(protocol),
569
- }));
454
+ const wire = transports.map(({ uri, protocol }) => ({ uri, protocol }));
570
455
  return this.host.setOwnTransports((0, codec_1.jsonToBytes)(wire));
571
456
  }
572
457
  /**
573
458
  * Decodes the `contact_wire_bytes` `derec_protocol_create_contact`
574
459
  * produces into a typed `ContactMessage`, so an application can read the
575
- * contact's `nonce`, `transport_protocol` and mode without a protobuf
460
+ * contact's `nonce`, `supported_transports` and mode without a protobuf
576
461
  * codec of its own. Feed the returned object straight back into
577
462
  * `start(FlowKind.Pairing, { contact })`, which re-encodes it.
578
463
  */
@@ -588,7 +473,17 @@ class DeRecProtocol {
588
473
  return decodeEvents(buffer);
589
474
  }
590
475
  async process(message) {
591
- const buffer = (await this.host.process(message));
476
+ let buffer;
477
+ try {
478
+ buffer = (await this.host.process(message));
479
+ }
480
+ catch (error) {
481
+ const failure = error;
482
+ if (Array.isArray(failure.events)) {
483
+ failure.events = reviveEventBytes(failure.events);
484
+ }
485
+ throw error;
486
+ }
592
487
  return decodeEvents(buffer);
593
488
  }
594
489
  async tick() {
@@ -605,6 +500,16 @@ class DeRecProtocol {
605
500
  async getFingerprint(channelId) {
606
501
  return (await this.host.getFingerprint(toBigInt(channelId)));
607
502
  }
503
+ /**
504
+ * Verify `fingerprint` against the channel's locally-derived one. On
505
+ * match, the channel transitions from `Pending` to `Paired`. Returns
506
+ * `true` on confirmation, `false` on mismatch.
507
+ *
508
+ * On a replica destination, confirming is also the decision to adopt the
509
+ * group's vault: the source's publish is then installed as it arrives,
510
+ * with no further prompt. Ask the user before calling this; to decline,
511
+ * never confirm.
512
+ */
608
513
  async verifyFingerprint(channelId, fingerprint) {
609
514
  return (await this.host.verifyFingerprint(toBigInt(channelId), fingerprint));
610
515
  }
@@ -612,6 +517,24 @@ class DeRecProtocol {
612
517
  const buffer = (await this.host.removeExpiredChannels(olderThanSecs));
613
518
  return (0, codec_1.jsonFromBytes)(buffer);
614
519
  }
520
+ /**
521
+ * Rebuild this protocol's `secret_id` namespace from a recovered `Secret`.
522
+ * Pass the `secret` carried by the `SecretRecovered` event verbatim.
523
+ * Recovery and restore are separate steps on purpose: `SecretRecovered`
524
+ * writes nothing. Show the user what was recovered, or ask them, before
525
+ * calling `restore`, which commits it to this device.
526
+ * A helper or member whose `transports` is empty gets no channel: it is
527
+ * reported as a `PeerNotRestored` event in the returned array and the rest
528
+ * of the roster is restored.
529
+ *
530
+ * Rejects with a `DeRecError`; among its codes:
531
+ *
532
+ * | code | meaning |
533
+ * |--------------------|-------------------------------------------------------------|
534
+ * | `already_restored` | A user-secret snapshot already exists for this `secret_id`. |
535
+ * | `restore_conflict` | Channels live at ids restore is about to write. The error |
536
+ * | | carries `channel_ids: string[]` listing the collisions. |
537
+ */
615
538
  async restore(recoveredSecret, version) {
616
539
  const params = buildRestoreParams(recoveredSecret, version);
617
540
  const buffer = (await this.host.restore((0, codec_1.jsonToBytes)(params)));
@@ -621,5 +544,9 @@ class DeRecProtocol {
621
544
  free() {
622
545
  this.host.free();
623
546
  }
547
+ /** Same as {@link DeRecProtocol.free}, for `using`. */
548
+ [Symbol.dispose]() {
549
+ this.free();
550
+ }
624
551
  }
625
552
  exports.DeRecProtocol = DeRecProtocol;