Skip to content

Commit 1819656

Browse files
committed
feat!: adopt flock 4 version encoding (#39)
1 parent c2eade3 commit 1819656

File tree

7 files changed

+113
-182
lines changed

7 files changed

+113
-182
lines changed

packages/loro-adaptors/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Changelog
22

3+
## Unreleased
4+
5+
### ⚠ BREAKING CHANGES
6+
7+
* Flock adaptors now encode version vectors using `@loro-dev/flock`'s binary `encodeVersionVector`; version payloads are no longer JSON.
8+
39
## [0.5.0](https://github.com/loro-dev/protocol/compare/loro-adaptors-v0.4.2...loro-adaptors-v0.5.0) (2025-12-07)
410

511

packages/loro-adaptors/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
"loro-protocol": "workspace:*"
1616
},
1717
"peerDependencies": {
18-
"@loro-dev/flock": "^3.1.0",
18+
"@loro-dev/flock": "^4.0.0",
1919
"loro-crdt": "^1.9.0",
2020
"yjs": "*"
2121
},

packages/loro-adaptors/src/flock-adaptor.ts

Lines changed: 9 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Flock } from "@loro-dev/flock";
1+
import { Flock, decodeVersionVector, encodeVersionVector } from "@loro-dev/flock";
22
import {
33
CrdtType,
44
JoinResponseOk,
@@ -11,42 +11,6 @@ type FlockExportBundle = Awaited<ReturnType<Flock["exportJson"]>>;
1111
const encoder = new TextEncoder();
1212
const decoder = new TextDecoder();
1313

14-
function serializeVersion(version: FlockVersion | undefined): Uint8Array {
15-
return encoder.encode(JSON.stringify(version ?? {}));
16-
}
17-
18-
function deserializeVersion(bytes: Uint8Array): FlockVersion {
19-
if (!bytes.length) return {};
20-
try {
21-
const parsed = JSON.parse(decoder.decode(bytes));
22-
if (!parsed || typeof parsed !== "object") return {};
23-
const next: FlockVersion = {};
24-
for (const [key, value] of Object.entries(
25-
parsed as Record<string, unknown>
26-
)) {
27-
if (!value || typeof value !== "object") continue;
28-
const entry = value as {
29-
logicalCounter?: unknown;
30-
physicalTime?: unknown;
31-
};
32-
const logicalCounter =
33-
typeof entry.logicalCounter === "number" &&
34-
Number.isFinite(entry.logicalCounter)
35-
? Math.trunc(entry.logicalCounter)
36-
: 0;
37-
const physicalTime =
38-
typeof entry.physicalTime === "number" &&
39-
Number.isFinite(entry.physicalTime)
40-
? entry.physicalTime
41-
: 0;
42-
next[key] = { logicalCounter, physicalTime };
43-
}
44-
return next;
45-
} catch {
46-
return {};
47-
}
48-
}
49-
5014
function compareVersions(
5115
a: FlockVersion,
5216
b: FlockVersion
@@ -145,8 +109,12 @@ export class FlockAdaptor implements CrdtDocAdaptor {
145109
}
146110

147111
cmpVersion(versionBytes: Uint8Array): 0 | 1 | -1 | undefined {
148-
const remote = deserializeVersion(versionBytes);
149-
return compareVersions(this.flock.version(), remote);
112+
try {
113+
const remote = decodeVersionVector(versionBytes);
114+
return compareVersions(this.flock.version(), remote);
115+
} catch {
116+
return undefined;
117+
}
150118
}
151119

152120
setCtx(ctx: CrdtAdaptorContext): void {
@@ -167,7 +135,7 @@ export class FlockAdaptor implements CrdtDocAdaptor {
167135
}
168136

169137
getVersion(): Uint8Array {
170-
return serializeVersion(this.flock.version());
138+
return encodeVersionVector(this.flock.version());
171139
}
172140

173141
getAlternativeVersion(): Uint8Array | undefined {
@@ -185,7 +153,7 @@ export class FlockAdaptor implements CrdtDocAdaptor {
185153
async handleJoinOk(res: JoinResponseOk): Promise<void> {
186154
if (this.destroyed) return;
187155
try {
188-
const serverVersion = deserializeVersion(res.version);
156+
const serverVersion = decodeVersionVector(res.version);
189157
this.initServerVersion = serverVersion;
190158
const comparison = compareVersions(this.flock.version(), serverVersion);
191159
if (comparison != null && comparison >= 0) {

packages/loro-adaptors/src/server/server-flock-adaptor.ts

Lines changed: 4 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Flock } from "@loro-dev/flock";
1+
import { Flock, decodeVersionVector, encodeVersionVector } from "@loro-dev/flock";
22
import type {
33
ExportBundle as FlockExportBundle,
44
VersionVector as FlockVersion,
@@ -31,45 +31,6 @@ function deserializeBundle(bytes: Uint8Array): FlockExportBundle {
3131
return { version: 0, entries: {} };
3232
}
3333

34-
function serializeVersion(version: FlockVersion | undefined): Uint8Array {
35-
return encoder.encode(JSON.stringify(version ?? {}));
36-
}
37-
38-
function deserializeVersion(bytes: Uint8Array): FlockVersion {
39-
if (!bytes.length) {
40-
return {};
41-
}
42-
try {
43-
const parsed = JSON.parse(decoder.decode(bytes));
44-
if (!parsed || typeof parsed !== "object") {
45-
return {};
46-
}
47-
const next: FlockVersion = {};
48-
for (const [peer, value] of Object.entries(
49-
parsed as Record<string, unknown>,
50-
)) {
51-
if (!value || typeof value !== "object") {
52-
continue;
53-
}
54-
const entry = value as { logicalCounter?: unknown; physicalTime?: unknown };
55-
const logicalCounter =
56-
typeof entry.logicalCounter === "number" &&
57-
Number.isFinite(entry.logicalCounter)
58-
? Math.trunc(entry.logicalCounter)
59-
: 0;
60-
const physicalTime =
61-
typeof entry.physicalTime === "number" &&
62-
Number.isFinite(entry.physicalTime)
63-
? entry.physicalTime
64-
: 0;
65-
next[peer] = { logicalCounter, physicalTime };
66-
}
67-
return next;
68-
} catch {
69-
return {};
70-
}
71-
}
72-
7334
function importSnapshot(flock: Flock, data: Uint8Array): void {
7435
if (!data.length) return;
7536
const bundle = deserializeBundle(data);
@@ -112,11 +73,11 @@ export class FlockServerAdaptor implements CrdtServerAdaptor {
11273
const flock = new Flock();
11374
importSnapshot(flock, documentData);
11475

115-
const serverVersion = serializeVersion(flock.version());
76+
const serverVersion = encodeVersionVector(flock.version());
11677
let updates: Uint8Array[] | undefined;
11778

11879
if (clientVersion.length > 0) {
119-
const clientVV = deserializeVersion(clientVersion);
80+
const clientVV = decodeVersionVector(clientVersion);
12081
const delta = exportBundle(flock, clientVV);
12182
updates = [serializeBundle(delta)];
12283
} else if (documentData.length > 0) {
@@ -167,7 +128,7 @@ export class FlockServerAdaptor implements CrdtServerAdaptor {
167128
getVersion(documentData: Uint8Array): Uint8Array {
168129
const flock = new Flock();
169130
importSnapshot(flock, documentData);
170-
return serializeVersion(flock.version());
131+
return encodeVersionVector(flock.version());
171132
}
172133

173134
merge(documents: Uint8Array[]): Uint8Array {

packages/loro-websocket/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Changelog
22

3+
## Unreleased
4+
5+
### ⚠ BREAKING CHANGES
6+
7+
* Flock client/server adaptors now emit binary-encoded version vectors using `@loro-dev/flock`'s `encodeVersionVector`; prior JSON encoding is no longer supported.
8+
39
## [0.5.0](https://github.com/loro-dev/protocol/compare/loro-websocket-v0.4.3...loro-websocket-v0.5.0) (2025-12-07)
410

511

packages/loro-websocket/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@
7272
"ws": "^8.18.3"
7373
},
7474
"devDependencies": {
75-
"@loro-dev/flock": "^3.1.0",
75+
"@loro-dev/flock": "^4.0.0",
7676
"get-port": "^7.1.0",
7777
"tsdown": "^0.14.1",
7878
"tsx": "^4.20.5",

0 commit comments

Comments
 (0)