@loro-dev/flock 2.1.2 → 3.1.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/README.md +8 -8
- package/dist/index.cjs +2 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +6 -6
- package/dist/index.d.ts +6 -6
- package/dist/index.mjs +2 -2
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/_moon_flock.d.ts +7 -5
- package/src/_moon_flock.ts +1425 -1242
- package/src/index.ts +47 -47
package/src/index.ts
CHANGED
|
@@ -39,6 +39,7 @@ type ExportOptions = {
|
|
|
39
39
|
from?: VersionVector;
|
|
40
40
|
hooks?: ExportHooks;
|
|
41
41
|
pruneTombstonesBefore?: number;
|
|
42
|
+
peerId?: string;
|
|
42
43
|
};
|
|
43
44
|
|
|
44
45
|
type ImportOptions = {
|
|
@@ -81,8 +82,7 @@ export type ExportBundle = { version: number, entries: Record<string, ExportReco
|
|
|
81
82
|
export type EntryClock = {
|
|
82
83
|
physicalTime: number;
|
|
83
84
|
logicalCounter: number;
|
|
84
|
-
|
|
85
|
-
peerId: Uint8Array;
|
|
85
|
+
peerId: string;
|
|
86
86
|
};
|
|
87
87
|
|
|
88
88
|
export type ExportPayload = {
|
|
@@ -181,30 +181,17 @@ export type EventBatch = {
|
|
|
181
181
|
events: Event[];
|
|
182
182
|
};
|
|
183
183
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
}
|
|
189
|
-
return hex;
|
|
184
|
+
const textEncoder = new TextEncoder();
|
|
185
|
+
|
|
186
|
+
function utf8ByteLength(value: string): number {
|
|
187
|
+
return textEncoder.encode(value).length;
|
|
190
188
|
}
|
|
191
189
|
|
|
192
|
-
function
|
|
193
|
-
|
|
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;
|
|
190
|
+
function isValidPeerId(peerId: unknown): peerId is string {
|
|
191
|
+
return typeof peerId === "string" && utf8ByteLength(peerId) < 128;
|
|
205
192
|
}
|
|
206
193
|
|
|
207
|
-
function createRandomPeerId():
|
|
194
|
+
function createRandomPeerId(): string {
|
|
208
195
|
const id = new Uint8Array(32);
|
|
209
196
|
if (typeof crypto !== "undefined" && typeof crypto.getRandomValues === "function") {
|
|
210
197
|
crypto.getRandomValues(id);
|
|
@@ -213,17 +200,17 @@ function createRandomPeerId(): Uint8Array {
|
|
|
213
200
|
id[i] = Math.floor(Math.random() * 256);
|
|
214
201
|
}
|
|
215
202
|
}
|
|
216
|
-
return id;
|
|
203
|
+
return Array.from(id, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
217
204
|
}
|
|
218
205
|
|
|
219
|
-
function normalizePeerId(peerId?:
|
|
206
|
+
function normalizePeerId(peerId?: string): string {
|
|
220
207
|
if (peerId === undefined) {
|
|
221
208
|
return createRandomPeerId();
|
|
222
209
|
}
|
|
223
|
-
if (!(peerId
|
|
224
|
-
throw new TypeError("peerId must be a
|
|
210
|
+
if (!isValidPeerId(peerId)) {
|
|
211
|
+
throw new TypeError("peerId must be a UTF-8 string under 128 bytes");
|
|
225
212
|
}
|
|
226
|
-
return
|
|
213
|
+
return peerId;
|
|
227
214
|
}
|
|
228
215
|
|
|
229
216
|
function encodeVersionVector(vv?: VersionVector): RawVersionVector | undefined {
|
|
@@ -235,6 +222,9 @@ function encodeVersionVector(vv?: VersionVector): RawVersionVector | undefined {
|
|
|
235
222
|
if (!entry) {
|
|
236
223
|
continue;
|
|
237
224
|
}
|
|
225
|
+
if (!isValidPeerId(peer)) {
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
238
228
|
const { physicalTime, logicalCounter } = entry;
|
|
239
229
|
if (typeof physicalTime !== "number" || Number.isNaN(physicalTime)) {
|
|
240
230
|
continue;
|
|
@@ -266,6 +256,9 @@ function decodeVersionVector(raw: unknown): VersionVector {
|
|
|
266
256
|
if (!Array.isArray(value) || value.length < 2) {
|
|
267
257
|
continue;
|
|
268
258
|
}
|
|
259
|
+
if (!isValidPeerId(peer)) {
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
269
262
|
const [physicalTime, logicalCounter] = value;
|
|
270
263
|
if (typeof physicalTime !== "number" || !Number.isFinite(physicalTime)) {
|
|
271
264
|
continue;
|
|
@@ -375,22 +368,21 @@ function cloneMetadata(metadata: unknown): MetadataMap | undefined {
|
|
|
375
368
|
}
|
|
376
369
|
|
|
377
370
|
function decodeClock(record: ExportRecord): EntryClock {
|
|
378
|
-
const
|
|
379
|
-
const
|
|
380
|
-
const
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
371
|
+
const rawClock = typeof record.c === "string" ? record.c : "";
|
|
372
|
+
const firstComma = rawClock.indexOf(",");
|
|
373
|
+
const secondComma = firstComma === -1 ? -1 : rawClock.indexOf(",", firstComma + 1);
|
|
374
|
+
if (firstComma === -1 || secondComma === -1) {
|
|
375
|
+
return { physicalTime: 0, logicalCounter: 0, peerId: "" };
|
|
376
|
+
}
|
|
377
|
+
const physicalTime = Number(rawClock.slice(0, firstComma));
|
|
378
|
+
const logicalCounter = Number(rawClock.slice(firstComma + 1, secondComma));
|
|
379
|
+
const peerIdRaw = rawClock.slice(secondComma + 1);
|
|
380
|
+
const peerId = isValidPeerId(peerIdRaw) ? peerIdRaw : "";
|
|
388
381
|
return {
|
|
389
382
|
physicalTime: Number.isFinite(physicalTime) ? physicalTime : 0,
|
|
390
383
|
logicalCounter: Number.isFinite(logicalCounter)
|
|
391
384
|
? Math.trunc(logicalCounter)
|
|
392
385
|
: 0,
|
|
393
|
-
peerIdHex,
|
|
394
386
|
peerId,
|
|
395
387
|
};
|
|
396
388
|
}
|
|
@@ -522,7 +514,8 @@ function isExportOptions(value: unknown): value is ExportOptions {
|
|
|
522
514
|
Object.prototype.hasOwnProperty.call(
|
|
523
515
|
value,
|
|
524
516
|
"pruneTombstonesBefore",
|
|
525
|
-
)
|
|
517
|
+
) ||
|
|
518
|
+
Object.prototype.hasOwnProperty.call(value, "peerId"))
|
|
526
519
|
);
|
|
527
520
|
}
|
|
528
521
|
|
|
@@ -537,7 +530,7 @@ function isImportOptions(value: unknown): value is ImportOptions {
|
|
|
537
530
|
export class Flock {
|
|
538
531
|
private inner: ReturnType<typeof newFlock>;
|
|
539
532
|
|
|
540
|
-
constructor(peerId?:
|
|
533
|
+
constructor(peerId?: string) {
|
|
541
534
|
this.inner = newFlock(normalizePeerId(peerId));
|
|
542
535
|
}
|
|
543
536
|
|
|
@@ -547,8 +540,8 @@ export class Flock {
|
|
|
547
540
|
return flock;
|
|
548
541
|
}
|
|
549
542
|
|
|
550
|
-
static fromJson(bundle: ExportBundle, peerId:
|
|
551
|
-
const inner = from_json_ffi(bundle,
|
|
543
|
+
static fromJson(bundle: ExportBundle, peerId: string): Flock {
|
|
544
|
+
const inner = from_json_ffi(bundle, normalizePeerId(peerId));
|
|
552
545
|
return Flock.fromInner(inner as ReturnType<typeof newFlock>);
|
|
553
546
|
}
|
|
554
547
|
|
|
@@ -560,7 +553,7 @@ export class Flock {
|
|
|
560
553
|
check_invariants_ffi(this.inner);
|
|
561
554
|
}
|
|
562
555
|
|
|
563
|
-
setPeerId(peerId:
|
|
556
|
+
setPeerId(peerId: string): void {
|
|
564
557
|
set_peer_id(this.inner, normalizePeerId(peerId));
|
|
565
558
|
}
|
|
566
559
|
|
|
@@ -652,12 +645,15 @@ export class Flock {
|
|
|
652
645
|
private exportJsonInternal(
|
|
653
646
|
from?: VersionVector,
|
|
654
647
|
pruneTombstonesBefore?: number,
|
|
648
|
+
peerId?: string,
|
|
655
649
|
): ExportBundle {
|
|
656
650
|
const pruneBefore = normalizePruneBefore(pruneTombstonesBefore);
|
|
651
|
+
const normalizedPeerId = peerId !== undefined && isValidPeerId(peerId) ? peerId : undefined;
|
|
657
652
|
return export_json_ffi(
|
|
658
653
|
this.inner,
|
|
659
654
|
encodeVersionVector(from),
|
|
660
655
|
pruneBefore,
|
|
656
|
+
normalizedPeerId,
|
|
661
657
|
) as ExportBundle;
|
|
662
658
|
}
|
|
663
659
|
|
|
@@ -665,6 +661,7 @@ export class Flock {
|
|
|
665
661
|
const base = this.exportJsonInternal(
|
|
666
662
|
options.from,
|
|
667
663
|
options.pruneTombstonesBefore,
|
|
664
|
+
options.peerId,
|
|
668
665
|
);
|
|
669
666
|
const transform = options.hooks?.transform;
|
|
670
667
|
if (!transform) {
|
|
@@ -754,12 +751,15 @@ export class Flock {
|
|
|
754
751
|
return Number(get_max_physical_time_ffi(this.inner));
|
|
755
752
|
}
|
|
756
753
|
|
|
757
|
-
peerId():
|
|
758
|
-
const
|
|
759
|
-
if (typeof
|
|
754
|
+
peerId(): string {
|
|
755
|
+
const id = peer_id_ffi(this.inner);
|
|
756
|
+
if (typeof id !== "string") {
|
|
760
757
|
throw new TypeError("peerId ffi returned unexpected value");
|
|
761
758
|
}
|
|
762
|
-
|
|
759
|
+
if (!isValidPeerId(id)) {
|
|
760
|
+
throw new TypeError("peerId ffi returned an invalid string");
|
|
761
|
+
}
|
|
762
|
+
return id;
|
|
763
763
|
}
|
|
764
764
|
|
|
765
765
|
digest(): string {
|