@pluv/crdt-yjs 2.1.0 → 2.2.1
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/.turbo/turbo-build.log +8 -8
- package/CHANGELOG.md +84 -0
- package/dist/index.d.mts +118 -9
- package/dist/index.d.ts +118 -9
- package/dist/index.js +223 -0
- package/dist/index.mjs +223 -0
- package/package.json +8 -7
- package/src/awareness/PluvYjsAwareness.ts +220 -0
- package/src/awareness/awareness.ts +15 -0
- package/src/awareness/index.ts +2 -0
- package/src/constants.ts +2 -0
- package/src/doc/CrdtYjsDoc.ts +4 -2
- package/src/doc/CrdtYjsDocFactory.ts +2 -1
- package/src/provider/PluvYjsProvider.ts +132 -0
- package/src/provider/index.ts +2 -0
- package/src/provider/provider.ts +15 -0
- package/src/types.ts +26 -0
- package/src/yjs.ts +2 -0
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
CrdtType,
|
|
3
|
+
IOLike,
|
|
4
|
+
OthersSubscriptionEvent,
|
|
5
|
+
PluvRouterEventConfig,
|
|
6
|
+
RoomLike,
|
|
7
|
+
UserInfo,
|
|
8
|
+
} from "@pluv/types";
|
|
9
|
+
import { ObservableV2 } from "lib0/observable";
|
|
10
|
+
import type { Doc as YDoc } from "yjs";
|
|
11
|
+
import { PLUV_PRESENCE_META_KEY, PLUV_PRESENCE_Y_ID_KEY } from "../constants";
|
|
12
|
+
import type { AwarenessPresence, MetaClientState, YjsAwarenessUpdate } from "../types";
|
|
13
|
+
|
|
14
|
+
export interface PluvYjsAwarenessParams<
|
|
15
|
+
TIO extends IOLike<any>,
|
|
16
|
+
TPresence extends Record<string, any>,
|
|
17
|
+
TStorage extends Record<string, CrdtType<any, any>>,
|
|
18
|
+
TEvents extends PluvRouterEventConfig,
|
|
19
|
+
TField extends keyof TPresence | null = null,
|
|
20
|
+
> {
|
|
21
|
+
field?: TField;
|
|
22
|
+
room: RoomLike<TIO, YDoc, TPresence, TStorage, TEvents>;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export class PluvYjsAwareness<
|
|
26
|
+
TIO extends IOLike,
|
|
27
|
+
TPresence extends Record<string, any>,
|
|
28
|
+
TStorage extends Record<string, CrdtType<any, any>>,
|
|
29
|
+
TEvents extends PluvRouterEventConfig,
|
|
30
|
+
TField extends keyof TPresence | null = null,
|
|
31
|
+
> extends ObservableV2<{
|
|
32
|
+
change: (updates: YjsAwarenessUpdate, kind: "presence") => void;
|
|
33
|
+
destroy: (awareness: PluvYjsAwareness<TIO, TPresence, TStorage, TEvents, TField>) => void;
|
|
34
|
+
update: (updates: YjsAwarenessUpdate, kind: "local" | "presence") => void;
|
|
35
|
+
}> {
|
|
36
|
+
public readonly doc: YDoc;
|
|
37
|
+
|
|
38
|
+
private readonly _field: TField = null as TField;
|
|
39
|
+
private readonly _idMap = new Map<[connectionId: string][0], [clientID: number][0]>();
|
|
40
|
+
private readonly _room: RoomLike<TIO, YDoc, TPresence, TStorage, TEvents>;
|
|
41
|
+
private readonly _unsubscribe: () => void;
|
|
42
|
+
|
|
43
|
+
public get clientID(): number {
|
|
44
|
+
return this.doc.clientID;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
public get states(): Map<number, TPresence> {
|
|
48
|
+
return this.getStates();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
constructor(params: PluvYjsAwarenessParams<TIO, TPresence, TStorage, TEvents, TField>) {
|
|
52
|
+
super();
|
|
53
|
+
|
|
54
|
+
const { field, room } = params;
|
|
55
|
+
|
|
56
|
+
this.doc = room.getDoc().value;
|
|
57
|
+
this._room = room;
|
|
58
|
+
|
|
59
|
+
if (!!field) this._field = field;
|
|
60
|
+
|
|
61
|
+
const myPresence: any = this._room.getMyPresence();
|
|
62
|
+
const myMeta = myPresence?.[PLUV_PRESENCE_META_KEY] || {};
|
|
63
|
+
|
|
64
|
+
this._room.updateMyPresence({
|
|
65
|
+
[PLUV_PRESENCE_META_KEY]: {
|
|
66
|
+
...myMeta,
|
|
67
|
+
[PLUV_PRESENCE_Y_ID_KEY]: this.clientID,
|
|
68
|
+
},
|
|
69
|
+
} as any);
|
|
70
|
+
|
|
71
|
+
this._unsubscribe = this._room.subscribe.others((others, event) => {
|
|
72
|
+
const updates = this._deriveUpdates(others, event);
|
|
73
|
+
|
|
74
|
+
if (!updates) return;
|
|
75
|
+
|
|
76
|
+
this.emit("change", [updates, "presence"]);
|
|
77
|
+
this.emit("update", [updates, "presence"]);
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
public destroy(): void {
|
|
82
|
+
this.emit("destroy", [this]);
|
|
83
|
+
this._unsubscribe();
|
|
84
|
+
super.destroy();
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
public getLocalState(): AwarenessPresence<TPresence, TField> {
|
|
88
|
+
return this._adaptPresence(this._room.getMyPresence());
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
public setLocalState(state: AwarenessPresence<TPresence, TField>): void {
|
|
92
|
+
const patch = (!!this._field
|
|
93
|
+
? { [this._field]: state }
|
|
94
|
+
: state) as unknown as Partial<TPresence>;
|
|
95
|
+
|
|
96
|
+
this._room.updateMyPresence(patch);
|
|
97
|
+
|
|
98
|
+
this.emit("update", [{ added: [], updated: [this.clientID], removed: [] }, "local"]);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
public setLocalStateField<TLocalField extends keyof AwarenessPresence<TPresence, TField>>(
|
|
102
|
+
field: TLocalField,
|
|
103
|
+
value: AwarenessPresence<TPresence, TField>[TLocalField],
|
|
104
|
+
): void {
|
|
105
|
+
/**
|
|
106
|
+
* !HACK
|
|
107
|
+
* @description Cast to unknown first, because field loses its type of TField when set in
|
|
108
|
+
* an object.
|
|
109
|
+
* @date May 14, 2025
|
|
110
|
+
*/
|
|
111
|
+
const patch = (!!this._field
|
|
112
|
+
? { [this._field]: { ...this.getLocalState(), [field]: value } }
|
|
113
|
+
: { [field]: value }) as unknown as Partial<TPresence>;
|
|
114
|
+
|
|
115
|
+
this._room.updateMyPresence(patch);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* !HACK
|
|
120
|
+
* @description We're building the awareness presences from the states stored on the room's
|
|
121
|
+
* presense instead of using the map defined in the awareness interface.
|
|
122
|
+
* @see https://github.com/yjs/y-protocols/blob/master/awareness.js
|
|
123
|
+
* @date May 13, 2025
|
|
124
|
+
*/
|
|
125
|
+
public getStates(): Map<number, AwarenessPresence<TPresence, TField>> {
|
|
126
|
+
const others = this._room.getOthers();
|
|
127
|
+
|
|
128
|
+
const states = others.reduce((map, other) => {
|
|
129
|
+
const presence = this._adaptPresence(other.presence);
|
|
130
|
+
const meta = other.presence?.[PLUV_PRESENCE_META_KEY] || {};
|
|
131
|
+
const clientID: number | undefined = meta[PLUV_PRESENCE_Y_ID_KEY];
|
|
132
|
+
|
|
133
|
+
return typeof presence !== "undefined" && typeof clientID !== "undefined"
|
|
134
|
+
? map.set(clientID, presence)
|
|
135
|
+
: map;
|
|
136
|
+
}, new Map<number, AwarenessPresence<TPresence, TField>>());
|
|
137
|
+
|
|
138
|
+
const myPresence = this.getLocalState();
|
|
139
|
+
|
|
140
|
+
if (typeof myPresence !== "undefined") states.set(this.clientID, myPresence);
|
|
141
|
+
|
|
142
|
+
return states;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
private _adaptPresence(presence: TPresence): AwarenessPresence<TPresence, TField> {
|
|
146
|
+
return (!!this._field ? presence[this._field] : presence) as AwarenessPresence<
|
|
147
|
+
TPresence,
|
|
148
|
+
TField
|
|
149
|
+
>;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
private _deriveUpdates(
|
|
153
|
+
others: readonly UserInfo<any, any>[],
|
|
154
|
+
event: OthersSubscriptionEvent<any, any>,
|
|
155
|
+
): YjsAwarenessUpdate | null {
|
|
156
|
+
switch (event.kind) {
|
|
157
|
+
case "clear": {
|
|
158
|
+
this._idMap.clear();
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
case "enter": {
|
|
162
|
+
const clientID = this._resetIdMap(others).get(event.user.connectionId);
|
|
163
|
+
|
|
164
|
+
return {
|
|
165
|
+
added: typeof clientID !== "undefined" ? [clientID] : [],
|
|
166
|
+
updated: [],
|
|
167
|
+
removed: [],
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
case "leave": {
|
|
171
|
+
const clientID = this._idMap.get(event.user.connectionId);
|
|
172
|
+
|
|
173
|
+
this._resetIdMap(others);
|
|
174
|
+
|
|
175
|
+
return {
|
|
176
|
+
added: [],
|
|
177
|
+
updated: [],
|
|
178
|
+
removed: typeof clientID !== "undefined" ? [clientID] : [],
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
case "sync": {
|
|
182
|
+
this._resetIdMap(others);
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
case "update": {
|
|
186
|
+
const clientID = this._resetIdMap(others).get(event.user.connectionId);
|
|
187
|
+
|
|
188
|
+
return {
|
|
189
|
+
added: [],
|
|
190
|
+
updated: typeof clientID !== "undefined" ? [clientID] : [],
|
|
191
|
+
removed: [],
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
default:
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
private _resetIdMap(others: readonly UserInfo<any, any>[]): Map<string, number> {
|
|
200
|
+
this._idMap.clear();
|
|
201
|
+
|
|
202
|
+
return others.reduce((map, other) => {
|
|
203
|
+
const clientID = other.presence[PLUV_PRESENCE_META_KEY]?.[PLUV_PRESENCE_Y_ID_KEY];
|
|
204
|
+
|
|
205
|
+
if (typeof clientID === "undefined") return map;
|
|
206
|
+
|
|
207
|
+
return map.set(other.connectionId, clientID);
|
|
208
|
+
}, this._idMap);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* !HACK
|
|
213
|
+
* @description We dgaf about these. We're just mirroring the fields that exist in the
|
|
214
|
+
* og awareness to make sure everything aligns.
|
|
215
|
+
* @see https://github.com/yjs/y-protocols/blob/master/awareness.js
|
|
216
|
+
* @date May 13, 2025
|
|
217
|
+
*/
|
|
218
|
+
public readonly meta = new Map<string, MetaClientState>();
|
|
219
|
+
public readonly _checkInterval: number = 0;
|
|
220
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { CrdtType, IOLike, PluvRouterEventConfig } from "@pluv/types";
|
|
2
|
+
import type { PluvYjsAwarenessParams } from "./PluvYjsAwareness";
|
|
3
|
+
import { PluvYjsAwareness } from "./PluvYjsAwareness";
|
|
4
|
+
|
|
5
|
+
export const awareness = <
|
|
6
|
+
TIO extends IOLike<any>,
|
|
7
|
+
TPresence extends Record<string, any>,
|
|
8
|
+
TStorage extends Record<string, CrdtType<any, any>>,
|
|
9
|
+
TEvents extends PluvRouterEventConfig,
|
|
10
|
+
TField extends keyof TPresence | null = null,
|
|
11
|
+
>(
|
|
12
|
+
params: PluvYjsAwarenessParams<TIO, TPresence, TStorage, TEvents, TField>,
|
|
13
|
+
): PluvYjsAwareness<TIO, TPresence, TStorage, TEvents, TField> => {
|
|
14
|
+
return new PluvYjsAwareness(params);
|
|
15
|
+
};
|
package/src/constants.ts
ADDED
package/src/doc/CrdtYjsDoc.ts
CHANGED
|
@@ -39,7 +39,7 @@ export type CrdtYjsDocParams<TStorage extends Record<string, YjsType<any, any>>>
|
|
|
39
39
|
) => TStorage;
|
|
40
40
|
|
|
41
41
|
export class CrdtYjsDoc<TStorage extends Record<string, YjsType<any, any>>>
|
|
42
|
-
implements CrdtDocLike<TStorage>
|
|
42
|
+
implements CrdtDocLike<YDoc, TStorage>
|
|
43
43
|
{
|
|
44
44
|
public value: YDoc = new YDoc();
|
|
45
45
|
|
|
@@ -285,7 +285,9 @@ export class CrdtYjsDoc<TStorage extends Record<string, YjsType<any, any>>>
|
|
|
285
285
|
return this;
|
|
286
286
|
}
|
|
287
287
|
|
|
288
|
-
public subscribe(
|
|
288
|
+
public subscribe(
|
|
289
|
+
listener: (params: DocSubscribeCallbackParams<YDoc, TStorage>) => void,
|
|
290
|
+
): () => void {
|
|
289
291
|
const fn = (update: Uint8Array, origin: any, doc: YDoc) => {
|
|
290
292
|
listener({
|
|
291
293
|
doc: this,
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { AbstractCrdtDocFactory } from "@pluv/crdt";
|
|
2
|
+
import type { Doc as YDoc } from "yjs";
|
|
2
3
|
import type { YjsType } from "../types";
|
|
3
4
|
import { CrdtYjsDoc } from "./CrdtYjsDoc";
|
|
4
5
|
import type { YjsBuilder } from "./builder";
|
|
5
6
|
|
|
6
7
|
export class CrdtYjsDocFactory<
|
|
7
8
|
TStorage extends Record<string, YjsType<any, any>> = {},
|
|
8
|
-
> extends AbstractCrdtDocFactory<TStorage> {
|
|
9
|
+
> extends AbstractCrdtDocFactory<YDoc, TStorage> {
|
|
9
10
|
public readonly _initialStorage: (builder: YjsBuilder) => TStorage;
|
|
10
11
|
|
|
11
12
|
constructor(initialStorage: (builder: YjsBuilder) => TStorage = () => ({}) as TStorage) {
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import type { CrdtType, IOLike, PluvRouterEventConfig, RoomLike } from "@pluv/types";
|
|
2
|
+
import { StorageState } from "@pluv/types";
|
|
3
|
+
import { ObservableV2 } from "lib0/observable";
|
|
4
|
+
import type { Doc as YDoc } from "yjs";
|
|
5
|
+
import type { PluvYjsAwareness } from "../awareness";
|
|
6
|
+
import { awareness } from "../awareness";
|
|
7
|
+
import type { YjsProviderStatus } from "../types";
|
|
8
|
+
|
|
9
|
+
export interface PluvYjsProviderParams<
|
|
10
|
+
TIO extends IOLike<any>,
|
|
11
|
+
TPresence extends Record<string, any>,
|
|
12
|
+
TStorage extends Record<string, CrdtType<any, any>>,
|
|
13
|
+
TEvents extends PluvRouterEventConfig,
|
|
14
|
+
TField extends keyof TPresence | null = null,
|
|
15
|
+
> {
|
|
16
|
+
field?: TField;
|
|
17
|
+
room: RoomLike<TIO, YDoc, TPresence, TStorage, TEvents>;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* !HACK
|
|
22
|
+
* @description This doesn't really do anything but emit states in a Yjs.Provider compatible way.
|
|
23
|
+
* Connections and document syncing are handled by the PluvRoom already.
|
|
24
|
+
* @date May 13, 2025
|
|
25
|
+
*/
|
|
26
|
+
export class PluvYjsProvider<
|
|
27
|
+
TIO extends IOLike<any>,
|
|
28
|
+
TPresence extends Record<string, any>,
|
|
29
|
+
TStorage extends Record<string, CrdtType<any, any>>,
|
|
30
|
+
TEvents extends PluvRouterEventConfig,
|
|
31
|
+
TField extends keyof TPresence | null = null,
|
|
32
|
+
> extends ObservableV2<{
|
|
33
|
+
"connection-close": (
|
|
34
|
+
event: CloseEvent | null,
|
|
35
|
+
provider: PluvYjsProvider<TIO, TPresence, TStorage, TEvents, TField>,
|
|
36
|
+
) => void;
|
|
37
|
+
"connection-error": (
|
|
38
|
+
event: Event,
|
|
39
|
+
provider: PluvYjsProvider<TIO, TPresence, TStorage, TEvents, TField>,
|
|
40
|
+
) => void;
|
|
41
|
+
status: (event: { status: YjsProviderStatus }) => void;
|
|
42
|
+
sync: (state: boolean) => void;
|
|
43
|
+
synced: (state: boolean) => void;
|
|
44
|
+
}> {
|
|
45
|
+
public readonly awareness: PluvYjsAwareness<TIO, TPresence, TStorage, TEvents, TField>;
|
|
46
|
+
public readonly doc: YDoc;
|
|
47
|
+
|
|
48
|
+
private readonly _room: RoomLike<TIO, YDoc, TPresence, TStorage, TEvents>;
|
|
49
|
+
private readonly _unsubscribe: () => void;
|
|
50
|
+
|
|
51
|
+
private _synced: boolean = false;
|
|
52
|
+
|
|
53
|
+
constructor(params: PluvYjsProviderParams<TIO, TPresence, TStorage, TEvents, TField>) {
|
|
54
|
+
super();
|
|
55
|
+
|
|
56
|
+
const { field, room } = params;
|
|
57
|
+
|
|
58
|
+
this.awareness = awareness({ field, room });
|
|
59
|
+
this.doc = room.getDoc().value;
|
|
60
|
+
|
|
61
|
+
this._room = room;
|
|
62
|
+
|
|
63
|
+
const subscriptions = [
|
|
64
|
+
this._room.subscribe.connection(({ storage }) => {
|
|
65
|
+
const status = this._toProviderStatus(storage.state);
|
|
66
|
+
|
|
67
|
+
if (!status) return;
|
|
68
|
+
|
|
69
|
+
this.emit("status", [{ status }]);
|
|
70
|
+
}),
|
|
71
|
+
this._room.addEventListener("close", (event) => {
|
|
72
|
+
this.emit("connection-close", [event, this]);
|
|
73
|
+
}),
|
|
74
|
+
this._room.addEventListener("error", (event) => {
|
|
75
|
+
this.emit("connection-error", [event, this]);
|
|
76
|
+
}),
|
|
77
|
+
];
|
|
78
|
+
|
|
79
|
+
this._unsubscribe = subscriptions.reduce(
|
|
80
|
+
(fn, unsubscribe) => () => {
|
|
81
|
+
fn();
|
|
82
|
+
unsubscribe();
|
|
83
|
+
},
|
|
84
|
+
() => undefined,
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
public get synced(): boolean {
|
|
89
|
+
return this._synced;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
public set synced(state: boolean) {
|
|
93
|
+
if (this._synced === state) return;
|
|
94
|
+
|
|
95
|
+
this._synced = state;
|
|
96
|
+
this.emit("sync", [state]);
|
|
97
|
+
this.emit("synced", [state]);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
public destroy(): void {
|
|
101
|
+
this.awareness.destroy();
|
|
102
|
+
this._unsubscribe();
|
|
103
|
+
super.destroy();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
private _toProviderStatus(state: StorageState): YjsProviderStatus | null {
|
|
107
|
+
switch (state) {
|
|
108
|
+
case StorageState.Loading: {
|
|
109
|
+
return "connecting";
|
|
110
|
+
}
|
|
111
|
+
case StorageState.Unavailable: {
|
|
112
|
+
return "disconnected";
|
|
113
|
+
}
|
|
114
|
+
case StorageState.Offline:
|
|
115
|
+
case StorageState.Synced: {
|
|
116
|
+
return "connected";
|
|
117
|
+
}
|
|
118
|
+
default:
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* !HACK
|
|
125
|
+
* @description We dgaf about these, since the room manages these already. We're just mirroring
|
|
126
|
+
* the fields that exist in the og provider to make sure everything aligns.
|
|
127
|
+
* @see https://github.com/yjs/y-websocket/blob/master/src/y-websocket.js
|
|
128
|
+
* @date May 13, 2025
|
|
129
|
+
*/
|
|
130
|
+
public connect(): void {}
|
|
131
|
+
public disconnect(): void {}
|
|
132
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { CrdtType, IOLike, PluvRouterEventConfig } from "@pluv/types";
|
|
2
|
+
import type { PluvYjsProviderParams } from "./PluvYjsProvider";
|
|
3
|
+
import { PluvYjsProvider } from "./PluvYjsProvider";
|
|
4
|
+
|
|
5
|
+
export const provider = <
|
|
6
|
+
TIO extends IOLike<any>,
|
|
7
|
+
TPresence extends Record<string, any>,
|
|
8
|
+
TStorage extends Record<string, CrdtType<any, any>>,
|
|
9
|
+
TEvents extends PluvRouterEventConfig,
|
|
10
|
+
TField extends keyof TPresence | null = null,
|
|
11
|
+
>(
|
|
12
|
+
params: PluvYjsProviderParams<TIO, TPresence, TStorage, TEvents, TField>,
|
|
13
|
+
) => {
|
|
14
|
+
return new PluvYjsProvider(params);
|
|
15
|
+
};
|
package/src/types.ts
CHANGED
|
@@ -7,3 +7,29 @@ export type YjsType<TValue extends unknown, TJson extends unknown = any> = CrdtT
|
|
|
7
7
|
initialValue?: any;
|
|
8
8
|
toJSON: () => any;
|
|
9
9
|
};
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @description Type internally defined by Yjs.Awareness
|
|
13
|
+
* @see https://github.com/yjs/y-protocols/blob/master/awareness.js
|
|
14
|
+
*/
|
|
15
|
+
export interface YjsAwarenessUpdate {
|
|
16
|
+
added: number[];
|
|
17
|
+
updated: number[];
|
|
18
|
+
removed: number[];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export type YjsProviderStatus = "connecting" | "connected" | "disconnected";
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @description Type internally defined by Yjs.Awareness
|
|
25
|
+
* @see https://github.com/yjs/y-protocols/blob/master/awareness.js
|
|
26
|
+
*/
|
|
27
|
+
export interface MetaClientState {
|
|
28
|
+
clock: number;
|
|
29
|
+
lastUpdated: number;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export type AwarenessPresence<
|
|
33
|
+
TPresence extends Record<string, any>,
|
|
34
|
+
TField extends keyof TPresence | null,
|
|
35
|
+
> = TField extends null ? TPresence : TField extends keyof TPresence ? TPresence[TField] : never;
|
package/src/yjs.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
export { array } from "./array";
|
|
2
|
+
export { awareness } from "./awareness";
|
|
2
3
|
export { doc } from "./doc";
|
|
3
4
|
export type { YjsBuilder } from "./doc";
|
|
4
5
|
export { map } from "./map";
|
|
5
6
|
export { object } from "./object";
|
|
7
|
+
export { provider } from "./provider";
|
|
6
8
|
export { text } from "./text";
|
|
7
9
|
export type { YjsType } from "./types";
|
|
8
10
|
export { xmlElement } from "./xmlElement";
|