@pluv/types 1.0.2 → 2.0.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 +8 -0
- package/dist/index.d.mts +132 -1
- package/dist/index.d.ts +132 -1
- package/dist/index.js +20 -0
- package/dist/index.mjs +11 -0
- package/package.json +6 -3
- package/src/index.ts +14 -0
- package/src/pluv/crdt.ts +58 -0
- package/src/pluv/enums.ts +9 -0
- package/src/pluv/index.ts +4 -0
- package/src/pluv/room.ts +156 -0
- package/src/{pluv.ts → pluv/shared.ts} +25 -1
- package/tsconfig.json +6 -1
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
> @pluv/types@
|
|
2
|
+
> @pluv/types@2.0.1 build /home/runner/work/pluv/pluv/packages/types
|
|
3
3
|
> tsup src/index.ts --format esm,cjs --dts
|
|
4
4
|
|
|
5
5
|
[34mCLI[39m Building entry: src/index.ts
|
|
@@ -8,11 +8,11 @@
|
|
|
8
8
|
[34mCLI[39m Target: es6
|
|
9
9
|
[34mESM[39m Build start
|
|
10
10
|
[34mCJS[39m Build start
|
|
11
|
-
[32mCJS[39m [1mdist/index.js [22m[
|
|
12
|
-
[32mCJS[39m ⚡️ Build success in
|
|
13
|
-
[32mESM[39m [1mdist/index.mjs [22m[
|
|
14
|
-
[32mESM[39m ⚡️ Build success in
|
|
11
|
+
[32mCJS[39m [1mdist/index.js [22m[32m1.21 KB[39m
|
|
12
|
+
[32mCJS[39m ⚡️ Build success in 35ms
|
|
13
|
+
[32mESM[39m [1mdist/index.mjs [22m[32m197.00 B[39m
|
|
14
|
+
[32mESM[39m ⚡️ Build success in 36ms
|
|
15
15
|
[34mDTS[39m Build start
|
|
16
|
-
[32mDTS[39m ⚡️ Build success in
|
|
17
|
-
[32mDTS[39m [1mdist/index.d.mts [22m[
|
|
18
|
-
[32mDTS[39m [1mdist/index.d.ts [22m[
|
|
16
|
+
[32mDTS[39m ⚡️ Build success in 1251ms
|
|
17
|
+
[32mDTS[39m [1mdist/index.d.mts [22m[32m14.71 KB[39m
|
|
18
|
+
[32mDTS[39m [1mdist/index.d.ts [22m[32m14.71 KB[39m
|
package/CHANGELOG.md
CHANGED
package/dist/index.d.mts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { Subject } from 'wonka';
|
|
2
|
+
|
|
1
3
|
type Constructable = {
|
|
2
4
|
new (...args: any[]): any;
|
|
3
5
|
};
|
|
@@ -40,6 +42,55 @@ type Json = JsonPrimitive | Json[] | readonly Json[] | {
|
|
|
40
42
|
type JsonObject = Record<string, Json>;
|
|
41
43
|
type UnionToIntersection<U> = (U extends any ? (arg: U) => any : never) extends (arg: infer I) => void ? I : never;
|
|
42
44
|
|
|
45
|
+
type CrdtType<TValue extends unknown, TJson extends unknown = any> = Omit<TValue, "__pluvType"> & {
|
|
46
|
+
__pluvType: () => TJson;
|
|
47
|
+
};
|
|
48
|
+
type InferCrdtJson<T extends unknown> = T extends CrdtType<any, infer IJson> ? InferCrdtJson<IJson> : T extends Record<string, any> ? {
|
|
49
|
+
[P in keyof T]: InferCrdtJson<T[P]>;
|
|
50
|
+
} : T extends (infer IJson)[] ? InferCrdtJson<IJson>[] : T extends readonly (infer IJson)[] ? readonly InferCrdtJson<IJson>[] : T extends Json ? T : string;
|
|
51
|
+
interface DocApplyEncodedStateParams {
|
|
52
|
+
origin?: string;
|
|
53
|
+
update?: Maybe<string | Uint8Array>;
|
|
54
|
+
}
|
|
55
|
+
interface DocBatchApplyEncodedStateParams {
|
|
56
|
+
origin?: string;
|
|
57
|
+
updates?: Maybe<readonly Maybe<string | Uint8Array>[]>;
|
|
58
|
+
}
|
|
59
|
+
interface DocSubscribeCallbackParams<TStorage extends Record<string, CrdtType<any, any>>> {
|
|
60
|
+
doc: CrdtDocLike<TStorage>;
|
|
61
|
+
local: boolean;
|
|
62
|
+
origin?: string | null;
|
|
63
|
+
update: string;
|
|
64
|
+
}
|
|
65
|
+
interface CrdtDocLike<TStorage extends Record<string, CrdtType<any, any>>> {
|
|
66
|
+
applyEncodedState(params: DocApplyEncodedStateParams): this;
|
|
67
|
+
batchApplyEncodedState(params: DocBatchApplyEncodedStateParams): this;
|
|
68
|
+
canRedo(): boolean;
|
|
69
|
+
canUndo(): boolean;
|
|
70
|
+
destroy(): void;
|
|
71
|
+
get(key?: undefined): TStorage;
|
|
72
|
+
get<TKey extends keyof TStorage>(key: TKey): TStorage[TKey];
|
|
73
|
+
getEncodedState(): string;
|
|
74
|
+
isEmpty(): boolean;
|
|
75
|
+
rebuildStorage(reference: TStorage): this;
|
|
76
|
+
redo(): this;
|
|
77
|
+
subscribe(listener: (params: DocSubscribeCallbackParams<TStorage>) => void): () => void;
|
|
78
|
+
toJson(): InferCrdtJson<TStorage>;
|
|
79
|
+
toJson<TKey extends keyof TStorage>(type: TKey): InferCrdtJson<TStorage[TKey]>;
|
|
80
|
+
track(): this;
|
|
81
|
+
transact(fn: () => void, origin?: string): this;
|
|
82
|
+
undo(): this;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
declare const ConnectionState: {
|
|
86
|
+
readonly Closed: "Closed";
|
|
87
|
+
readonly Connecting: "Connecting";
|
|
88
|
+
readonly Open: "Open";
|
|
89
|
+
readonly Unavailable: "Unavailable";
|
|
90
|
+
readonly Untouched: "Untouched";
|
|
91
|
+
};
|
|
92
|
+
type ConnectionState = (typeof ConnectionState)[keyof typeof ConnectionState];
|
|
93
|
+
|
|
43
94
|
type BaseUser = {
|
|
44
95
|
id: string;
|
|
45
96
|
};
|
|
@@ -178,5 +229,85 @@ type InferEventMessage<TEvents = EventRecord<string, any>, TEvent extends keyof
|
|
|
178
229
|
type IOEventMessage<TIO extends IOLike<any, any>, TEvent extends keyof InferIOOutput<TIO> = keyof InferIOOutput<TIO>> = Id<{
|
|
179
230
|
room: string;
|
|
180
231
|
} & InferEventMessage<InferIOOutput<TIO>, TEvent> & (InferEventMessage<InferIOOutput<TIO>, TEvent>["type"] extends "$error" ? Partial<IOAuthorizeEventMessage<TIO>> : IOAuthorizeEventMessage<TIO>)>;
|
|
232
|
+
type PluvRouterEventConfig = {
|
|
233
|
+
[P: string]: Pick<ProcedureLike<any, any>, "config">;
|
|
234
|
+
};
|
|
235
|
+
type MergeEvents<TClientEvents extends PluvRouterEventConfig, TServerIO extends IOLike<any, any>> = TServerIO extends IOLike<infer IAuthorize, infer IServerEvents> ? IOLike<IAuthorize, {
|
|
236
|
+
[P in keyof TClientEvents]: TClientEvents[P] extends ProcedureLike<infer IClientInput, infer IClientOutput> ? {
|
|
237
|
+
[K in keyof IClientOutput]: K extends keyof IServerEvents ? IServerEvents[K] : ProcedureLike<IClientInput, Id<Pick<IClientOutput, K>>>;
|
|
238
|
+
}[keyof IClientOutput] : never;
|
|
239
|
+
} & IServerEvents> : never;
|
|
240
|
+
|
|
241
|
+
interface AuthorizationState<TIO extends IOLike> {
|
|
242
|
+
token: string | null;
|
|
243
|
+
user: Id<InferIOAuthorizeUser<InferIOAuthorize<TIO>>> | null;
|
|
244
|
+
}
|
|
245
|
+
type EventNotifierSubscriptionCallback<TIO extends IOLike<any, any>, TEvent extends keyof InferIOOutput<TIO>> = (value: Id<IOEventMessage<TIO, TEvent>>) => void;
|
|
246
|
+
type OtherNotifierSubscriptionCallback<TIO extends IOLike, TPresence extends JsonObject> = (value: Id<UserInfo<TIO, TPresence>> | null) => void;
|
|
247
|
+
interface StateNotifierSubjects<TIO extends IOLike, TPresence extends JsonObject> {
|
|
248
|
+
connection: Subject<Id<WebSocketState<TIO>>>;
|
|
249
|
+
"my-presence": Subject<TPresence | null>;
|
|
250
|
+
myself: Subject<Readonly<Id<UserInfo<TIO>>> | null>;
|
|
251
|
+
others: Subject<readonly Id<UserInfo<TIO>>[]>;
|
|
252
|
+
"storage-loaded": Subject<boolean>;
|
|
253
|
+
}
|
|
254
|
+
type InferSubjectValue<TIO extends IOLike, TPresence extends JsonObject, TSubject extends keyof StateNotifierSubjects<TIO, TPresence>> = StateNotifierSubjects<TIO, TPresence>[TSubject] extends Subject<infer IValue> ? Id<IValue> : never;
|
|
255
|
+
type SubscriptionCallback<TIO extends IOLike, TPresence extends JsonObject, TSubject extends keyof StateNotifierSubjects<TIO, TPresence>> = (value: InferSubjectValue<TIO, TPresence, TSubject>) => void;
|
|
256
|
+
type UpdateMyPresenceAction<TPresence extends JsonObject> = Partial<TPresence> | ((oldPresence: TPresence | null) => Partial<TPresence>);
|
|
257
|
+
interface UserInfo<TIO extends IOLike, TPresence extends JsonObject = {}> {
|
|
258
|
+
connectionId: string;
|
|
259
|
+
presence: TPresence;
|
|
260
|
+
user: Id<InferIOAuthorizeUser<InferIOAuthorize<TIO>>>;
|
|
261
|
+
}
|
|
262
|
+
interface WebSocketConnection {
|
|
263
|
+
/**
|
|
264
|
+
* @description How many times a connection attempt was made. This will increment upon each
|
|
265
|
+
* unsuccessful attempt, and will reset upon a successful connection.
|
|
266
|
+
* @date April 13, 2025
|
|
267
|
+
*/
|
|
268
|
+
attempts: number;
|
|
269
|
+
/**
|
|
270
|
+
* @description How many times the user has connected to the room. This can be more than 1 in
|
|
271
|
+
* the case of reconnects. This is tracked because on the very first connection (i.e. when
|
|
272
|
+
* count is 0) we don't want to send the storage state as an update to the room and overwrite
|
|
273
|
+
* the upstream state.
|
|
274
|
+
* @date April 13, 2025
|
|
275
|
+
*/
|
|
276
|
+
count: number;
|
|
277
|
+
id: string | null;
|
|
278
|
+
state: ConnectionState;
|
|
279
|
+
}
|
|
280
|
+
interface WebSocketState<TIO extends IOLike> {
|
|
281
|
+
authorization: AuthorizationState<TIO>;
|
|
282
|
+
connection: WebSocketConnection;
|
|
283
|
+
webSocket: WebSocket | null;
|
|
284
|
+
}
|
|
285
|
+
interface RoomLike<TIO extends IOLike, TPresence extends JsonObject = {}, TStorage extends Record<string, CrdtType<any, any>> = {}, TEvents extends PluvRouterEventConfig = {}> {
|
|
286
|
+
id: string;
|
|
287
|
+
storageLoaded: boolean;
|
|
288
|
+
broadcast<TEvent extends keyof InferIOInput<MergeEvents<TEvents, TIO>>>(event: TEvent, data: Id<InferIOInput<MergeEvents<TEvents, TIO>>[TEvent]>): void;
|
|
289
|
+
canRedo(): boolean;
|
|
290
|
+
canUndo(): boolean;
|
|
291
|
+
getDoc(): CrdtDocLike<TStorage>;
|
|
292
|
+
event<TEvent extends keyof InferIOOutput<MergeEvents<TEvents, TIO>>>(event: TEvent, callback: EventNotifierSubscriptionCallback<MergeEvents<TEvents, TIO>, TEvent>): () => void;
|
|
293
|
+
getConnection(): WebSocketConnection;
|
|
294
|
+
getMyPresence(): TPresence;
|
|
295
|
+
getMyself(): Id<UserInfo<TIO, TPresence>> | null;
|
|
296
|
+
getOther(connectionId: string): Id<UserInfo<TIO, TPresence>> | null;
|
|
297
|
+
getOthers(): readonly Id<UserInfo<TIO, TPresence>>[];
|
|
298
|
+
getStorage<TKey extends keyof TStorage>(type: TKey): TStorage[TKey] | null;
|
|
299
|
+
getStorageJson(): InferCrdtJson<TStorage> | null;
|
|
300
|
+
getStorageJson<TKey extends keyof TStorage>(type: TKey): InferCrdtJson<TStorage[TKey]> | null;
|
|
301
|
+
other(connectionId: string, callback: OtherNotifierSubscriptionCallback<TIO, TPresence>): () => void;
|
|
302
|
+
redo(): void;
|
|
303
|
+
storage<TKey extends keyof TStorage>(key: TKey, fn: (value: InferCrdtJson<TStorage[TKey]>) => void): () => void;
|
|
304
|
+
storageRoot(fn: (value: {
|
|
305
|
+
[P in keyof TStorage]: InferCrdtJson<TStorage[P]>;
|
|
306
|
+
}) => void): () => void;
|
|
307
|
+
subscribe<TSubject extends keyof StateNotifierSubjects<TIO, TPresence>>(name: TSubject, callback: SubscriptionCallback<TIO, TPresence, TSubject>): () => void;
|
|
308
|
+
transact(fn: (storage: TStorage) => void, origin?: string): void;
|
|
309
|
+
undo(): void;
|
|
310
|
+
updateMyPresence(presence: UpdateMyPresenceAction<TPresence>): void;
|
|
311
|
+
}
|
|
181
312
|
|
|
182
|
-
export type
|
|
313
|
+
export { type BaseClientEventRecord, type BaseClientMessage, type BaseIOEventRecord, type BaseUser, ConnectionState, type Constructable, type CrdtDocLike, type CrdtType, type DeepPartial, type DeepWriteable, type DocApplyEncodedStateParams, type DocBatchApplyEncodedStateParams, type DocSubscribeCallbackParams, type EventMessage, type EventNotifierSubscriptionCallback, type EventRecord, type GetEventMessage, type IOAuthorize, type IOAuthorizeEventMessage, type IOEventMessage, type IOLike, type IORouterLike, type Id, type InferEventMessage, type InferEventsInput, type InferEventsOutput, type InferIOAuthorize, type InferIOAuthorizeUser, type InferIOInput, type InferIOOutput, type InputZodLike, type Json, type JsonObject, type JsonPrimitive, type Maybe, type MaybePromise, type MaybeReadonlyArray, type MergeEvents, type NonNilProps, type OptionalProps, type OtherNotifierSubscriptionCallback, type ProcedureLike, type RoomLike, type Spread, type SpreadProperties, type UndefinedProps, type UpdateMyPresenceAction, type UserInfo, type WebSocketConnection, type WebSocketState };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { Subject } from 'wonka';
|
|
2
|
+
|
|
1
3
|
type Constructable = {
|
|
2
4
|
new (...args: any[]): any;
|
|
3
5
|
};
|
|
@@ -40,6 +42,55 @@ type Json = JsonPrimitive | Json[] | readonly Json[] | {
|
|
|
40
42
|
type JsonObject = Record<string, Json>;
|
|
41
43
|
type UnionToIntersection<U> = (U extends any ? (arg: U) => any : never) extends (arg: infer I) => void ? I : never;
|
|
42
44
|
|
|
45
|
+
type CrdtType<TValue extends unknown, TJson extends unknown = any> = Omit<TValue, "__pluvType"> & {
|
|
46
|
+
__pluvType: () => TJson;
|
|
47
|
+
};
|
|
48
|
+
type InferCrdtJson<T extends unknown> = T extends CrdtType<any, infer IJson> ? InferCrdtJson<IJson> : T extends Record<string, any> ? {
|
|
49
|
+
[P in keyof T]: InferCrdtJson<T[P]>;
|
|
50
|
+
} : T extends (infer IJson)[] ? InferCrdtJson<IJson>[] : T extends readonly (infer IJson)[] ? readonly InferCrdtJson<IJson>[] : T extends Json ? T : string;
|
|
51
|
+
interface DocApplyEncodedStateParams {
|
|
52
|
+
origin?: string;
|
|
53
|
+
update?: Maybe<string | Uint8Array>;
|
|
54
|
+
}
|
|
55
|
+
interface DocBatchApplyEncodedStateParams {
|
|
56
|
+
origin?: string;
|
|
57
|
+
updates?: Maybe<readonly Maybe<string | Uint8Array>[]>;
|
|
58
|
+
}
|
|
59
|
+
interface DocSubscribeCallbackParams<TStorage extends Record<string, CrdtType<any, any>>> {
|
|
60
|
+
doc: CrdtDocLike<TStorage>;
|
|
61
|
+
local: boolean;
|
|
62
|
+
origin?: string | null;
|
|
63
|
+
update: string;
|
|
64
|
+
}
|
|
65
|
+
interface CrdtDocLike<TStorage extends Record<string, CrdtType<any, any>>> {
|
|
66
|
+
applyEncodedState(params: DocApplyEncodedStateParams): this;
|
|
67
|
+
batchApplyEncodedState(params: DocBatchApplyEncodedStateParams): this;
|
|
68
|
+
canRedo(): boolean;
|
|
69
|
+
canUndo(): boolean;
|
|
70
|
+
destroy(): void;
|
|
71
|
+
get(key?: undefined): TStorage;
|
|
72
|
+
get<TKey extends keyof TStorage>(key: TKey): TStorage[TKey];
|
|
73
|
+
getEncodedState(): string;
|
|
74
|
+
isEmpty(): boolean;
|
|
75
|
+
rebuildStorage(reference: TStorage): this;
|
|
76
|
+
redo(): this;
|
|
77
|
+
subscribe(listener: (params: DocSubscribeCallbackParams<TStorage>) => void): () => void;
|
|
78
|
+
toJson(): InferCrdtJson<TStorage>;
|
|
79
|
+
toJson<TKey extends keyof TStorage>(type: TKey): InferCrdtJson<TStorage[TKey]>;
|
|
80
|
+
track(): this;
|
|
81
|
+
transact(fn: () => void, origin?: string): this;
|
|
82
|
+
undo(): this;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
declare const ConnectionState: {
|
|
86
|
+
readonly Closed: "Closed";
|
|
87
|
+
readonly Connecting: "Connecting";
|
|
88
|
+
readonly Open: "Open";
|
|
89
|
+
readonly Unavailable: "Unavailable";
|
|
90
|
+
readonly Untouched: "Untouched";
|
|
91
|
+
};
|
|
92
|
+
type ConnectionState = (typeof ConnectionState)[keyof typeof ConnectionState];
|
|
93
|
+
|
|
43
94
|
type BaseUser = {
|
|
44
95
|
id: string;
|
|
45
96
|
};
|
|
@@ -178,5 +229,85 @@ type InferEventMessage<TEvents = EventRecord<string, any>, TEvent extends keyof
|
|
|
178
229
|
type IOEventMessage<TIO extends IOLike<any, any>, TEvent extends keyof InferIOOutput<TIO> = keyof InferIOOutput<TIO>> = Id<{
|
|
179
230
|
room: string;
|
|
180
231
|
} & InferEventMessage<InferIOOutput<TIO>, TEvent> & (InferEventMessage<InferIOOutput<TIO>, TEvent>["type"] extends "$error" ? Partial<IOAuthorizeEventMessage<TIO>> : IOAuthorizeEventMessage<TIO>)>;
|
|
232
|
+
type PluvRouterEventConfig = {
|
|
233
|
+
[P: string]: Pick<ProcedureLike<any, any>, "config">;
|
|
234
|
+
};
|
|
235
|
+
type MergeEvents<TClientEvents extends PluvRouterEventConfig, TServerIO extends IOLike<any, any>> = TServerIO extends IOLike<infer IAuthorize, infer IServerEvents> ? IOLike<IAuthorize, {
|
|
236
|
+
[P in keyof TClientEvents]: TClientEvents[P] extends ProcedureLike<infer IClientInput, infer IClientOutput> ? {
|
|
237
|
+
[K in keyof IClientOutput]: K extends keyof IServerEvents ? IServerEvents[K] : ProcedureLike<IClientInput, Id<Pick<IClientOutput, K>>>;
|
|
238
|
+
}[keyof IClientOutput] : never;
|
|
239
|
+
} & IServerEvents> : never;
|
|
240
|
+
|
|
241
|
+
interface AuthorizationState<TIO extends IOLike> {
|
|
242
|
+
token: string | null;
|
|
243
|
+
user: Id<InferIOAuthorizeUser<InferIOAuthorize<TIO>>> | null;
|
|
244
|
+
}
|
|
245
|
+
type EventNotifierSubscriptionCallback<TIO extends IOLike<any, any>, TEvent extends keyof InferIOOutput<TIO>> = (value: Id<IOEventMessage<TIO, TEvent>>) => void;
|
|
246
|
+
type OtherNotifierSubscriptionCallback<TIO extends IOLike, TPresence extends JsonObject> = (value: Id<UserInfo<TIO, TPresence>> | null) => void;
|
|
247
|
+
interface StateNotifierSubjects<TIO extends IOLike, TPresence extends JsonObject> {
|
|
248
|
+
connection: Subject<Id<WebSocketState<TIO>>>;
|
|
249
|
+
"my-presence": Subject<TPresence | null>;
|
|
250
|
+
myself: Subject<Readonly<Id<UserInfo<TIO>>> | null>;
|
|
251
|
+
others: Subject<readonly Id<UserInfo<TIO>>[]>;
|
|
252
|
+
"storage-loaded": Subject<boolean>;
|
|
253
|
+
}
|
|
254
|
+
type InferSubjectValue<TIO extends IOLike, TPresence extends JsonObject, TSubject extends keyof StateNotifierSubjects<TIO, TPresence>> = StateNotifierSubjects<TIO, TPresence>[TSubject] extends Subject<infer IValue> ? Id<IValue> : never;
|
|
255
|
+
type SubscriptionCallback<TIO extends IOLike, TPresence extends JsonObject, TSubject extends keyof StateNotifierSubjects<TIO, TPresence>> = (value: InferSubjectValue<TIO, TPresence, TSubject>) => void;
|
|
256
|
+
type UpdateMyPresenceAction<TPresence extends JsonObject> = Partial<TPresence> | ((oldPresence: TPresence | null) => Partial<TPresence>);
|
|
257
|
+
interface UserInfo<TIO extends IOLike, TPresence extends JsonObject = {}> {
|
|
258
|
+
connectionId: string;
|
|
259
|
+
presence: TPresence;
|
|
260
|
+
user: Id<InferIOAuthorizeUser<InferIOAuthorize<TIO>>>;
|
|
261
|
+
}
|
|
262
|
+
interface WebSocketConnection {
|
|
263
|
+
/**
|
|
264
|
+
* @description How many times a connection attempt was made. This will increment upon each
|
|
265
|
+
* unsuccessful attempt, and will reset upon a successful connection.
|
|
266
|
+
* @date April 13, 2025
|
|
267
|
+
*/
|
|
268
|
+
attempts: number;
|
|
269
|
+
/**
|
|
270
|
+
* @description How many times the user has connected to the room. This can be more than 1 in
|
|
271
|
+
* the case of reconnects. This is tracked because on the very first connection (i.e. when
|
|
272
|
+
* count is 0) we don't want to send the storage state as an update to the room and overwrite
|
|
273
|
+
* the upstream state.
|
|
274
|
+
* @date April 13, 2025
|
|
275
|
+
*/
|
|
276
|
+
count: number;
|
|
277
|
+
id: string | null;
|
|
278
|
+
state: ConnectionState;
|
|
279
|
+
}
|
|
280
|
+
interface WebSocketState<TIO extends IOLike> {
|
|
281
|
+
authorization: AuthorizationState<TIO>;
|
|
282
|
+
connection: WebSocketConnection;
|
|
283
|
+
webSocket: WebSocket | null;
|
|
284
|
+
}
|
|
285
|
+
interface RoomLike<TIO extends IOLike, TPresence extends JsonObject = {}, TStorage extends Record<string, CrdtType<any, any>> = {}, TEvents extends PluvRouterEventConfig = {}> {
|
|
286
|
+
id: string;
|
|
287
|
+
storageLoaded: boolean;
|
|
288
|
+
broadcast<TEvent extends keyof InferIOInput<MergeEvents<TEvents, TIO>>>(event: TEvent, data: Id<InferIOInput<MergeEvents<TEvents, TIO>>[TEvent]>): void;
|
|
289
|
+
canRedo(): boolean;
|
|
290
|
+
canUndo(): boolean;
|
|
291
|
+
getDoc(): CrdtDocLike<TStorage>;
|
|
292
|
+
event<TEvent extends keyof InferIOOutput<MergeEvents<TEvents, TIO>>>(event: TEvent, callback: EventNotifierSubscriptionCallback<MergeEvents<TEvents, TIO>, TEvent>): () => void;
|
|
293
|
+
getConnection(): WebSocketConnection;
|
|
294
|
+
getMyPresence(): TPresence;
|
|
295
|
+
getMyself(): Id<UserInfo<TIO, TPresence>> | null;
|
|
296
|
+
getOther(connectionId: string): Id<UserInfo<TIO, TPresence>> | null;
|
|
297
|
+
getOthers(): readonly Id<UserInfo<TIO, TPresence>>[];
|
|
298
|
+
getStorage<TKey extends keyof TStorage>(type: TKey): TStorage[TKey] | null;
|
|
299
|
+
getStorageJson(): InferCrdtJson<TStorage> | null;
|
|
300
|
+
getStorageJson<TKey extends keyof TStorage>(type: TKey): InferCrdtJson<TStorage[TKey]> | null;
|
|
301
|
+
other(connectionId: string, callback: OtherNotifierSubscriptionCallback<TIO, TPresence>): () => void;
|
|
302
|
+
redo(): void;
|
|
303
|
+
storage<TKey extends keyof TStorage>(key: TKey, fn: (value: InferCrdtJson<TStorage[TKey]>) => void): () => void;
|
|
304
|
+
storageRoot(fn: (value: {
|
|
305
|
+
[P in keyof TStorage]: InferCrdtJson<TStorage[P]>;
|
|
306
|
+
}) => void): () => void;
|
|
307
|
+
subscribe<TSubject extends keyof StateNotifierSubjects<TIO, TPresence>>(name: TSubject, callback: SubscriptionCallback<TIO, TPresence, TSubject>): () => void;
|
|
308
|
+
transact(fn: (storage: TStorage) => void, origin?: string): void;
|
|
309
|
+
undo(): void;
|
|
310
|
+
updateMyPresence(presence: UpdateMyPresenceAction<TPresence>): void;
|
|
311
|
+
}
|
|
181
312
|
|
|
182
|
-
export type
|
|
313
|
+
export { type BaseClientEventRecord, type BaseClientMessage, type BaseIOEventRecord, type BaseUser, ConnectionState, type Constructable, type CrdtDocLike, type CrdtType, type DeepPartial, type DeepWriteable, type DocApplyEncodedStateParams, type DocBatchApplyEncodedStateParams, type DocSubscribeCallbackParams, type EventMessage, type EventNotifierSubscriptionCallback, type EventRecord, type GetEventMessage, type IOAuthorize, type IOAuthorizeEventMessage, type IOEventMessage, type IOLike, type IORouterLike, type Id, type InferEventMessage, type InferEventsInput, type InferEventsOutput, type InferIOAuthorize, type InferIOAuthorizeUser, type InferIOInput, type InferIOOutput, type InputZodLike, type Json, type JsonObject, type JsonPrimitive, type Maybe, type MaybePromise, type MaybeReadonlyArray, type MergeEvents, type NonNilProps, type OptionalProps, type OtherNotifierSubscriptionCallback, type ProcedureLike, type RoomLike, type Spread, type SpreadProperties, type UndefinedProps, type UpdateMyPresenceAction, type UserInfo, type WebSocketConnection, type WebSocketState };
|
package/dist/index.js
CHANGED
|
@@ -3,6 +3,10 @@ var __defProp = Object.defineProperty;
|
|
|
3
3
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
5
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
6
10
|
var __copyProps = (to, from, except, desc) => {
|
|
7
11
|
if (from && typeof from === "object" || typeof from === "function") {
|
|
8
12
|
for (let key of __getOwnPropNames(from))
|
|
@@ -15,4 +19,20 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
15
19
|
|
|
16
20
|
// src/index.ts
|
|
17
21
|
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
ConnectionState: () => ConnectionState
|
|
24
|
+
});
|
|
18
25
|
module.exports = __toCommonJS(index_exports);
|
|
26
|
+
|
|
27
|
+
// src/pluv/enums.ts
|
|
28
|
+
var ConnectionState = {
|
|
29
|
+
Closed: "Closed",
|
|
30
|
+
Connecting: "Connecting",
|
|
31
|
+
Open: "Open",
|
|
32
|
+
Unavailable: "Unavailable",
|
|
33
|
+
Untouched: "Untouched"
|
|
34
|
+
};
|
|
35
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
36
|
+
0 && (module.exports = {
|
|
37
|
+
ConnectionState
|
|
38
|
+
});
|
package/dist/index.mjs
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pluv/types",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.1",
|
|
4
4
|
"description": "@pluv/io shared types",
|
|
5
5
|
"author": "leedavidcs",
|
|
6
6
|
"license": "MIT",
|
|
@@ -19,8 +19,11 @@
|
|
|
19
19
|
"eslint": "^9.25.1",
|
|
20
20
|
"tsup": "^8.4.0",
|
|
21
21
|
"typescript": "^5.8.3",
|
|
22
|
-
"@pluv/tsconfig": "^
|
|
23
|
-
"eslint-config-pluv": "^
|
|
22
|
+
"@pluv/tsconfig": "^2.0.1",
|
|
23
|
+
"eslint-config-pluv": "^2.0.1"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"wonka": "^6.3.5"
|
|
24
27
|
},
|
|
25
28
|
"scripts": {
|
|
26
29
|
"build": "tsup src/index.ts --format esm,cjs --dts",
|
package/src/index.ts
CHANGED
|
@@ -15,12 +15,19 @@ export type {
|
|
|
15
15
|
SpreadProperties,
|
|
16
16
|
UndefinedProps,
|
|
17
17
|
} from "./general";
|
|
18
|
+
export { ConnectionState } from "./pluv";
|
|
18
19
|
export type {
|
|
19
20
|
BaseClientEventRecord,
|
|
20
21
|
BaseClientMessage,
|
|
21
22
|
BaseIOEventRecord,
|
|
22
23
|
BaseUser,
|
|
24
|
+
CrdtDocLike,
|
|
25
|
+
CrdtType,
|
|
26
|
+
DocApplyEncodedStateParams,
|
|
27
|
+
DocBatchApplyEncodedStateParams,
|
|
28
|
+
DocSubscribeCallbackParams,
|
|
23
29
|
EventMessage,
|
|
30
|
+
EventNotifierSubscriptionCallback,
|
|
24
31
|
EventRecord,
|
|
25
32
|
GetEventMessage,
|
|
26
33
|
InferEventMessage,
|
|
@@ -36,5 +43,12 @@ export type {
|
|
|
36
43
|
IOEventMessage,
|
|
37
44
|
IOLike,
|
|
38
45
|
IORouterLike,
|
|
46
|
+
MergeEvents,
|
|
47
|
+
OtherNotifierSubscriptionCallback,
|
|
39
48
|
ProcedureLike,
|
|
49
|
+
RoomLike,
|
|
50
|
+
UpdateMyPresenceAction,
|
|
51
|
+
UserInfo,
|
|
52
|
+
WebSocketConnection,
|
|
53
|
+
WebSocketState,
|
|
40
54
|
} from "./pluv";
|
package/src/pluv/crdt.ts
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { Json, Maybe } from "../general";
|
|
2
|
+
|
|
3
|
+
export type CrdtType<TValue extends unknown, TJson extends unknown = any> = Omit<
|
|
4
|
+
TValue,
|
|
5
|
+
"__pluvType"
|
|
6
|
+
> & {
|
|
7
|
+
__pluvType: () => TJson;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export type InferCrdtJson<T extends unknown> =
|
|
11
|
+
T extends CrdtType<any, infer IJson>
|
|
12
|
+
? InferCrdtJson<IJson>
|
|
13
|
+
: T extends Record<string, any>
|
|
14
|
+
? { [P in keyof T]: InferCrdtJson<T[P]> }
|
|
15
|
+
: T extends (infer IJson)[]
|
|
16
|
+
? InferCrdtJson<IJson>[]
|
|
17
|
+
: T extends readonly (infer IJson)[]
|
|
18
|
+
? readonly InferCrdtJson<IJson>[]
|
|
19
|
+
: T extends Json
|
|
20
|
+
? T
|
|
21
|
+
: string;
|
|
22
|
+
|
|
23
|
+
export interface DocApplyEncodedStateParams {
|
|
24
|
+
origin?: string;
|
|
25
|
+
update?: Maybe<string | Uint8Array>;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface DocBatchApplyEncodedStateParams {
|
|
29
|
+
origin?: string;
|
|
30
|
+
updates?: Maybe<readonly Maybe<string | Uint8Array>[]>;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface DocSubscribeCallbackParams<TStorage extends Record<string, CrdtType<any, any>>> {
|
|
34
|
+
doc: CrdtDocLike<TStorage>;
|
|
35
|
+
local: boolean;
|
|
36
|
+
origin?: string | null;
|
|
37
|
+
update: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface CrdtDocLike<TStorage extends Record<string, CrdtType<any, any>>> {
|
|
41
|
+
applyEncodedState(params: DocApplyEncodedStateParams): this;
|
|
42
|
+
batchApplyEncodedState(params: DocBatchApplyEncodedStateParams): this;
|
|
43
|
+
canRedo(): boolean;
|
|
44
|
+
canUndo(): boolean;
|
|
45
|
+
destroy(): void;
|
|
46
|
+
get(key?: undefined): TStorage;
|
|
47
|
+
get<TKey extends keyof TStorage>(key: TKey): TStorage[TKey];
|
|
48
|
+
getEncodedState(): string;
|
|
49
|
+
isEmpty(): boolean;
|
|
50
|
+
rebuildStorage(reference: TStorage): this;
|
|
51
|
+
redo(): this;
|
|
52
|
+
subscribe(listener: (params: DocSubscribeCallbackParams<TStorage>) => void): () => void;
|
|
53
|
+
toJson(): InferCrdtJson<TStorage>;
|
|
54
|
+
toJson<TKey extends keyof TStorage>(type: TKey): InferCrdtJson<TStorage[TKey]>;
|
|
55
|
+
track(): this;
|
|
56
|
+
transact(fn: () => void, origin?: string): this;
|
|
57
|
+
undo(): this;
|
|
58
|
+
}
|
package/src/pluv/room.ts
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import type { Subject } from "wonka";
|
|
2
|
+
import type { ConnectionState } from "./enums";
|
|
3
|
+
import type { Id, JsonObject } from "../general";
|
|
4
|
+
import type { CrdtDocLike, CrdtType, InferCrdtJson } from "./crdt";
|
|
5
|
+
import type {
|
|
6
|
+
InferIOAuthorize,
|
|
7
|
+
InferIOAuthorizeUser,
|
|
8
|
+
InferIOInput,
|
|
9
|
+
InferIOOutput,
|
|
10
|
+
IOEventMessage,
|
|
11
|
+
IOLike,
|
|
12
|
+
MergeEvents,
|
|
13
|
+
PluvRouterEventConfig,
|
|
14
|
+
} from "./shared";
|
|
15
|
+
|
|
16
|
+
export interface AuthorizationState<TIO extends IOLike> {
|
|
17
|
+
token: string | null;
|
|
18
|
+
user: Id<InferIOAuthorizeUser<InferIOAuthorize<TIO>>> | null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export type EventNotifierSubscriptionCallback<
|
|
22
|
+
TIO extends IOLike<any, any>,
|
|
23
|
+
TEvent extends keyof InferIOOutput<TIO>,
|
|
24
|
+
> = (value: Id<IOEventMessage<TIO, TEvent>>) => void;
|
|
25
|
+
|
|
26
|
+
export type OtherNotifierSubscriptionCallback<TIO extends IOLike, TPresence extends JsonObject> = (
|
|
27
|
+
value: Id<UserInfo<TIO, TPresence>> | null,
|
|
28
|
+
) => void;
|
|
29
|
+
|
|
30
|
+
export interface StateNotifierSubjects<TIO extends IOLike, TPresence extends JsonObject> {
|
|
31
|
+
connection: Subject<Id<WebSocketState<TIO>>>;
|
|
32
|
+
"my-presence": Subject<TPresence | null>;
|
|
33
|
+
myself: Subject<Readonly<Id<UserInfo<TIO>>> | null>;
|
|
34
|
+
others: Subject<readonly Id<UserInfo<TIO>>[]>;
|
|
35
|
+
"storage-loaded": Subject<boolean>;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
type InferSubjectValue<
|
|
39
|
+
TIO extends IOLike,
|
|
40
|
+
TPresence extends JsonObject,
|
|
41
|
+
TSubject extends keyof StateNotifierSubjects<TIO, TPresence>,
|
|
42
|
+
> =
|
|
43
|
+
StateNotifierSubjects<TIO, TPresence>[TSubject] extends Subject<infer IValue>
|
|
44
|
+
? Id<IValue>
|
|
45
|
+
: never;
|
|
46
|
+
|
|
47
|
+
export type SubscriptionCallback<
|
|
48
|
+
TIO extends IOLike,
|
|
49
|
+
TPresence extends JsonObject,
|
|
50
|
+
TSubject extends keyof StateNotifierSubjects<TIO, TPresence>,
|
|
51
|
+
> = (value: InferSubjectValue<TIO, TPresence, TSubject>) => void;
|
|
52
|
+
|
|
53
|
+
export type UpdateMyPresenceAction<TPresence extends JsonObject> =
|
|
54
|
+
| Partial<TPresence>
|
|
55
|
+
| ((oldPresence: TPresence | null) => Partial<TPresence>);
|
|
56
|
+
|
|
57
|
+
export interface UserInfo<TIO extends IOLike, TPresence extends JsonObject = {}> {
|
|
58
|
+
connectionId: string;
|
|
59
|
+
presence: TPresence;
|
|
60
|
+
user: Id<InferIOAuthorizeUser<InferIOAuthorize<TIO>>>;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface WebSocketConnection {
|
|
64
|
+
/**
|
|
65
|
+
* @description How many times a connection attempt was made. This will increment upon each
|
|
66
|
+
* unsuccessful attempt, and will reset upon a successful connection.
|
|
67
|
+
* @date April 13, 2025
|
|
68
|
+
*/
|
|
69
|
+
attempts: number;
|
|
70
|
+
/**
|
|
71
|
+
* @description How many times the user has connected to the room. This can be more than 1 in
|
|
72
|
+
* the case of reconnects. This is tracked because on the very first connection (i.e. when
|
|
73
|
+
* count is 0) we don't want to send the storage state as an update to the room and overwrite
|
|
74
|
+
* the upstream state.
|
|
75
|
+
* @date April 13, 2025
|
|
76
|
+
*/
|
|
77
|
+
count: number;
|
|
78
|
+
id: string | null;
|
|
79
|
+
state: ConnectionState;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export interface WebSocketState<TIO extends IOLike> {
|
|
83
|
+
authorization: AuthorizationState<TIO>;
|
|
84
|
+
connection: WebSocketConnection;
|
|
85
|
+
webSocket: WebSocket | null;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export interface RoomLike<
|
|
89
|
+
TIO extends IOLike,
|
|
90
|
+
TPresence extends JsonObject = {},
|
|
91
|
+
TStorage extends Record<string, CrdtType<any, any>> = {},
|
|
92
|
+
TEvents extends PluvRouterEventConfig = {},
|
|
93
|
+
> {
|
|
94
|
+
id: string;
|
|
95
|
+
storageLoaded: boolean;
|
|
96
|
+
|
|
97
|
+
broadcast<TEvent extends keyof InferIOInput<MergeEvents<TEvents, TIO>>>(
|
|
98
|
+
event: TEvent,
|
|
99
|
+
data: Id<InferIOInput<MergeEvents<TEvents, TIO>>[TEvent]>,
|
|
100
|
+
): void;
|
|
101
|
+
|
|
102
|
+
canRedo(): boolean;
|
|
103
|
+
|
|
104
|
+
canUndo(): boolean;
|
|
105
|
+
|
|
106
|
+
getDoc(): CrdtDocLike<TStorage>;
|
|
107
|
+
|
|
108
|
+
event<TEvent extends keyof InferIOOutput<MergeEvents<TEvents, TIO>>>(
|
|
109
|
+
event: TEvent,
|
|
110
|
+
callback: EventNotifierSubscriptionCallback<MergeEvents<TEvents, TIO>, TEvent>,
|
|
111
|
+
): () => void;
|
|
112
|
+
|
|
113
|
+
getConnection(): WebSocketConnection;
|
|
114
|
+
|
|
115
|
+
getMyPresence(): TPresence;
|
|
116
|
+
|
|
117
|
+
getMyself(): Id<UserInfo<TIO, TPresence>> | null;
|
|
118
|
+
|
|
119
|
+
getOther(connectionId: string): Id<UserInfo<TIO, TPresence>> | null;
|
|
120
|
+
|
|
121
|
+
getOthers(): readonly Id<UserInfo<TIO, TPresence>>[];
|
|
122
|
+
|
|
123
|
+
getStorage<TKey extends keyof TStorage>(type: TKey): TStorage[TKey] | null;
|
|
124
|
+
|
|
125
|
+
getStorageJson(): InferCrdtJson<TStorage> | null;
|
|
126
|
+
getStorageJson<TKey extends keyof TStorage>(type: TKey): InferCrdtJson<TStorage[TKey]> | null;
|
|
127
|
+
|
|
128
|
+
other(
|
|
129
|
+
connectionId: string,
|
|
130
|
+
callback: OtherNotifierSubscriptionCallback<TIO, TPresence>,
|
|
131
|
+
): () => void;
|
|
132
|
+
|
|
133
|
+
redo(): void;
|
|
134
|
+
|
|
135
|
+
storage<TKey extends keyof TStorage>(
|
|
136
|
+
key: TKey,
|
|
137
|
+
fn: (value: InferCrdtJson<TStorage[TKey]>) => void,
|
|
138
|
+
): () => void;
|
|
139
|
+
|
|
140
|
+
storageRoot(
|
|
141
|
+
fn: (value: {
|
|
142
|
+
[P in keyof TStorage]: InferCrdtJson<TStorage[P]>;
|
|
143
|
+
}) => void,
|
|
144
|
+
): () => void;
|
|
145
|
+
|
|
146
|
+
subscribe<TSubject extends keyof StateNotifierSubjects<TIO, TPresence>>(
|
|
147
|
+
name: TSubject,
|
|
148
|
+
callback: SubscriptionCallback<TIO, TPresence, TSubject>,
|
|
149
|
+
): () => void;
|
|
150
|
+
|
|
151
|
+
transact(fn: (storage: TStorage) => void, origin?: string): void;
|
|
152
|
+
|
|
153
|
+
undo(): void;
|
|
154
|
+
|
|
155
|
+
updateMyPresence(presence: UpdateMyPresenceAction<TPresence>): void;
|
|
156
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Id, JsonObject, MaybePromise, UnionToIntersection } from "
|
|
1
|
+
import type { Id, JsonObject, MaybePromise, UnionToIntersection } from "../general";
|
|
2
2
|
|
|
3
3
|
export type BaseUser = {
|
|
4
4
|
id: string;
|
|
@@ -183,3 +183,27 @@ export type IOEventMessage<
|
|
|
183
183
|
? Partial<IOAuthorizeEventMessage<TIO>>
|
|
184
184
|
: IOAuthorizeEventMessage<TIO>)
|
|
185
185
|
>;
|
|
186
|
+
|
|
187
|
+
export type PluvRouterEventConfig = { [P: string]: Pick<ProcedureLike<any, any>, "config"> };
|
|
188
|
+
|
|
189
|
+
export type MergeEvents<
|
|
190
|
+
TClientEvents extends PluvRouterEventConfig,
|
|
191
|
+
TServerIO extends IOLike<any, any>,
|
|
192
|
+
> =
|
|
193
|
+
TServerIO extends IOLike<infer IAuthorize, infer IServerEvents>
|
|
194
|
+
? IOLike<
|
|
195
|
+
IAuthorize,
|
|
196
|
+
{
|
|
197
|
+
[P in keyof TClientEvents]: TClientEvents[P] extends ProcedureLike<
|
|
198
|
+
infer IClientInput,
|
|
199
|
+
infer IClientOutput
|
|
200
|
+
>
|
|
201
|
+
? {
|
|
202
|
+
[K in keyof IClientOutput]: K extends keyof IServerEvents
|
|
203
|
+
? IServerEvents[K]
|
|
204
|
+
: ProcedureLike<IClientInput, Id<Pick<IClientOutput, K>>>;
|
|
205
|
+
}[keyof IClientOutput]
|
|
206
|
+
: never;
|
|
207
|
+
} & IServerEvents
|
|
208
|
+
>
|
|
209
|
+
: never;
|
package/tsconfig.json
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"extends": "@pluv/tsconfig/library.json",
|
|
3
3
|
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
|
|
4
|
-
"exclude": ["node_modules"]
|
|
4
|
+
"exclude": ["node_modules"],
|
|
5
|
+
"compilerOptions": {
|
|
6
|
+
"lib": ["dom", "esnext"],
|
|
7
|
+
"module": "esnext",
|
|
8
|
+
"target": "es6"
|
|
9
|
+
}
|
|
5
10
|
}
|