@hamradio/meshcore 1.2.2 → 1.3.2

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/dist/index.js CHANGED
@@ -1,44 +1,3 @@
1
- "use strict";
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __export = (target, all) => {
7
- for (var name in all)
8
- __defProp(target, name, { get: all[name], enumerable: true });
9
- };
10
- var __copyProps = (to, from, except, desc) => {
11
- if (from && typeof from === "object" || typeof from === "function") {
12
- for (let key of __getOwnPropNames(from))
13
- if (!__hasOwnProp.call(to, key) && key !== except)
14
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
- }
16
- return to;
17
- };
18
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
-
20
- // src/index.ts
21
- var index_exports = {};
22
- __export(index_exports, {
23
- Contact: () => Contact,
24
- Contacts: () => Contacts,
25
- Group: () => Group,
26
- Identity: () => Identity,
27
- LocalIdentity: () => LocalIdentity,
28
- NodeType: () => NodeType,
29
- Packet: () => Packet,
30
- PayloadType: () => PayloadType,
31
- PrivateKey: () => PrivateKey,
32
- PublicKey: () => PublicKey,
33
- RequestType: () => RequestType,
34
- RouteType: () => RouteType,
35
- SharedSecret: () => SharedSecret,
36
- StaticSecret: () => StaticSecret,
37
- TextType: () => TextType,
38
- parseNodeHash: () => parseNodeHash
39
- });
40
- module.exports = __toCommonJS(index_exports);
41
-
42
1
  // src/packet.types.ts
