@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/MockedRoom.ts
CHANGED
|
@@ -8,18 +8,23 @@ import type { EventNotifierSubscriptionCallback } from "./EventNotifier";
|
|
|
8
8
|
import { EventNotifier } from "./EventNotifier";
|
|
9
9
|
import type { OtherNotifierSubscriptionCallback } from "./OtherNotifier";
|
|
10
10
|
import { OtherNotifier } from "./OtherNotifier";
|
|
11
|
+
import { PluvProcedure } from "./PluvProcedure";
|
|
12
|
+
import type { MergeEvents, PluvRouterEventConfig } from "./PluvRouter";
|
|
13
|
+
import { PluvRouter } from "./PluvRouter";
|
|
11
14
|
import type { StateNotifierSubjects, SubscriptionCallback } from "./StateNotifier";
|
|
12
15
|
import { StateNotifier } from "./StateNotifier";
|
|
13
16
|
import type { UsersManagerConfig } from "./UsersManager";
|
|
14
17
|
import { UsersManager } from "./UsersManager";
|
|
18
|
+
import { ConnectionState } from "./enums";
|
|
15
19
|
import type {
|
|
20
|
+
EventResolver,
|
|
21
|
+
EventResolverContext,
|
|
16
22
|
InternalSubscriptions,
|
|
17
23
|
UpdateMyPresenceAction,
|
|
18
24
|
UserInfo,
|
|
19
25
|
WebSocketConnection,
|
|
20
26
|
WebSocketState,
|
|
21
27
|
} from "./types";
|
|
22
|
-
import { ConnectionState } from "./types";
|
|
23
28
|
|
|
24
29
|
export type MockedRoomEvents<TIO extends IOLike> = Partial<{
|
|
25
30
|
[P in keyof InferIOInput<TIO>]: (data: Id<InferIOInput<TIO>[P]>) => Partial<InferIOOutput<TIO>>;
|
|
@@ -29,19 +34,25 @@ export type MockedRoomConfig<
|
|
|
29
34
|
TIO extends IOLike,
|
|
30
35
|
TPresence extends JsonObject = {},
|
|
31
36
|
TStorage extends Record<string, CrdtType<any, any>> = {},
|
|
32
|
-
|
|
37
|
+
TEvents extends PluvRouterEventConfig<TIO, TPresence, TStorage> = {},
|
|
38
|
+
> = {
|
|
39
|
+
events?: MockedRoomEvents<MergeEvents<TEvents, TIO>>;
|
|
40
|
+
router?: PluvRouter<TIO, TPresence, TStorage, TEvents>;
|
|
41
|
+
} & Pick<CrdtManagerOptions<TStorage>, "initialStorage"> &
|
|
33
42
|
UsersManagerConfig<TPresence>;
|
|
34
43
|
|
|
35
44
|
export class MockedRoom<
|
|
36
45
|
TIO extends IOLike,
|
|
37
46
|
TPresence extends JsonObject = {},
|
|
38
47
|
TStorage extends Record<string, CrdtType<any, any>> = {},
|
|
48
|
+
TEvents extends PluvRouterEventConfig<TIO, TPresence, TStorage> = {},
|
|
39
49
|
> extends AbstractRoom<TIO, TPresence, TStorage> {
|
|
40
50
|
private _crdtManager: CrdtManager<TStorage>;
|
|
41
51
|
private _crdtNotifier = new CrdtNotifier<TStorage>();
|
|
42
|
-
private _eventNotifier = new EventNotifier<TIO
|
|
43
|
-
private _events?: MockedRoomEvents<TIO
|
|
52
|
+
private _eventNotifier = new EventNotifier<MergeEvents<TEvents, TIO>>();
|
|
53
|
+
private _events?: MockedRoomEvents<MergeEvents<TEvents, TIO>>;
|
|
44
54
|
private _otherNotifier = new OtherNotifier<TIO>();
|
|
55
|
+
private _router: PluvRouter<TIO, TPresence, TStorage, TEvents>;
|
|
45
56
|
private _state: WebSocketState<TIO> = {
|
|
46
57
|
authorization: {
|
|
47
58
|
token: null,
|
|
@@ -60,13 +71,14 @@ export class MockedRoom<
|
|
|
60
71
|
};
|
|
61
72
|
private _usersManager: UsersManager<TIO, TPresence>;
|
|
62
73
|
|
|
63
|
-
constructor(room: string, options: MockedRoomConfig<TIO, TPresence, TStorage>) {
|
|
64
|
-
const { events, initialPresence, initialStorage, presence } = options;
|
|
74
|
+
constructor(room: string, options: MockedRoomConfig<TIO, TPresence, TStorage, TEvents>) {
|
|
75
|
+
const { events, initialPresence, initialStorage, presence, router } = options;
|
|
65
76
|
|
|
66
77
|
super(room);
|
|
67
78
|
|
|
68
79
|
this._events = events;
|
|
69
80
|
|
|
81
|
+
this._router = router ?? (new PluvRouter({}) as PluvRouter<TIO, TPresence, TStorage, TEvents>);
|
|
70
82
|
this._usersManager = new UsersManager<TIO, TPresence>({
|
|
71
83
|
initialPresence,
|
|
72
84
|
presence,
|
|
@@ -83,24 +95,64 @@ export class MockedRoom<
|
|
|
83
95
|
return true;
|
|
84
96
|
}
|
|
85
97
|
|
|
86
|
-
public broadcast
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
98
|
+
public broadcast = new Proxy(
|
|
99
|
+
async <TEvent extends keyof InferIOInput<MergeEvents<TEvents, TIO>>>(
|
|
100
|
+
event: TEvent,
|
|
101
|
+
data: Id<InferIOInput<MergeEvents<TEvents, TIO>>[TEvent]>,
|
|
102
|
+
): Promise<void> => {
|
|
103
|
+
if (!this._state.webSocket) return;
|
|
104
|
+
if (this._state.connection.state !== ConnectionState.Open) return;
|
|
105
|
+
|
|
106
|
+
const type = event.toString();
|
|
107
|
+
|
|
108
|
+
const procedure = this._router._defs.events[type] as PluvProcedure<
|
|
109
|
+
TIO,
|
|
110
|
+
any,
|
|
111
|
+
any,
|
|
112
|
+
TPresence,
|
|
113
|
+
TStorage
|
|
114
|
+
> | null;
|
|
115
|
+
|
|
116
|
+
if (!procedure?.config.broadcast) {
|
|
117
|
+
this._simulateEvent(type as TEvent, data);
|
|
118
|
+
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const myself = this._usersManager.myself;
|
|
123
|
+
|
|
124
|
+
if (!myself) return;
|
|
125
|
+
|
|
126
|
+
const parsed = procedure.config.input ? procedure.config.input.parse(data) : data;
|
|
127
|
+
const context: EventResolverContext<TIO, TPresence, TStorage> = {
|
|
128
|
+
doc: this._crdtManager.doc,
|
|
129
|
+
others: this._usersManager.getOthers(),
|
|
130
|
+
room: this.id,
|
|
131
|
+
user: myself,
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
const output = await (procedure.config.broadcast as EventResolver<TIO, any, any, TPresence, TStorage>)(
|
|
135
|
+
parsed,
|
|
136
|
+
context,
|
|
137
|
+
);
|
|
100
138
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
139
|
+
Object.entries(output).forEach(([_type, _data]) => {
|
|
140
|
+
this._simulateEvent(_type as TEvent, _data as any);
|
|
141
|
+
});
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
get(fn, prop) {
|
|
145
|
+
return async (data: Id<InferIOInput<MergeEvents<TEvents, TIO>>[any]>): Promise<void> => {
|
|
146
|
+
return await fn(prop, data);
|
|
147
|
+
};
|
|
148
|
+
},
|
|
149
|
+
},
|
|
150
|
+
) as (<TEvent extends keyof InferIOInput<MergeEvents<TEvents, TIO>>>(
|
|
151
|
+
event: TEvent,
|
|
152
|
+
data: Id<InferIOInput<MergeEvents<TEvents, TIO>>[TEvent]>,
|
|
153
|
+
) => Promise<void>) & {
|
|
154
|
+
[event in keyof InferIOInput<TIO>]: (data: Id<InferIOInput<TIO>[event]>) => Promise<void>;
|
|
155
|
+
};
|
|
104
156
|
|
|
105
157
|
public canRedo = (): boolean => {
|
|
106
158
|
return !!this._crdtManager.doc.canRedo();
|
|
@@ -110,11 +162,24 @@ export class MockedRoom<
|
|
|
110
162
|
return !!this._crdtManager.doc.canUndo();
|
|
111
163
|
};
|
|
112
164
|
|
|
113
|
-
public event =
|
|
165
|
+
public event = new Proxy(
|
|
166
|
+
<TEvent extends keyof InferIOOutput<MergeEvents<TEvents, TIO>>>(
|
|
167
|
+
event: TEvent,
|
|
168
|
+
callback: EventNotifierSubscriptionCallback<MergeEvents<TEvents, TIO>, any>,
|
|
169
|
+
): (() => void) => this._eventNotifier.subscribe(event, callback),
|
|
170
|
+
{
|
|
171
|
+
get(fn, prop) {
|
|
172
|
+
return (callback: EventNotifierSubscriptionCallback<MergeEvents<TEvents, TIO>, any>): (() => void) =>
|
|
173
|
+
fn(prop as any, callback);
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
) as (<TEvent extends keyof InferIOOutput<MergeEvents<TEvents, TIO>>>(
|
|
114
177
|
event: TEvent,
|
|
115
|
-
callback: EventNotifierSubscriptionCallback<
|
|
116
|
-
)
|
|
117
|
-
|
|
178
|
+
callback: EventNotifierSubscriptionCallback<MergeEvents<TEvents, TIO>, TEvent>,
|
|
179
|
+
) => () => void) & {
|
|
180
|
+
[event in keyof InferIOOutput<MergeEvents<TEvents, TIO>>]: (
|
|
181
|
+
callback: EventNotifierSubscriptionCallback<MergeEvents<TEvents, TIO>, any>,
|
|
182
|
+
) => () => void;
|
|
118
183
|
};
|
|
119
184
|
|
|
120
185
|
public getConnection = (): WebSocketConnection => {
|
|
@@ -260,4 +325,27 @@ export class MockedRoom<
|
|
|
260
325
|
|
|
261
326
|
this._subscriptions.observeCrdt = unsubscribe;
|
|
262
327
|
}
|
|
328
|
+
|
|
329
|
+
private _simulateEvent<TEvent extends keyof InferIOInput<MergeEvents<TEvents, TIO>>>(
|
|
330
|
+
event: TEvent,
|
|
331
|
+
data: Id<InferIOInput<MergeEvents<TEvents, TIO>>[TEvent]>,
|
|
332
|
+
) {
|
|
333
|
+
if (!this._events) return;
|
|
334
|
+
|
|
335
|
+
const type = event.toString();
|
|
336
|
+
const resolver = this._events[type as TEvent];
|
|
337
|
+
|
|
338
|
+
if (!resolver) return;
|
|
339
|
+
|
|
340
|
+
const result = resolver(data);
|
|
341
|
+
|
|
342
|
+
Object.keys(result).forEach((type) => {
|
|
343
|
+
const _type = type.toString() as keyof Partial<InferIOOutput<TIO>>;
|
|
344
|
+
const data = result[_type] as any;
|
|
345
|
+
|
|
346
|
+
if (!data) return;
|
|
347
|
+
|
|
348
|
+
this._eventNotifier.subject(_type).next(data);
|
|
349
|
+
});
|
|
350
|
+
}
|
|
263
351
|
}
|
package/src/PluvClient.ts
CHANGED
|
@@ -1,44 +1,92 @@
|
|
|
1
|
-
import type { CrdtType } from "@pluv/crdt";
|
|
2
|
-
import type { IOLike, JsonObject } from "@pluv/types";
|
|
3
|
-
import
|
|
1
|
+
import type { AbstractCrdtDocFactory, CrdtType } from "@pluv/crdt";
|
|
2
|
+
import type { InputZodLike, IOLike, JsonObject } from "@pluv/types";
|
|
3
|
+
import { PluvProcedure } from "./PluvProcedure";
|
|
4
|
+
import type { AuthEndpoint, PluvRoomAddon, PluvRoomDebug, RoomConfig, RoomEndpoints, WsEndpoint } from "./PluvRoom";
|
|
4
5
|
import { PluvRoom } from "./PluvRoom";
|
|
6
|
+
import type { PluvRouterEventConfig } from "./PluvRouter";
|
|
7
|
+
import { PluvRouter } from "./PluvRouter";
|
|
8
|
+
import type { identity } from "./utils";
|
|
5
9
|
|
|
6
|
-
export type
|
|
10
|
+
export type InferCallback<TIO extends IOLike> = (i: typeof identity) => {
|
|
11
|
+
io: (io: TIO) => TIO;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export type PluvClientOptions<
|
|
15
|
+
TIO extends IOLike,
|
|
16
|
+
TPresence extends JsonObject,
|
|
17
|
+
TStorage extends Record<string, CrdtType<any, any>>,
|
|
18
|
+
TMetadata extends JsonObject,
|
|
19
|
+
> = RoomEndpoints<TIO, TMetadata> & {
|
|
7
20
|
debug?: boolean;
|
|
21
|
+
infer: InferCallback<TIO>;
|
|
22
|
+
initialStorage?: AbstractCrdtDocFactory<TStorage>;
|
|
23
|
+
metadata?: InputZodLike<TMetadata>;
|
|
24
|
+
presence?: InputZodLike<TPresence>;
|
|
8
25
|
};
|
|
9
26
|
|
|
10
|
-
export
|
|
11
|
-
|
|
27
|
+
export type CreateRoomOptions<
|
|
28
|
+
TIO extends IOLike,
|
|
29
|
+
TPresence extends JsonObject,
|
|
30
|
+
TStorage extends Record<string, CrdtType<any, any>>,
|
|
31
|
+
TMetadata extends JsonObject,
|
|
32
|
+
TEvents extends PluvRouterEventConfig<TIO, TPresence, TStorage> = {},
|
|
33
|
+
> = {
|
|
34
|
+
addons?: readonly PluvRoomAddon<TIO, TMetadata, TPresence, TStorage>[];
|
|
35
|
+
debug?: boolean | PluvRoomDebug<TIO>;
|
|
36
|
+
initialPresence?: TPresence;
|
|
37
|
+
initialStorage?: AbstractCrdtDocFactory<TStorage>;
|
|
38
|
+
onAuthorizationFail?: (error: Error) => void;
|
|
39
|
+
router?: PluvRouter<TIO, TPresence, TStorage, TEvents>;
|
|
40
|
+
} & (keyof TMetadata extends never ? { metadata?: undefined } : { metadata: TMetadata });
|
|
41
|
+
|
|
42
|
+
export class PluvClient<
|
|
43
|
+
TIO extends IOLike = IOLike,
|
|
44
|
+
TPresence extends JsonObject = {},
|
|
45
|
+
TStorage extends Record<string, CrdtType<any, any>> = {},
|
|
46
|
+
TMetadata extends JsonObject = {},
|
|
47
|
+
> {
|
|
48
|
+
private _authEndpoint: AuthEndpoint<TMetadata> | undefined;
|
|
12
49
|
private _debug: boolean;
|
|
13
|
-
private
|
|
50
|
+
private _initialStorage?: AbstractCrdtDocFactory<TStorage>;
|
|
51
|
+
private _presence?: InputZodLike<TPresence>;
|
|
52
|
+
private _wsEndpoint: WsEndpoint<TMetadata> | undefined;
|
|
14
53
|
|
|
15
|
-
private _rooms = new Map<string, PluvRoom<TIO,
|
|
54
|
+
private _rooms = new Map<string, PluvRoom<TIO, TMetadata, TPresence, TStorage, any>>();
|
|
55
|
+
|
|
56
|
+
public get procedure(): PluvProcedure<TIO, {}, {}, TPresence, TStorage> {
|
|
57
|
+
return new PluvProcedure<TIO, {}, {}, TPresence, TStorage>();
|
|
58
|
+
}
|
|
16
59
|
|
|
17
|
-
constructor(options: PluvClientOptions<TIO>) {
|
|
18
|
-
const { authEndpoint, debug = false, wsEndpoint } = options;
|
|
60
|
+
constructor(options: PluvClientOptions<TIO, TPresence, TStorage, TMetadata>) {
|
|
61
|
+
const { authEndpoint, debug = false, initialStorage, presence, wsEndpoint } = options;
|
|
19
62
|
|
|
20
|
-
this._authEndpoint = authEndpoint as AuthEndpoint
|
|
63
|
+
this._authEndpoint = authEndpoint as AuthEndpoint<TMetadata>;
|
|
21
64
|
this._debug = debug;
|
|
65
|
+
this._initialStorage = initialStorage;
|
|
66
|
+
this._presence = presence;
|
|
22
67
|
this._wsEndpoint = wsEndpoint;
|
|
23
68
|
}
|
|
24
69
|
|
|
25
|
-
public createRoom = <
|
|
70
|
+
public createRoom = <TEvents extends PluvRouterEventConfig<TIO, TPresence, TStorage> = {}>(
|
|
26
71
|
room: string,
|
|
27
|
-
options:
|
|
28
|
-
): PluvRoom<TIO, TPresence, TStorage> => {
|
|
29
|
-
const oldRoom = this.getRoom
|
|
72
|
+
options: CreateRoomOptions<TIO, TPresence, TStorage, TMetadata, TEvents>,
|
|
73
|
+
): PluvRoom<TIO, TMetadata, TPresence, TStorage> => {
|
|
74
|
+
const oldRoom = this.getRoom(room);
|
|
30
75
|
|
|
31
76
|
if (oldRoom) return oldRoom;
|
|
32
77
|
|
|
33
|
-
const newRoom = new PluvRoom<TIO, TPresence, TStorage>(room, {
|
|
78
|
+
const newRoom = new PluvRoom<TIO, TMetadata, TPresence, TStorage, TEvents>(room, {
|
|
34
79
|
addons: options.addons,
|
|
35
80
|
authEndpoint: this._authEndpoint,
|
|
36
81
|
debug: options.debug,
|
|
37
82
|
initialPresence: options.initialPresence,
|
|
38
|
-
initialStorage:
|
|
83
|
+
initialStorage: this._initialStorage,
|
|
84
|
+
metadata: options.metadata,
|
|
39
85
|
onAuthorizationFail: options.onAuthorizationFail,
|
|
86
|
+
presence: this._presence,
|
|
87
|
+
router: options.router,
|
|
40
88
|
wsEndpoint: this._wsEndpoint,
|
|
41
|
-
} as RoomConfig<TIO, TPresence, TStorage>);
|
|
89
|
+
} as RoomConfig<TIO, TMetadata, TPresence, TStorage, TEvents>);
|
|
42
90
|
|
|
43
91
|
this._rooms.set(room, newRoom);
|
|
44
92
|
|
|
@@ -47,12 +95,12 @@ export class PluvClient<TIO extends IOLike = IOLike> {
|
|
|
47
95
|
return newRoom;
|
|
48
96
|
};
|
|
49
97
|
|
|
50
|
-
public enter = async (
|
|
98
|
+
public enter = async (
|
|
99
|
+
room: string | PluvRoom<TIO, any, any, any>,
|
|
100
|
+
): Promise<PluvRoom<TIO, TMetadata, TPresence, TStorage, any>> => {
|
|
51
101
|
const toEnter = typeof room === "string" ? this.getRoom(room) : room;
|
|
52
102
|
|
|
53
|
-
if (!toEnter) {
|
|
54
|
-
throw new Error(`Could not find room: ${room}.`);
|
|
55
|
-
}
|
|
103
|
+
if (!toEnter) throw new Error(`Could not find room: ${room}.`);
|
|
56
104
|
|
|
57
105
|
this._rooms.set(toEnter.id, toEnter);
|
|
58
106
|
|
|
@@ -63,19 +111,17 @@ export class PluvClient<TIO extends IOLike = IOLike> {
|
|
|
63
111
|
return toEnter;
|
|
64
112
|
};
|
|
65
113
|
|
|
66
|
-
public getRoom =
|
|
67
|
-
room
|
|
68
|
-
): PluvRoom<TIO, TPresence, TStorage> | null => {
|
|
69
|
-
const found = this._rooms.get(room) as PluvRoom<TIO, TPresence, TStorage> | undefined;
|
|
114
|
+
public getRoom = (room: string): PluvRoom<TIO, TMetadata, TPresence, TStorage, any> | null => {
|
|
115
|
+
const found = this._rooms.get(room) as PluvRoom<TIO, TMetadata, TPresence, TStorage, any> | undefined;
|
|
70
116
|
|
|
71
117
|
return found ?? null;
|
|
72
118
|
};
|
|
73
119
|
|
|
74
|
-
public getRooms = (): readonly PluvRoom<TIO,
|
|
120
|
+
public getRooms = (): readonly PluvRoom<TIO, TMetadata, TPresence, TStorage, any>[] => {
|
|
75
121
|
return Array.from(this._rooms.values());
|
|
76
122
|
};
|
|
77
123
|
|
|
78
|
-
public leave = async (room: string | PluvRoom<TIO, any, any>): Promise<void> => {
|
|
124
|
+
public leave = async (room: string | PluvRoom<TIO, any, any, any>): Promise<void> => {
|
|
79
125
|
const toLeave = typeof room === "string" ? this.getRoom(room) : room;
|
|
80
126
|
|
|
81
127
|
if (!toLeave) return;
|
|
@@ -87,6 +133,12 @@ export class PluvClient<TIO extends IOLike = IOLike> {
|
|
|
87
133
|
this._logDebug(`Left and deleted room: ${room}`);
|
|
88
134
|
};
|
|
89
135
|
|
|
136
|
+
public router<TEvents extends PluvRouterEventConfig<TIO, TPresence, TStorage> = {}>(
|
|
137
|
+
events: TEvents,
|
|
138
|
+
): PluvRouter<TIO, TPresence, TStorage, TEvents> {
|
|
139
|
+
return new PluvRouter<TIO, TPresence, TStorage, TEvents>(events);
|
|
140
|
+
}
|
|
141
|
+
|
|
90
142
|
private _logDebug(...data: any[]): void {
|
|
91
143
|
// eslint-disable-next-line turbo/no-undeclared-env-vars
|
|
92
144
|
if (process?.env?.NODE_ENV === "production") return;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import type { CrdtType } from "@pluv/crdt";
|
|
2
|
+
import type { EventRecord, IOLike, InputZodLike, JsonObject, ProcedureLike } from "@pluv/types";
|
|
3
|
+
import type { EventResolver, MergeEventRecords } from "./types";
|
|
4
|
+
|
|
5
|
+
export interface PluvProcedureConfig<
|
|
6
|
+
TIO extends IOLike = IOLike,
|
|
7
|
+
TInput extends JsonObject = {},
|
|
8
|
+
TOutput extends EventRecord<string, any> = {},
|
|
9
|
+
TPresence extends JsonObject = {},
|
|
10
|
+
TStorage extends Record<string, CrdtType<any, any>> = {},
|
|
11
|
+
> {
|
|
12
|
+
broadcast?: EventResolver<TIO, TInput, TOutput, TPresence, TStorage> | null;
|
|
13
|
+
input?: InputZodLike<TInput>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export class PluvProcedure<
|
|
17
|
+
TIO extends IOLike = IOLike,
|
|
18
|
+
TInput extends JsonObject = {},
|
|
19
|
+
TOutput extends EventRecord<string, any> = {},
|
|
20
|
+
TPresence extends JsonObject = {},
|
|
21
|
+
TStorage extends Record<string, CrdtType<any, any>> = {},
|
|
22
|
+
TFilled extends "input" | "broadcast" | "" = "",
|
|
23
|
+
> {
|
|
24
|
+
private _broadcast: EventResolver<TIO, TInput, Partial<TOutput>, TPresence, TStorage> | null = null;
|
|
25
|
+
private _input: InputZodLike<TInput> | null = null;
|
|
26
|
+
|
|
27
|
+
public get config(): ProcedureLike<TInput, TOutput>["config"] {
|
|
28
|
+
return {
|
|
29
|
+
broadcast: this._broadcast?.bind(this) ?? null,
|
|
30
|
+
input: this._input ?? null,
|
|
31
|
+
resolver: this._resolver(),
|
|
32
|
+
} as ProcedureLike<TInput, TOutput>["config"];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
constructor(config: PluvProcedureConfig<TIO, TInput, TOutput, TPresence, TStorage> = {}) {
|
|
36
|
+
const { broadcast, input } = config;
|
|
37
|
+
|
|
38
|
+
this._broadcast = broadcast ?? null;
|
|
39
|
+
this._input = input ?? null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
public broadcast<TResult extends EventRecord<string, any> = {}>(
|
|
43
|
+
resolver: EventResolver<TIO, TInput, TResult, TPresence, TStorage>,
|
|
44
|
+
): Omit<
|
|
45
|
+
PluvProcedure<
|
|
46
|
+
TIO,
|
|
47
|
+
TInput,
|
|
48
|
+
MergeEventRecords<[TOutput, TResult]>,
|
|
49
|
+
TPresence,
|
|
50
|
+
TStorage,
|
|
51
|
+
TFilled | "input" | "broadcast"
|
|
52
|
+
>,
|
|
53
|
+
TFilled | "input" | "broadcast"
|
|
54
|
+
> {
|
|
55
|
+
const { broadcast } = this.config;
|
|
56
|
+
|
|
57
|
+
if (!!broadcast) throw new Error("Broadcast was already defined for this procedure");
|
|
58
|
+
|
|
59
|
+
return new PluvProcedure<
|
|
60
|
+
TIO,
|
|
61
|
+
TInput,
|
|
62
|
+
MergeEventRecords<[TOutput, TResult]>,
|
|
63
|
+
TPresence,
|
|
64
|
+
TStorage,
|
|
65
|
+
TFilled | "input" | "broadcast"
|
|
66
|
+
>({
|
|
67
|
+
...(this.config as any),
|
|
68
|
+
broadcast: resolver as any,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
public input<TData extends JsonObject>(
|
|
73
|
+
input: InputZodLike<TData>,
|
|
74
|
+
): Omit<PluvProcedure<TIO, TData, {}, TPresence, TStorage, TFilled | "input">, TFilled | "input"> {
|
|
75
|
+
return new PluvProcedure<TIO, TData, {}, TPresence, TStorage, TFilled | "input">({ input });
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
private _resolver(): EventResolver<TIO, TInput, TOutput, TPresence, TStorage> {
|
|
79
|
+
return (data, context) => this._broadcast?.(data, context) as TOutput;
|
|
80
|
+
}
|
|
81
|
+
}
|