Skip to content

Commit d5f2460

Browse files
authored
refactor!: Introduce subpath exports and remove server-registry abstraction in loro-adaptors (#25)
* feat(loro-adaptors): Introduce subpath exports and remove server-registry abstraction This commit refactors the `loro-adaptors` package to improve modularity and make peer dependencies truly optional. Previously, importing from `loro-adaptors` would implicitly pull in all peer dependencies, leading to "Could not resolve" errors if a user only intended to use a subset of adaptors (e.g., only `flock` without `loro-crdt`). Additionally, the `server-registry` abstraction was identified as unnecessary. This change addresses these issues by: - Introducing subpath exports for specific adaptor groups: - `loro-adaptors/loro`: Exports `LoroAdaptor`, `LoroEphemeralAdaptor`, `LoroPersistentStoreAdaptor`, `EloAdaptor`, and their server counterparts. This requires `loro-crdt` as a peer dependency. - `loro-adaptors/flock`: Exports `FlockAdaptor` and `FlockServerAdaptor`. This requires `@loro-dev/flock` as a peer dependency. - `loro-adaptors/yjs`: Exports `YjsAwarenessServerAdaptor` for Yjs awareness. This requires `yjs` as a peer dependency. - Refactoring `src/index.ts` to only export general types and core utilities, effectively making it a lean entry point without direct adaptor implementations. - Removing `src/server/server-registry.ts` and the `AdaptorsForServer` interface from `src/types.ts`, as this abstraction was deemed unnecessary. - Updating `package.json` `exports` and `tsdown.config.ts` entry points to reflect the new modular structure. - Updating `README.md` with revised installation instructions and usage examples for the new subpath imports. * chore: update docs
1 parent 3d0dcb5 commit d5f2460

18 files changed

Lines changed: 94 additions & 116 deletions

File tree

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
"clean": "pnpm -r clean",
1313
"format": "prettier --write .",
1414
"format:check": "prettier --check .",
15-
"dev-simple-server": "pnpm --filter loro-websocket exec tsx src/server/bin.ts"
15+
"dev-simple-server": "pnpm --filter loro-websocket exec tsx src/server/bin.ts",
16+
"check": "pnpm lint && pnpm typecheck && pnpm test"
1617
},
1718
"devDependencies": {
1819
"@rslint/core": "^0.1.11",

packages/loro-adaptors/README.md

Lines changed: 50 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,20 @@
11
# loro-adaptors
22

3-
Adaptors that bridge the Loro protocol to `loro-crdt` documents and the ephemeral store. Includes an end‑to‑end encrypted adaptor for %ELO.
3+
Adaptors that bridge the Loro protocol to `loro-crdt` documents, `flock` replicas, and the ephemeral store. Includes an end‑to‑end encrypted adaptor for %ELO.
44

55
## Install
66

77
```bash
8-
pnpm add loro-adaptors loro-protocol loro-crdt
8+
pnpm add loro-adaptors loro-protocol
9+
10+
# If using loro-crdt:
11+
pnpm add loro-crdt
12+
13+
# If using flock:
14+
pnpm add @loro-dev/flock
15+
16+
# If using yjs:
17+
pnpm add yjs
918
```
1019

1120
## Why
@@ -15,18 +24,22 @@ The websocket client (`loro-websocket`) speaks the binary wire protocol. These a
1524
- `LoroAdaptor`: wraps a `LoroDoc` and streams local updates to the connection; applies remote updates on receipt
1625
- `LoroEphemeralAdaptor`: wraps an `EphemeralStore` for transient presence/cursor data
1726
- `LoroPersistentStoreAdaptor`: wraps an `EphemeralStore` but marks updates as persisted so the server stores them for new peers
18-
- `EloLoroAdaptor`: wraps a `LoroDoc` and packages updates into %ELO containers with AES‑GCM; decrypts inbound containers and imports plaintext.
27+
- `EloAdaptor`: wraps a `LoroDoc` and packages updates into %ELO containers with AES‑GCM; decrypts inbound containers and imports plaintext.
28+
- `FlockAdaptor`: wraps a `Flock` replica and streams local updates to the connection; applies remote updates on receipt.
29+
- `YjsAwarenessServerAdaptor`: handles Yjs awareness updates on the server side (opaque blob merging).
1930

2031
## Usage
2132

33+
### Loro
34+
2235
```ts
2336
import { LoroWebsocketClient } from "loro-websocket";
2437
import {
2538
LoroAdaptor,
2639
LoroEphemeralAdaptor,
2740
LoroPersistentStoreAdaptor,
28-
EloLoroAdaptor,
29-
} from "loro-adaptors";
41+
EloAdaptor,
42+
} from "loro-adaptors/loro"; // Import from "loro-adaptors/loro" to avoid pulling in unused peer dependencies
3043
import { LoroDoc, EphemeralStore } from "loro-crdt";
3144

3245
const client = new LoroWebsocketClient({ url: "ws://localhost:8787" });
@@ -53,7 +66,7 @@ const roomPersisted = await client.join({
5366

5467
// %ELO (end‑to‑end encrypted Loro)
5568
const key = new Uint8Array(32);
56-
const elo = new EloLoroAdaptor({
69+
const elo = new EloAdaptor({
5770
getPrivateKey: async () => ({ keyId: "k1", key }),
5871
});
5972
const secure = await client.join({ roomId: "secure-room", crdtAdaptor: elo });
@@ -69,19 +82,39 @@ await roomPersisted.destroy();
6982
await secure.destroy();
7083
```
7184

72-
## API
85+
### Flock
7386

74-
- `new LoroAdaptor(doc?: LoroDoc, config?: { onImportError?, onUpdateError? })`
75-
- `new LoroEphemeralAdaptor(store?: EphemeralStore)`
76-
- `new LoroPersistentStoreAdaptor(store?: EphemeralStore)`
77-
- `new EloLoroAdaptor(docOrConfig: LoroDoc | { getPrivateKey, ivFactory?, onDecryptError?, onUpdateError? })`
78-
- `getPrivateKey: (keyId?) => Promise<{ keyId: string, key: CryptoKey | Uint8Array }>`
79-
- Optional `ivFactory()` for testing (12‑byte IV)
87+
```ts
88+
import { LoroWebsocketClient } from "loro-websocket";
89+
import { FlockAdaptor } from "loro-adaptors/flock"; // Import from "loro-adaptors/flock"
90+
import { Flock } from "@loro-dev/flock";
8091

81-
Notes (E2EE)
92+
const client = new LoroWebsocketClient({ url: "ws://localhost:8787" });
93+
await client.waitConnected();
94+
95+
const flock = new Flock();
96+
const adaptor = new FlockAdaptor(flock);
97+
const room = await client.join({ roomId: "flock-demo", crdtAdaptor: adaptor });
98+
```
99+
100+
### YJS Awareness
101+
102+
```ts
103+
import { YjsAwarenessServerAdaptor } from "loro-adaptors/yjs";
104+
// This is primarily for server-side use or specific awareness integration
105+
```
106+
107+
## API
82108

83-
- IV must be 12 bytes and unique per key. The `ivFactory` is for tests only.
84-
- The server never decrypts; it indexes plaintext headers to select backfill.
109+
- `loro-adaptors/loro`
110+
- `new LoroAdaptor(doc?: LoroDoc, config?: { onImportError?, onUpdateError? })`
111+
- `new LoroEphemeralAdaptor(store?: EphemeralStore)`
112+
- `new LoroPersistentStoreAdaptor(store?: EphemeralStore)`
113+
- `new EloAdaptor(docOrConfig: LoroDoc | { getPrivateKey, ivFactory?, onDecryptError?, onUpdateError? })`
114+
- `loro-adaptors/flock`
115+
- `new FlockAdaptor(flock: Flock, config?: { onImportError?, onUpdateError? })`
116+
- `loro-adaptors/yjs`
117+
- `new YjsAwarenessServerAdaptor()`
85118

86119
## Development
87120

@@ -93,4 +126,4 @@ pnpm typecheck
93126

94127
## License
95128

96-
MIT
129+
MIT

packages/loro-adaptors/package.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,21 @@
6868
"types": "./dist/index.d.ts",
6969
"import": "./dist/index.js",
7070
"require": "./dist/index.cjs"
71+
},
72+
"./flock": {
73+
"types": "./dist/flock.d.ts",
74+
"import": "./dist/flock.js",
75+
"require": "./dist/flock.cjs"
76+
},
77+
"./yjs": {
78+
"types": "./dist/yjs.d.ts",
79+
"import": "./dist/yjs.js",
80+
"require": "./dist/yjs.cjs"
81+
},
82+
"./loro": {
83+
"types": "./dist/loro.d.ts",
84+
"import": "./dist/loro.js",
85+
"require": "./dist/loro.cjs"
7186
}
7287
},
7388
"files": [

packages/loro-adaptors/src/adaptors.ts

Lines changed: 0 additions & 5 deletions
This file was deleted.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from "./flock-adaptor";
2+
export * from "./server/server-flock-adaptor";
Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1 @@
11
export * from "./types";
2-
export * from "./adaptors";
3-
export * from "./server";

packages/loro-adaptors/src/loro.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export * from "./loro-adaptor";
2+
export * from "./loro-ephemeral-adaptor";
3+
export * from "./loro-persistent-store-adaptor";
4+
export * from "./elo-adaptor";
5+
export * from "./server/server-loro-adaptor";
6+
export * from "./server/server-loro-ephemeral-adaptor";
7+
export * from "./server/server-loro-persistent-store-adaptor";
Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
export * from "./server-registry";
21
export * from "./server-loro-adaptor";
32
export * from "./server-loro-ephemeral-adaptor";
43
export * from "./server-loro-persistent-store-adaptor";
5-
export * from "./server-yjs-awareness-adaptor";
64
export * from "./server-flock-adaptor";

packages/loro-adaptors/src/server/server-registry.ts

Lines changed: 0 additions & 50 deletions
This file was deleted.

packages/loro-adaptors/src/types.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -81,11 +81,3 @@ export interface CrdtServerAdaptor {
8181

8282
merge(documents: Uint8Array[]): Uint8Array;
8383
}
84-
85-
export interface AdaptorsForServer {
86-
register(adaptor: CrdtServerAdaptor): void;
87-
registerMany(adaptors: Iterable<CrdtServerAdaptor>): void;
88-
get(crdtType: CrdtType): CrdtServerAdaptor | undefined;
89-
clear(): void;
90-
list(): CrdtServerAdaptor[];
91-
}

0 commit comments

Comments
 (0)