@pluv/io 0.0.0-canary-20261027014949
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/LICENSE +21 -0
- package/README.md +18 -0
- package/dist/index.d.mts +952 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +3591 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +50 -0
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,952 @@
|
|
|
1
|
+
import { AbstractCrdtDocFactory, CrdtLibraryType, HasStorage, InferDocLike } from "@pluv/crdt";
|
|
2
|
+
import { BaseUser, BaseUser as BaseUser$1, CrdtDocFactory, CrdtDocLike, CrdtDocLike as CrdtDocLike$1, EventMessage, EventRecord, EventRecord as EventRecord$1, HasRequiredProperty, IOAuthorize, IOEventMessage, IOEventMessage as IOEventMessage$1, IOLike, IOLikeDefs, IORouterLike, Id, InferEventMessage, InferEventsOutput, InferIOAuthorize, InferIOAuthorizeUser, InferIOInput, InferIOOutput, InferTreatyProcedureInput, InferTreatyUser, JsonObject, ListUsersCursor, ListUsersError, ListUsersOptions, ListUsersResult, Maybe, MaybePromise, OnGetOperator, OnGetOperator as OnGetOperator$1, OperatorUser, OperatorUser as OperatorUser$1, ParticipantKind, ParticipantKind as ParticipantKind$1, PresenceProcedureProxy, PresenceSeq, ProcedureLike, ProcedureLike as ProcedureLike$1, RoomStatsByKind, ServerOriginEvent, SessionPresence, SetKey, SetKey as SetKey$1, StandardSchemaV1, StandardSchemaV1 as StandardSchemaV1$1, TreatyLike, TreatyProcedureInputJson, TreatyProcedureLike } from "@pluv/types";
|
|
3
|
+
export declare abstract class AbstractPersistence {
|
|
4
|
+
abstract addUser(room: string, connectionId: string, user: JsonObject | null): Promise<void>;
|
|
5
|
+
abstract deleteStorageState(room: string): Promise<void>;
|
|
6
|
+
abstract deleteUser(room: string, connectionId: string): Promise<void>;
|
|
7
|
+
abstract deleteUsers(room: string): Promise<void>;
|
|
8
|
+
abstract getStorageState(room: string): Promise<string | null>;
|
|
9
|
+
abstract getUser(room: string, connectionId: string): Promise<JsonObject | null>;
|
|
10
|
+
abstract getUsers(room: string): Promise<Map<[connectionId: string][0], JsonObject | null>>;
|
|
11
|
+
abstract getUsersSize(room: string): Promise<number>;
|
|
12
|
+
abstract initialize(roomContext: any): typeof this;
|
|
13
|
+
abstract setStorageState(room: string, state: string): Promise<void>;
|
|
14
|
+
}
|
|
15
|
+
type IOPubSubEventMessage<TIO extends IOLike, TEvent extends keyof InferIOOutput<TIO> = keyof InferIOOutput<TIO>> = IOEventMessage$1<TIO, TEvent> & {
|
|
16
|
+
options?: SendMessageOptions;
|
|
17
|
+
};
|
|
18
|
+
export declare abstract class AbstractPubSub {
|
|
19
|
+
abstract publish(roomName: string, payload: IOPubSubEventMessage<any>): Promise<void>;
|
|
20
|
+
abstract subscribe(roomName: string, onMessage: (message: IOPubSubEventMessage<any>) => void): Promise<number>;
|
|
21
|
+
abstract unsubscribe(subscriptionId: number): void;
|
|
22
|
+
}
|
|
23
|
+
type AbstractEvent = {};
|
|
24
|
+
type AbstractCloseEvent = AbstractEvent & {
|
|
25
|
+
readonly code: number;
|
|
26
|
+
readonly reason: string;
|
|
27
|
+
};
|
|
28
|
+
type AbstractMessageEvent<T = any> = AbstractEvent & {
|
|
29
|
+
readonly data: T;
|
|
30
|
+
};
|
|
31
|
+
type AbstractErrorEvent = AbstractEvent & {
|
|
32
|
+
error: unknown;
|
|
33
|
+
message?: string;
|
|
34
|
+
};
|
|
35
|
+
interface AbstractEventMap {
|
|
36
|
+
close: AbstractCloseEvent;
|
|
37
|
+
message: AbstractMessageEvent;
|
|
38
|
+
error: AbstractErrorEvent;
|
|
39
|
+
}
|
|
40
|
+
type EventType = keyof AbstractEventMap;
|
|
41
|
+
interface AbstractWebSocketHandleErrorParams {
|
|
42
|
+
error: unknown;
|
|
43
|
+
message?: string;
|
|
44
|
+
room: string;
|
|
45
|
+
session?: WebSocketSession<any>;
|
|
46
|
+
}
|
|
47
|
+
type AbstractListener<TType extends keyof AbstractEventMap> = (event: AbstractEventMap[TType]) => MaybePromise<void>;
|
|
48
|
+
type InferWebSocketSource<TAbstractWebSocket extends AbstractWebSocket> = TAbstractWebSocket extends AbstractWebSocket<infer IWebSocket> ? IWebSocket : never;
|
|
49
|
+
interface AbstractWebSocketConfig {
|
|
50
|
+
persistence: AbstractPersistence;
|
|
51
|
+
platform: AbstractPlatform<any>;
|
|
52
|
+
room: string;
|
|
53
|
+
}
|
|
54
|
+
export declare abstract class AbstractWebSocket<TWebSocket = any> {
|
|
55
|
+
/** The connection is not yet open. */
|
|
56
|
+
readonly CONNECTING = 0;
|
|
57
|
+
/** The connection is open and ready to communicate. */
|
|
58
|
+
readonly OPEN = 1;
|
|
59
|
+
/** The connection is in the process of closing. */
|
|
60
|
+
readonly CLOSING = 2;
|
|
61
|
+
/** The connection is closed. */
|
|
62
|
+
readonly CLOSED = 3;
|
|
63
|
+
readonly persistence: AbstractPersistence;
|
|
64
|
+
readonly room: string;
|
|
65
|
+
readonly webSocket: TWebSocket;
|
|
66
|
+
protected readonly _platform: AbstractPlatform<this>;
|
|
67
|
+
abstract set presence(presence: JsonObject | null);
|
|
68
|
+
abstract get readyState(): 0 | 1 | 2 | 3;
|
|
69
|
+
abstract get session(): WebSocketSession<any>;
|
|
70
|
+
abstract get sessionId(): string;
|
|
71
|
+
abstract get state(): WebSocketSerializedState;
|
|
72
|
+
abstract set state(state: WebSocketSerializedState);
|
|
73
|
+
/**
|
|
74
|
+
* @description Authorized user for this socket. `null` until `register` (or hibernation
|
|
75
|
+
* reattach) has assigned one.
|
|
76
|
+
*/
|
|
77
|
+
abstract get user(): BaseUser$1 | null;
|
|
78
|
+
abstract set user(user: BaseUser$1);
|
|
79
|
+
constructor(webSocket: TWebSocket, config: AbstractWebSocketConfig);
|
|
80
|
+
/**
|
|
81
|
+
* TODO
|
|
82
|
+
* @description Return an unsubscribe method to remove the event-listener.
|
|
83
|
+
* This is preferred over adding an explicit abstract removeEventListener
|
|
84
|
+
* method, because the implementing class might have to transform the
|
|
85
|
+
* listener, and removing the same listener might be complicated as a
|
|
86
|
+
* result.
|
|
87
|
+
* @date August 28, 2022
|
|
88
|
+
*/
|
|
89
|
+
abstract addEventListener<TType extends EventType>(type: TType, handler: AbstractListener<TType>): void;
|
|
90
|
+
abstract close(code?: number, reason?: string): void;
|
|
91
|
+
abstract send(message: ArrayBuffer | ArrayBufferView | string): void;
|
|
92
|
+
abstract terminate(): void;
|
|
93
|
+
handleError(params: AbstractWebSocketHandleErrorParams): void;
|
|
94
|
+
sendMessage<TMessage extends IOEventMessage$1<any> = IOEventMessage$1<any>>(data: TMessage): void;
|
|
95
|
+
}
|
|
96
|
+
interface JWT<TUser extends BaseUser$1> {
|
|
97
|
+
kind: ParticipantKind$1;
|
|
98
|
+
operator: OperatorUser$1 | null;
|
|
99
|
+
room: string;
|
|
100
|
+
sub: string;
|
|
101
|
+
user: TUser;
|
|
102
|
+
}
|
|
103
|
+
type JWTEncodeBase<TPlatform extends AbstractPlatform<any, any>> = {
|
|
104
|
+
/**
|
|
105
|
+
* Token lifetime in milliseconds. Converted to whole seconds for JWT `exp`.
|
|
106
|
+
* @default 60_000
|
|
107
|
+
*/
|
|
108
|
+
maxAge?: number;
|
|
109
|
+
room: string;
|
|
110
|
+
} & InferInitContextType<TPlatform>;
|
|
111
|
+
type OccupantTokenIdentity<TUser extends BaseUser$1> = {
|
|
112
|
+
kind?: "user";
|
|
113
|
+
user: TUser;
|
|
114
|
+
};
|
|
115
|
+
type OperatorTokenIdentity = {
|
|
116
|
+
kind: "operator";
|
|
117
|
+
operator: OperatorUser$1 & {
|
|
118
|
+
email: string;
|
|
119
|
+
};
|
|
120
|
+
};
|
|
121
|
+
type OperatorSessionIdentity = {
|
|
122
|
+
kind: "operator";
|
|
123
|
+
operator: OperatorUser$1;
|
|
124
|
+
};
|
|
125
|
+
type JwtTokenIdentity<TUser extends BaseUser$1> = OccupantTokenIdentity<TUser> | (OperatorSessionIdentity & {
|
|
126
|
+
user: TUser;
|
|
127
|
+
});
|
|
128
|
+
type CreateUserTokenParams<TUser extends BaseUser$1, TPlatform extends AbstractPlatform<any, any>> = JWTEncodeBase<TPlatform> & OccupantTokenIdentity<TUser>;
|
|
129
|
+
type CreateOperatorTokenParams<TPlatform extends AbstractPlatform<any, any>> = JWTEncodeBase<TPlatform> & OperatorTokenIdentity;
|
|
130
|
+
type CreateTokenParams<TUser extends BaseUser$1, TPlatform extends AbstractPlatform<any, any>> = CreateUserTokenParams<TUser, TPlatform> | CreateOperatorTokenParams<TPlatform>;
|
|
131
|
+
type JWTEncodeParams<TUser extends BaseUser$1, TPlatform extends AbstractPlatform<any, any>> = JWTEncodeBase<TPlatform> & JwtTokenIdentity<TUser>;
|
|
132
|
+
interface AuthorizeParams {
|
|
133
|
+
platform: AbstractPlatform<any, any>;
|
|
134
|
+
secret: string;
|
|
135
|
+
}
|
|
136
|
+
interface AuthorizeModule {
|
|
137
|
+
decode: <TUser extends BaseUser$1>(jwt: string) => Promise<JWT<TUser> | null>;
|
|
138
|
+
encode: <TUser extends BaseUser$1, TPlatform extends AbstractPlatform<any, any>>(params: JWTEncodeParams<TUser, TPlatform>) => Promise<string>;
|
|
139
|
+
}
|
|
140
|
+
export declare const authorize: (params: AuthorizeParams) => {
|
|
141
|
+
decode: <TUser extends BaseUser$1>(jwt: string) => Promise<JWT<TUser> | null>;
|
|
142
|
+
encode: <TUser extends BaseUser$1, TPlatform extends AbstractPlatform<any, any>>(encodeParams: JWTEncodeParams<TUser, TPlatform>) => Promise<string>;
|
|
143
|
+
};
|
|
144
|
+
type InferPlatformWebSocketType<TPlatform extends AbstractPlatform> = TPlatform extends AbstractPlatform<infer IAbstractWebSocket> ? IAbstractWebSocket : never;
|
|
145
|
+
type InferPlatformWebSocketSource<TPlatform> = TPlatform extends AbstractPlatform<infer IAbstractWebSocket> ? InferWebSocketSource<IAbstractWebSocket> : never;
|
|
146
|
+
type InferInitContextType<TPlatform extends AbstractPlatform> = TPlatform extends AbstractPlatform<any, infer IInitContext> ? IInitContext : never;
|
|
147
|
+
type InferRoomContextType<TPlatform extends AbstractPlatform> = TPlatform extends AbstractPlatform<any, any, infer IRoomContext> ? IRoomContext : never;
|
|
148
|
+
type AbstractPlatformConfig<TRoomContext extends Record<string, any> = {}> = {
|
|
149
|
+
persistence?: AbstractPersistence;
|
|
150
|
+
pubSub?: AbstractPubSub;
|
|
151
|
+
roomContext?: TRoomContext;
|
|
152
|
+
};
|
|
153
|
+
interface ConvertWebSocketConfig {
|
|
154
|
+
room: string;
|
|
155
|
+
}
|
|
156
|
+
export declare abstract class AbstractPlatform<TWebSocket extends AbstractWebSocket<any> = AbstractWebSocket<any>, TInitContext extends Record<string, any> = {}, TRoomContext extends Record<string, any> = {}> {
|
|
157
|
+
protected readonly _initContext: TInitContext | undefined;
|
|
158
|
+
protected readonly _roomContext: TRoomContext | undefined;
|
|
159
|
+
private _initialized;
|
|
160
|
+
private readonly _keptAlive;
|
|
161
|
+
persistence: AbstractPersistence;
|
|
162
|
+
pubSub: AbstractPubSub;
|
|
163
|
+
abstract readonly id: string;
|
|
164
|
+
readonly _createToken?: (params: JWTEncodeParams<any, any> & {
|
|
165
|
+
authorize: ResolvedPluvIOAuthorize;
|
|
166
|
+
}) => Promise<string>;
|
|
167
|
+
abstract readonly _name: string;
|
|
168
|
+
constructor(config?: AbstractPlatformConfig<TRoomContext>);
|
|
169
|
+
abstract acceptWebSocket(webSocket: AbstractWebSocket): Promise<void>;
|
|
170
|
+
abstract convertWebSocket(webSocket: InferWebSocketSource<TWebSocket>, config: ConvertWebSocketConfig): AbstractWebSocket;
|
|
171
|
+
abstract getLastPing(webSocket: AbstractWebSocket): number | null;
|
|
172
|
+
abstract getSerializedState(webSocket: InferWebSocketSource<TWebSocket>): WebSocketSerializedState | null;
|
|
173
|
+
abstract getSessionId(webSocket: InferWebSocketSource<TWebSocket>): string | null;
|
|
174
|
+
abstract getWebSockets(): readonly InferWebSocketSource<TWebSocket>[];
|
|
175
|
+
abstract initialize(config: AbstractPlatformConfig<TRoomContext>): this;
|
|
176
|
+
abstract parseData(data: string | ArrayBuffer): Record<string, any>;
|
|
177
|
+
abstract randomUUID(): string;
|
|
178
|
+
abstract setSerializedState(webSocket: AbstractWebSocket, state: WebSocketSerializedState): WebSocketSerializedState;
|
|
179
|
+
/**
|
|
180
|
+
* Retain `promise` until it settles. Cloudflare uses this to keep the isolate awake.
|
|
181
|
+
* The default holds the reference and does nothing else.
|
|
182
|
+
*/
|
|
183
|
+
keepAlive(promise: Promise<unknown>): void;
|
|
184
|
+
/**
|
|
185
|
+
* Normalize register/createToken init context before authorize runs.
|
|
186
|
+
* Platforms may convert runtime-specific request types (e.g. Node `IncomingMessage`).
|
|
187
|
+
*/
|
|
188
|
+
normalizeInitContext(initContext: TInitContext): TInitContext;
|
|
189
|
+
validateConfig(config: any): void;
|
|
190
|
+
protected _initialize(): typeof this;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Server IO snapshot. Dependent constraints (secret vs platform) are
|
|
194
|
+
* enforced on `createIO().platform().config()`, not inside this object type.
|
|
195
|
+
*/
|
|
196
|
+
type IODefs = IOLikeDefs & {
|
|
197
|
+
platform: AbstractPlatform<any, any, any>;
|
|
198
|
+
context: Record<string, any>;
|
|
199
|
+
};
|
|
200
|
+
type PatchDefs<T extends IODefs, P extends Partial<IODefs>> = Omit<T, keyof P> & P;
|
|
201
|
+
type IOLikeFromDefs<T extends IODefs> = Pick<T, "treaty" | "events">;
|
|
202
|
+
interface PluvProcedureConfig<T extends IODefs, TInput extends JsonObject = {}, TOutput extends EventRecord$1<string, any> = {}> {
|
|
203
|
+
broadcast?: EventResolver<"broadcast", T, TInput, Partial<TOutput>> | null;
|
|
204
|
+
input?: StandardSchemaV1$1<unknown, TInput>;
|
|
205
|
+
self?: EventResolver<"self", T, TInput, Partial<TOutput>> | null;
|
|
206
|
+
}
|
|
207
|
+
export declare class PluvProcedure<T extends IODefs = IODefs, TInput extends JsonObject = {}, TOutput extends EventRecord$1<string, any> = {}, TFilled extends "input" | "broadcast" | "self" | "" = ""> {
|
|
208
|
+
private _broadcast;
|
|
209
|
+
private _input;
|
|
210
|
+
private _self;
|
|
211
|
+
get config(): ProcedureLike$1<TInput, TOutput>["config"];
|
|
212
|
+
constructor(config?: PluvProcedureConfig<T, TInput, TOutput>);
|
|
213
|
+
broadcast<TResult extends EventRecord$1<string, any> = {}>(resolver: EventResolver<"broadcast", T, TInput, TResult>): Omit<PluvProcedure<T, TInput, MergeEventRecords<[TOutput, TResult]>, TFilled | "input" | "broadcast">, TFilled | "input" | "broadcast">;
|
|
214
|
+
input<TData extends JsonObject>(input: StandardSchemaV1$1<unknown, TData>): Omit<PluvProcedure<T, TData, {}, TFilled | "input">, TFilled | "input">;
|
|
215
|
+
self<TResult extends EventRecord$1<string, any> = {}>(resolver: EventResolver<"self", T, TInput, TResult>): Omit<PluvProcedure<T, TInput, MergeEventRecords<[TOutput, TResult]>, TFilled | "input" | "self">, TFilled | "input" | "self">;
|
|
216
|
+
}
|
|
217
|
+
type PluvRouterEventConfig<T extends IODefs = IODefs> = {
|
|
218
|
+
[P: string]: Pick<PluvProcedure<T, any, any>, "config">;
|
|
219
|
+
};
|
|
220
|
+
type MergedRouter<TRouters extends PluvRouter<any>[] = [], T extends IODefs = IODefs, TRoot extends Record<string, any> = {}> = TRouters extends [infer IHead extends PluvRouter<any>, ...infer ITail extends PluvRouter<any>[]] ? MergedRouter<ITail, T, TRoot & IHead["_defs"]["events"]> : PluvRouter<SetKey<T, "events", TRoot>>;
|
|
221
|
+
export declare class PluvRouter<T extends IODefs = IODefs> implements IORouterLike<T["events"]> {
|
|
222
|
+
readonly _defs: {
|
|
223
|
+
events: T["events"];
|
|
224
|
+
};
|
|
225
|
+
/** Event names this router defines. */
|
|
226
|
+
readonly eventNames: ReadonlySet<string>;
|
|
227
|
+
constructor(events: T["events"]);
|
|
228
|
+
static merge<TRouters extends PluvRouter<any>[]>(...routers: TRouters): MergedRouter<TRouters>;
|
|
229
|
+
}
|
|
230
|
+
declare const DEFERRED_RESULT: "$u";
|
|
231
|
+
/** A handler promise the queue must not wait on. The session manager settles this on a later join. */
|
|
232
|
+
type DeferredResult<T> = {
|
|
233
|
+
readonly [DEFERRED_RESULT]: Promise<T>;
|
|
234
|
+
};
|
|
235
|
+
export declare const deferredResult: <T>(promise: Promise<T>) => DeferredResult<T>;
|
|
236
|
+
export declare const isDeferredResult: <T>(value: T | DeferredResult<T>) => value is DeferredResult<T>;
|
|
237
|
+
export declare const deferredResultPromise: <T>(result: DeferredResult<T>) => Promise<T>;
|
|
238
|
+
export declare const isSuspended: (value: unknown) => value is Suspended;
|
|
239
|
+
export declare const isErrorMessage: (value: unknown) => value is {
|
|
240
|
+
message: string;
|
|
241
|
+
};
|
|
242
|
+
type PluvContext<TPlatform extends AbstractPlatform, TContext extends Record<string, any>> = MaybePromise<TContext> | ((params: InferRoomContextType<TPlatform>) => MaybePromise<TContext>);
|
|
243
|
+
type EventResolverKind = "broadcast" | "self";
|
|
244
|
+
type EventResolver<_TKind extends EventResolverKind = EventResolverKind, T extends IODefs = IODefs, TInput extends JsonObject = {}, TOutput extends EventRecord$1<string, any> = {}> = (data: TInput, context: EventResolverContext<T>) => MaybePromise<TOutput | void>;
|
|
245
|
+
type EventResolverPresence<T extends IODefs = IODefs> = T["treaty"]["presence"] extends StandardSchemaV1$1<any, infer TPresence> ? TPresence | null : JsonObject | null;
|
|
246
|
+
type EventResolverDoc<T extends IODefs = IODefs> = T["treaty"]["storage"] extends AbstractCrdtDocFactory<any, any, any, any> ? InferDocLike<T["treaty"]["storage"]> : CrdtDocLike$1<any, any>;
|
|
247
|
+
/**
|
|
248
|
+
* Context passed to user procedures. Session and storage protocol procedures
|
|
249
|
+
* use a wider internal context.
|
|
250
|
+
*/
|
|
251
|
+
interface EventResolverContext<T extends IODefs = IODefs> {
|
|
252
|
+
connectionId: string;
|
|
253
|
+
context: T["context"];
|
|
254
|
+
doc: EventResolverDoc<T>;
|
|
255
|
+
kind: ParticipantKind$1;
|
|
256
|
+
operator: OperatorUser$1 | null;
|
|
257
|
+
presence: EventResolverPresence<T>;
|
|
258
|
+
room: string;
|
|
259
|
+
time: number;
|
|
260
|
+
user: InferTreatyUser<T["treaty"]>;
|
|
261
|
+
}
|
|
262
|
+
type SendMessageOptions = {
|
|
263
|
+
type?: "broadcast";
|
|
264
|
+
sessionIds?: readonly string[];
|
|
265
|
+
} | {
|
|
266
|
+
type: "self";
|
|
267
|
+
};
|
|
268
|
+
interface WebSocketSessionTimers {
|
|
269
|
+
ping: number;
|
|
270
|
+
}
|
|
271
|
+
type WebSocketSessionSeq = PresenceSeq;
|
|
272
|
+
interface WebSocketSerializedState extends SessionPresence<JsonObject | null> {
|
|
273
|
+
quit: boolean;
|
|
274
|
+
room: string;
|
|
275
|
+
timers: WebSocketSessionTimers;
|
|
276
|
+
}
|
|
277
|
+
type WebSocketSession<T extends IODefs = IODefs> = WebSocketSerializedState & {
|
|
278
|
+
id: string;
|
|
279
|
+
kind: ParticipantKind$1;
|
|
280
|
+
operator: OperatorUser$1 | null;
|
|
281
|
+
user: InferTreatyUser<T["treaty"]>;
|
|
282
|
+
webSocket: AbstractWebSocket;
|
|
283
|
+
};
|
|
284
|
+
type MergeEventRecords<TEventRecords extends EventRecord$1<string, any>[], TRoot extends EventRecord$1<string, any> = {}> = TEventRecords extends [infer IHead extends EventRecord$1<string, any>, ...infer ITail extends EventRecord$1<string, any>[]] ? MergeEventRecords<ITail, Omit<TRoot, keyof IHead> & { [P in keyof IHead]: TRoot extends Record<P, any> ? TRoot[P] | IHead[P] : IHead[P]; }> : Id<TRoot>;
|
|
285
|
+
type GetInitialStorageEvent<TContext extends Record<string, any>> = {
|
|
286
|
+
context: TContext;
|
|
287
|
+
room: string;
|
|
288
|
+
};
|
|
289
|
+
type GetInitialStorageFn<TContext extends Record<string, any>> = (event: GetInitialStorageEvent<TContext>) => MaybePromise<Maybe<string>>;
|
|
290
|
+
type ResolvedPluvIOAuthorize<TUser extends BaseUser$1 = any> = {
|
|
291
|
+
user: StandardSchemaV1$1<unknown, TUser>;
|
|
292
|
+
secret: string;
|
|
293
|
+
};
|
|
294
|
+
type PluvIOSecret<TPlatform extends AbstractPlatform<any, any> = AbstractPlatform<any, any>> = string | ((context: InferInitContextType<TPlatform>) => string);
|
|
295
|
+
interface PluvIOLimits {
|
|
296
|
+
/**
|
|
297
|
+
* @description Allow a maxConnections value that would otherwise be rejected.
|
|
298
|
+
*/
|
|
299
|
+
dangerouslyAllowHighPresenceFanout?: boolean | null;
|
|
300
|
+
/**
|
|
301
|
+
* @description Maximum live sockets in a room. Extra registers are rejected.
|
|
302
|
+
* Must be a positive integer.
|
|
303
|
+
* @default 256
|
|
304
|
+
*/
|
|
305
|
+
maxConnections?: number | null;
|
|
306
|
+
/**
|
|
307
|
+
* @description Maximum size of presence object in bytes
|
|
308
|
+
*/
|
|
309
|
+
presenceMaxSize?: number | null;
|
|
310
|
+
/**
|
|
311
|
+
* @description Maximum size of storage state in bytes
|
|
312
|
+
*/
|
|
313
|
+
storageMaxSize?: number | null;
|
|
314
|
+
/**
|
|
315
|
+
* @description Maximum length of user id in characters
|
|
316
|
+
*/
|
|
317
|
+
userIdMaxLength?: number | null;
|
|
318
|
+
/**
|
|
319
|
+
* @description Maximum combined size, in bytes, of the objects stored for one
|
|
320
|
+
* session. User sessions measure the user object. Operator sessions measure
|
|
321
|
+
* that user object and the operator profile together. Email is not included.
|
|
322
|
+
*/
|
|
323
|
+
userMaxSize?: number | null;
|
|
324
|
+
}
|
|
325
|
+
type BasePluvIOListeners<T extends IODefs = IODefs> = {
|
|
326
|
+
onRoomDestroyed: (event: IORoomDestroyedEvent<T>) => void;
|
|
327
|
+
onRoomMessage: (event: IORoomMessageEvent<T>) => void;
|
|
328
|
+
onStorageDestroyed: (event: IORoomListenerEvent<T>) => void;
|
|
329
|
+
/**
|
|
330
|
+
* Document changes, about once a second. This is not every update in the
|
|
331
|
+
* document. Use the snapshot for the full document, and handle gaps yourself.
|
|
332
|
+
*
|
|
333
|
+
* Storage already saved when the room opens is not reported. An update that
|
|
334
|
+
* arrives before the changes it depends on is reported only after those
|
|
335
|
+
* changes arrive. The part that already applied is saved with the room, and
|
|
336
|
+
* if the room closes first it never appears here. Updates past the document
|
|
337
|
+
* size limit can land in the document and still be left out. If this
|
|
338
|
+
* listener throws, that batch is not sent again.
|
|
339
|
+
*/
|
|
340
|
+
onStorageEdits: (event: IOStorageEditsEvent<T>) => MaybePromise<void>;
|
|
341
|
+
onStorageSnapshot: (event: IOStorageSnapshotEvent<T>) => MaybePromise<void>;
|
|
342
|
+
onUserConnected: (event: IOUserConnectedEvent<T>) => void;
|
|
343
|
+
onUserDisconnected: (event: IOUserDisconnectedEvent<T>) => void;
|
|
344
|
+
};
|
|
345
|
+
type PluvIOListeners<T extends IODefs = IODefs> = BasePluvIOListeners<T>;
|
|
346
|
+
type PluvIORouter<T extends IODefs = IODefs> = {
|
|
347
|
+
router?: PluvRouter<T>;
|
|
348
|
+
};
|
|
349
|
+
type IORoomListenerEvent<T extends IODefs = IODefs> = {
|
|
350
|
+
context: T["context"];
|
|
351
|
+
encodedState: string | null;
|
|
352
|
+
room: string;
|
|
353
|
+
};
|
|
354
|
+
type IORoomDestroyedEvent<T extends IODefs = IODefs> = {
|
|
355
|
+
context: T["context"];
|
|
356
|
+
room: string;
|
|
357
|
+
};
|
|
358
|
+
type IORoomMessageEvent<T extends IODefs = IODefs> = {
|
|
359
|
+
context: T["context"];
|
|
360
|
+
message: InferEventMessage<InferEventsOutput<T["events"]>, keyof InferEventsOutput<T["events"]>>;
|
|
361
|
+
kind?: ParticipantKind$1;
|
|
362
|
+
operator?: OperatorUser$1 | null;
|
|
363
|
+
room: string;
|
|
364
|
+
user?: InferTreatyUser<T["treaty"]>;
|
|
365
|
+
};
|
|
366
|
+
/**
|
|
367
|
+
* `user` and `operator` come from the session. `$updateStorage` supplies
|
|
368
|
+
* `procedure` from the sender. `_commitLoadedStorage` names the procedure
|
|
369
|
+
* the server ran. A write that was never reserved has all three null.
|
|
370
|
+
*/
|
|
371
|
+
type IOStorageEdit<T extends IODefs = IODefs> = {
|
|
372
|
+
update: string;
|
|
373
|
+
user: InferTreatyUser<T["treaty"]> | null;
|
|
374
|
+
operator: OperatorUser$1 | null;
|
|
375
|
+
procedure: string | null;
|
|
376
|
+
};
|
|
377
|
+
type IOStorageEditsEvent<T extends IODefs = IODefs> = {
|
|
378
|
+
changes: readonly IOStorageEdit<T>[];
|
|
379
|
+
context: T["context"];
|
|
380
|
+
room: string;
|
|
381
|
+
};
|
|
382
|
+
type IOStorageSnapshotEvent<T extends IODefs = IODefs> = IORoomListenerEvent<T>;
|
|
383
|
+
type IOUserConnectedEvent<T extends IODefs = IODefs> = IORoomListenerEvent<T> & {
|
|
384
|
+
kind?: ParticipantKind$1;
|
|
385
|
+
operator?: OperatorUser$1 | null;
|
|
386
|
+
user?: InferTreatyUser<T["treaty"]>;
|
|
387
|
+
};
|
|
388
|
+
type IOUserDisconnectedEvent<T extends IODefs = IODefs> = IORoomListenerEvent<T> & {
|
|
389
|
+
kind?: ParticipantKind$1;
|
|
390
|
+
operator?: OperatorUser$1 | null;
|
|
391
|
+
user?: InferTreatyUser<T["treaty"]>;
|
|
392
|
+
};
|
|
393
|
+
type WebSocketType<TPlatform extends AbstractPlatform> = InferPlatformWebSocketType<TPlatform> | InferPlatformWebSocketSource<TPlatform>;
|
|
394
|
+
type SnapshotFlush = "clean" | "dirty" | "closed";
|
|
395
|
+
interface RoomSessionSnapshot<T extends IODefs = IODefs> {
|
|
396
|
+
id: string;
|
|
397
|
+
user: InferTreatyUser<T["treaty"]>;
|
|
398
|
+
presence: JsonObject | null;
|
|
399
|
+
kind: ParticipantKind$1;
|
|
400
|
+
operator: OperatorUser$1 | null;
|
|
401
|
+
seq: {
|
|
402
|
+
presence: number | null;
|
|
403
|
+
};
|
|
404
|
+
lastPing: number;
|
|
405
|
+
quit: boolean;
|
|
406
|
+
}
|
|
407
|
+
type RoomPresenceSession<T extends IODefs = IODefs> = Pick<RoomSessionSnapshot<T>, "id" | "operator" | "presence" | "user">;
|
|
408
|
+
type RoomStorageActor<T extends IODefs = IODefs> = Pick<RoomSessionSnapshot<T>, "id" | "kind" | "operator" | "presence" | "user">;
|
|
409
|
+
/** A storage `prepare` finished off the queue. The next turn writes this. */
|
|
410
|
+
type LoadedStorage<T extends IODefs = IODefs> = {
|
|
411
|
+
actor: RoomStorageActor<T>;
|
|
412
|
+
data: unknown;
|
|
413
|
+
/** `StorageSnapshot.generation` when this write started. A commit after `seal` is dropped. */
|
|
414
|
+
generation: number;
|
|
415
|
+
name: string;
|
|
416
|
+
prepared: unknown;
|
|
417
|
+
};
|
|
418
|
+
type RoomJoinActor<T extends IODefs = IODefs> = Pick<RoomSessionSnapshot<T>, "id" | "kind" | "operator" | "user">;
|
|
419
|
+
interface RoomEffectFrame<T extends IODefs = IODefs> {
|
|
420
|
+
type: string;
|
|
421
|
+
data: unknown;
|
|
422
|
+
user: InferTreatyUser<T["treaty"]> | null;
|
|
423
|
+
operator: OperatorUser$1 | null;
|
|
424
|
+
connectionId: string | null;
|
|
425
|
+
}
|
|
426
|
+
interface RoomAttachment {
|
|
427
|
+
presence: JsonObject | null;
|
|
428
|
+
seq: number | null;
|
|
429
|
+
sessionId: string;
|
|
430
|
+
}
|
|
431
|
+
interface RoomEffects<T extends IODefs = IODefs> {
|
|
432
|
+
attachments: RoomAttachment[];
|
|
433
|
+
broadcast: RoomEffectFrame<T>[];
|
|
434
|
+
closes: string[];
|
|
435
|
+
/** Connection that produced this turn. Null when the result has no single actor. */
|
|
436
|
+
connectionId: string | null;
|
|
437
|
+
destroy: boolean;
|
|
438
|
+
error: unknown;
|
|
439
|
+
self: RoomEffectFrame<T>[];
|
|
440
|
+
/**
|
|
441
|
+
* The in-memory document is ahead of the last snapshot, or this effect still carries a
|
|
442
|
+
* `$storageUpdated` frame. The snapshot loop delivers those frames. A hosted supervisor reads
|
|
443
|
+
* this off the call result.
|
|
444
|
+
*/
|
|
445
|
+
storageDirty: boolean;
|
|
446
|
+
}
|
|
447
|
+
interface RoomRegisterResult<T extends IODefs = IODefs> {
|
|
448
|
+
user: InferTreatyUser<T["treaty"]> | null;
|
|
449
|
+
kind: ParticipantKind$1 | null;
|
|
450
|
+
operator: OperatorUser$1 | null;
|
|
451
|
+
encodedState: string | null;
|
|
452
|
+
error: Error | null;
|
|
453
|
+
}
|
|
454
|
+
/** A hosted call released the queue. The supervisor applies the effects on a later call. */
|
|
455
|
+
type Suspended = {
|
|
456
|
+
readonly suspended: true;
|
|
457
|
+
};
|
|
458
|
+
type WebSocketRegisterConfig<TPlatform extends AbstractPlatform<any> = AbstractPlatform<any>> = {
|
|
459
|
+
token?: string | null;
|
|
460
|
+
} & InferInitContextType<TPlatform>;
|
|
461
|
+
/** Internal methods `RoomSessionManager` uses. */
|
|
462
|
+
interface IORoomInternal<T extends IODefs = IODefs> {
|
|
463
|
+
applyPresencePatch(params: {
|
|
464
|
+
name: string;
|
|
465
|
+
data: unknown;
|
|
466
|
+
session: RoomPresenceSession<T>;
|
|
467
|
+
}): Promise<JsonObject | null | DeferredResult<JsonObject | null> | Suspended>;
|
|
468
|
+
/**
|
|
469
|
+
* The deferred value stays `LoadedStorage | RoomEffects`. A hosted delivery
|
|
470
|
+
* resolves with empty effects after the frames were sent.
|
|
471
|
+
*/
|
|
472
|
+
applyStorageProcedure(params: {
|
|
473
|
+
name: string;
|
|
474
|
+
data: unknown;
|
|
475
|
+
actor: RoomStorageActor<T>;
|
|
476
|
+
}): Promise<RoomEffects<T> | DeferredResult<LoadedStorage<T> | RoomEffects<T>>>;
|
|
477
|
+
/** Writes a loaded storage procedure. `releasePending` attaches updates already waiting. */
|
|
478
|
+
commitLoadedStorage(loaded: LoadedStorage<T>, releasePending?: boolean): Promise<RoomEffects<T>>;
|
|
479
|
+
close(session: RoomSessionSnapshot<T>): Promise<RoomEffects<T>>;
|
|
480
|
+
destroy(): Promise<void>;
|
|
481
|
+
flushSnapshot(): Promise<{
|
|
482
|
+
broadcast: RoomEffectFrame<T>[];
|
|
483
|
+
status: SnapshotFlush;
|
|
484
|
+
}>;
|
|
485
|
+
initializeStorage(params: {
|
|
486
|
+
input: {
|
|
487
|
+
stateVector?: string | null;
|
|
488
|
+
update?: string | null;
|
|
489
|
+
};
|
|
490
|
+
actor: RoomJoinActor<T>;
|
|
491
|
+
}): Promise<{
|
|
492
|
+
changeKind: "unchanged" | "initialized" | "empty";
|
|
493
|
+
state: string | null;
|
|
494
|
+
storageDirty: boolean;
|
|
495
|
+
}>;
|
|
496
|
+
logDebug(...data: unknown[]): void;
|
|
497
|
+
message(session: RoomSessionSnapshot<T>, event: {
|
|
498
|
+
data: unknown;
|
|
499
|
+
type: string;
|
|
500
|
+
}): Promise<RoomEffects<T> | DeferredResult<RoomEffects<T>> | Suspended>;
|
|
501
|
+
register(...options: keyof InferInitContextType<T["platform"]> extends never ? [{
|
|
502
|
+
token?: string;
|
|
503
|
+
}?] : [WebSocketRegisterConfig<T["platform"]>]): Promise<RoomRegisterResult<T>>;
|
|
504
|
+
dropStorageUpdates(frames: readonly RoomEffectFrame<T>[]): Promise<void>;
|
|
505
|
+
queueStorageFrames(frames: readonly RoomEffectFrame<T>[]): void;
|
|
506
|
+
storageDirty(): boolean | Promise<boolean>;
|
|
507
|
+
userConnected(params: {
|
|
508
|
+
id: string;
|
|
509
|
+
encodedState: string | null;
|
|
510
|
+
kind: ParticipantKind$1;
|
|
511
|
+
operator: OperatorUser$1 | null;
|
|
512
|
+
user: InferTreatyUser<T["treaty"]>;
|
|
513
|
+
}): Promise<RoomEffects<T>>;
|
|
514
|
+
whenReady(): Promise<boolean>;
|
|
515
|
+
}
|
|
516
|
+
/**
|
|
517
|
+
* What `manageSessions` calls. `IORoom` implements this in-process.
|
|
518
|
+
* A hosted supervisor passes a stub of the same shape beside its own platform.
|
|
519
|
+
*/
|
|
520
|
+
interface SessionRoom<T extends IODefs = IODefs> {
|
|
521
|
+
readonly id: string;
|
|
522
|
+
readonly limits: PluvIOLimits | null;
|
|
523
|
+
readonly treaty: T["treaty"];
|
|
524
|
+
readonly "~": IORoomInternal<T>;
|
|
525
|
+
}
|
|
526
|
+
declare const resolveMaxConnections: (limits: Pick<PluvIOLimits, "maxConnections">) => number;
|
|
527
|
+
declare const assertPresenceFanoutBudget: (limits: PluvIOLimits) => void;
|
|
528
|
+
/**
|
|
529
|
+
* Build a router that may include `$` protocol events. Not part of the public
|
|
530
|
+
* package API — import via `__internal` or sibling modules under `@pluv/io`.
|
|
531
|
+
*/
|
|
532
|
+
declare const createInternalPluvRouter: <T extends IODefs = IODefs>(events: T["events"]) => PluvRouter<T>;
|
|
533
|
+
declare const getMyConnectionIds: (sessions: readonly WebSocketSession<any>[], requester: WebSocketSession<any> | null) => string[];
|
|
534
|
+
declare const getRoomStatsFromSessions: (sessions: readonly WebSocketSession<any>[]) => RoomStatsByKind;
|
|
535
|
+
type GroupedRoomUser = {
|
|
536
|
+
connectionIds: string[];
|
|
537
|
+
data: JsonObject;
|
|
538
|
+
key: string;
|
|
539
|
+
kind: ParticipantKind$1;
|
|
540
|
+
operator: OperatorUser$1 | null;
|
|
541
|
+
presence: JsonObject | null;
|
|
542
|
+
};
|
|
543
|
+
/**
|
|
544
|
+
* Group live sockets by `(kind, user id)`. Extra tabs of the same kind share
|
|
545
|
+
* one row. The socket with the newest `seq.presence` supplies `presence`,
|
|
546
|
+
* `data`, and `operator`. `excludeSessionId` drops that person's whole row.
|
|
547
|
+
* `key` is `user.id`.
|
|
548
|
+
*/
|
|
549
|
+
declare const groupLiveUsers: (sessions: readonly WebSocketSession<any>[], options?: {
|
|
550
|
+
excludeSessionId?: string;
|
|
551
|
+
kinds?: readonly ParticipantKind$1[];
|
|
552
|
+
}) => GroupedRoomUser[];
|
|
553
|
+
declare const listLiveUsers: (sessions: readonly WebSocketSession<any>[], options: {
|
|
554
|
+
cursor?: ListUsersCursor | null;
|
|
555
|
+
kinds?: readonly ParticipantKind$1[];
|
|
556
|
+
limit: number;
|
|
557
|
+
}) => {
|
|
558
|
+
pageInfo: {
|
|
559
|
+
endCursor: ListUsersCursor | null;
|
|
560
|
+
hasNextPage: boolean;
|
|
561
|
+
};
|
|
562
|
+
users: {
|
|
563
|
+
data: JsonObject;
|
|
564
|
+
kind: ParticipantKind$1;
|
|
565
|
+
operator: OperatorUser$1 | null;
|
|
566
|
+
}[];
|
|
567
|
+
};
|
|
568
|
+
declare const pageLiveUsers: (sessions: readonly WebSocketSession<any>[], options?: ListUsersOptions) => ListUsersResult<any>;
|
|
569
|
+
export declare function parsePluvSchema<TData extends JsonObject>(schema: StandardSchemaV1$1<unknown, TData>, data: unknown): TData;
|
|
570
|
+
declare const resolveListUsersLimit: (limit?: number | null) => {
|
|
571
|
+
success: true;
|
|
572
|
+
limit: number;
|
|
573
|
+
} | {
|
|
574
|
+
success: false;
|
|
575
|
+
error: ListUsersError;
|
|
576
|
+
};
|
|
577
|
+
type Throttle = {
|
|
578
|
+
cancel: () => void;
|
|
579
|
+
schedule: () => void;
|
|
580
|
+
};
|
|
581
|
+
declare const throttle: (fn: () => void | Promise<void>, options: {
|
|
582
|
+
wait: number;
|
|
583
|
+
}) => Throttle;
|
|
584
|
+
/**
|
|
585
|
+
* Orders one commit after another.
|
|
586
|
+
* `idle` means the latest reserved turn has already finished.
|
|
587
|
+
* `reserve` takes the next turn. `previous` settles when the earlier turn
|
|
588
|
+
* finishes. `finish` runs once, when this turn's commit has ended.
|
|
589
|
+
*/
|
|
590
|
+
export declare class CommitGate {
|
|
591
|
+
#private;
|
|
592
|
+
get idle(): boolean;
|
|
593
|
+
reserve(): {
|
|
594
|
+
previous: Promise<void>;
|
|
595
|
+
finish: () => void;
|
|
596
|
+
};
|
|
597
|
+
}
|
|
598
|
+
export declare const DEFAULT_MAX_CONNECTIONS = 256;
|
|
599
|
+
export declare const LIST_USERS_DEFAULT_LIMIT = 50;
|
|
600
|
+
export declare const LIST_USERS_MAX_LIMIT = 100;
|
|
601
|
+
declare const isServerOriginEvent: (type: string) => type is ServerOriginEvent;
|
|
602
|
+
/**
|
|
603
|
+
* @experimental
|
|
604
|
+
* @description Connection the host treaty procedure invoke runs as. Breaking changes may ship
|
|
605
|
+
* without a major version bump.
|
|
606
|
+
*/
|
|
607
|
+
type HostTreatyProcedureActor = {
|
|
608
|
+
senderId: string;
|
|
609
|
+
};
|
|
610
|
+
/**
|
|
611
|
+
* @experimental
|
|
612
|
+
* @description Bound host invoke for treaty storage procedures (async). Client
|
|
613
|
+
* `StorageProcedureProxy` methods return `void`; host invokes are async. Breaking changes may ship
|
|
614
|
+
* without a major version bump.
|
|
615
|
+
*/
|
|
616
|
+
type HostStorageProcedureProxy<TProcedures extends Record<string, TreatyProcedureLike<"storage">> = {}> = (<TName extends Extract<keyof TProcedures, string>>(name: TName, data: InferTreatyProcedureInput<TProcedures[TName]>) => Promise<void>) & { [TName in Extract<keyof TProcedures, string>]: (data: InferTreatyProcedureInput<TProcedures[TName]>) => Promise<void>; };
|
|
617
|
+
/**
|
|
618
|
+
* @experimental
|
|
619
|
+
* @description Binds a live connection id and returns the client-shaped presence procedure proxy.
|
|
620
|
+
* Breaking changes may ship without a major version bump.
|
|
621
|
+
*/
|
|
622
|
+
type HostPresenceInvoke<TIO extends IORoom<any>> = (actor: HostTreatyProcedureActor) => PresenceProcedureProxy<TIO["_defs"]["treaty"]["_defs"]["procedures"]["presence"]>;
|
|
623
|
+
/**
|
|
624
|
+
* @experimental
|
|
625
|
+
* @description Binds a live connection id and returns {@link HostStorageProcedureProxy}. Breaking
|
|
626
|
+
* changes may ship without a major version bump.
|
|
627
|
+
*/
|
|
628
|
+
type HostStorageInvoke<TIO extends IORoom<any>> = (actor: HostTreatyProcedureActor) => HostStorageProcedureProxy<TIO["_defs"]["treaty"]["_defs"]["procedures"]["storage"]>;
|
|
629
|
+
interface IORoomListeners<T extends IODefs = IODefs> {
|
|
630
|
+
onRoomDestroyed: (event: IORoomDestroyedEvent<T>) => void;
|
|
631
|
+
onStorageDestroyed: (event: IORoomListenerEvent<T>) => void;
|
|
632
|
+
onMessage: (event: IORoomMessageEvent<T>) => void;
|
|
633
|
+
onStorageEdits?: (event: IOStorageEditsEvent<T>) => MaybePromise<void>;
|
|
634
|
+
onStorageSnapshot: (event: IOStorageSnapshotEvent<T>) => MaybePromise<void>;
|
|
635
|
+
onUserConnected: (event: IOUserConnectedEvent<T>) => void;
|
|
636
|
+
onUserDisconnected: (event: IOUserDisconnectedEvent<T>) => void;
|
|
637
|
+
}
|
|
638
|
+
type IORoomConfig<T extends IODefs = IODefs> = Partial<IORoomListeners<T>> & {
|
|
639
|
+
context: PluvContext<T["platform"], T["context"]>;
|
|
640
|
+
debug: boolean;
|
|
641
|
+
getInitialStorage: GetInitialStorageFn<T["context"]>;
|
|
642
|
+
limits: PluvIOLimits;
|
|
643
|
+
platform: T["platform"];
|
|
644
|
+
roomContext: InferRoomContextType<T["platform"]>;
|
|
645
|
+
router: PluvRouter<T>;
|
|
646
|
+
secret?: PluvIOSecret<T["platform"]>;
|
|
647
|
+
treaty: T["treaty"];
|
|
648
|
+
};
|
|
649
|
+
declare class IORoom<T extends IODefs = IODefs> implements IOLike<IOLikeFromDefs<T>>, SessionRoom<T> {
|
|
650
|
+
readonly id: string;
|
|
651
|
+
readonly limits: PluvIOLimits;
|
|
652
|
+
readonly treaty: T["treaty"];
|
|
653
|
+
readonly "~": IORoomInternal<T>;
|
|
654
|
+
private _teardown;
|
|
655
|
+
private _uninitialize;
|
|
656
|
+
private readonly _edits;
|
|
657
|
+
private readonly _snapshot;
|
|
658
|
+
private readonly _context;
|
|
659
|
+
private readonly _debug;
|
|
660
|
+
private readonly _listeners;
|
|
661
|
+
private readonly _platform;
|
|
662
|
+
private readonly _roomContext;
|
|
663
|
+
private readonly _router;
|
|
664
|
+
private readonly _secret?;
|
|
665
|
+
private readonly _storage;
|
|
666
|
+
/**
|
|
667
|
+
* @ignore
|
|
668
|
+
* @readonly
|
|
669
|
+
* @deprecated Internal use only. Changes to this will never be marked as breaking.
|
|
670
|
+
*/
|
|
671
|
+
get _defs(): {
|
|
672
|
+
context: T["context"];
|
|
673
|
+
events: T["events"];
|
|
674
|
+
platform: T["platform"];
|
|
675
|
+
treaty: T["treaty"];
|
|
676
|
+
};
|
|
677
|
+
constructor(id: string, config: IORoomConfig<T>);
|
|
678
|
+
/**
|
|
679
|
+
* @internal Opens the stored document and applies a treaty presence procedure.
|
|
680
|
+
*/
|
|
681
|
+
private _applyPresencePatch;
|
|
682
|
+
private _applyStorageProcedure;
|
|
683
|
+
private _commitLoadedStorage;
|
|
684
|
+
private _close;
|
|
685
|
+
private _destroyRoom;
|
|
686
|
+
private get _doc();
|
|
687
|
+
private _emitDisconnected;
|
|
688
|
+
private _getAuthorizedUser;
|
|
689
|
+
/** User context for session procedures. `.config({ context })` runs here. */
|
|
690
|
+
private _getContext;
|
|
691
|
+
private _getProcedure;
|
|
692
|
+
private _initialize;
|
|
693
|
+
private get _initialized();
|
|
694
|
+
private _initializeStorage;
|
|
695
|
+
private _logDebug;
|
|
696
|
+
private _message;
|
|
697
|
+
private _register;
|
|
698
|
+
private _resolverContext;
|
|
699
|
+
private _dropStorageUpdates;
|
|
700
|
+
/** Keeps frames from a failed send on the pending list. Ones already queued stay once. */
|
|
701
|
+
private _queueStorageFrames;
|
|
702
|
+
private _releaseStorage;
|
|
703
|
+
private _userConnected;
|
|
704
|
+
private _userContext;
|
|
705
|
+
}
|
|
706
|
+
type InferIORoom<TServer extends PluvServer<any>> = TServer extends PluvServer<infer IDefs extends IODefs> ? IORoom<IDefs> : never;
|
|
707
|
+
type PluvServerConfig<T extends IODefs = IODefs> = Partial<PluvIOListeners<T>> & {
|
|
708
|
+
context?: PluvContext<T["platform"], T["context"]>;
|
|
709
|
+
debug?: boolean;
|
|
710
|
+
limits: PluvIOLimits;
|
|
711
|
+
io: PluvIO<SetKey$1<T, "events", {}>>;
|
|
712
|
+
onGetOperator?: OnGetOperator$1<InferTreatyUser<T["treaty"]>>;
|
|
713
|
+
platform: () => T["platform"];
|
|
714
|
+
router?: PluvRouter<T>;
|
|
715
|
+
secret: PluvIOSecret<T["platform"]>;
|
|
716
|
+
treaty: T["treaty"];
|
|
717
|
+
} & (HasStorage<T["treaty"]["storage"]> extends true ? {
|
|
718
|
+
getInitialStorage: GetInitialStorageFn<T["context"]>;
|
|
719
|
+
} : {
|
|
720
|
+
getInitialStorage?: "[ERROR]: Must specify storage on treaty to use getInitialStorage";
|
|
721
|
+
});
|
|
722
|
+
type BaseCreateRoomOptions<_T extends IODefs> = {
|
|
723
|
+
debug?: boolean;
|
|
724
|
+
};
|
|
725
|
+
type CreateRoomOptions<T extends IODefs = IODefs> = keyof Omit<InferRoomContextType<T["platform"]>, "meta"> extends never ? [BaseCreateRoomOptions<T>] | [] : [Id<BaseCreateRoomOptions<T> & InferRoomContextType<T["platform"]>>];
|
|
726
|
+
export declare class PluvServer<T extends IODefs = IODefs> implements IOLike<IOLikeFromDefs<T>> {
|
|
727
|
+
readonly version: string;
|
|
728
|
+
private readonly _config;
|
|
729
|
+
private readonly _docFactory;
|
|
730
|
+
private readonly _listeners;
|
|
731
|
+
private readonly _onGetOperator;
|
|
732
|
+
private readonly _onStorageEdits;
|
|
733
|
+
/**
|
|
734
|
+
* @description The `createIO()` instance this server was built from.
|
|
735
|
+
*/
|
|
736
|
+
get io(): PluvIO<SetKey$1<T, "events", {}>>;
|
|
737
|
+
/**
|
|
738
|
+
* @ignore
|
|
739
|
+
* @readonly
|
|
740
|
+
* @deprecated Internal use only. Changes to this will never be marked as breaking.
|
|
741
|
+
*/
|
|
742
|
+
get _defs(): T;
|
|
743
|
+
/**
|
|
744
|
+
* `_router` fills `_defs` with the session slice, the storage slice, and the
|
|
745
|
+
* user's router. `createRoom` receives `_docRouter`: storage events plus the
|
|
746
|
+
* user's router. `manageSessions` dispatches session events.
|
|
747
|
+
*/
|
|
748
|
+
private get _baseRouter();
|
|
749
|
+
private get _router();
|
|
750
|
+
private get _docRouter();
|
|
751
|
+
constructor(options: PluvServerConfig<T>);
|
|
752
|
+
createRoom(room: string, ...options: CreateRoomOptions<T>): IORoom<T>;
|
|
753
|
+
createToken(params: CreateTokenParams<InferTreatyUser<T["treaty"]>, T["platform"]>): Promise<string>;
|
|
754
|
+
private _getInitialStorage;
|
|
755
|
+
private _logDebug;
|
|
756
|
+
}
|
|
757
|
+
type PluvIOConfig<T extends IODefs = IODefs> = {
|
|
758
|
+
context?: PluvContext<T["platform"], T["context"]>;
|
|
759
|
+
debug?: boolean;
|
|
760
|
+
limits?: PluvIOLimits;
|
|
761
|
+
platform: () => T["platform"];
|
|
762
|
+
secret: PluvIOSecret<T["platform"]>;
|
|
763
|
+
treaty: T["treaty"];
|
|
764
|
+
};
|
|
765
|
+
type ResolvedServerConfig<T extends IODefs = IODefs> = Partial<PluvIOListeners<T>> & PluvIORouter<T> & {
|
|
766
|
+
onGetOperator?: OnGetOperator$1<InferTreatyUser<T["treaty"]>>;
|
|
767
|
+
} & (HasStorage<T["treaty"]["storage"]> extends true ? {
|
|
768
|
+
getInitialStorage: GetInitialStorageFn<T["context"]>;
|
|
769
|
+
} : {
|
|
770
|
+
getInitialStorage?: "[ERROR]: Must specify storage on treaty to use getInitialStorage";
|
|
771
|
+
});
|
|
772
|
+
type BaseServerConfig<T extends IODefs = IODefs> = { [P in keyof ResolvedServerConfig<T> as ResolvedServerConfig<T>[P] extends undefined ? never : P]: ResolvedServerConfig<T>[P]; };
|
|
773
|
+
type ServerConfig<T extends IODefs = IODefs> = HasRequiredProperty<BaseServerConfig<T>> extends true ? [BaseServerConfig<T>] : [BaseServerConfig<T>?];
|
|
774
|
+
export declare class PluvIO<T extends IODefs = IODefs> {
|
|
775
|
+
readonly version: string;
|
|
776
|
+
private readonly _context;
|
|
777
|
+
private readonly _debug;
|
|
778
|
+
private readonly _limits;
|
|
779
|
+
private readonly _platform;
|
|
780
|
+
private readonly _secret;
|
|
781
|
+
private readonly _treaty;
|
|
782
|
+
get procedure(): PluvProcedure<T, {}, {}>;
|
|
783
|
+
constructor(options: PluvIOConfig<T>);
|
|
784
|
+
createToken(params: CreateUserTokenParams<InferTreatyUser<T["treaty"]>, T["platform"]>): Promise<string>;
|
|
785
|
+
createToken(params: CreateTokenParams<InferTreatyUser<T["treaty"]>, T["platform"]>, options: {
|
|
786
|
+
onGetOperator?: OnGetOperator$1<InferTreatyUser<T["treaty"]>> | null;
|
|
787
|
+
}): Promise<string>;
|
|
788
|
+
private _resolveEncodeParams;
|
|
789
|
+
private _assertIdLength;
|
|
790
|
+
private _assertUserLimits;
|
|
791
|
+
mergeRouters<TRouters extends PluvRouter<SetKey<T, "events", any>>[]>(...routers: TRouters): MergedRouter<TRouters, T>;
|
|
792
|
+
router<TEvents extends PluvRouterEventConfig<T>>(events: TEvents): PluvRouter<SetKey<T, "events", TEvents>>;
|
|
793
|
+
server<TEvents extends PluvRouterEventConfig<T> = {}>(...config: ServerConfig<SetKey<T, "events", TEvents>>): PluvServer<SetKey<T, "events", TEvents>>;
|
|
794
|
+
}
|
|
795
|
+
type IOConfigParams<TPlatform extends AbstractPlatform<any> = AbstractPlatform<any>, TTreaty extends TreatyLike = TreatyLike, TContext extends Record<string, any> = {}> = {
|
|
796
|
+
context?: PluvContext<TPlatform, TContext>;
|
|
797
|
+
debug?: boolean;
|
|
798
|
+
limits?: PluvIOLimits;
|
|
799
|
+
secret: PluvIOSecret<TPlatform>;
|
|
800
|
+
treaty: TTreaty;
|
|
801
|
+
};
|
|
802
|
+
type IOPlatformFactory<TPlatform extends AbstractPlatform<any> = AbstractPlatform<any>> = () => TPlatform;
|
|
803
|
+
type ConfiguredIODefs<TPlatform extends AbstractPlatform<any>, TTreaty extends TreatyLike, TContext extends Record<string, any>> = {
|
|
804
|
+
platform: TPlatform;
|
|
805
|
+
treaty: TTreaty;
|
|
806
|
+
context: TContext;
|
|
807
|
+
events: {};
|
|
808
|
+
};
|
|
809
|
+
interface IOConfigBuilder<TPlatform extends AbstractPlatform<any>> {
|
|
810
|
+
config: <TTreaty extends TreatyLike, TContext extends Record<string, any> = {}>(params: IOConfigParams<TPlatform, TTreaty, TContext>) => PluvIO<ConfiguredIODefs<TPlatform, TTreaty, TContext>>;
|
|
811
|
+
}
|
|
812
|
+
interface IOPlatformBuilder {
|
|
813
|
+
platform: <TPlatform extends AbstractPlatform<any>>(platform: IOPlatformFactory<TPlatform>) => IOConfigBuilder<TPlatform>;
|
|
814
|
+
}
|
|
815
|
+
export declare const createIO: () => IOPlatformBuilder;
|
|
816
|
+
type CreateBaseRouterParams<T extends IODefs = IODefs> = {
|
|
817
|
+
limits: Pick<PluvIOLimits, "presenceMaxSize" | "storageMaxSize">;
|
|
818
|
+
logDebug?: (...data: any[]) => void;
|
|
819
|
+
onStorageSnapshot: (event: IOStorageSnapshotEvent<T>) => void;
|
|
820
|
+
};
|
|
821
|
+
type BaseRouter<T extends IODefs> = PluvRouter<SetKey$1<T, "events", {}>>;
|
|
822
|
+
/**
|
|
823
|
+
* Built-in `$` protocol events. Kept separate from `PluvServer` so the protocol
|
|
824
|
+
* can be constructed/tested without a full server instance. This merge only
|
|
825
|
+
* fills `_defs`. `manageSessions` dispatches the session slice. The room
|
|
826
|
+
* dispatches storage events plus the user's router.
|
|
827
|
+
*/
|
|
828
|
+
declare const createBaseRouter: <T extends IODefs = IODefs>(params: CreateBaseRouterParams<T>) => BaseRouter<T>;
|
|
829
|
+
/**
|
|
830
|
+
* Maintainer / test-only surface. Not part of the public API — shape and
|
|
831
|
+
* contents may change without a semver bump. Prefer public exports for apps.
|
|
832
|
+
*/
|
|
833
|
+
export declare const __internal: {
|
|
834
|
+
assertPresenceFanoutBudget: typeof assertPresenceFanoutBudget;
|
|
835
|
+
createBaseRouter: typeof createBaseRouter;
|
|
836
|
+
createInternalPluvRouter: typeof createInternalPluvRouter;
|
|
837
|
+
getMyConnectionIds: typeof getMyConnectionIds;
|
|
838
|
+
getRoomStatsFromSessions: typeof getRoomStatsFromSessions;
|
|
839
|
+
groupLiveUsers: typeof groupLiveUsers;
|
|
840
|
+
isServerOriginEvent: typeof isServerOriginEvent;
|
|
841
|
+
listLiveUsers: typeof listLiveUsers;
|
|
842
|
+
pageLiveUsers: typeof pageLiveUsers;
|
|
843
|
+
resolveListUsersLimit: typeof resolveListUsersLimit;
|
|
844
|
+
resolveMaxConnections: typeof resolveMaxConnections;
|
|
845
|
+
ROOM_STATS_THROTTLE_MS: number;
|
|
846
|
+
SERVER_ORIGIN_EVENTS: readonly ["$error", "$roomStats", "$syncStateReceived"];
|
|
847
|
+
throttle: typeof throttle;
|
|
848
|
+
};
|
|
849
|
+
type TreatyDomainSchema = {
|
|
850
|
+
schema: unknown;
|
|
851
|
+
procedures: Record<string, TreatyProcedureInputJson>;
|
|
852
|
+
};
|
|
853
|
+
type TreatySchemaJson = {
|
|
854
|
+
user: unknown;
|
|
855
|
+
presence: TreatyDomainSchema | null;
|
|
856
|
+
storage: TreatyDomainSchema | null;
|
|
857
|
+
};
|
|
858
|
+
type BroadcastProxy<TIO extends IORoom<any>> = (<TEvent extends keyof InferIOInput<TIO>>(event: TEvent, data: Id<InferIOInput<TIO>[TEvent]>, senderId: string) => Promise<void>) & { [event in keyof InferIOInput<TIO>]: (data: Id<InferIOInput<TIO>>[event], senderId: string) => Promise<void>; };
|
|
859
|
+
/**
|
|
860
|
+
* Session manager for one room. `createRoom` returns the room.
|
|
861
|
+
* `manageSessions` attaches the sockets.
|
|
862
|
+
*/
|
|
863
|
+
declare class RoomSessionManager<T extends IODefs = IODefs> {
|
|
864
|
+
private readonly _room;
|
|
865
|
+
readonly broadcast: BroadcastProxy<IORoom<T>>;
|
|
866
|
+
readonly __experimental_presence: HostPresenceInvoke<IORoom<T>>;
|
|
867
|
+
readonly __experimental_storage: HostStorageInvoke<IORoom<T>>;
|
|
868
|
+
private readonly _platform;
|
|
869
|
+
private readonly _sessions;
|
|
870
|
+
private readonly _sessionRouter;
|
|
871
|
+
private readonly _roomStats;
|
|
872
|
+
private readonly _queue;
|
|
873
|
+
private readonly _limits;
|
|
874
|
+
private _closing;
|
|
875
|
+
private _connecting;
|
|
876
|
+
private _registering;
|
|
877
|
+
private _storageSnapshotRun;
|
|
878
|
+
private readonly _commitGate;
|
|
879
|
+
private _retry;
|
|
880
|
+
private _lastGarbageCollectMs;
|
|
881
|
+
/** Procedure frames whose delivery failed. This isolate outlives the facet. */
|
|
882
|
+
private _unsent;
|
|
883
|
+
private _unsentId;
|
|
884
|
+
private _treatySchema?;
|
|
885
|
+
constructor(_room: SessionRoom<T>, platform: T["platform"]);
|
|
886
|
+
get id(): string;
|
|
887
|
+
getTreatySchema(): TreatySchemaJson;
|
|
888
|
+
register(webSocket: InferPlatformWebSocketSource<T["platform"]>, ...options: keyof InferInitContextType<T["platform"]> extends never ? [{
|
|
889
|
+
token?: string;
|
|
890
|
+
}?] : [WebSocketRegisterConfig<T["platform"]>]): Promise<void>;
|
|
891
|
+
applyFrames(effects: RoomEffects<T>, senderId: string): Promise<void>;
|
|
892
|
+
/** Writes sockets only. The facet drops storage after `deliverFrames` returns. */
|
|
893
|
+
writeFrames(effects: RoomEffects<T>, senderId: string): Promise<void>;
|
|
894
|
+
applyPresence(params: {
|
|
895
|
+
name: string;
|
|
896
|
+
patch: JsonObject | null;
|
|
897
|
+
senderId: string;
|
|
898
|
+
}): Promise<void>;
|
|
899
|
+
onMessage(webSocket: WebSocketType<T["platform"]>): (event: AbstractMessageEvent) => Promise<void>;
|
|
900
|
+
onClose(webSocket: WebSocketType<T["platform"]>): (event: AbstractCloseEvent) => Promise<void>;
|
|
901
|
+
onError(webSocket: WebSocketType<T["platform"]>): (event: AbstractErrorEvent) => Promise<void>;
|
|
902
|
+
garbageCollect(): Promise<void>;
|
|
903
|
+
evict(sessionId: string): Promise<void>;
|
|
904
|
+
evictAll(): Promise<void>;
|
|
905
|
+
getSize(): number;
|
|
906
|
+
noteStorageDirty(): void;
|
|
907
|
+
retryUnsent(effects: RoomEffects<T>): void;
|
|
908
|
+
listUsers(options?: ListUsersOptions): ListUsersResult<IORoom<T>>;
|
|
909
|
+
private _socket;
|
|
910
|
+
private _applyEffects;
|
|
911
|
+
private _collectPong;
|
|
912
|
+
private _createBroadcast;
|
|
913
|
+
private _createPresence;
|
|
914
|
+
private _createStorage;
|
|
915
|
+
private _deliverRecord;
|
|
916
|
+
private _destroyEmptyRoom;
|
|
917
|
+
private _destroyIdleRoom;
|
|
918
|
+
private _disconnect;
|
|
919
|
+
private _emitRoomStats;
|
|
920
|
+
private _emitSyncState;
|
|
921
|
+
private _evictSockets;
|
|
922
|
+
private _hydrate;
|
|
923
|
+
private _isLiveSocket;
|
|
924
|
+
private _noteStorage;
|
|
925
|
+
/** A failed send leaves the queued update for the snapshot loop. */
|
|
926
|
+
private _retainStorage;
|
|
927
|
+
private _onPublished;
|
|
928
|
+
private _onSocketMessage;
|
|
929
|
+
private _openSession;
|
|
930
|
+
private _parseMessage;
|
|
931
|
+
private _publish;
|
|
932
|
+
private _readInputs;
|
|
933
|
+
private _removeSocket;
|
|
934
|
+
private _runSessionProcedure;
|
|
935
|
+
/** Sends procedure frames that `deliverFrames` did not. A failed send stays in this promise and retries after the snapshot interval. */
|
|
936
|
+
private _deliverUnsent;
|
|
937
|
+
/** One promise. Wait, ask the room for a snapshot, and repeat until that snapshot is current. */
|
|
938
|
+
private _snapshotUntilClean;
|
|
939
|
+
private _sessionContext;
|
|
940
|
+
private _toSnapshot;
|
|
941
|
+
/**
|
|
942
|
+
* Removes each socket on the queue, then runs `onUserDisconnected` after that
|
|
943
|
+
* join so the listener can await the session manager. `$exit` is written on
|
|
944
|
+
* a later join. Destroy waits until every unregister in progress has sent `$exit`.
|
|
945
|
+
*/
|
|
946
|
+
private _unregisterSockets;
|
|
947
|
+
private _writeFrames;
|
|
948
|
+
}
|
|
949
|
+
export declare function manageSessions<T extends IODefs = IODefs>(room: IORoom<T>): RoomSessionManager<T>;
|
|
950
|
+
export declare function manageSessions<T extends IODefs = IODefs>(room: SessionRoom<T>, platform: T["platform"]): RoomSessionManager<T>;
|
|
951
|
+
export type { AbstractCloseEvent, AbstractErrorEvent, AbstractEvent, AbstractEventMap, AbstractListener, AbstractMessageEvent, AbstractPlatformConfig, AbstractWebSocketConfig, AbstractWebSocketHandleErrorParams, AuthorizeModule, AuthorizeParams, BaseUser, ConvertWebSocketConfig, CrdtDocFactory, CrdtDocLike, CrdtLibraryType, CreateRoomOptions, CreateTokenParams, DeferredResult, EventMessage, EventRecord, GetInitialStorageFn, HostPresenceInvoke, HostStorageInvoke, HostStorageProcedureProxy, HostTreatyProcedureActor, IOAuthorize, IOConfigBuilder, IOConfigParams, IODefs, IOEventMessage, IOLikeFromDefs, IOPlatformBuilder, IOPlatformFactory, IOPubSubEventMessage, IORoom, IORoomDestroyedEvent, IORoomInternal, IORoomListenerEvent, IORoomMessageEvent, IOStorageEdit, IOStorageEditsEvent, IOStorageSnapshotEvent, IOUserConnectedEvent, IOUserDisconnectedEvent, InferIOAuthorize, InferIOAuthorizeUser, InferIORoom, InferInitContextType, InferPlatformWebSocketType, InferRoomContextType, JWT, JWTEncodeParams, LoadedStorage, MergedRouter, OnGetOperator, OperatorUser, ParticipantKind, PatchDefs, PluvContext, PluvIOConfig, PluvIOLimits, PluvIOListeners, PluvIOSecret, PluvProcedureConfig, PluvRouterEventConfig, PluvServerConfig, ProcedureLike, RoomEffects, RoomJoinActor, RoomPresenceSession, RoomRegisterResult, RoomSessionManager, RoomStorageActor, ServerConfig, SessionRoom, StandardSchemaV1, Suspended, TreatyDomainSchema, TreatySchemaJson, WebSocketSerializedState, WebSocketSession, WebSocketSessionSeq };
|
|
952
|
+
//# sourceMappingURL=index.d.mts.map
|