@loro-dev/flock 2.1.2 → 3.0.0

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/index.ts CHANGED
@@ -81,8 +81,7 @@ export type ExportBundle = { version: number, entries: Record<string, ExportReco
81
81
  export type EntryClock = {
82
82
  physicalTime: number;
83
83
  logicalCounter: number;
84
- peerIdHex: string;
85
- peerId: Uint8Array;
84
+ peerId: string;
86
85
  };
87
86
 
88
87
  export type ExportPayload = {
@@ -181,30 +180,17 @@ export type EventBatch = {
181
180
  events: Event[];
182
181
  };
183
182
 
184
- function bytesToHex(bytes: Uint8Array): string {
185
- let hex = "";
186
- for (let i = 0; i < bytes.length; i += 1) {
187
- hex += bytes[i].toString(16).padStart(2, "0");
188
- }
189
- return hex;
183
+ const textEncoder = new TextEncoder();
184
+
185
+ function utf8ByteLength(value: string): number {
186
+ return textEncoder.encode(value).length;
190
187
  }
191
188
 
192
- function hexToBytes(hex: string): Uint8Array {
193
- if (hex.length % 2 !== 0) {
194
- throw new TypeError("peerId hex must have even length");
195
- }
196
- const bytes = new Uint8Array(hex.length / 2);
197
- for (let i = 0; i < bytes.length; i += 1) {
198
- const byte = Number.parseInt(hex.slice(i * 2, i * 2 + 2), 16);
199
- if (Number.isNaN(byte)) {
200
- throw new TypeError("peerId hex contains invalid characters");
201
- }
202
- bytes[i] = byte;
203
- }
204
- return bytes;
189
+ function isValidPeerId(peerId: unknown): peerId is string {
190
+ return typeof peerId === "string" && utf8ByteLength(peerId) < 128;
205
191
  }
206
192
 
207
- function createRandomPeerId(): Uint8Array {
193
+ function createRandomPeerId(): string {
208
194
  const id = new Uint8Array(32);
209
195
  if (typeof crypto !== "undefined" && typeof crypto.getRandomValues === "function") {
210
196
  crypto.getRandomValues(id);
@@ -213,17 +199,17 @@ function createRandomPeerId(): Uint8Array {
213
199
  id[i] = Math.floor(Math.random() * 256);
214
200
  }
215
201
  }
216
- return id;
202
+ return Array.from(id, (byte) => byte.toString(16).padStart(2, "0")).join("");
217
203
  }
218
204
 
219
- function normalizePeerId(peerId?: Uint8Array): Uint8Array {
205
+ function normalizePeerId(peerId?: string): string {
220
206
  if (peerId === undefined) {
221
207
  return createRandomPeerId();
222
208
  }
223
- if (!(peerId instanceof Uint8Array)) {
224
- throw new TypeError("peerId must be a Uint8Array");
209
+ if (!isValidPeerId(peerId)) {
210
+ throw new TypeError("peerId must be a UTF-8 string under 128 bytes");
225
211
  }
226
- return new Uint8Array(peerId);
212
+ return peerId;
227
213
  }
228
214
 
229
215
  function encodeVersionVector(vv?: VersionVector): RawVersionVector | undefined {
@@ -235,6 +221,9 @@ function encodeVersionVector(vv?: VersionVector): RawVersionVector | undefined {
235
221
  if (!entry) {
236
222
  continue;
237
223
  }
224
+ if (!isValidPeerId(peer)) {
225
+ continue;
226
+ }
238
227
  const { physicalTime, logicalCounter } = entry;
239
228
  if (typeof physicalTime !== "number" || Number.isNaN(physicalTime)) {
240
229
  continue;
@@ -266,6 +255,9 @@ function decodeVersionVector(raw: unknown): VersionVector {
266
255
  if (!Array.isArray(value) || value.length < 2) {
267
256
  continue;
268
257
  }
258
+ if (!isValidPeerId(peer)) {
259
+ continue;
260
+ }
269
261
  const [physicalTime, logicalCounter] = value;
270
262
  if (typeof physicalTime !== "number" || !Number.isFinite(physicalTime)) {
271
263
  continue;
@@ -375,22 +367,21 @@ function cloneMetadata(metadata: unknown): MetadataMap | undefined {
375
367
  }
376
368
 
377
369
  function decodeClock(record: ExportRecord): EntryClock {
378
- const [physicalRaw, logicalRaw, peerHexRaw] = record.c.split(",");
379
- const physicalTime = Number(physicalRaw);
380
- const logicalCounter = Number(logicalRaw);
381
- const peerIdHex = peerHexRaw ?? "";
382
- let peerId: Uint8Array;
383
- try {
384
- peerId = hexToBytes(peerIdHex);
385
- } catch {
386
- peerId = new Uint8Array();
387
- }
370
+ const rawClock = typeof record.c === "string" ? record.c : "";
371
+ const firstComma = rawClock.indexOf(",");
372
+ const secondComma = firstComma === -1 ? -1 : rawClock.indexOf(",", firstComma + 1);
373
+ if (firstComma === -1 || secondComma === -1) {
374
+ return { physicalTime: 0, logicalCounter: 0, peerId: "" };
375
+ }
376
+ const physicalTime = Number(rawClock.slice(0, firstComma));
377
+ const logicalCounter = Number(rawClock.slice(firstComma + 1, secondComma));
378
+ const peerIdRaw = rawClock.slice(secondComma + 1);
379
+ const peerId = isValidPeerId(peerIdRaw) ? peerIdRaw : "";
388
380
  return {
389
381
  physicalTime: Number.isFinite(physicalTime) ? physicalTime : 0,
390
382
  logicalCounter: Number.isFinite(logicalCounter)
391
383
  ? Math.trunc(logicalCounter)
392
384
  : 0,
393
- peerIdHex,
394
385
  peerId,
395
386
  };
396
387
  }
@@ -537,7 +528,7 @@ function isImportOptions(value: unknown): value is ImportOptions {
537
528
  export class Flock {
538
529
  private inner: ReturnType<typeof newFlock>;
539
530
 
540
- constructor(peerId?: Uint8Array) {
531
+ constructor(peerId?: string) {
541
532
  this.inner = newFlock(normalizePeerId(peerId));
542
533
  }
543
534
 
@@ -547,8 +538,8 @@ export class Flock {
547
538
  return flock;
548
539
  }
549
540
 
550
- static fromJson(bundle: ExportBundle, peerId: Uint8Array): Flock {
551
- const inner = from_json_ffi(bundle, bytesToHex(normalizePeerId(peerId)));
541
+ static fromJson(bundle: ExportBundle, peerId: string): Flock {
542
+ const inner = from_json_ffi(bundle, normalizePeerId(peerId));
552
543
  return Flock.fromInner(inner as ReturnType<typeof newFlock>);
553
544
  }
554
545
 
@@ -560,7 +551,7 @@ export class Flock {
560
551
  check_invariants_ffi(this.inner);
561
552
  }
562
553
 
563
- setPeerId(peerId: Uint8Array): void {
554
+ setPeerId(peerId: string): void {
564
555
  set_peer_id(this.inner, normalizePeerId(peerId));
565
556
  }
566
557
 
@@ -754,12 +745,15 @@ export class Flock {
754
745
  return Number(get_max_physical_time_ffi(this.inner));
755
746
  }
756
747
 
757
- peerId(): Uint8Array {
758
- const hex = peer_id_ffi(this.inner);
759
- if (typeof hex !== "string") {
748
+ peerId(): string {
749
+ const id = peer_id_ffi(this.inner);
750
+ if (typeof id !== "string") {
760
751
  throw new TypeError("peerId ffi returned unexpected value");
761
752
  }
762
- return hexToBytes(hex);
753
+ if (!isValidPeerId(id)) {
754
+ throw new TypeError("peerId ffi returned an invalid string");
755
+ }
756
+ return id;
763
757
  }
764
758
 
765
759
  digest(): string {