@defensestation/ysync-go 0.1.0-dev.3.3d87b03
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 +101 -0
- package/dist/client/connect-yjs-provider.d.ts +107 -0
- package/dist/client/connect-yjs-provider.d.ts.map +1 -0
- package/dist/client/connect-yjs-provider.js +333 -0
- package/dist/client/connect-yjs-provider.js.map +1 -0
- package/dist/client/id.d.ts +2 -0
- package/dist/client/id.d.ts.map +1 -0
- package/dist/client/id.js +21 -0
- package/dist/client/id.js.map +1 -0
- package/dist/client/provider-status.d.ts +8 -0
- package/dist/client/provider-status.d.ts.map +1 -0
- package/dist/client/provider-status.js +2 -0
- package/dist/client/provider-status.js.map +1 -0
- package/dist/client/websocket-yjs-provider.d.ts +51 -0
- package/dist/client/websocket-yjs-provider.d.ts.map +1 -0
- package/dist/client/websocket-yjs-provider.js +270 -0
- package/dist/client/websocket-yjs-provider.js.map +1 -0
- package/dist/gen/ysync/v1/sync_pb.d.ts +630 -0
- package/dist/gen/ysync/v1/sync_pb.d.ts.map +1 -0
- package/dist/gen/ysync/v1/sync_pb.js +133 -0
- package/dist/gen/ysync/v1/sync_pb.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/package.json +64 -0
- package/src/client/connect-yjs-provider.ts +358 -0
- package/src/client/id.ts +22 -0
- package/src/client/provider-status.ts +9 -0
- package/src/client/websocket-yjs-provider.ts +335 -0
- package/src/gen/ysync/v1/sync_pb.ts +732 -0
- package/src/index.ts +7 -0
package/README.md
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# @defensestation/ysync-go
|
|
2
|
+
|
|
3
|
+
Browser [Yjs](https://yjs.dev) providers for the
|
|
4
|
+
[ysync-go](https://github.com/defensestation/ysync-go) collaboration backend.
|
|
5
|
+
Two interchangeable providers - pick per environment, mix freely: a
|
|
6
|
+
WebSocket client and a Connect RPC client collaborate live on the same
|
|
7
|
+
document.
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @defensestation/ysync-go yjs
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick start
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import * as Y from "yjs";
|
|
17
|
+
import { WebSocketYjsProvider } from "@defensestation/ysync-go";
|
|
18
|
+
|
|
19
|
+
const ydoc = new Y.Doc();
|
|
20
|
+
const provider = new WebSocketYjsProvider({
|
|
21
|
+
ydoc,
|
|
22
|
+
docId: "doc-1",
|
|
23
|
+
baseUrl: "https://collab.example.com",
|
|
24
|
+
user: { name: "Ada", color: "#4338ca" },
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// Bind ydoc to your editor (BlockNote, Tiptap, y-prosemirror, y-codemirror…)
|
|
28
|
+
// and provider.awareness to its cursor/presence plugin.
|
|
29
|
+
|
|
30
|
+
provider.onStatus((status) => console.log(status)); // connecting | connected | disconnected
|
|
31
|
+
provider.destroy(); // on unmount: announces departure, closes cleanly
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Or over Connect RPC (unary + server-streaming; works everywhere `fetch`
|
|
35
|
+
streams responses):
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
import { ConnectYjsProvider } from "@defensestation/ysync-go";
|
|
39
|
+
|
|
40
|
+
const provider = new ConnectYjsProvider({
|
|
41
|
+
ydoc,
|
|
42
|
+
docId: "doc-1",
|
|
43
|
+
baseUrl: "https://collab.example.com",
|
|
44
|
+
user: { name: "Ada" },
|
|
45
|
+
});
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Choosing a provider
|
|
49
|
+
|
|
50
|
+
| | `WebSocketYjsProvider` | `ConnectYjsProvider` |
|
|
51
|
+
| --- | --- | --- |
|
|
52
|
+
| Wire | JSON frames over `/ysync/ws` | `ysync.v1.YjsSyncService` (Connect) |
|
|
53
|
+
| Edits upstream | one message per update | micro-batched unary pushes with idempotency keys |
|
|
54
|
+
| Best when | plain WebSockets fit your infra | you already run Connect/gRPC middleware (auth interceptors, gateways) |
|
|
55
|
+
|
|
56
|
+
Both reconnect automatically with exponential backoff (500 ms → 5 s),
|
|
57
|
+
re-join with the document's state vector, and resync - offline edits made
|
|
58
|
+
while disconnected are delivered on reconnect (Yjs deduplicates replays).
|
|
59
|
+
|
|
60
|
+
## Common API (both providers)
|
|
61
|
+
|
|
62
|
+
| Member | Description |
|
|
63
|
+
| --- | --- |
|
|
64
|
+
| `awareness` | The `y-protocols` `Awareness` instance - hand it to your editor's cursor plugin. The local state's `user` field is prefilled from `options.user`. |
|
|
65
|
+
| `clientId` | Stable per-provider ID (UUID unless you pass `clientId`). |
|
|
66
|
+
| `status` / `onStatus(fn)` | `"connecting" \| "connected" \| "disconnected"`; returns an unsubscribe function. |
|
|
67
|
+
| `identity` / `onIdentity(fn)` | Server-assigned presence identity (sanitized `name`, palette `color` distinct among participants) - use it for cursor rendering instead of trusting local input. |
|
|
68
|
+
| `destroy()` | Detaches from the doc, announces departure so peers drop the cursor immediately, and closes the connection. |
|
|
69
|
+
|
|
70
|
+
### Options
|
|
71
|
+
|
|
72
|
+
Shared: `ydoc`, `docId`, `user: { name, color? }`, `baseUrl?` (defaults to
|
|
73
|
+
`window.location.origin`), `clientId?`, `reconnectInitialDelayMs?`,
|
|
74
|
+
`reconnectMaxDelayMs?`, `awarenessThrottleMs?` (default 150 ms,
|
|
75
|
+
leading+trailing throttle for cursor moves).
|
|
76
|
+
|
|
77
|
+
`ConnectYjsProvider` only: `updateFlushDelayMs?` (default 100 ms) -
|
|
78
|
+
micro-batch window merging keystrokes into one push while a request is in
|
|
79
|
+
flight; retries resend the identical payload under the same idempotency
|
|
80
|
+
key. `rpcClient?` lets you supply your own Connect client (e.g. with auth
|
|
81
|
+
interceptors); the default client forwards `user.name`/`user.color` as
|
|
82
|
+
`x-user-*` headers, which the server treats as unauthenticated hints.
|
|
83
|
+
|
|
84
|
+
## Server
|
|
85
|
+
|
|
86
|
+
This package is the client half. The server is a Go library -
|
|
87
|
+
[github.com/defensestation/ysync-go](https://github.com/defensestation/ysync-go) - with
|
|
88
|
+
Postgres persistence, Redis multi-node fanout, and auth hooks. Clone the
|
|
89
|
+
repo and `go run ./examples/minimal -addr :8080` for a complete local
|
|
90
|
+
server.
|
|
91
|
+
|
|
92
|
+
The full wire protocol (for writing clients in other languages) is
|
|
93
|
+
documented in
|
|
94
|
+
[docs/protocol.md](https://github.com/defensestation/ysync-go/blob/main/docs/protocol.md).
|
|
95
|
+
|
|
96
|
+
Generated protobuf descriptors are exported under `@defensestation/ysync-go/ysync/v1`
|
|
97
|
+
if you need raw RPC access.
|
|
98
|
+
|
|
99
|
+
## License
|
|
100
|
+
|
|
101
|
+
MIT
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { Awareness } from "y-protocols/awareness";
|
|
2
|
+
import * as Y from "yjs";
|
|
3
|
+
import type { YjsProviderIdentity, YjsProviderIdentityListener, YjsProviderStatus, YjsProviderStatusListener } from "./provider-status.js";
|
|
4
|
+
declare function createDefaultClient(baseUrl: string, user?: {
|
|
5
|
+
name: string;
|
|
6
|
+
color?: string;
|
|
7
|
+
}): import("@connectrpc/connect").Client<import("@bufbuild/protobuf/codegenv2").GenService<{
|
|
8
|
+
sync: {
|
|
9
|
+
methodKind: "bidi_streaming";
|
|
10
|
+
input: typeof import("../gen/ysync/v1/sync_pb.js").SyncRequestSchema;
|
|
11
|
+
output: typeof import("../gen/ysync/v1/sync_pb.js").SyncResponseSchema;
|
|
12
|
+
};
|
|
13
|
+
joinDocument: {
|
|
14
|
+
methodKind: "unary";
|
|
15
|
+
input: typeof import("../gen/ysync/v1/sync_pb.js").JoinDocumentRequestSchema;
|
|
16
|
+
output: typeof import("../gen/ysync/v1/sync_pb.js").JoinDocumentResponseSchema;
|
|
17
|
+
};
|
|
18
|
+
pushYjsUpdate: {
|
|
19
|
+
methodKind: "unary";
|
|
20
|
+
input: typeof import("../gen/ysync/v1/sync_pb.js").PushYjsUpdateRequestSchema;
|
|
21
|
+
output: typeof import("../gen/ysync/v1/sync_pb.js").PushYjsUpdateResponseSchema;
|
|
22
|
+
};
|
|
23
|
+
pushAwarenessUpdate: {
|
|
24
|
+
methodKind: "unary";
|
|
25
|
+
input: typeof import("../gen/ysync/v1/sync_pb.js").PushAwarenessUpdateRequestSchema;
|
|
26
|
+
output: typeof import("../gen/ysync/v1/sync_pb.js").PushAwarenessUpdateResponseSchema;
|
|
27
|
+
};
|
|
28
|
+
streamDocumentFrames: {
|
|
29
|
+
methodKind: "server_streaming";
|
|
30
|
+
input: typeof import("../gen/ysync/v1/sync_pb.js").StreamDocumentFramesRequestSchema;
|
|
31
|
+
output: typeof import("../gen/ysync/v1/sync_pb.js").StreamDocumentFramesResponseSchema;
|
|
32
|
+
};
|
|
33
|
+
leaveDocument: {
|
|
34
|
+
methodKind: "unary";
|
|
35
|
+
input: typeof import("../gen/ysync/v1/sync_pb.js").LeaveDocumentRequestSchema;
|
|
36
|
+
output: typeof import("../gen/ysync/v1/sync_pb.js").LeaveDocumentResponseSchema;
|
|
37
|
+
};
|
|
38
|
+
listDocumentUpdates: {
|
|
39
|
+
methodKind: "unary";
|
|
40
|
+
input: typeof import("../gen/ysync/v1/sync_pb.js").ListDocumentUpdatesRequestSchema;
|
|
41
|
+
output: typeof import("../gen/ysync/v1/sync_pb.js").ListDocumentUpdatesResponseSchema;
|
|
42
|
+
};
|
|
43
|
+
compactDocument: {
|
|
44
|
+
methodKind: "unary";
|
|
45
|
+
input: typeof import("../gen/ysync/v1/sync_pb.js").CompactDocumentRequestSchema;
|
|
46
|
+
output: typeof import("../gen/ysync/v1/sync_pb.js").CompactDocumentResponseSchema;
|
|
47
|
+
};
|
|
48
|
+
}>>;
|
|
49
|
+
export type YjsSyncRpcClient = ReturnType<typeof createDefaultClient>;
|
|
50
|
+
export interface ConnectYjsProviderOptions {
|
|
51
|
+
ydoc: Y.Doc;
|
|
52
|
+
docId: string;
|
|
53
|
+
baseUrl?: string;
|
|
54
|
+
rpcClient?: YjsSyncRpcClient;
|
|
55
|
+
clientId?: string;
|
|
56
|
+
user: {
|
|
57
|
+
name: string;
|
|
58
|
+
color?: string;
|
|
59
|
+
};
|
|
60
|
+
reconnectInitialDelayMs?: number;
|
|
61
|
+
reconnectMaxDelayMs?: number;
|
|
62
|
+
awarenessThrottleMs?: number;
|
|
63
|
+
updateFlushDelayMs?: number;
|
|
64
|
+
}
|
|
65
|
+
export declare class ConnectYjsProvider {
|
|
66
|
+
readonly awareness: Awareness;
|
|
67
|
+
readonly clientId: string;
|
|
68
|
+
private readonly client;
|
|
69
|
+
private readonly options;
|
|
70
|
+
private abortController?;
|
|
71
|
+
private reconnectTimer?;
|
|
72
|
+
private awarenessSendTimer?;
|
|
73
|
+
private awarenessDirty;
|
|
74
|
+
private updateFlushTimer?;
|
|
75
|
+
private updatesDirty;
|
|
76
|
+
private destroyed;
|
|
77
|
+
private joined;
|
|
78
|
+
private reconnectDelayMs;
|
|
79
|
+
private pendingUpdates;
|
|
80
|
+
private inFlightBatch?;
|
|
81
|
+
private pushInFlight;
|
|
82
|
+
private updateCounter;
|
|
83
|
+
private currentStatus;
|
|
84
|
+
private readonly statusListeners;
|
|
85
|
+
private currentIdentity?;
|
|
86
|
+
private readonly identityListeners;
|
|
87
|
+
private readonly yjsUpdateHandler;
|
|
88
|
+
private readonly awarenessUpdateHandler;
|
|
89
|
+
constructor(options: ConnectYjsProviderOptions);
|
|
90
|
+
get status(): YjsProviderStatus;
|
|
91
|
+
get identity(): YjsProviderIdentity | undefined;
|
|
92
|
+
onIdentity(listener: YjsProviderIdentityListener): () => void;
|
|
93
|
+
private applyIdentity;
|
|
94
|
+
onStatus(listener: YjsProviderStatusListener): () => void;
|
|
95
|
+
private setStatus;
|
|
96
|
+
destroy(): void;
|
|
97
|
+
private connect;
|
|
98
|
+
private runSession;
|
|
99
|
+
private scheduleReconnect;
|
|
100
|
+
private scheduleUpdateFlush;
|
|
101
|
+
private flushUpdates;
|
|
102
|
+
private scheduleAwarenessSend;
|
|
103
|
+
private sendLocalAwarenessNow;
|
|
104
|
+
private applyFrame;
|
|
105
|
+
}
|
|
106
|
+
export {};
|
|
107
|
+
//# sourceMappingURL=connect-yjs-provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connect-yjs-provider.d.ts","sourceRoot":"","sources":["../../src/client/connect-yjs-provider.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAA+C,MAAM,uBAAuB,CAAC;AAC/F,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC;AAGzB,OAAO,KAAK,EAAE,mBAAmB,EAAE,2BAA2B,EAAE,iBAAiB,EAAE,yBAAyB,EAAE,MAAM,sBAAsB,CAAC;AAY3I,iBAAS,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAapF;AAED,MAAM,MAAM,gBAAgB,GAAG,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEtE,MAAM,WAAW,yBAAyB;IACxC,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAC7B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACvC,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAI7B,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAQD,qBAAa,kBAAkB;IAC7B,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAC9B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAE1B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAmB;IAC1C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA0K;IAClM,OAAO,CAAC,eAAe,CAAC,CAAkB;IAC1C,OAAO,CAAC,cAAc,CAAC,CAAgC;IACvD,OAAO,CAAC,kBAAkB,CAAC,CAAgC;IAC3D,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,gBAAgB,CAAC,CAAgC;IACzD,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,gBAAgB,CAAS;IACjC,OAAO,CAAC,cAAc,CAAoB;IAI1C,OAAO,CAAC,aAAa,CAAC,CAAsC;IAC5D,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,aAAa,CAAmC;IACxD,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAwC;IACxE,OAAO,CAAC,eAAe,CAAC,CAAsB;IAC9C,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAA0C;IAE5E,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAI/B;IAKF,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAIrC;gBAEU,OAAO,EAAE,yBAAyB;IAsB9C,IAAI,MAAM,IAAI,iBAAiB,CAE9B;IAGD,IAAI,QAAQ,IAAI,mBAAmB,GAAG,SAAS,CAE9C;IAGD,UAAU,CAAC,QAAQ,EAAE,2BAA2B,GAAG,MAAM,IAAI;IAK7D,OAAO,CAAC,aAAa;IAYrB,QAAQ,CAAC,QAAQ,EAAE,yBAAyB,GAAG,MAAM,IAAI;IAKzD,OAAO,CAAC,SAAS;IAMjB,OAAO;IA2BP,OAAO,CAAC,OAAO;YAQD,UAAU;IA8CxB,OAAO,CAAC,iBAAiB;IAWzB,OAAO,CAAC,mBAAmB;YAoBb,YAAY;IAuC1B,OAAO,CAAC,qBAAqB;IAgB7B,OAAO,CAAC,qBAAqB;IAW7B,OAAO,CAAC,UAAU;CAenB"}
|
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
import { Code, ConnectError, createClient } from "@connectrpc/connect";
|
|
2
|
+
import { createConnectTransport } from "@connectrpc/connect-web";
|
|
3
|
+
import { Awareness, applyAwarenessUpdate, encodeAwarenessUpdate } from "y-protocols/awareness";
|
|
4
|
+
import * as Y from "yjs";
|
|
5
|
+
import { YjsSyncService } from "../gen/ysync/v1/sync_pb.js";
|
|
6
|
+
import { makeClientId } from "./id.js";
|
|
7
|
+
const REMOTE_ORIGIN = "connectrpc-remote";
|
|
8
|
+
const DEFAULT_RECONNECT_INITIAL_DELAY_MS = 500;
|
|
9
|
+
const DEFAULT_RECONNECT_MAX_DELAY_MS = 5000;
|
|
10
|
+
const DEFAULT_AWARENESS_THROTTLE_MS = 150;
|
|
11
|
+
const DEFAULT_UPDATE_FLUSH_DELAY_MS = 100;
|
|
12
|
+
function defaultBaseUrl() {
|
|
13
|
+
return typeof window !== "undefined" && window.location?.origin ? window.location.origin : "http://localhost:8080";
|
|
14
|
+
}
|
|
15
|
+
function createDefaultClient(baseUrl, user) {
|
|
16
|
+
const interceptors = [];
|
|
17
|
+
if (user) {
|
|
18
|
+
// Actor identity for audit history; the server reads these headers on
|
|
19
|
+
// every RPC. Real deployments should derive identity from auth instead.
|
|
20
|
+
const asciiOnly = (value) => /^[\x20-\x7E]*$/.test(value);
|
|
21
|
+
interceptors.push((next) => (req) => {
|
|
22
|
+
if (asciiOnly(user.name))
|
|
23
|
+
req.header.set("x-user-name", user.name);
|
|
24
|
+
if (user.color && asciiOnly(user.color))
|
|
25
|
+
req.header.set("x-user-color", user.color);
|
|
26
|
+
return next(req);
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
return createClient(YjsSyncService, createConnectTransport({ baseUrl, useBinaryFormat: true, interceptors }));
|
|
30
|
+
}
|
|
31
|
+
// Browser-compatible Connect RPC provider. Browsers cannot open bidirectional
|
|
32
|
+
// Connect streams (fetch has no streaming request bodies), so this provider
|
|
33
|
+
// uses the unary/server-stream workflow from the proto contract:
|
|
34
|
+
// JoinDocument -> StreamDocumentFrames + PushYjsUpdate/PushAwarenessUpdate,
|
|
35
|
+
// then LeaveDocument on teardown. The bidirectional Sync RPC remains available
|
|
36
|
+
// for clients on transports that support it (e.g. connect-go over HTTP/2).
|
|
37
|
+
export class ConnectYjsProvider {
|
|
38
|
+
awareness;
|
|
39
|
+
clientId;
|
|
40
|
+
client;
|
|
41
|
+
options;
|
|
42
|
+
abortController;
|
|
43
|
+
reconnectTimer;
|
|
44
|
+
awarenessSendTimer;
|
|
45
|
+
awarenessDirty = false;
|
|
46
|
+
updateFlushTimer;
|
|
47
|
+
updatesDirty = false;
|
|
48
|
+
destroyed = false;
|
|
49
|
+
joined = false;
|
|
50
|
+
reconnectDelayMs;
|
|
51
|
+
pendingUpdates = [];
|
|
52
|
+
// The current batch is frozen with its idempotency key so a retry after a
|
|
53
|
+
// failed push resends the exact same payload; updates queued in the
|
|
54
|
+
// meantime merge into the NEXT batch.
|
|
55
|
+
inFlightBatch;
|
|
56
|
+
pushInFlight = false;
|
|
57
|
+
updateCounter = 0;
|
|
58
|
+
currentStatus = "connecting";
|
|
59
|
+
statusListeners = new Set();
|
|
60
|
+
currentIdentity;
|
|
61
|
+
identityListeners = new Set();
|
|
62
|
+
yjsUpdateHandler = (update, origin) => {
|
|
63
|
+
if (origin === REMOTE_ORIGIN)
|
|
64
|
+
return;
|
|
65
|
+
this.pendingUpdates.push(update);
|
|
66
|
+
this.scheduleUpdateFlush();
|
|
67
|
+
};
|
|
68
|
+
// Only push changes to the LOCAL awareness state. Remote states arrive from
|
|
69
|
+
// the server already fanned out; re-broadcasting them would multiply every
|
|
70
|
+
// cursor move by the number of connected clients.
|
|
71
|
+
awarenessUpdateHandler = ({ added, updated, removed }) => {
|
|
72
|
+
const local = this.awareness.clientID;
|
|
73
|
+
if (!added.includes(local) && !updated.includes(local) && !removed.includes(local))
|
|
74
|
+
return;
|
|
75
|
+
this.scheduleAwarenessSend();
|
|
76
|
+
};
|
|
77
|
+
constructor(options) {
|
|
78
|
+
this.options = {
|
|
79
|
+
...options,
|
|
80
|
+
reconnectInitialDelayMs: options.reconnectInitialDelayMs ?? DEFAULT_RECONNECT_INITIAL_DELAY_MS,
|
|
81
|
+
reconnectMaxDelayMs: options.reconnectMaxDelayMs ?? DEFAULT_RECONNECT_MAX_DELAY_MS,
|
|
82
|
+
awarenessThrottleMs: options.awarenessThrottleMs ?? DEFAULT_AWARENESS_THROTTLE_MS,
|
|
83
|
+
updateFlushDelayMs: options.updateFlushDelayMs ?? DEFAULT_UPDATE_FLUSH_DELAY_MS,
|
|
84
|
+
};
|
|
85
|
+
this.clientId = options.clientId ?? makeClientId();
|
|
86
|
+
this.client = options.rpcClient ?? createDefaultClient(options.baseUrl ?? defaultBaseUrl(), options.user);
|
|
87
|
+
this.reconnectDelayMs = this.options.reconnectInitialDelayMs;
|
|
88
|
+
this.awareness = new Awareness(options.ydoc);
|
|
89
|
+
this.awareness.setLocalStateField("user", { name: options.user.name, color: options.user.color });
|
|
90
|
+
this.options.ydoc.on("update", this.yjsUpdateHandler);
|
|
91
|
+
// No extra heartbeat timer: y-protocols renews the local awareness state
|
|
92
|
+
// (bumping its clock) roughly every 15s while idle, which fires this
|
|
93
|
+
// handler. A fixed-interval resend of the same clock would be ignored by
|
|
94
|
+
// receivers anyway.
|
|
95
|
+
this.awareness.on("update", this.awarenessUpdateHandler);
|
|
96
|
+
this.connect();
|
|
97
|
+
}
|
|
98
|
+
get status() {
|
|
99
|
+
return this.currentStatus;
|
|
100
|
+
}
|
|
101
|
+
// Server-assigned presence identity; undefined until the join completes.
|
|
102
|
+
get identity() {
|
|
103
|
+
return this.currentIdentity;
|
|
104
|
+
}
|
|
105
|
+
// Returns an unsubscribe function.
|
|
106
|
+
onIdentity(listener) {
|
|
107
|
+
this.identityListeners.add(listener);
|
|
108
|
+
return () => this.identityListeners.delete(listener);
|
|
109
|
+
}
|
|
110
|
+
applyIdentity(name, color) {
|
|
111
|
+
if (!name && !color)
|
|
112
|
+
return;
|
|
113
|
+
const identity = {
|
|
114
|
+
name: name || this.options.user.name,
|
|
115
|
+
color: color ?? this.options.user.color,
|
|
116
|
+
};
|
|
117
|
+
this.currentIdentity = identity;
|
|
118
|
+
this.awareness.setLocalStateField("user", { name: identity.name, color: identity.color });
|
|
119
|
+
for (const listener of this.identityListeners)
|
|
120
|
+
listener(identity);
|
|
121
|
+
}
|
|
122
|
+
// Returns an unsubscribe function.
|
|
123
|
+
onStatus(listener) {
|
|
124
|
+
this.statusListeners.add(listener);
|
|
125
|
+
return () => this.statusListeners.delete(listener);
|
|
126
|
+
}
|
|
127
|
+
setStatus(status) {
|
|
128
|
+
if (this.currentStatus === status)
|
|
129
|
+
return;
|
|
130
|
+
this.currentStatus = status;
|
|
131
|
+
for (const listener of this.statusListeners)
|
|
132
|
+
listener(status);
|
|
133
|
+
}
|
|
134
|
+
destroy() {
|
|
135
|
+
if (this.destroyed)
|
|
136
|
+
return;
|
|
137
|
+
this.options.ydoc.off("update", this.yjsUpdateHandler);
|
|
138
|
+
this.awareness.off("update", this.awarenessUpdateHandler);
|
|
139
|
+
// Announce departure so peers drop the cursor immediately instead of
|
|
140
|
+
// after the 30s awareness timeout.
|
|
141
|
+
this.awareness.setLocalState(null);
|
|
142
|
+
if (this.joined) {
|
|
143
|
+
void this.client
|
|
144
|
+
.pushAwarenessUpdate({
|
|
145
|
+
docId: this.options.docId,
|
|
146
|
+
clientId: this.clientId,
|
|
147
|
+
awarenessUpdate: encodeAwarenessUpdate(this.awareness, [this.awareness.clientID]),
|
|
148
|
+
})
|
|
149
|
+
.catch(() => undefined);
|
|
150
|
+
}
|
|
151
|
+
this.destroyed = true;
|
|
152
|
+
this.setStatus("disconnected");
|
|
153
|
+
if (this.reconnectTimer)
|
|
154
|
+
clearTimeout(this.reconnectTimer);
|
|
155
|
+
if (this.awarenessSendTimer)
|
|
156
|
+
clearTimeout(this.awarenessSendTimer);
|
|
157
|
+
if (this.updateFlushTimer)
|
|
158
|
+
clearTimeout(this.updateFlushTimer);
|
|
159
|
+
void this.client
|
|
160
|
+
.leaveDocument({ docId: this.options.docId, clientId: this.clientId })
|
|
161
|
+
.catch(() => undefined);
|
|
162
|
+
this.abortController?.abort();
|
|
163
|
+
}
|
|
164
|
+
connect() {
|
|
165
|
+
if (this.destroyed)
|
|
166
|
+
return;
|
|
167
|
+
this.abortController?.abort();
|
|
168
|
+
this.abortController = new AbortController();
|
|
169
|
+
this.setStatus("connecting");
|
|
170
|
+
void this.runSession(this.abortController.signal);
|
|
171
|
+
}
|
|
172
|
+
async runSession(signal) {
|
|
173
|
+
try {
|
|
174
|
+
const join = await this.client.joinDocument({
|
|
175
|
+
docId: this.options.docId,
|
|
176
|
+
clientId: this.clientId,
|
|
177
|
+
stateVector: Y.encodeStateVector(this.options.ydoc),
|
|
178
|
+
}, { signal });
|
|
179
|
+
for (const update of join.syncUpdates) {
|
|
180
|
+
if (update.length > 0)
|
|
181
|
+
Y.applyUpdate(this.options.ydoc, update, REMOTE_ORIGIN);
|
|
182
|
+
}
|
|
183
|
+
this.joined = true;
|
|
184
|
+
this.applyIdentity(join.assignedDisplayName, join.assignedColor);
|
|
185
|
+
this.setStatus("connected");
|
|
186
|
+
this.reconnectDelayMs = this.options.reconnectInitialDelayMs;
|
|
187
|
+
this.sendLocalAwarenessNow();
|
|
188
|
+
void this.flushUpdates();
|
|
189
|
+
const frames = this.client.streamDocumentFrames({
|
|
190
|
+
docId: this.options.docId,
|
|
191
|
+
clientId: this.clientId,
|
|
192
|
+
afterSequence: join.sequence,
|
|
193
|
+
}, { signal });
|
|
194
|
+
for await (const response of frames) {
|
|
195
|
+
if (response.frame)
|
|
196
|
+
this.applyFrame(response.frame);
|
|
197
|
+
}
|
|
198
|
+
this.joined = false;
|
|
199
|
+
if (!this.destroyed && !signal.aborted) {
|
|
200
|
+
this.setStatus("disconnected");
|
|
201
|
+
this.scheduleReconnect();
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
catch (error) {
|
|
205
|
+
this.joined = false;
|
|
206
|
+
if (!this.destroyed && !isAbortError(error)) {
|
|
207
|
+
this.setStatus("disconnected");
|
|
208
|
+
console.error("Yjs sync stream failed", error);
|
|
209
|
+
this.scheduleReconnect();
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
scheduleReconnect() {
|
|
214
|
+
if (this.destroyed || this.reconnectTimer)
|
|
215
|
+
return;
|
|
216
|
+
const delay = this.reconnectDelayMs;
|
|
217
|
+
this.reconnectDelayMs = Math.min(this.reconnectDelayMs * 2, this.options.reconnectMaxDelayMs);
|
|
218
|
+
this.reconnectTimer = setTimeout(() => {
|
|
219
|
+
this.reconnectTimer = undefined;
|
|
220
|
+
this.connect();
|
|
221
|
+
}, delay);
|
|
222
|
+
}
|
|
223
|
+
// Leading + trailing micro-batch, same shape as the awareness throttle.
|
|
224
|
+
scheduleUpdateFlush() {
|
|
225
|
+
if (this.destroyed)
|
|
226
|
+
return;
|
|
227
|
+
if (this.options.updateFlushDelayMs <= 0) {
|
|
228
|
+
void this.flushUpdates();
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
if (this.updateFlushTimer) {
|
|
232
|
+
this.updatesDirty = true;
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
void this.flushUpdates();
|
|
236
|
+
this.updateFlushTimer = setTimeout(() => {
|
|
237
|
+
this.updateFlushTimer = undefined;
|
|
238
|
+
if (this.updatesDirty) {
|
|
239
|
+
this.updatesDirty = false;
|
|
240
|
+
this.scheduleUpdateFlush();
|
|
241
|
+
}
|
|
242
|
+
}, this.options.updateFlushDelayMs);
|
|
243
|
+
}
|
|
244
|
+
async flushUpdates() {
|
|
245
|
+
if (this.pushInFlight || !this.joined)
|
|
246
|
+
return;
|
|
247
|
+
this.pushInFlight = true;
|
|
248
|
+
try {
|
|
249
|
+
while ((this.inFlightBatch || this.pendingUpdates.length > 0) && this.joined && !this.destroyed) {
|
|
250
|
+
if (!this.inFlightBatch) {
|
|
251
|
+
// Merge everything queued so far into one push. Under network
|
|
252
|
+
// latency this naturally batches keystrokes: while one push is in
|
|
253
|
+
// flight, new updates accumulate and go out as a single request.
|
|
254
|
+
const batch = this.pendingUpdates.splice(0);
|
|
255
|
+
this.inFlightBatch = {
|
|
256
|
+
key: `${this.clientId}-${this.updateCounter++}`,
|
|
257
|
+
update: batch.length === 1 ? batch[0] : Y.mergeUpdates(batch),
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
try {
|
|
261
|
+
await this.client.pushYjsUpdate({
|
|
262
|
+
docId: this.options.docId,
|
|
263
|
+
clientId: this.clientId,
|
|
264
|
+
yjsUpdate: this.inFlightBatch.update,
|
|
265
|
+
idempotencyKey: this.inFlightBatch.key,
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
catch (error) {
|
|
269
|
+
if (!this.destroyed && !isAbortError(error)) {
|
|
270
|
+
console.error("Yjs update push failed", error);
|
|
271
|
+
this.scheduleReconnect();
|
|
272
|
+
}
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
this.inFlightBatch = undefined;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
finally {
|
|
279
|
+
this.pushInFlight = false;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
// Leading + trailing throttle: the first change sends immediately, changes
|
|
283
|
+
// arriving inside the window (e.g. cursor movement while typing or
|
|
284
|
+
// selecting) collapse into one trailing send.
|
|
285
|
+
scheduleAwarenessSend() {
|
|
286
|
+
if (this.destroyed)
|
|
287
|
+
return;
|
|
288
|
+
if (this.awarenessSendTimer) {
|
|
289
|
+
this.awarenessDirty = true;
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
this.sendLocalAwarenessNow();
|
|
293
|
+
this.awarenessSendTimer = setTimeout(() => {
|
|
294
|
+
this.awarenessSendTimer = undefined;
|
|
295
|
+
if (this.awarenessDirty) {
|
|
296
|
+
this.awarenessDirty = false;
|
|
297
|
+
this.scheduleAwarenessSend();
|
|
298
|
+
}
|
|
299
|
+
}, this.options.awarenessThrottleMs);
|
|
300
|
+
}
|
|
301
|
+
sendLocalAwarenessNow() {
|
|
302
|
+
if (this.destroyed || !this.joined)
|
|
303
|
+
return;
|
|
304
|
+
void this.client
|
|
305
|
+
.pushAwarenessUpdate({
|
|
306
|
+
docId: this.options.docId,
|
|
307
|
+
clientId: this.clientId,
|
|
308
|
+
awarenessUpdate: encodeAwarenessUpdate(this.awareness, [this.awareness.clientID]),
|
|
309
|
+
})
|
|
310
|
+
.catch(() => undefined);
|
|
311
|
+
}
|
|
312
|
+
applyFrame(frame) {
|
|
313
|
+
switch (frame.payload.case) {
|
|
314
|
+
case "syncUpdate":
|
|
315
|
+
case "yjsUpdate":
|
|
316
|
+
Y.applyUpdate(this.options.ydoc, frame.payload.value, REMOTE_ORIGIN);
|
|
317
|
+
this.reconnectDelayMs = this.options.reconnectInitialDelayMs;
|
|
318
|
+
break;
|
|
319
|
+
case "awarenessUpdate":
|
|
320
|
+
applyAwarenessUpdate(this.awareness, frame.payload.value, REMOTE_ORIGIN);
|
|
321
|
+
break;
|
|
322
|
+
case "error":
|
|
323
|
+
console.error("Yjs sync server error", frame.payload.value);
|
|
324
|
+
break;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
function isAbortError(error) {
|
|
329
|
+
if (error instanceof DOMException && error.name === "AbortError")
|
|
330
|
+
return true;
|
|
331
|
+
return error instanceof ConnectError && error.code === Code.Canceled;
|
|
332
|
+
}
|
|
333
|
+
//# sourceMappingURL=connect-yjs-provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connect-yjs-provider.js","sourceRoot":"","sources":["../../src/client/connect-yjs-provider.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"id.d.ts","sourceRoot":"","sources":["../../src/client/id.ts"],"names":[],"mappings":"AAAA,wBAAgB,YAAY,WAqB3B"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export function makeClientId() {
|
|
2
|
+
const cryptoApi = globalThis.crypto;
|
|
3
|
+
if (typeof cryptoApi?.randomUUID === "function") {
|
|
4
|
+
return cryptoApi.randomUUID();
|
|
5
|
+
}
|
|
6
|
+
if (typeof cryptoApi?.getRandomValues === "function") {
|
|
7
|
+
const bytes = cryptoApi.getRandomValues(new Uint8Array(16));
|
|
8
|
+
bytes[6] = (bytes[6] & 0x0f) | 0x40;
|
|
9
|
+
bytes[8] = (bytes[8] & 0x3f) | 0x80;
|
|
10
|
+
const hex = [...bytes].map((byte) => byte.toString(16).padStart(2, "0"));
|
|
11
|
+
return [
|
|
12
|
+
hex.slice(0, 4).join(""),
|
|
13
|
+
hex.slice(4, 6).join(""),
|
|
14
|
+
hex.slice(6, 8).join(""),
|
|
15
|
+
hex.slice(8, 10).join(""),
|
|
16
|
+
hex.slice(10, 16).join(""),
|
|
17
|
+
].join("-");
|
|
18
|
+
}
|
|
19
|
+
return `client-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=id.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"id.js","sourceRoot":"","sources":["../../src/client/id.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,YAAY;IAC1B,MAAM,SAAS,GAAG,UAAU,CAAC,MAAM,CAAC;IACpC,IAAI,OAAO,SAAS,EAAE,UAAU,KAAK,UAAU,EAAE,CAAC;QAChD,OAAO,SAAS,CAAC,UAAU,EAAE,CAAC;IAChC,CAAC;IAED,IAAI,OAAO,SAAS,EAAE,eAAe,KAAK,UAAU,EAAE,CAAC;QACrD,MAAM,KAAK,GAAG,SAAS,CAAC,eAAe,CAAC,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC;QAC5D,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC;QACpC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC;QACpC,MAAM,GAAG,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;QACzE,OAAO;YACL,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;YACxB,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;YACxB,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;YACxB,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;YACzB,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;SAC3B,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACd,CAAC;IAED,OAAO,UAAU,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;AACxF,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export type YjsProviderStatus = "connecting" | "connected" | "disconnected";
|
|
2
|
+
export type YjsProviderStatusListener = (status: YjsProviderStatus) => void;
|
|
3
|
+
export type YjsProviderIdentity = {
|
|
4
|
+
name: string;
|
|
5
|
+
color?: string;
|
|
6
|
+
};
|
|
7
|
+
export type YjsProviderIdentityListener = (identity: YjsProviderIdentity) => void;
|
|
8
|
+
//# sourceMappingURL=provider-status.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider-status.d.ts","sourceRoot":"","sources":["../../src/client/provider-status.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,iBAAiB,GAAG,YAAY,GAAG,WAAW,GAAG,cAAc,CAAC;AAE5E,MAAM,MAAM,yBAAyB,GAAG,CAAC,MAAM,EAAE,iBAAiB,KAAK,IAAI,CAAC;AAI5E,MAAM,MAAM,mBAAmB,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnE,MAAM,MAAM,2BAA2B,GAAG,CAAC,QAAQ,EAAE,mBAAmB,KAAK,IAAI,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider-status.js","sourceRoot":"","sources":["../../src/client/provider-status.ts"],"names":[],"mappings":""}
|