@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/types.js CHANGED
@@ -2,11 +2,14 @@
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
  // Copyright (c) 2026 DeRec Alliance. All rights reserved.
4
4
  Object.defineProperty(exports, "__esModule", { value: true });
5
- exports.FlowKind = exports.ContactMode = exports.SenderKind = void 0;
5
+ exports.StatusEnum = exports.FlowKind = exports.ContactMode = exports.SenderKind = void 0;
6
6
  exports.channelFilterMatches = channelFilterMatches;
7
7
  exports.advertisedEndpoints = advertisedEndpoints;
8
+ exports.protocol_version = protocol_version;
9
+ exports.generate_replica_id = generate_replica_id;
8
10
  exports.sequentialFailover = sequentialFailover;
9
11
  exports.singleEndpointTransport = singleEndpointTransport;
12
+ const native_1 = require("./native");
10
13
  /**
11
14
  * Whether a channel or member with these attributes survives `filter`.
12
15
  *
@@ -37,13 +40,6 @@ function channelFilterMatches(filter, id, status, role) {
37
40
  /**
38
41
  * The endpoints a peer-supplied message advertises, in the peer's own order.
39
42
  *
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
43
  * Reports what was advertised, not what is acceptable — nothing here is
48
44
  * validated, and the protocol still applies its own transport policy to
49
45
  * whatever it records.
@@ -51,10 +47,23 @@ function channelFilterMatches(filter, id, status, role) {
51
47
  function advertisedEndpoints(message) {
52
48
  if (!message)
53
49
  return [];
54
- const offers = message.supported_transports ?? [];
55
- if (offers.length > 0)
56
- return offers;
57
- return message.transport_protocol ? [message.transport_protocol] : [];
50
+ return message.supported_transports ?? [];
51
+ }
52
+ /**
53
+ * The DeRec protocol version this build speaks — the `protocolVersionMajor` /
54
+ * `protocolVersionMinor` it writes into every envelope it produces. Not the
55
+ * package version.
56
+ */
57
+ function protocol_version() {
58
+ return (0, native_1.getNative)().version();
59
+ }
60
+ /**
61
+ * A fresh, random replica id — never `0`. Generate it once per device,
62
+ * persist it, and pass the same value to
63
+ * `DeRecProtocolBuilder.withReplicaId` on every init.
64
+ */
65
+ function generate_replica_id() {
66
+ return (0, native_1.getNative)().generate_replica_id();
58
67
  }
59
68
  /**
60
69
  * Builds a {@link Transport} that tries each endpoint in the order the peer
@@ -126,7 +135,7 @@ var SenderKind;
126
135
  * the scanner must fetch the actual keys over the wire via the `PrePair`
127
136
  * round-trip and verify them against the commitment before pairing.
128
137
  * - `NoKeys`: no key material and no commitment. The contact carries only
129
- * `channel_id`, `nonce`, and `transport_protocol` — small enough to be
138
+ * `channel_id`, `nonce`, and `supported_transports` — small enough to be
130
139
  * hand-typed or dictated. Keys are generated on the fly by the contact
131
140
  * creator when the `PrePairRequest` arrives; the scanner accepts them
132
141
  * without cryptographic verification. Trust rests entirely on the OOB
@@ -168,3 +177,24 @@ var FlowKind;
168
177
  * string so ids above 2^53 survive JS number handling. */
169
178
  FlowKind[FlowKind["UnpairReplica"] = 8] = "UnpairReplica";
170
179
  })(FlowKind || (exports.FlowKind = FlowKind = {}));
180
+ /** Result status carried in every protocol response (`result.proto`). Passed
181
+ * to `reject()` to say why an inbound request was refused. */
182
+ var StatusEnum;
183
+ (function (StatusEnum) {
184
+ StatusEnum[StatusEnum["Ok"] = 0] = "Ok";
185
+ StatusEnum[StatusEnum["Partial"] = 1] = "Partial";
186
+ StatusEnum[StatusEnum["Fail"] = 2] = "Fail";
187
+ StatusEnum[StatusEnum["SizeLimitExceeded"] = 3] = "SizeLimitExceeded";
188
+ StatusEnum[StatusEnum["TooFrequent"] = 4] = "TooFrequent";
189
+ StatusEnum[StatusEnum["UnknownSecretId"] = 5] = "UnknownSecretId";
190
+ StatusEnum[StatusEnum["UnknownShareVersion"] = 6] = "UnknownShareVersion";
191
+ StatusEnum[StatusEnum["DecryptionFailed"] = 7] = "DecryptionFailed";
192
+ StatusEnum[StatusEnum["VerificationFailed"] = 8] = "VerificationFailed";
193
+ StatusEnum[StatusEnum["FormatError"] = 9] = "FormatError";
194
+ StatusEnum[StatusEnum["Rejected"] = 10] = "Rejected";
195
+ StatusEnum[StatusEnum["IncompatibleParameterRange"] = 11] = "IncompatibleParameterRange";
196
+ StatusEnum[StatusEnum["UnsupportedTransportProtocol"] = 12] = "UnsupportedTransportProtocol";
197
+ StatusEnum[StatusEnum["VersionConflict"] = 13] = "VersionConflict";
198
+ StatusEnum[StatusEnum["ReplicaIdConflict"] = 14] = "ReplicaIdConflict";
199
+ StatusEnum[StatusEnum["RequestToClose"] = 99] = "RequestToClose";
200
+ })(StatusEnum || (exports.StatusEnum = StatusEnum = {}));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@derec-alliance/react-native",
3
- "version": "0.0.5",
3
+ "version": "0.0.7",
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": {
package/src/errors.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  // SPDX-License-Identifier: Apache-2.0
2
2
  // Copyright (c) 2026 DeRec Alliance. All rights reserved.
3
3
 
4
+ import type { DeRecEvent } from './types';
5
+
4
6
  /**
5
7
  * Every category name `derec_error_category_name` can return, and no others.
6
8
  *
@@ -42,4 +44,19 @@ export interface DeRecError {
42
44
  memo?: string;
43
45
  expected?: number;
44
46
  got?: number;
47
+ /**
48
+ * Present only when `code` is `"restore_conflict"`: the channels already
49
+ * living at the recovered canonical ids, as decimal strings — the same
50
+ * field `@derec-alliance/nodejs` carries on its restore conflict error.
51
+ * Clear exactly these and retry `restore`.
52
+ */
53
+ channel_ids?: string[];
54
+ /** The channel the failing inbound message arrived on, when `process()` could tell. */
55
+ channel_id?: string;
56
+ /**
57
+ * On a failed `process()`: the events it produced before failing, such as
58
+ * sharing-round and unpair timeouts. They are not reported again, so
59
+ * handle them as you would a successful call's events.
60
+ */
61
+ events?: DeRecEvent[];
45
62
  }
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