43
2
  var RouteType = /* @__PURE__ */ ((RouteType2) => {
44
3
  RouteType2[RouteType2["TRANSPORT_FLOOD"] = 0] = "TRANSPORT_FLOOD";
@@ -86,127 +45,8 @@ var NodeType = /* @__PURE__ */ ((NodeType2) => {
86
45
  })(NodeType || {});
87
46
 
88
47
  // src/packet.ts
89
- var import_sha2 = require("@noble/hashes/sha2.js");
90
-
91
- // src/parser.ts
92
- var import_utils = require("@noble/ciphers/utils.js");
93
- var import_utils2 = require("@noble/hashes/utils.js");
94
- var base64ToBytes = (base64, size) => {
95
- let normalized = base64.replace(/-/g, "+").replace(/_/g, "/");
96
- while (normalized.length % 4 !== 0) {
97
- normalized += "=";
98
- }
99
- const binaryString = atob(normalized);
100
- const bytes = new Uint8Array(binaryString.length);
101
- for (let i = 0; i < binaryString.length; i++) {
102
- bytes[i] = binaryString.charCodeAt(i);
103
- }
104
- if (size !== void 0 && bytes.length !== size) {
105
- throw new Error(`Invalid base64 length: expected ${size} bytes, got ${bytes.length}`);
106
- }
107
- return bytes;
108
- };
109
- var hexToBytes = (hex, size) => {
110
- const bytes = (0, import_utils2.hexToBytes)(hex);
111
- if (size !== void 0 && bytes.length !== size) {
112
- throw new Error(`Invalid hex length: expected ${size} bytes, got ${bytes.length}`);
113
- }
114
- return bytes;
115
- };
116
- var BufferReader = class {
117
- constructor(buffer) {
118
- this.buffer = buffer;
119
- this.offset = 0;
120
- }
121
- readByte() {
122
- if (!this.hasMore()) throw new Error("read past end");
123
- return this.buffer[this.offset++];
124
- }
125
- readBytes(length) {
126
- if (length === void 0) {
127
- length = this.buffer.length - this.offset;
128
- }
129
- if (this.remainingBytes() < length) throw new Error("read past end");
130
- const bytes = this.buffer.slice(this.offset, this.offset + length);
131
- this.offset += length;
132
- return bytes;
133
- }
134
- hasMore() {
135
- return this.offset < this.buffer.length;
136
- }
137
- remainingBytes() {
138
- return this.buffer.length - this.offset;
139
- }
140
- peekByte() {
141
- if (!this.hasMore()) throw new Error("read past end");
142
- return this.buffer[this.offset];
143
- }
144
- readUint16LE() {
145
- if (this.remainingBytes() < 2) throw new Error("read past end");
146
- const value = this.buffer[this.offset] | this.buffer[this.offset + 1] << 8;
147
- this.offset += 2;
148
- return value;
149
- }
150
- readUint32LE() {
151
- if (this.remainingBytes() < 4) throw new Error("read past end");
152
- const value = (this.buffer[this.offset] | this.buffer[this.offset + 1] << 8 | this.buffer[this.offset + 2] << 16 | this.buffer[this.offset + 3] << 24) >>> 0;
153
- this.offset += 4;
154
- return value;
155
- }
156
- readInt16LE() {
157
- if (this.remainingBytes() < 2) throw new Error("read past end");
158
- const value = this.buffer[this.offset] | this.buffer[this.offset + 1] << 8;
159
- this.offset += 2;
160
- return value < 32768 ? value : value - 65536;
161
- }
162
- readInt32LE() {
163
- if (this.remainingBytes() < 4) throw new Error("read past end");
164
- const u = (this.buffer[this.offset] | this.buffer[this.offset + 1] << 8 | this.buffer[this.offset + 2] << 16 | this.buffer[this.offset + 3] << 24) >>> 0;
165
- this.offset += 4;
166
- return u < 2147483648 ? u : u - 4294967296;
167
- }
168
- readTimestamp() {
169
- const timestamp = this.readUint32LE();
170
- return new Date(timestamp * 1e3);
171
- }
172
- };
173
- var BufferWriter = class {
174
- constructor() {
175
- this.buffer = [];
176
- }
177
- writeByte(value) {
178
- this.buffer.push(value & 255);
179
- }
180
- writeBytes(bytes) {
181
- this.buffer.push(...bytes);
182
- }
183
- writeUint16LE(value) {
184
- this.buffer.push(value & 255, value >> 8 & 255);
185
- }
186
- writeUint32LE(value) {
187
- this.buffer.push(
188
- value & 255,
189
- value >> 8 & 255,
190
- value >> 16 & 255,
191
- value >> 24 & 255
192
- );
193
- }
194
- writeInt16LE(value) {
195
- this.writeUint16LE(value < 0 ? value + 65536 : value);
196
- }
197
- writeInt32LE(value) {
198
- this.writeUint32LE(value < 0 ? value + 4294967296 : value);
199
- }
200
- writeTimestamp(date) {
201
- const timestamp = Math.floor(date.getTime() / 1e3);
202
- this.writeUint32LE(timestamp);
203
- }
204
- toBytes() {
205
- return new Uint8Array(this.buffer);
206
- }
207
- };
208
-
209
- // src/packet.ts
48
+ import { sha256 } from "@noble/hashes/sha2.js";
49
+ import { base64ToBytes, bytesToHex, FieldType, Reader } from "@hamradio/packet";
210
50
  var Packet = class _Packet {
211
51
  constructor(header, transport, pathLength, path, payload) {
212
52
  this.header = header;
@@ -223,7 +63,7 @@ var Packet = class _Packet {
223
63
  this.pathHashes = [];
224
64
  for (let i = 0; i < this.pathHashBytes; i += this.pathHashSize) {
225
65
  const hashBytes = this.path.slice(i, i + this.pathHashSize);
226
- const hashHex = (0, import_utils2.bytesToHex)(hashBytes);
66
+ const hashHex = bytesToHex(hashBytes);
227
67
  this.pathHashes.push(hashHex);
228
68
  }
229
69
  }
@@ -250,14 +90,14 @@ var Packet = class _Packet {
250
90
  return routeType === 0 /* TRANSPORT_FLOOD */ || routeType === 3 /* TRANSPORT_DIRECT */;
251
91
  }
252
92
  hash() {
253
- const hash = import_sha2.sha256.create();
93
+ const hash = sha256.create();
254
94
  hash.update(new Uint8Array([this.payloadType]));
255
95
  if (this.payloadType === 9 /* TRACE */) {
256
96
  hash.update(new Uint8Array([this.pathLength]));
257
97
  }
258
98
  hash.update(this.payload);
259
99
  const digest = hash.digest();
260
- return (0, import_utils2.bytesToHex)(digest.slice(0, 8));
100
+ return bytesToHex(digest.slice(0, 8));
261
101
  }
262
102
  ensureStructure() {
263
103
  if (typeof this.structure !== "undefined") {
@@ -266,13 +106,13 @@ var Packet = class _Packet {
266
106
  let pathHashType;
267
107
  switch (this.pathHashSize) {
268
108
  case 1:
269
- pathHashType = 6 /* BYTES */;
109
+ pathHashType = FieldType.BYTES;
270
110
  break;
271
111
  case 2:
272
- pathHashType = 7 /* WORDS */;
112
+ pathHashType = FieldType.WORDS;
273
113
  break;
274
114
  case 4:
275
- pathHashType = 8 /* DWORDS */;
115
+ pathHashType = FieldType.DWORDS;
276
116
  break;
277
117
  default:
278
118
  throw new Error(`Unsupported path hash size: ${this.pathHashSize}`);
@@ -281,13 +121,13 @@ var Packet = class _Packet {
281
121
  /* Header segment */
282
122
  {
283
123
  name: "header",
284
- data: new Uint8Array([this.header]),
124
+ data: new Uint8Array([this.header]).buffer,
285
125
  fields: [
286
126
  /* Header flags */
287
127
  {
288
128
  name: "flags",
289
- type: 0 /* BITS */,
290
- size: 1,
129
+ type: FieldType.BITS,
130
+ length: 1,
291
131
  bits: [
292
132
  { name: "payload version", size: 2 },
293
133
  { name: "payload type", size: 4 },
@@ -297,38 +137,40 @@ var Packet = class _Packet {
297
137
  ]
298
138
  },
299
139
  /* Transport codes */
300
- ..._Packet.hasTransportCodes(this.routeType) ? [{
301
- name: "transport codes",
302
- data: new Uint8Array([
303
- this.transport[0] >> 8 & 255,
304
- this.transport[0] & 255,
305
- this.transport[1] >> 8 & 255,
306
- this.transport[1] & 255
307
- ]),
308
- fields: [
309
- {
310
- name: "transport code 1",
311
- type: 3 /* UINT16_BE */,
312
- size: 2,
313
- value: this.transport[0]
314
- },
315
- {
316
- name: "transport code 2",
317
- type: 3 /* UINT16_BE */,
318
- size: 2,
319
- value: this.transport[1]
320
- }
321
- ]
322
- }] : [],
140
+ ..._Packet.hasTransportCodes(this.routeType) ? [
141
+ {
142
+ name: "transport codes",
143
+ data: new Uint8Array([
144
+ this.transport[0] >> 8 & 255,
145
+ this.transport[0] & 255,
146
+ this.transport[1] >> 8 & 255,
147
+ this.transport[1] & 255
148
+ ]).buffer,
149
+ fields: [
150
+ {
151
+ name: "transport code 1",
152
+ type: FieldType.UINT16_BE,
153
+ length: 2,
154
+ value: this.transport[0]
155
+ },
156
+ {
157
+ name: "transport code 2",
158
+ type: FieldType.UINT16_BE,
159
+ length: 2,
160
+ value: this.transport[1]
161
+ }
162
+ ]
163
+ }
164
+ ] : [],
323
165
  /* Path length and hashes */
324
166
  {
325
167
  name: "path",
326
- data: new Uint8Array([this.pathLength, ...this.path]),
168
+ data: new Uint8Array([this.pathLength, ...this.path]).buffer,
327
169
  fields: [
328
170
  {
329
171
  name: "path length",
330
- type: 1 /* UINT8 */,
331
- size: 1,
172
+ type: FieldType.UINT8,
173
+ length: 1,
332
174
  bits: [
333
175
  { name: "path hash size", size: 2 },
334
176
  { name: "path hash count", size: 6 }
@@ -337,7 +179,7 @@ var Packet = class _Packet {
337
179
  {
338
180
  name: "path hashes",
339
181
  type: pathHashType,
340
- size: this.path.length
182
+ length: this.path.length
341
183
  }
342
184
  ]
343
185
  }
@@ -382,7 +224,6 @@ var Packet = class _Packet {
382
224
  default:
383
225
  throw new Error(`Unsupported payload type: ${this.payloadType}`);
384
226
  }
385
- console.log("packet decode with structure:", typeof withStructure, withStructure, { result });
386
227
  if (typeof withStructure === "boolean" && withStructure && "segment" in result && "payload" in result) {
387
228
  this.ensureStructure();
388
229
  const structure = [...this.structure, result.segment];
@@ -391,17 +232,17 @@ var Packet = class _Packet {
391
232
  return result;
392
233
  }
393
234
  decodeEncryptedPayload(reader) {
394
- const cipherMAC = reader.readBytes(2);
395
- const cipherText = reader.readBytes(reader.remainingBytes());
235
+ const cipherMAC = reader.bytes(2);
236
+ const cipherText = reader.bytes();
396
237
  return { cipherMAC, cipherText };
397
238
  }
398
239
  decodeRequest(withSegment) {
399
240
  if (this.payload.length < 4) {
400
241
  throw new Error("Invalid request payload: too short");
401
242
  }
402
- const reader = new BufferReader(this.payload);
403
- const dst = reader.readByte();
404
- const src = reader.readByte();
243
+ const reader = Reader.fromBytes(this.payload);
244
+ const dst = reader.uint8();
245
+ const src = reader.uint8();
405
246
  const encrypted = this.decodeEncryptedPayload(reader);
406
247
  const payload = {
407
248
  type: 0 /* REQUEST */,
@@ -412,12 +253,17 @@ var Packet = class _Packet {
412
253
  if (typeof withSegment === "boolean" && withSegment) {
413
254
  const segment = {
414
255
  name: "request payload",
415
- data: this.payload,
256
+ data: new Uint8Array(this.payload).buffer,
416
257
  fields: [
417
- { name: "destination hash", type: 1 /* UINT8 */, size: 1, value: dst },
418
- { name: "source hash", type: 1 /* UINT8 */, size: 1, value: src },
419
- { name: "cipher MAC", type: 6 /* BYTES */, size: 2, value: encrypted.cipherMAC },
420
- { name: "cipher text", type: 6 /* BYTES */, size: encrypted.cipherText.length, value: encrypted.cipherText }
258
+ { name: "destination hash", type: FieldType.UINT8, length: 1, value: dst },
259
+ { name: "source hash", type: FieldType.UINT8, length: 1, value: src },
260
+ { name: "cipher MAC", type: FieldType.BYTES, length: 2, value: encrypted.cipherMAC },
261
+ {
262
+ name: "cipher text",
263
+ type: FieldType.BYTES,
264
+ length: encrypted.cipherText.length,
265
+ value: encrypted.cipherText
266
+ }
421
267
  ]
422
268
  };
423
269
  return { payload, segment };
@@ -428,9 +274,9 @@ var Packet = class _Packet {
428
274
  if (this.payload.length < 4) {
429
275
  throw new Error("Invalid response payload: too short");
430
276
  }
431
- const reader = new BufferReader(this.payload);
432
- const dst = reader.readByte();
433
- const src = reader.readByte();
277
+ const reader = Reader.fromBytes(this.payload);
278
+ const dst = reader.uint8();
279
+ const src = reader.uint8();
434
280
  const encrypted = this.decodeEncryptedPayload(reader);
435
281
  const payload = {
436
282
  type: 1 /* RESPONSE */,
@@ -441,12 +287,17 @@ var Packet = class _Packet {
441
287
  if (typeof withSegment === "boolean" && withSegment) {
442
288
  const segment = {
443
289
  name: "response payload",
444
- data: this.payload,
290
+ data: new Uint8Array(this.payload).buffer,
445
291
  fields: [
446
- { name: "destination hash", type: 1 /* UINT8 */, size: 1, value: dst },
447
- { name: "source hash", type: 1 /* UINT8 */, size: 1, value: src },
448
- { name: "cipher MAC", type: 6 /* BYTES */, size: 2, value: encrypted.cipherMAC },
449
- { name: "cipher text", type: 6 /* BYTES */, size: encrypted.cipherText.length, value: encrypted.cipherText }
292
+ { name: "destination hash", type: FieldType.UINT8, length: 1, value: dst },
293
+ { name: "source hash", type: FieldType.UINT8, length: 1, value: src },
294
+ { name: "cipher MAC", type: FieldType.BYTES, length: 2, value: encrypted.cipherMAC },
295
+ {
296
+ name: "cipher text",
297
+ type: FieldType.BYTES,
298
+ length: encrypted.cipherText.length,
299
+ value: encrypted.cipherText
300
+ }
450
301
  ]
451
302
  };
452
303
  return { payload, segment };
@@ -457,9 +308,9 @@ var Packet = class _Packet {
457
308
  if (this.payload.length < 4) {
458
309
  throw new Error("Invalid text payload: too short");
459
310
  }
460
- const reader = new BufferReader(this.payload);
461
- const dst = reader.readByte();
462
- const src = reader.readByte();
311
+ const reader = Reader.fromBytes(this.payload);
312
+ const dst = reader.uint8();
313
+ const src = reader.uint8();
463
314
  const encrypted = this.decodeEncryptedPayload(reader);
464
315
  const payload = {
465
316
  type: 2 /* TEXT */,
@@ -470,12 +321,17 @@ var Packet = class _Packet {
470
321
  if (typeof withSegment === "boolean" && withSegment) {
471
322
  const segment = {
472
323
  name: "text payload",
473
- data: this.payload,
324
+ data: new Uint8Array(this.payload).buffer,
474
325
  fields: [
475
- { name: "destination hash", type: 1 /* UINT8 */, size: 1, value: dst },
476
- { name: "source hash", type: 1 /* UINT8 */, size: 1, value: src },
477
- { name: "cipher MAC", type: 6 /* BYTES */, size: 2, value: encrypted.cipherMAC },
478
- { name: "cipher text", type: 6 /* BYTES */, size: encrypted.cipherText.length, value: encrypted.cipherText }
326
+ { name: "destination hash", type: FieldType.UINT8, length: 1, value: dst },
327
+ { name: "source hash", type: FieldType.UINT8, length: 1, value: src },
328
+ { name: "cipher MAC", type: FieldType.BYTES, length: 2, value: encrypted.cipherMAC },
329
+ {
330
+ name: "cipher text",
331
+ type: FieldType.BYTES,
332
+ length: encrypted.cipherText.length,
333
+ value: encrypted.cipherText
334
+ }
479
335
  ]
480
336
  };
481
337
  return { payload, segment };
@@ -486,8 +342,8 @@ var Packet = class _Packet {
486
342
  if (this.payload.length < 4) {
487
343
  throw new Error("Invalid ack payload: too short");
488
344
  }
489
- const reader = new BufferReader(this.payload);
490
- const checksum = reader.readBytes(4);
345
+ const reader = Reader.fromBytes(this.payload);
346
+ const checksum = reader.bytes(4);
491
347
  const payload = {
492
348
  type: 3 /* ACK */,
493
349
  checksum
@@ -495,10 +351,8 @@ var Packet = class _Packet {
495
351
  if (typeof withSegment === "boolean" && withSegment) {
496
352
  const segment = {
497
353
  name: "ack payload",
498
- data: this.payload,
499
- fields: [
500
- { name: "checksum", type: 6 /* BYTES */, size: 4, value: checksum }
501
- ]
354
+ data: new Uint8Array(this.payload).buffer,
355
+ fields: [{ name: "checksum", type: FieldType.BYTES, length: 4, value: checksum }]
502
356
  };
503
357
  return { payload, segment };
504
358
  }
@@ -508,26 +362,26 @@ var Packet = class _Packet {
508
362
  if (this.payload.length < 4) {
509
363
  throw new Error("Invalid advert payload: too short");
510
364
  }
511
- const reader = new BufferReader(this.payload);
365
+ const reader = Reader.fromBytes(this.payload);
512
366
  const payload = {
513
367
  type: 4 /* ADVERT */,
514
- publicKey: reader.readBytes(32),
515
- timestamp: reader.readTimestamp(),
516
- signature: reader.readBytes(64)
368
+ publicKey: reader.bytes(32),
369
+ timestamp: reader.date32(),
370
+ signature: reader.bytes(64)
517
371
  };
518
372
  let segment;
519
373
  if (typeof withSegment === "boolean" && withSegment) {
520
374
  segment = {
521
375
  name: "advert payload",
522
- data: this.payload,
376
+ data: new Uint8Array(this.payload).buffer,
523
377
  fields: [
524
- { type: 6 /* BYTES */, name: "public key", size: 32 },
525
- { type: 4 /* UINT32_LE */, name: "timestamp", size: 4, value: payload.timestamp },
526
- { type: 6 /* BYTES */, name: "signature", size: 64 }
378
+ { type: FieldType.BYTES, name: "public key", length: 32 },
379
+ { type: FieldType.UINT32_LE, name: "timestamp", length: 4, value: payload.timestamp },
380
+ { type: FieldType.BYTES, name: "signature", length: 64 }
527
381
  ]
528
382
  };
529
383
  }
530
- const flags = reader.readByte();
384
+ const flags = reader.uint8();
531
385
  const appdata = {
532
386
  nodeType: flags & 15,
533
387
  hasLocation: (flags & 16 /* HAS_LOCATION */) !== 0,
@@ -536,44 +390,50 @@ var Packet = class _Packet {
536
390
  hasName: (flags & 128 /* HAS_NAME */) !== 0
537
391
  };
538
392
  if (typeof withSegment === "boolean" && withSegment) {
539
- segment.fields.push({ type: 0 /* BITS */, name: "flags", size: 1, value: flags, bits: [
540
- { size: 1, name: "name flag" },
541
- { size: 1, name: "feature2 flag" },
542
- { size: 1, name: "feature1 flag" },
543
- { size: 1, name: "location flag" },
544
- { size: 4, name: "node type" }
545
- ] });
393
+ segment.fields.push({
394
+ type: FieldType.BITS,
395
+ name: "flags",
396
+ length: 1,
397
+ value: flags,
398
+ bits: [
399
+ { size: 1, name: "name flag" },
400
+ { size: 1, name: "feature2 flag" },
401
+ { size: 1, name: "feature1 flag" },
402
+ { size: 1, name: "location flag" },
403
+ { size: 4, name: "node type" }
404
+ ]
405
+ });
546
406
  }
547
407
  if (appdata.hasLocation) {
548
- const lat = reader.readInt32LE() / 1e5;
549
- const lon = reader.readInt32LE() / 1e5;
408
+ const lat = reader.int32() / 1e6;
409
+ const lon = reader.int32() / 1e6;
550
410
  appdata.location = [lat, lon];
551
411
  if (typeof withSegment === "boolean" && withSegment) {
552
- segment.fields.push({ type: 4 /* UINT32_LE */, name: "latitude", size: 4, value: lat });
553
- segment.fields.push({ type: 4 /* UINT32_LE */, name: "longitude", size: 4, value: lon });
412
+ segment.fields.push({ type: FieldType.UINT32_LE, name: "latitude", length: 4, value: lat });
413
+ segment.fields.push({ type: FieldType.UINT32_LE, name: "longitude", length: 4, value: lon });
554
414
  }
555
415
  }
556
416
  if (appdata.hasFeature1) {
557
- appdata.feature1 = reader.readUint16LE();
417
+ appdata.feature1 = reader.uint16();
558
418
  if (typeof withSegment === "boolean" && withSegment) {
559
- segment.fields.push({ type: 2 /* UINT16_LE */, name: "feature1", size: 2, value: appdata.feature1 });
419
+ segment.fields.push({ type: FieldType.UINT16_LE, name: "feature1", length: 2, value: appdata.feature1 });
560
420
  }
561
421
  }
562
422
  if (appdata.hasFeature2) {
563
- appdata.feature2 = reader.readUint16LE();
423
+ appdata.feature2 = reader.uint16();
564
424
  if (typeof withSegment === "boolean" && withSegment) {
565
- segment.fields.push({ type: 2 /* UINT16_LE */, name: "feature2", size: 2, value: appdata.feature2 });
425
+ segment.fields.push({ type: FieldType.UINT16_LE, name: "feature2", length: 2, value: appdata.feature2 });
566
426
  }
567
427
  }
568
428
  if (appdata.hasName) {
569
- const nameBytes = reader.readBytes();
570
- let nullPos = nameBytes.indexOf(0);
571
- if (nullPos === -1) {
572
- nullPos = nameBytes.length;
573
- }
574
- appdata.name = new TextDecoder("utf-8").decode(nameBytes.subarray(0, nullPos));
429
+ appdata.name = reader.cString();
575
430
  if (typeof withSegment === "boolean" && withSegment) {
576
- segment.fields.push({ type: 10 /* C_STRING */, name: "name", size: nameBytes.length, value: appdata.name });
431
+ segment.fields.push({
432
+ type: FieldType.C_STRING,
433
+ name: "name",
434
+ length: appdata.name.length,
435
+ value: appdata.name
436
+ });
577
437
  }
578
438
  }
579
439
  if (typeof withSegment === "boolean" && withSegment && typeof segment !== "undefined") {
@@ -585,8 +445,8 @@ var Packet = class _Packet {
585
445
  if (this.payload.length < 3) {
586
446
  throw new Error("Invalid group text payload: too short");
587
447
  }
588
- const reader = new BufferReader(this.payload);
589
- const channelHash = reader.readByte();
448
+ const reader = Reader.fromBytes(this.payload);
449
+ const channelHash = reader.uint8();
590
450
  const encrypted = this.decodeEncryptedPayload(reader);
591
451
  const payload = {
592
452
  type: 5 /* GROUP_TEXT */,
@@ -596,11 +456,16 @@ var Packet = class _Packet {
596
456
  if (typeof withSegment === "boolean" && withSegment) {
597
457
  const segment = {
598
458
  name: "group text payload",
599
- data: this.payload,
459
+ data: new Uint8Array(this.payload).buffer,
600
460
  fields: [
601
- { name: "channel hash", type: 1 /* UINT8 */, size: 1, value: channelHash },
602
- { name: "cipher MAC", type: 6 /* BYTES */, size: 2, value: encrypted.cipherMAC },
603
- { name: "cipher text", type: 6 /* BYTES */, size: encrypted.cipherText.length, value: encrypted.cipherText }
461
+ { name: "channel hash", type: FieldType.UINT8, length: 1, value: channelHash },
462
+ { name: "cipher MAC", type: FieldType.BYTES, length: 2, value: encrypted.cipherMAC },
463
+ {
464
+ name: "cipher text",
465
+ type: FieldType.BYTES,
466
+ length: encrypted.cipherText.length,
467
+ value: encrypted.cipherText
468
+ }
604
469
  ]
605
470
  };
606
471
  return { payload, segment };
@@ -611,20 +476,25 @@ var Packet = class _Packet {
611
476
  if (this.payload.length < 3) {
612
477
  throw new Error("Invalid group data payload: too short");
613
478
  }
614
- const reader = new BufferReader(this.payload);
479
+ const reader = Reader.fromBytes(this.payload);
615
480
  const payload = {
616
481
  type: 6 /* GROUP_DATA */,
617
- channelHash: reader.readByte(),
482
+ channelHash: reader.uint8(),
618
483
  encrypted: this.decodeEncryptedPayload(reader)
619
484
  };
620
485
  if (typeof withSegment === "boolean" && withSegment) {
621
486
  const segment = {
622
487
  name: "group data payload",
623
- data: this.payload,
488
+ data: new Uint8Array(this.payload).buffer,
624
489
  fields: [
625
- { name: "channel hash", type: 1 /* UINT8 */, size: 1, value: payload.channelHash },
626
- { name: "cipher MAC", type: 6 /* BYTES */, size: 2, value: payload.encrypted.cipherMAC },
627
- { name: "cipher text", type: 6 /* BYTES */, size: payload.encrypted.cipherText.length, value: payload.encrypted.cipherText }
490
+ { name: "channel hash", type: FieldType.UINT8, length: 1, value: payload.channelHash },
491
+ { name: "cipher MAC", type: FieldType.BYTES, length: 2, value: payload.encrypted.cipherMAC },
492
+ {
493
+ name: "cipher text",
494
+ type: FieldType.BYTES,
495
+ length: payload.encrypted.cipherText.length,
496
+ value: payload.encrypted.cipherText
497
+ }
628
498
  ]
629
499
  };
630
500
  return { payload, segment };
@@ -635,22 +505,27 @@ var Packet = class _Packet {
635
505
  if (this.payload.length < 1 + 32 + 2) {
636
506
  throw new Error("Invalid anon req payload: too short");
637
507
  }
638
- const reader = new BufferReader(this.payload);
508
+ const reader = Reader.fromBytes(this.payload);
639
509
  const payload = {
640
510
  type: 7 /* ANON_REQ */,
641
- dst: reader.readByte(),
642
- publicKey: reader.readBytes(32),
511
+ dst: reader.uint8(),
512
+ publicKey: reader.bytes(32),
643
513
  encrypted: this.decodeEncryptedPayload(reader)
644
514
  };
645
515
  if (typeof withSegment === "boolean" && withSegment) {
646
516
  const segment = {
647
517
  name: "anon req payload",
648
- data: this.payload,
518
+ data: new Uint8Array(this.payload).buffer,
649
519
  fields: [
650
- { name: "destination hash", type: 1 /* UINT8 */, size: 1, value: payload.dst },
651
- { name: "public key", type: 6 /* BYTES */, size: 32, value: payload.publicKey },
652
- { name: "cipher MAC", type: 6 /* BYTES */, size: 2, value: payload.encrypted.cipherMAC },
653
- { name: "cipher text", type: 6 /* BYTES */, size: payload.encrypted.cipherText.length, value: payload.encrypted.cipherText }
520
+ { name: "destination hash", type: FieldType.UINT8, length: 1, value: payload.dst },
521
+ { name: "public key", type: FieldType.BYTES, length: 32, value: payload.publicKey },
522
+ { name: "cipher MAC", type: FieldType.BYTES, length: 2, value: payload.encrypted.cipherMAC },
523
+ {
524
+ name: "cipher text",
525
+ type: FieldType.BYTES,
526
+ length: payload.encrypted.cipherText.length,
527
+ value: payload.encrypted.cipherText
528
+ }
654
529
  ]
655
530
  };
656
531
  return { payload, segment };
@@ -661,19 +536,19 @@ var Packet = class _Packet {
661
536
  if (this.payload.length < 2) {
662
537
  throw new Error("Invalid path payload: too short");
663
538
  }
664
- const reader = new BufferReader(this.payload);
539
+ const reader = Reader.fromBytes(this.payload);
665
540
  const payload = {
666
541
  type: 8 /* PATH */,
667
- dst: reader.readByte(),
668
- src: reader.readByte()
542
+ dst: reader.uint8(),
543
+ src: reader.uint8()
669
544
  };
670
545
  if (typeof withSegment === "boolean" && withSegment) {
671
546
  const segment = {
672
547
  name: "path payload",
673
- data: this.payload,
548
+ data: new Uint8Array(this.payload).buffer,
674
549
  fields: [
675
- { name: "destination hash", type: 1 /* UINT8 */, size: 1, value: payload.dst },
676
- { name: "source hash", type: 1 /* UINT8 */, size: 1, value: payload.src }
550
+ { name: "destination hash", type: FieldType.UINT8, length: 1, value: payload.dst },
551
+ { name: "source hash", type: FieldType.UINT8, length: 1, value: payload.src }
677
552
  ]
678
553
  };
679
554
  return { payload, segment };
@@ -684,23 +559,23 @@ var Packet = class _Packet {
684
559
  if (this.payload.length < 9) {
685
560
  throw new Error("Invalid trace payload: too short");
686
561
  }
687
- const reader = new BufferReader(this.payload);
562
+ const reader = Reader.fromBytes(this.payload);
688
563
  const payload = {
689
564
  type: 9 /* TRACE */,
690
- tag: reader.readUint32LE() >>> 0,
691
- authCode: reader.readUint32LE() >>> 0,
692
- flags: reader.readByte() & 3,
693
- nodes: reader.readBytes()
565
+ tag: reader.uint32(),
566
+ authCode: reader.uint32(),
567
+ flags: reader.uint8() & 3,
568
+ nodes: reader.bytes()
694
569
  };
695
570
  if (typeof withSegment === "boolean" && withSegment) {
696
571
  const segment = {
697
572
  name: "trace payload",
698
- data: this.payload,
573
+ data: new Uint8Array(this.payload).buffer,
699
574
  fields: [
700
- { name: "tag", type: 8 /* DWORDS */, size: 4, value: payload.tag },
701
- { name: "auth code", type: 8 /* DWORDS */, size: 4, value: payload.authCode },
702
- { name: "flags", type: 1 /* UINT8 */, size: 1, value: payload.flags },
703
- { name: "nodes", type: 6 /* BYTES */, size: payload.nodes.length, value: payload.nodes }
575
+ { name: "tag", type: FieldType.DWORDS, length: 4, value: payload.tag },
576
+ { name: "auth code", type: FieldType.DWORDS, length: 4, value: payload.authCode },
577
+ { name: "flags", type: FieldType.UINT8, length: 1, value: payload.flags },
578
+ { name: "nodes", type: FieldType.BYTES, length: payload.nodes.length, value: payload.nodes }
704
579
  ]
705
580
  };
706
581
  return { payload, segment };
@@ -715,10 +590,8 @@ var Packet = class _Packet {
715
590
  if (typeof withSegment === "boolean" && withSegment) {
716
591
  const segment = {
717
592
  name: "raw custom payload",
718
- data: this.payload,
719
- fields: [
720
- { name: "data", type: 6 /* BYTES */, size: this.payload.length, value: this.payload }
721
- ]
593
+ data: new Uint8Array(this.payload).buffer,
594
+ fields: [{ name: "data", type: FieldType.BYTES, length: this.payload.length, value: this.payload }]
722
595
  };
723
596
  return { payload, segment };
724
597
  }
@@ -727,35 +600,39 @@ var Packet = class _Packet {
727
600
  };
728
601
 
729
602
  // src/crypto.ts
730
- var import_ed25519 = require("@noble/curves/ed25519.js");
731
- var import_sha22 = require("@noble/hashes/sha2.js");
732
- var import_hmac = require("@noble/hashes/hmac.js");
733
- var import_aes = require("@noble/ciphers/aes.js");
603
+ import { ed25519, x25519 } from "@noble/curves/ed25519.js";
604
+ import { sha256 as sha2562 } from "@noble/hashes/sha2.js";
605
+ import { hmac } from "@noble/hashes/hmac.js";
606
+ import { ecb } from "@noble/ciphers/aes.js";
607
+ import { bytesToHex as bytesToHex2, equalBytes, hexToBytes } from "@hamradio/packet";
734
608
  var PUBLIC_KEY_SIZE = 32;
735
609
  var SEED_SIZE = 32;
736
610
  var HMAC_SIZE = 2;
737
- var SHARED_SECRET_SIZE = 32;
611
+ var SHARED_SECRET_SIZE = 16;
738
612
  var SIGNATURE_SIZE = 64;
739
613
  var STATIC_SECRET_SIZE = 32;
740
- var publicSecret = hexToBytes("8b3387e9c5cdea6ac9e5edbaa115cd72", 16);
614
+ var publicSecret = hexToBytes("8b3387e9c5cdea6ac9e5edbaa115cd72");
741
615
  var PublicKey = class _PublicKey {
742
616
  constructor(key) {
743
617
  if (typeof key === "string") {
744
- this.key = hexToBytes(key, PUBLIC_KEY_SIZE);
618
+ this.key = hexToBytes(key);
745
619
  } else if (key instanceof Uint8Array) {
746
620
  this.key = key;
747
621
  } else {
748
622
  throw new Error("Invalid type for PublicKey constructor");
749
623
  }
624
+ if (this.key.length !== PUBLIC_KEY_SIZE) {
625
+ throw new Error(`Invalid public key length: expected ${PUBLIC_KEY_SIZE} bytes, got ${this.key.length}`);
626
+ }
750
627
  }
751
628
  toHash() {
752
- return import_sha22.sha256.create().update(this.key).digest()[0];
629
+ return sha2562.create().update(this.key).digest()[0];
753
630
  }
754
631
  toBytes() {
755
632
  return this.key;
756
633
  }
757
634
  toString() {
758
- return (0, import_utils2.bytesToHex)(this.key);
635
+ return bytesToHex2(this.key);
759
636
  }
760
637
  equals(other) {
761
638
  let otherKey;
@@ -764,28 +641,39 @@ var PublicKey = class _PublicKey {
764
641
  } else if (other instanceof Uint8Array) {
765
642
  otherKey = other;
766
643
  } else if (typeof other === "string") {
767
- otherKey = hexToBytes(other, PUBLIC_KEY_SIZE);
644
+ otherKey = hexToBytes(other);
768
645
  } else {
769
646
  throw new Error("Invalid type for PublicKey comparison");
770
647
  }
771
- return (0, import_utils.equalBytes)(this.key, otherKey);
648
+ if (otherKey.length !== PUBLIC_KEY_SIZE) {
649
+ throw new Error(
650
+ `Invalid public key length for comparison: expected ${PUBLIC_KEY_SIZE} bytes, got ${otherKey.length}`
651
+ );
652
+ }
653
+ return equalBytes(this.key, otherKey);
772
654
  }
773
655
  verify(message, signature) {
774
656
  if (signature.length !== SIGNATURE_SIZE) {
775
657
  throw new Error(`Invalid signature length: expected ${SIGNATURE_SIZE} bytes, got ${signature.length}`);
776
658
  }
777
- return import_ed25519.ed25519.verify(signature, message, this.key);
659
+ return ed25519.verify(signature, message, this.key);
660
+ }
661
+ static fromBytes(key) {
662
+ return new _PublicKey(key);
663
+ }
664
+ static fromString(key) {
665
+ return new _PublicKey(key);
778
666
  }
779
667
  };
780
668
  var PrivateKey = class _PrivateKey {
781
669
  constructor(seed) {
782
670
  if (typeof seed === "string") {
783
- seed = hexToBytes(seed, SEED_SIZE);
671
+ seed = hexToBytes(seed);
784
672
  }
785
673
  if (seed.length !== SEED_SIZE) {
786
674
  throw new Error(`Invalid seed length: expected ${SEED_SIZE} bytes, got ${seed.length}`);
787
675
  }
788
- const { secretKey, publicKey } = import_ed25519.ed25519.keygen(seed);
676
+ const { secretKey, publicKey } = ed25519.keygen(seed);
789
677
  this.secretKey = secretKey;
790
678
  this.publicKey = new PublicKey(publicKey);
791
679
  }
@@ -796,10 +684,10 @@ var PrivateKey = class _PrivateKey {
796
684
  return this.secretKey;
797
685
  }
798
686
  toString() {
799
- return (0, import_utils2.bytesToHex)(this.secretKey);
687
+ return bytesToHex2(this.secretKey);
800
688
  }
801
689
  sign(message) {
802
- return import_ed25519.ed25519.sign(message, this.secretKey);
690
+ return ed25519.sign(message, this.secretKey);
803
691
  }
804
692
  calculateSharedSecret(other) {
805
693
  let otherPublicKey;
@@ -812,43 +700,40 @@ var PrivateKey = class _PrivateKey {
812
700
  } else {
813
701
  throw new Error("Invalid type for calculateSharedSecret comparison");
814
702
  }
815
- return import_ed25519.x25519.getSharedSecret(this.secretKey, otherPublicKey.toBytes());
703
+ return x25519.getSharedSecret(this.secretKey, otherPublicKey.toBytes());
816
704
  }
817
705
  static generate() {
818
- const { secretKey } = import_ed25519.ed25519.keygen();
706
+ const { secretKey } = ed25519.keygen();
819
707
  return new _PrivateKey(secretKey);
820
708
  }
821
709
  };
822
710
  var SharedSecret = class _SharedSecret {
823
711
  constructor(secret) {
824
- if (secret.length === SHARED_SECRET_SIZE / 2) {
825
- const padded = new Uint8Array(SHARED_SECRET_SIZE);
826
- padded.set(secret, SHARED_SECRET_SIZE - secret.length);
827
- secret = padded;
828
- }
829
712
  if (secret.length !== SHARED_SECRET_SIZE) {
830
713
  throw new Error(`Invalid shared secret length: expected ${SHARED_SECRET_SIZE} bytes, got ${secret.length}`);
831
714
  }
832
- this.secret = secret;
715
+ this.secret = new Uint8Array(SHARED_SECRET_SIZE * 2);
716
+ this.secret.set(secret, 0);
833
717
  }
834
718
  toHash() {
835
- return this.secret[0];
719
+ const hash = sha2562.create().update(this.secret.slice(0, 16)).digest();
720
+ return hash[0];
836
721
  }
837
722
  toBytes() {
838
- return this.secret;
723
+ return this.secret.slice(0, 16);
839
724
  }
840
725
  toString() {
841
- return (0, import_utils2.bytesToHex)(this.secret);
726
+ return bytesToHex2(this.secret.slice(0, 16));
842
727
  }
843
728
  decrypt(hmac2, ciphertext) {
844
729
  if (hmac2.length !== HMAC_SIZE) {
845
730
  throw new Error(`Invalid HMAC length: expected ${HMAC_SIZE} bytes, got ${hmac2.length}`);
846
731
  }
847
732
  const expectedHmac = this.calculateHmac(ciphertext);
848
- if (!(0, import_utils.equalBytes)(hmac2, expectedHmac)) {
849
- throw new Error(`Invalid HMAC: decryption failed: expected ${(0, import_utils2.bytesToHex)(expectedHmac)}, got ${(0, import_utils2.bytesToHex)(hmac2)}`);
733
+ if (!equalBytes(hmac2, expectedHmac)) {
734
+ throw new Error(`Invalid HMAC: decryption failed: expected ${bytesToHex2(expectedHmac)}, got ${bytesToHex2(hmac2)}`);
850
735
  }
851
- const cipher = (0, import_aes.ecb)(this.secret.slice(0, 16), { disablePadding: true });
736
+ const cipher = ecb(this.secret.slice(0, 16), { disablePadding: true });
852
737
  const plaintext = new Uint8Array(ciphertext.length);
853
738
  for (let i = 0; i < ciphertext.length; i += 16) {
854
739
  const block = ciphertext.slice(i, i + 16);
@@ -863,7 +748,7 @@ var SharedSecret = class _SharedSecret {
863
748
  }
864
749
  encrypt(data) {
865
750
  const key = this.secret.slice(0, 16);
866
- const cipher = (0, import_aes.ecb)(key, { disablePadding: true });
751
+ const cipher = ecb(key, { disablePadding: true });
867
752
  const fullBlocks = Math.floor(data.length / 16);
868
753
  const remaining = data.length % 16;
869
754
  const ciphertext = new Uint8Array((fullBlocks + (remaining > 0 ? 1 : 0)) * 16);
@@ -882,7 +767,7 @@ var SharedSecret = class _SharedSecret {
882
767
  return { hmac: hmac2, ciphertext };
883
768
  }
884
769
  calculateHmac(data) {
885
- return (0, import_hmac.hmac)(import_sha22.sha256, this.secret, data).slice(0, HMAC_SIZE);
770
+ return hmac(sha2562, this.secret, data).slice(0, HMAC_SIZE);
886
771
  }
887
772
  static fromName(name) {
888
773
  if (name === "Public") {
@@ -890,14 +775,14 @@ var SharedSecret = class _SharedSecret {
890
775
  } else if (!/^#/.test(name)) {
891
776
  throw new Error("Only the 'Public' group or groups starting with '#' are supported");
892
777
  }
893
- const hash = import_sha22.sha256.create().update(new TextEncoder().encode(name)).digest();
778
+ const hash = sha2562.create().update(new TextEncoder().encode(name)).digest();
894
779
  return new _SharedSecret(hash.slice(0, SHARED_SECRET_SIZE));
895
780
  }
896
781
  };
897
782
  var StaticSecret = class {
898
783
  constructor(secret) {
899
784
  if (typeof secret === "string") {
900
- secret = hexToBytes(secret, STATIC_SECRET_SIZE);
785
+ secret = hexToBytes(secret);
901
786
  }
902
787
  if (secret.length !== STATIC_SECRET_SIZE) {
903
788
  throw new Error(`Invalid static secret length: expected ${STATIC_SECRET_SIZE} bytes, got ${secret.length}`);
@@ -905,16 +790,17 @@ var StaticSecret = class {
905
790
  this.secret = secret;
906
791
  }
907
792
  publicKey() {
908
- const publicKey = import_ed25519.x25519.getPublicKey(this.secret);
793
+ const publicKey = x25519.getPublicKey(this.secret);
909
794
  return new PublicKey(publicKey);
910
795
  }
911
796
  diffieHellman(otherPublicKey) {
912
- const sharedSecret = import_ed25519.x25519.getSharedSecret(this.secret, otherPublicKey.toBytes());
913
- return new SharedSecret(sharedSecret);
797
+ const sharedSecret = x25519.getSharedSecret(this.secret, otherPublicKey.toBytes());
798
+ return new SharedSecret(sharedSecret.slice(0, 16));
914
799
  }
915
800
  };
916
801
 
917
802
  // src/identity.ts
803
+ import { hexToBytes as hexToBytes2, Reader as Reader2, Writer } from "@hamradio/packet";
918
804
  var parseNodeHash = (hash) => {
919
805
  if (hash instanceof Uint8Array) {
920
806
  return hash[0];
@@ -925,7 +811,7 @@ var parseNodeHash = (hash) => {
925
811
  }
926
812
  return hash;
927
813
  } else if (typeof hash === "string") {
928
- const parsed = hexToBytes(hash);
814
+ const parsed = hexToBytes2(hash);
929
815
  if (parsed.length !== 1) {
930
816
  throw new Error("NodeHash string must represent a single byte");
931
817
  }
@@ -941,7 +827,7 @@ var toPublicKeyBytes = (key) => {
941
827
  } else if (key instanceof Uint8Array) {
942
828
  return key;
943
829
  } else if (typeof key === "string") {
944
- return hexToBytes(key);
830
+ return hexToBytes2(key);
945
831
  } else {
946
832
  throw new Error("Invalid type for toPublicKeyBytes");
947
833
  }
@@ -1000,7 +886,7 @@ var LocalIdentity = class extends Identity {
1000
886
  } else {
1001
887
  throw new Error("Invalid type for calculateSharedSecret comparison");
1002
888
  }
1003
- return new SharedSecret(this.privateKey.calculateSharedSecret(otherPublicKey));
889
+ return new SharedSecret(this.privateKey.calculateSharedSecret(otherPublicKey).slice(0, 16));
1004
890
  }
1005
891
  };
1006
892
  var Contact = class {
@@ -1044,12 +930,12 @@ var Group = class {
1044
930
  if (data.length < 5) {
1045
931
  throw new Error("Invalid ciphertext");
1046
932
  }
1047
- const reader = new BufferReader(data);
1048
- const timestamp = reader.readTimestamp();
1049
- const flags = reader.readByte();
933
+ const reader = new Reader2(data);
934
+ const timestamp = reader.date32();
935
+ const flags = reader.uint8();
1050
936
  const textType = flags >> 2 & 63;
1051
937
  const attempt = flags & 3;
1052
- const message = new TextDecoder("utf-8").decode(reader.readBytes());
938
+ const message = new TextDecoder("utf-8").decode(reader.bytes());
1053
939
  return {
1054
940
  timestamp,
1055
941
  textType,
@@ -1058,11 +944,11 @@ var Group = class {
1058
944
  };
1059
945
  }
1060
946
  encryptText(plain) {
1061
- const writer = new BufferWriter();
1062
- writer.writeTimestamp(plain.timestamp);
947
+ const writer = new Writer(4 + 1 + new TextEncoder().encode(plain.message).length);
948
+ writer.date32(plain.timestamp);
1063
949
  const flags = (plain.textType & 63) << 2 | plain.attempt & 3;
1064
- writer.writeByte(flags);
1065
- writer.writeBytes(new TextEncoder().encode(plain.message));
950
+ writer.uint8(flags);
951
+ writer.utf8String(plain.message);
1066
952
  const data = writer.toBytes();
1067
953
  return this.secret.encrypt(data);
1068
954
  }
@@ -1071,16 +957,16 @@ var Group = class {
1071
957
  if (data.length < 4) {
1072
958
  throw new Error("Invalid ciphertext");
1073
959
  }
1074
- const reader = new BufferReader(data);
960
+ const reader = new Reader2(data);
1075
961
  return {
1076
- timestamp: reader.readTimestamp(),
1077
- data: reader.readBytes(reader.remainingBytes())
962
+ timestamp: reader.date32(),
963
+ data: reader.bytes()
1078
964
  };
1079
965
  }
1080
966
  encryptData(plain) {
1081
- const writer = new BufferWriter();
1082
- writer.writeTimestamp(plain.timestamp);
1083
- writer.writeBytes(plain.data);
967
+ const writer = new Writer(4 + plain.data.length);
968
+ writer.date32(plain.timestamp);
969
+ writer.bytes(plain.data);
1084
970
  const data = writer.toBytes();
1085
971
  return this.secret.encrypt(data);
1086
972
  }
@@ -1090,6 +976,8 @@ var Contacts = class {
1090
976
  this.localIdentities = [];
1091
977
  this.contacts = {};
1092
978
  this.groups = {};
979
+ this.addGroup(new Group("Public"));
980
+ this.addGroup(new Group("#test"));
1093
981
  }
1094
982
  addLocalIdentity(identity) {
1095
983
  this.localIdentities.push({ identity, sharedSecrets: {} });
@@ -1101,6 +989,23 @@ var Contacts = class {
1101
989
  }
1102
990
  this.contacts[hash].push(contact);
1103
991
  }
992
+ hasContact(nameOrHash) {
993
+ if (typeof nameOrHash === "string") {
994
+ return Object.values(this.contacts).flat().some((contact) => contact.name.toLowerCase() === nameOrHash.toLowerCase());
995
+ } else {
996
+ const hash = parseNodeHash(nameOrHash);
997
+ return (this.contacts[hash] || []).length > 0;
998
+ }
999
+ }
1000
+ getContactByName(name) {
1001
+ const contact = Object.values(this.contacts).flat().find((contact2) => contact2.name.toLowerCase() === name.toLowerCase());
1002
+ return contact || null;
1003
+ }
1004
+ getContacts() {
1005
+ const contacts = Object.values(this.contacts).flat();
1006
+ contacts.sort((a, b) => a.name.localeCompare(b.name));
1007
+ return contacts;
1008
+ }
1104
1009
  decrypt(src, dst, hmac2, ciphertext) {
1105
1010
  let contacts = [];
1106
1011
  if (src instanceof PublicKey) {
@@ -1148,17 +1053,45 @@ var Contacts = class {
1148
1053
  return sharedSecret;
1149
1054
  }
1150
1055
  addGroup(group) {
1151
- const hash = parseNodeHash(group.hash());
1056
+ for (const key of Object.keys(this.groups)) {
1057
+ const hash2 = Number(key);
1058
+ this.groups[hash2] = this.groups[hash2].filter((g) => g.name.toLowerCase() !== group.name.toLowerCase());
1059
+ if (this.groups[hash2].length === 0) {
1060
+ delete this.groups[hash2];
1061
+ }
1062
+ }
1063
+ const hash = group.hash();
1152
1064
  if (!this.groups[hash]) {
1153
1065
  this.groups[hash] = [];
1154
1066
  }
1155
1067
  this.groups[hash].push(group);
1156
1068
  }
1069
+ hasGroup(nameOrHash) {
1070
+ if (typeof nameOrHash === "string") {
1071
+ return Object.values(this.groups).flat().some((group) => group.name.toLowerCase() === nameOrHash.toLowerCase());
1072
+ } else {
1073
+ const hash = parseNodeHash(nameOrHash);
1074
+ return (this.groups[hash] || []).length > 0;
1075
+ }
1076
+ }
1077
+ getGroupByName(name) {
1078
+ const group = Object.values(this.groups).flat().find((group2) => group2.name.toLowerCase() === name.toLowerCase());
1079
+ return group || null;
1080
+ }
1081
+ getGroups() {
1082
+ const groups = Object.values(this.groups).flat();
1083
+ groups.sort((a, b) => {
1084
+ if (a.name === "Public") return -1;
1085
+ if (b.name === "Public") return 1;
1086
+ return a.name.localeCompare(b.name);
1087
+ });
1088
+ return groups;
1089
+ }
1157
1090
  decryptGroupText(channelHash, hmac2, ciphertext) {
1158
1091
  const hash = parseNodeHash(channelHash);
1159
1092
  const groups = this.groups[hash] || [];
1160
1093
  if (groups.length === 0) {
1161
- throw new Error("Unknown group hash");
1094
+ throw new Error(`Unknown group hash ${hash.toString(16).padStart(2, "0")}`);
1162
1095
  }
1163
1096
  for (const group of groups) {
1164
1097
  try {
@@ -1167,13 +1100,13 @@ var Contacts = class {
1167
1100
  } catch {
1168
1101
  }
1169
1102
  }
1170
- throw new Error("Decryption failed with all known groups");
1103
+ throw new Error(`Decryption failed with all known groups with hash ${hash.toString(16).padStart(2, "0")}`);
1171
1104
  }
1172
1105
  decryptGroupData(channelHash, hmac2, ciphertext) {
1173
1106
  const hash = parseNodeHash(channelHash);
1174
1107
  const groups = this.groups[hash] || [];
1175
1108
  if (groups.length === 0) {
1176
- throw new Error("Unknown group hash");
1109
+ throw new Error(`Unknown group hash ${hash.toString(16).padStart(2, "0")}`);
1177
1110
  }
1178
1111
  for (const group of groups) {
1179
1112
  try {
@@ -1182,11 +1115,10 @@ var Contacts = class {
1182
1115
  } catch {
1183
1116
  }
1184
1117
  }
1185
- throw new Error("Decryption failed with all known groups");
1118
+ throw new Error(`Decryption failed with all known groups with hash ${hash.toString(16).padStart(2, "0")}`);
1186
1119
  }
1187
1120
  };
1188
- // Annotate the CommonJS export names for ESM import in node:
1189
- 0 && (module.exports = {
1121
+ export {
1190
1122
  Contact,
1191
1123
  Contacts,
1192
1124
  Group,
@@ -1203,4 +1135,4 @@ var Contacts = class {
1203
1135
  StaticSecret,
1204
1136
  TextType,
1205
1137
  parseNodeHash
1206
- });
1138
+ };