@pluv/client 0.25.4 → 0.27.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/.turbo/turbo-build.log +9 -9
- package/CHANGELOG.md +179 -0
- package/dist/index.d.mts +163 -77
- package/dist/index.d.ts +163 -77
- package/dist/index.js +318 -189
- package/dist/index.mjs +318 -189
- package/package.json +7 -7
- package/src/AbstractRoom.ts +6 -4
- package/src/MockedRoom.ts +115 -27
- package/src/PluvClient.ts +80 -28
- package/src/PluvProcedure.ts +81 -0
- package/src/PluvRoom.ts +113 -45
- package/src/PluvRouter.ts +70 -0
- package/src/createClient.ts +11 -3
- package/src/enums.ts +7 -0
- package/src/index.ts +5 -3
- package/src/types.ts +43 -7
- package/src/utils/identity.ts +1 -0
- package/src/utils/index.ts +1 -0
package/src/PluvRoom.ts
CHANGED
|
@@ -21,20 +21,25 @@ import type { EventNotifierSubscriptionCallback } from "./EventNotifier";
|
|
|
21
21
|
import { EventNotifier } from "./EventNotifier";
|
|
22
22
|
import type { OtherNotifierSubscriptionCallback } from "./OtherNotifier";
|
|
23
23
|
import { OtherNotifier } from "./OtherNotifier";
|
|
24
|
+
import { PluvProcedure } from "./PluvProcedure";
|
|
25
|
+
import type { MergeEvents, PluvRouterEventConfig } from "./PluvRouter";
|
|
26
|
+
import { PluvRouter } from "./PluvRouter";
|
|
24
27
|
import type { StateNotifierSubjects, SubscriptionCallback } from "./StateNotifier";
|
|
25
28
|
import { StateNotifier } from "./StateNotifier";
|
|
26
29
|
import { StorageStore } from "./StorageStore";
|
|
27
30
|
import type { UsersManagerConfig } from "./UsersManager";
|
|
28
31
|
import { UsersManager } from "./UsersManager";
|
|
32
|
+
import { ConnectionState } from "./enums";
|
|
29
33
|
import type {
|
|
30
34
|
AuthorizationState,
|
|
35
|
+
EventResolver,
|
|
36
|
+
EventResolverContext,
|
|
31
37
|
InternalSubscriptions,
|
|
32
38
|
UpdateMyPresenceAction,
|
|
33
39
|
UserInfo,
|
|
34
40
|
WebSocketConnection,
|
|
35
41
|
WebSocketState,
|
|
36
42
|
} from "./types";
|
|
37
|
-
import { ConnectionState } from "./types";
|
|
38
43
|
import { debounce } from "./utils";
|
|
39
44
|
|
|
40
45
|
const ADD_TO_STORAGE_STATE_DEBOUNCE_MS = 1_000;
|
|
@@ -46,10 +51,11 @@ const ORIGIN_STORAGE_UPDATED = "$STORAGE_UPDATED";
|
|
|
46
51
|
|
|
47
52
|
export const DEFAULT_PLUV_CLIENT_ADDON = <
|
|
48
53
|
TIO extends IOLike = IOLike,
|
|
54
|
+
TMetadata extends JsonObject = {},
|
|
49
55
|
TPresence extends JsonObject = {},
|
|
50
56
|
TStorage extends Record<string, CrdtType<any, any>> = {},
|
|
51
57
|
>(
|
|
52
|
-
input: PluvRoomAddonInput<TIO, TPresence, TStorage>,
|
|
58
|
+
input: PluvRoomAddonInput<TIO, TMetadata, TPresence, TStorage>,
|
|
53
59
|
): PluvRoomAddonResult => ({
|
|
54
60
|
storage: new StorageStore(input.room.id),
|
|
55
61
|
});
|
|
@@ -83,15 +89,22 @@ interface TimeoutIds {
|
|
|
83
89
|
reconnect: number | null;
|
|
84
90
|
}
|
|
85
91
|
|
|
86
|
-
export
|
|
87
|
-
|
|
92
|
+
export interface EndpointParams<TMetadata extends JsonObject> {
|
|
93
|
+
metadata: TMetadata;
|
|
94
|
+
room: string;
|
|
95
|
+
}
|
|
96
|
+
export type AuthEndpoint<TMetadata extends JsonObject> =
|
|
97
|
+
| string
|
|
98
|
+
| ((params: EndpointParams<TMetadata>) => string | FetchOptions)
|
|
99
|
+
| true;
|
|
100
|
+
export type WsEndpoint<TMetadata extends JsonObject> = string | ((params: EndpointParams<TMetadata>) => string);
|
|
88
101
|
|
|
89
102
|
type FetchOptions = { url: string; options?: RequestInit };
|
|
90
103
|
|
|
91
|
-
export type RoomEndpoints<TIO extends IOLike> = {
|
|
92
|
-
wsEndpoint?: WsEndpoint
|
|
104
|
+
export type RoomEndpoints<TIO extends IOLike, TMetadata extends JsonObject> = {
|
|
105
|
+
wsEndpoint?: WsEndpoint<TMetadata>;
|
|
93
106
|
} & (InferIOAuthorizeRequired<InferIOAuthorize<TIO>> extends true
|
|
94
|
-
? { authEndpoint: AuthEndpoint }
|
|
107
|
+
? { authEndpoint: AuthEndpoint<TMetadata> }
|
|
95
108
|
: { authEndpoint?: undefined });
|
|
96
109
|
|
|
97
110
|
interface InternalListeners {
|
|
@@ -100,16 +113,18 @@ interface InternalListeners {
|
|
|
100
113
|
|
|
101
114
|
export type PluvRoomAddon<
|
|
102
115
|
TIO extends IOLike = IOLike,
|
|
116
|
+
TMetadata extends JsonObject = {},
|
|
103
117
|
TPresence extends JsonObject = {},
|
|
104
118
|
TStorage extends Record<string, CrdtType<any, any>> = {},
|
|
105
|
-
> = (input: PluvRoomAddonInput<TIO, TPresence, TStorage>) => Partial<PluvRoomAddonResult>;
|
|
119
|
+
> = (input: PluvRoomAddonInput<TIO, TMetadata, TPresence, TStorage>) => Partial<PluvRoomAddonResult>;
|
|
106
120
|
|
|
107
121
|
export interface PluvRoomAddonInput<
|
|
108
122
|
TIO extends IOLike = IOLike,
|
|
123
|
+
TMetadata extends JsonObject = {},
|
|
109
124
|
TPresence extends JsonObject = {},
|
|
110
125
|
TStorage extends Record<string, CrdtType<any, any>> = {},
|
|
111
126
|
> {
|
|
112
|
-
room: PluvRoom<TIO, TPresence, TStorage>;
|
|
127
|
+
room: PluvRoom<TIO, TMetadata, TPresence, TStorage>;
|
|
113
128
|
}
|
|
114
129
|
|
|
115
130
|
export interface PluvRoomAddonResult {
|
|
@@ -121,39 +136,44 @@ export type PluvRoomDebug<TIO extends IOLike> = Id<{
|
|
|
121
136
|
input: readonly (keyof InferIOInput<TIO>)[];
|
|
122
137
|
}>;
|
|
123
138
|
|
|
124
|
-
export type PluvRoomOptions<
|
|
125
|
-
TIO extends IOLike,
|
|
126
|
-
TPresence extends JsonObject = {},
|
|
127
|
-
TStorage extends Record<string, CrdtType<any, any>> = {},
|
|
128
|
-
> = {
|
|
129
|
-
addons?: readonly PluvRoomAddon<TIO, TPresence, TStorage>[];
|
|
130
|
-
debug?: boolean | PluvRoomDebug<TIO>;
|
|
131
|
-
onAuthorizationFail?: (error: Error) => void;
|
|
132
|
-
} & Omit<CrdtManagerOptions<TStorage>, "encodedState"> &
|
|
133
|
-
UsersManagerConfig<TPresence>;
|
|
134
|
-
|
|
135
139
|
export type RoomConfig<
|
|
136
140
|
TIO extends IOLike,
|
|
141
|
+
TMetadata extends JsonObject = {},
|
|
137
142
|
TPresence extends JsonObject = {},
|
|
138
143
|
TStorage extends Record<string, CrdtType<any, any>> = {},
|
|
139
|
-
|
|
144
|
+
TEvents extends PluvRouterEventConfig<TIO, TPresence, TStorage> = {},
|
|
145
|
+
> = Id<
|
|
146
|
+
{
|
|
147
|
+
addons?: readonly PluvRoomAddon<TIO, TMetadata, TPresence, TStorage>[];
|
|
148
|
+
debug?: boolean | PluvRoomDebug<TIO>;
|
|
149
|
+
onAuthorizationFail?: (error: Error) => void;
|
|
150
|
+
router?: PluvRouter<TIO, TPresence, TStorage, TEvents>;
|
|
151
|
+
} & RoomEndpoints<TIO, TMetadata> &
|
|
152
|
+
Pick<CrdtManagerOptions<TStorage>, "initialStorage"> &
|
|
153
|
+
UsersManagerConfig<TPresence> &
|
|
154
|
+
(keyof TMetadata extends never ? { metadata?: undefined } : { metadata: TMetadata })
|
|
155
|
+
>;
|
|
140
156
|
|
|
141
157
|
export class PluvRoom<
|
|
142
158
|
TIO extends IOLike,
|
|
159
|
+
TMetadata extends JsonObject = {},
|
|
143
160
|
TPresence extends JsonObject = {},
|
|
144
161
|
TStorage extends Record<string, CrdtType<any, any>> = {},
|
|
162
|
+
TEvents extends PluvRouterEventConfig<TIO, TPresence, TStorage> = {},
|
|
145
163
|
> extends AbstractRoom<TIO, TPresence, TStorage> {
|
|
146
|
-
readonly _endpoints: RoomEndpoints<TIO>;
|
|
164
|
+
readonly _endpoints: RoomEndpoints<TIO, TMetadata>;
|
|
147
165
|
|
|
148
166
|
private _crdtManager: CrdtManager<TStorage>;
|
|
149
167
|
private _crdtNotifier = new CrdtNotifier<TStorage>();
|
|
150
168
|
private _debug: boolean | PluvRoomDebug<TIO>;
|
|
151
|
-
private _eventNotifier = new EventNotifier<TIO
|
|
169
|
+
private _eventNotifier = new EventNotifier<MergeEvents<TEvents, TIO>>();
|
|
152
170
|
private _intervals: IntervalIds = {
|
|
153
171
|
heartbeat: null,
|
|
154
172
|
};
|
|
155
173
|
private _listeners: InternalListeners;
|
|
174
|
+
private _metadata: TMetadata = {} as TMetadata;
|
|
156
175
|
private _otherNotifier = new OtherNotifier<TIO>();
|
|
176
|
+
private _router: PluvRouter<TIO, TPresence, TStorage, TEvents>;
|
|
157
177
|
private _state: WebSocketState<TIO> = {
|
|
158
178
|
authorization: {
|
|
159
179
|
token: null,
|
|
@@ -179,15 +199,17 @@ export class PluvRoom<
|
|
|
179
199
|
private _wsListeners: WebSocketListeners | null = null;
|
|
180
200
|
private _usersManager: UsersManager<TIO, TPresence>;
|
|
181
201
|
|
|
182
|
-
constructor(room: string, options: RoomConfig<TIO, TPresence, TStorage>) {
|
|
202
|
+
constructor(room: string, options: RoomConfig<TIO, TMetadata, TPresence, TStorage, TEvents>) {
|
|
183
203
|
const {
|
|
184
204
|
addons = [],
|
|
185
205
|
authEndpoint,
|
|
186
206
|
debug = false,
|
|
187
207
|
initialPresence,
|
|
188
208
|
initialStorage,
|
|
209
|
+
metadata,
|
|
189
210
|
onAuthorizationFail,
|
|
190
211
|
presence,
|
|
212
|
+
router,
|
|
191
213
|
wsEndpoint,
|
|
192
214
|
} = options;
|
|
193
215
|
|
|
@@ -200,11 +222,11 @@ export class PluvRoom<
|
|
|
200
222
|
...addon({ room: this }),
|
|
201
223
|
};
|
|
202
224
|
|
|
203
|
-
this._storageStore = storage;
|
|
204
|
-
|
|
205
225
|
this._debug = debug;
|
|
226
|
+
this._endpoints = { authEndpoint, wsEndpoint } as RoomEndpoints<TIO, TMetadata>;
|
|
227
|
+
this._storageStore = storage;
|
|
206
228
|
|
|
207
|
-
this.
|
|
229
|
+
if (!!metadata) this._metadata = metadata;
|
|
208
230
|
|
|
209
231
|
this._listeners = {
|
|
210
232
|
onAuthorizationFail: (error) => {
|
|
@@ -215,6 +237,7 @@ export class PluvRoom<
|
|
|
215
237
|
},
|
|
216
238
|
};
|
|
217
239
|
|
|
240
|
+
this._router = router ?? (new PluvRouter({}) as PluvRouter<TIO, TPresence, TStorage, TEvents>);
|
|
218
241
|
this._usersManager = new UsersManager<TIO, TPresence>({ initialPresence, presence });
|
|
219
242
|
this._crdtManager = new CrdtManager<TStorage>({ initialStorage });
|
|
220
243
|
}
|
|
@@ -228,23 +251,62 @@ export class PluvRoom<
|
|
|
228
251
|
}
|
|
229
252
|
|
|
230
253
|
public broadcast = new Proxy(
|
|
231
|
-
<TEvent extends keyof InferIOInput<
|
|
254
|
+
async <TEvent extends keyof InferIOInput<MergeEvents<TEvents, TIO>>>(
|
|
255
|
+
event: TEvent,
|
|
256
|
+
data: Id<InferIOInput<MergeEvents<TEvents, TIO>>[TEvent]>,
|
|
257
|
+
): Promise<void> => {
|
|
232
258
|
if (!this._state.webSocket) return;
|
|
233
259
|
if (this._state.connection.state !== ConnectionState.Open) return;
|
|
234
260
|
|
|
235
261
|
const type = event.toString();
|
|
236
262
|
|
|
237
|
-
this.
|
|
263
|
+
const procedure = this._router._defs.events[type] as PluvProcedure<
|
|
264
|
+
TIO,
|
|
265
|
+
any,
|
|
266
|
+
any,
|
|
267
|
+
TPresence,
|
|
268
|
+
TStorage
|
|
269
|
+
> | null;
|
|
270
|
+
|
|
271
|
+
if (!procedure?.config.broadcast) {
|
|
272
|
+
this._sendMessage({ data, type });
|
|
273
|
+
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const myself = this._usersManager.myself;
|
|
278
|
+
|
|
279
|
+
if (!myself) return;
|
|
280
|
+
|
|
281
|
+
const parsed = procedure.config.input ? procedure.config.input.parse(data) : data;
|
|
282
|
+
const context: EventResolverContext<TIO, TPresence, TStorage> = {
|
|
283
|
+
doc: this._crdtManager.doc,
|
|
284
|
+
others: this._usersManager.getOthers(),
|
|
285
|
+
room: this.id,
|
|
286
|
+
user: myself,
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
const output = await (procedure.config.broadcast as EventResolver<TIO, any, any, TPresence, TStorage>)(
|
|
290
|
+
parsed,
|
|
291
|
+
context,
|
|
292
|
+
);
|
|
293
|
+
|
|
294
|
+
Object.entries(output).forEach(([_type, _data]) => {
|
|
295
|
+
this._sendMessage({ data: _data, type: _type });
|
|
296
|
+
});
|
|
238
297
|
},
|
|
239
298
|
{
|
|
240
299
|
get(fn, prop) {
|
|
241
|
-
return (data: Id<InferIOInput<TIO
|
|
242
|
-
return fn(prop, data);
|
|
300
|
+
return async (data: Id<InferIOInput<MergeEvents<TEvents, TIO>>[any]>): Promise<void> => {
|
|
301
|
+
return await fn(prop, data);
|
|
243
302
|
};
|
|
244
303
|
},
|
|
245
304
|
},
|
|
246
|
-
) as (<TEvent extends keyof InferIOInput<
|
|
247
|
-
|
|
305
|
+
) as (<TEvent extends keyof InferIOInput<MergeEvents<TEvents, TIO>>>(
|
|
306
|
+
event: TEvent,
|
|
307
|
+
data: Id<InferIOInput<MergeEvents<TEvents, TIO>>[TEvent]>,
|
|
308
|
+
) => Promise<void>) & {
|
|
309
|
+
[event in keyof InferIOInput<TIO>]: (data: Id<InferIOInput<TIO>[event]>) => Promise<void>;
|
|
248
310
|
};
|
|
249
311
|
|
|
250
312
|
public canRedo = (): boolean => {
|
|
@@ -332,20 +394,23 @@ export class PluvRoom<
|
|
|
332
394
|
}
|
|
333
395
|
|
|
334
396
|
public event = new Proxy(
|
|
335
|
-
<TEvent extends keyof InferIOOutput<TIO
|
|
397
|
+
<TEvent extends keyof InferIOOutput<MergeEvents<TEvents, TIO>>>(
|
|
336
398
|
event: TEvent,
|
|
337
|
-
callback: EventNotifierSubscriptionCallback<
|
|
399
|
+
callback: EventNotifierSubscriptionCallback<MergeEvents<TEvents, TIO>, any>,
|
|
338
400
|
): (() => void) => this._eventNotifier.subscribe(event, callback),
|
|
339
401
|
{
|
|
340
402
|
get(fn, prop) {
|
|
341
|
-
return (callback: EventNotifierSubscriptionCallback<
|
|
403
|
+
return (callback: EventNotifierSubscriptionCallback<MergeEvents<TEvents, TIO>, any>): (() => void) =>
|
|
404
|
+
fn(prop as any, callback);
|
|
342
405
|
},
|
|
343
406
|
},
|
|
344
|
-
) as (<TEvent extends keyof InferIOOutput<TIO
|
|
407
|
+
) as (<TEvent extends keyof InferIOOutput<MergeEvents<TEvents, TIO>>>(
|
|
345
408
|
event: TEvent,
|
|
346
|
-
callback: EventNotifierSubscriptionCallback<
|
|
409
|
+
callback: EventNotifierSubscriptionCallback<MergeEvents<TEvents, TIO>, TEvent>,
|
|
347
410
|
) => () => void) & {
|
|
348
|
-
[event in keyof InferIOOutput<TIO
|
|
411
|
+
[event in keyof InferIOOutput<MergeEvents<TEvents, TIO>>]: (
|
|
412
|
+
callback: EventNotifierSubscriptionCallback<MergeEvents<TEvents, TIO>, any>,
|
|
413
|
+
) => () => void;
|
|
349
414
|
};
|
|
350
415
|
|
|
351
416
|
public getConnection = (): WebSocketConnection => {
|
|
@@ -459,7 +524,10 @@ export class PluvRoom<
|
|
|
459
524
|
this._stateNotifier.subjects["my-presence"].next(myPresence);
|
|
460
525
|
!!myself && this._stateNotifier.subjects["myself"].next(myself);
|
|
461
526
|
|
|
462
|
-
this.broadcast(
|
|
527
|
+
this.broadcast(
|
|
528
|
+
"$UPDATE_PRESENCE" as keyof InferIOInput<MergeEvents<TEvents, TIO>>,
|
|
529
|
+
{ presence: newPresence } as any,
|
|
530
|
+
);
|
|
463
531
|
};
|
|
464
532
|
|
|
465
533
|
private async _addToStorageStore(update: string): Promise<void> {
|
|
@@ -607,9 +675,9 @@ export class PluvRoom<
|
|
|
607
675
|
);
|
|
608
676
|
|
|
609
677
|
private _getAddon = (
|
|
610
|
-
addons: readonly PluvRoomAddon<TIO, TPresence, TStorage>[],
|
|
611
|
-
): PluvRoomAddon<TIO, TPresence, TStorage> => {
|
|
612
|
-
return addons.reduce<PluvRoomAddon<TIO, TPresence, TStorage>>(
|
|
678
|
+
addons: readonly PluvRoomAddon<TIO, TMetadata, TPresence, TStorage>[],
|
|
679
|
+
): PluvRoomAddon<TIO, TMetadata, TPresence, TStorage> => {
|
|
680
|
+
return addons.reduce<PluvRoomAddon<TIO, TMetadata, TPresence, TStorage>>(
|
|
613
681
|
(acc, addon) => () => ({
|
|
614
682
|
...acc({ room: this }),
|
|
615
683
|
...addon({ room: this }),
|
|
@@ -635,7 +703,7 @@ export class PluvRoom<
|
|
|
635
703
|
};
|
|
636
704
|
}
|
|
637
705
|
|
|
638
|
-
const result = this._endpoints.authEndpoint(room);
|
|
706
|
+
const result = this._endpoints.authEndpoint({ metadata: this._metadata, room });
|
|
639
707
|
|
|
640
708
|
return typeof result === "string" ? { url: result, options: {} } : result;
|
|
641
709
|
}
|
|
@@ -667,7 +735,7 @@ export class PluvRoom<
|
|
|
667
735
|
case "string":
|
|
668
736
|
return this._endpoints.wsEndpoint;
|
|
669
737
|
default:
|
|
670
|
-
return this._endpoints.wsEndpoint(room);
|
|
738
|
+
return this._endpoints.wsEndpoint({ metadata: this._metadata, room });
|
|
671
739
|
}
|
|
672
740
|
}
|
|
673
741
|
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { CrdtType } from "@pluv/crdt";
|
|
2
|
+
import type { Id, IOLike, IORouterLike, JsonObject, ProcedureLike } from "@pluv/types";
|
|
3
|
+
import type { PluvProcedure } from "./PluvProcedure";
|
|
4
|
+
|
|
5
|
+
export type PluvRouterEventConfig<
|
|
6
|
+
TIO extends IOLike = IOLike,
|
|
7
|
+
TPresence extends JsonObject = {},
|
|
8
|
+
TStorage extends Record<string, CrdtType<any, any>> = {},
|
|
9
|
+
> = { [P: string]: Pick<PluvProcedure<TIO, any, any, TPresence, TStorage>, "config"> };
|
|
10
|
+
|
|
11
|
+
export type MergedRouter<
|
|
12
|
+
TRouters extends PluvRouter<TIO, TPresence, TStorage, any>[] = [],
|
|
13
|
+
TIO extends IOLike = IOLike,
|
|
14
|
+
TPresence extends JsonObject = {},
|
|
15
|
+
TStorage extends Record<string, CrdtType<any, any>> = {},
|
|
16
|
+
TRoot extends TRouters[0]["_defs"]["events"] = {},
|
|
17
|
+
> = TRouters extends [
|
|
18
|
+
infer IHead extends PluvRouter<TIO, TPresence, TStorage, any>,
|
|
19
|
+
...infer ITail extends PluvRouter<TIO, TPresence, TStorage, any>[],
|
|
20
|
+
]
|
|
21
|
+
? MergedRouter<ITail, TIO, TPresence, TStorage, TRoot & IHead["_defs"]["events"]>
|
|
22
|
+
: PluvRouter<TIO, TPresence, TStorage, TRoot>;
|
|
23
|
+
|
|
24
|
+
export type MergeEvents<
|
|
25
|
+
TClientEvents extends PluvRouterEventConfig<any, any, any>,
|
|
26
|
+
TServerIO extends IOLike<any, any>,
|
|
27
|
+
> =
|
|
28
|
+
TServerIO extends IOLike<infer IAuthorize, infer IServerEvents>
|
|
29
|
+
? IOLike<
|
|
30
|
+
IAuthorize,
|
|
31
|
+
{
|
|
32
|
+
[P in keyof TClientEvents]: TClientEvents[P] extends ProcedureLike<
|
|
33
|
+
infer IClientInput,
|
|
34
|
+
infer IClientOutput
|
|
35
|
+
>
|
|
36
|
+
? {
|
|
37
|
+
[K in keyof IClientOutput]: K extends keyof IServerEvents
|
|
38
|
+
? IServerEvents[K]
|
|
39
|
+
: ProcedureLike<IClientInput, Id<Pick<IClientOutput, K>>>;
|
|
40
|
+
}[keyof IClientOutput]
|
|
41
|
+
: never;
|
|
42
|
+
} & IServerEvents
|
|
43
|
+
>
|
|
44
|
+
: never;
|
|
45
|
+
|
|
46
|
+
export class PluvRouter<
|
|
47
|
+
TIO extends IOLike = IOLike,
|
|
48
|
+
TPresence extends JsonObject = {},
|
|
49
|
+
TStorage extends Record<string, CrdtType<any, any>> = {},
|
|
50
|
+
TEvents extends PluvRouterEventConfig<TIO, TPresence, TStorage> = {},
|
|
51
|
+
> implements IORouterLike<TEvents>
|
|
52
|
+
{
|
|
53
|
+
readonly _defs: { events: TEvents } = { events: {} as TEvents };
|
|
54
|
+
|
|
55
|
+
constructor(events: TEvents) {
|
|
56
|
+
this._defs = { events };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
public static merge<TRouters extends PluvRouter<any, any, any, any>[]>(
|
|
60
|
+
...routers: TRouters
|
|
61
|
+
): MergedRouter<TRouters> {
|
|
62
|
+
const events = Object.assign(Object.create(null), ...routers.map((router) => router._defs.events));
|
|
63
|
+
|
|
64
|
+
if (Object.keys(events).some((key) => key.startsWith("$"))) {
|
|
65
|
+
throw new Error('Procedures may not start with "$"');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return new PluvRouter<any, any, any, any>(events) as MergedRouter<TRouters>;
|
|
69
|
+
}
|
|
70
|
+
}
|
package/src/createClient.ts
CHANGED
|
@@ -1,7 +1,15 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { CrdtType } from "@pluv/crdt";
|
|
2
|
+
import type { IOLike, JsonObject } from "@pluv/types";
|
|
2
3
|
import type { PluvClientOptions } from "./PluvClient";
|
|
3
4
|
import { PluvClient } from "./PluvClient";
|
|
4
5
|
|
|
5
|
-
export const createClient = <
|
|
6
|
-
|
|
6
|
+
export const createClient = <
|
|
7
|
+
TIO extends IOLike = IOLike,
|
|
8
|
+
TPresence extends JsonObject = {},
|
|
9
|
+
TStorage extends Record<string, CrdtType<any, any>> = {},
|
|
10
|
+
TMetadata extends JsonObject = {},
|
|
11
|
+
>(
|
|
12
|
+
options: PluvClientOptions<TIO, TPresence, TStorage, TMetadata>,
|
|
13
|
+
): PluvClient<TIO, TPresence, TStorage, TMetadata> => {
|
|
14
|
+
return new PluvClient<TIO, TPresence, TStorage, TMetadata>(options);
|
|
7
15
|
};
|
package/src/enums.ts
ADDED
package/src/index.ts
CHANGED
|
@@ -2,12 +2,14 @@ export type { CrdtType, InferCrdtJson } from "@pluv/crdt";
|
|
|
2
2
|
export type { BaseUser, EventMessage, EventRecord, IOEventMessage } from "@pluv/types";
|
|
3
3
|
export { AbstractRoom } from "./AbstractRoom";
|
|
4
4
|
export { AbstractStorageStore } from "./AbstractStorageStore";
|
|
5
|
+
export { createClient } from "./createClient";
|
|
6
|
+
export { ConnectionState } from "./enums";
|
|
5
7
|
export { MockedRoom } from "./MockedRoom";
|
|
6
8
|
export type { MockedRoomConfig, MockedRoomEvents } from "./MockedRoom";
|
|
7
9
|
export { PluvClient } from "./PluvClient";
|
|
8
|
-
export type { PluvClientOptions } from "./PluvClient";
|
|
10
|
+
export type { CreateRoomOptions, PluvClientOptions } from "./PluvClient";
|
|
11
|
+
export type { PluvProcedureConfig } from "./PluvProcedure";
|
|
9
12
|
export { PluvRoom } from "./PluvRoom";
|
|
10
13
|
export type { PluvRoomAddon, PluvRoomAddonInput, PluvRoomAddonResult, PluvRoomDebug } from "./PluvRoom";
|
|
11
|
-
export {
|
|
12
|
-
export { ConnectionState } from "./types";
|
|
14
|
+
export type { MergeEvents, PluvRouter, PluvRouterEventConfig } from "./PluvRouter";
|
|
13
15
|
export type { UserInfo, WebSocketConnection, WebSocketState } from "./types";
|
package/src/types.ts
CHANGED
|
@@ -1,22 +1,58 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { AbstractCrdtDoc, CrdtType } from "@pluv/crdt";
|
|
2
|
+
import type {
|
|
3
|
+
EventRecord,
|
|
4
|
+
Id,
|
|
5
|
+
InferIOAuthorize,
|
|
6
|
+
InferIOAuthorizeUser,
|
|
7
|
+
IOLike,
|
|
8
|
+
JsonObject,
|
|
9
|
+
MaybePromise,
|
|
10
|
+
} from "@pluv/types";
|
|
11
|
+
import type { ConnectionState } from "./enums";
|
|
2
12
|
|
|
3
13
|
export interface AuthorizationState<TIO extends IOLike> {
|
|
4
14
|
token: string | null;
|
|
5
15
|
user: InferIOAuthorizeUser<InferIOAuthorize<TIO>> | null;
|
|
6
16
|
}
|
|
7
17
|
|
|
8
|
-
export
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
18
|
+
export type EventResolver<
|
|
19
|
+
TIO extends IOLike = IOLike,
|
|
20
|
+
TInput extends JsonObject = {},
|
|
21
|
+
TOutput extends EventRecord<string, any> = {},
|
|
22
|
+
TPresence extends JsonObject = {},
|
|
23
|
+
TStorage extends Record<string, CrdtType<any, any>> = {},
|
|
24
|
+
> = (data: TInput, context: EventResolverContext<TIO, TPresence, TStorage>) => MaybePromise<TOutput | void>;
|
|
25
|
+
|
|
26
|
+
export interface EventResolverContext<
|
|
27
|
+
TIO extends IOLike = IOLike,
|
|
28
|
+
TPresence extends JsonObject = {},
|
|
29
|
+
TStorage extends Record<string, CrdtType<any, any>> = {},
|
|
30
|
+
> {
|
|
31
|
+
doc: AbstractCrdtDoc<TStorage>;
|
|
32
|
+
others: readonly UserInfo<TIO, TPresence>[];
|
|
33
|
+
room: string;
|
|
34
|
+
user: UserInfo<TIO, TPresence>;
|
|
14
35
|
}
|
|
15
36
|
|
|
16
37
|
export interface InternalSubscriptions {
|
|
17
38
|
observeCrdt: (() => void) | null;
|
|
18
39
|
}
|
|
19
40
|
|
|
41
|
+
export type MergeEventRecords<
|
|
42
|
+
TEventRecords extends EventRecord<string, any>[],
|
|
43
|
+
TRoot extends EventRecord<string, any> = {},
|
|
44
|
+
> = TEventRecords extends [
|
|
45
|
+
infer IHead extends EventRecord<string, any>,
|
|
46
|
+
...infer ITail extends EventRecord<string, any>[],
|
|
47
|
+
]
|
|
48
|
+
? MergeEventRecords<
|
|
49
|
+
ITail,
|
|
50
|
+
Omit<TRoot, keyof IHead> & {
|
|
51
|
+
[P in keyof IHead]: TRoot extends Record<P, any> ? TRoot[P] | IHead[P] : IHead[P];
|
|
52
|
+
}
|
|
53
|
+
>
|
|
54
|
+
: Id<TRoot>;
|
|
55
|
+
|
|
20
56
|
export type UpdateMyPresenceAction<TPresence extends JsonObject> =
|
|
21
57
|
| Partial<TPresence>
|
|
22
58
|
| ((oldPresence: TPresence | null) => Partial<TPresence>);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const identity = <T extends unknown>(x: T): T => x;
|
package/src/utils/index.ts
CHANGED