@pluv/io 5.0.1 → 5.0.2

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.
@@ -1,81 +0,0 @@
1
- import type { JsonObject } from "@pluv/types";
2
- import { AbstractPersistence } from "./AbstractPersistence";
3
-
4
- export class Persistence extends AbstractPersistence {
5
- private _storages = new Map<string, string>();
6
- private _users = new Map<string, Map<string, JsonObject | null>>();
7
-
8
- public addUser(room: string, connectionId: string, user: JsonObject | null): Promise<void> {
9
- const users = this._users.get(room) ?? new Map<string, JsonObject | null>();
10
-
11
- this._users.set(room, users);
12
- users.set(connectionId, user);
13
-
14
- return Promise.resolve();
15
- }
16
-
17
- public deleteStorageState(room: string): Promise<void> {
18
- this._storages.delete(room);
19
-
20
- return Promise.resolve();
21
- }
22
-
23
- public async deleteUser(room: string, connectionId: string): Promise<void> {
24
- const users = this._users.get(room);
25
-
26
- if (!users) return;
27
-
28
- users.delete(connectionId);
29
-
30
- if (!users.size) {
31
- await this.deleteUsers(room);
32
- await this.deleteStorageState(room);
33
- }
34
-
35
- return Promise.resolve();
36
- }
37
-
38
- public deleteUsers(room: string): Promise<void> {
39
- this._users.delete(room);
40
-
41
- return Promise.resolve();
42
- }
43
-
44
- public getSize(room: string): Promise<number> {
45
- return Promise.resolve(this._users.get(room)?.size ?? 0);
46
- }
47
-
48
- public getStorageState(room: string): Promise<string | null> {
49
- const storage = this._storages.get(room) ?? null;
50
-
51
- return Promise.resolve(storage);
52
- }
53
-
54
- public getUser(room: string, connectionId: string): Promise<JsonObject | null> {
55
- const user = this._users.get(room)?.get(connectionId);
56
-
57
- return Promise.resolve(user ?? null);
58
- }
59
-
60
- public async getUsers(room: string): Promise<Map<string, JsonObject | null>> {
61
- const users = this._users.get(room);
62
-
63
- if (!users) return new Map();
64
-
65
- return users;
66
- }
67
-
68
- public async getUsersSize(room: string): Promise<number> {
69
- return this._users.size;
70
- }
71
-
72
- public initialize(roomContext: any): this {
73
- return this;
74
- }
75
-
76
- public setStorageState(room: string, state: string): Promise<void> {
77
- this._storages.set(room, state);
78
-
79
- return Promise.resolve();
80
- }
81
- }
package/src/PluvIO.ts DELETED
@@ -1,239 +0,0 @@
1
- import type { AbstractCrdtDocFactory, CrdtLibraryType, NoopCrdtDocFactory } from "@pluv/crdt";
2
- import { noop } from "@pluv/crdt";
3
- import type { BaseUser, HasRequiredProperty, IOAuthorize, InferIOAuthorizeUser } from "@pluv/types";
4
- import type { AbstractPlatform, InferInitContextType } from "./AbstractPlatform";
5
- import type { WebSocketRegisterConfig } from "./IORoom";
6
- import { PluvProcedure } from "./PluvProcedure";
7
- import type { MergedRouter, PluvRouterEventConfig } from "./PluvRouter";
8
- import { PluvRouter } from "./PluvRouter";
9
- import { PluvServer, PluvServerConfig } from "./PluvServer";
10
- import type { JWTEncodeParams } from "./authorize";
11
- import { authorize } from "./authorize";
12
- import {
13
- MAX_PRESENCE_SIZE_BYTES,
14
- MAX_STORAGE_SIZE_BYTES,
15
- MAX_USER_ID_LENGTH,
16
- MAX_USER_SIZE_BYTES,
17
- } from "./constants";
18
- import type {
19
- GetInitialStorageFn,
20
- PluvContext,
21
- PluvIOAuthorize,
22
- PluvIOLimits,
23
- PluvIOListeners,
24
- PluvIORouter,
25
- ResolvedPluvIOAuthorize,
26
- } from "./types";
27
- import { oneLine } from "./utils";
28
- import { __PLUV_VERSION } from "./version";
29
-
30
- export type PluvIOConfig<
31
- TPlatform extends AbstractPlatform<any>,
32
- TAuthorize extends IOAuthorize<any, InferInitContextType<TPlatform>> | null,
33
- TContext extends Record<string, any>,
34
- TCrdt extends CrdtLibraryType<any> = CrdtLibraryType<any>,
35
- > = {
36
- authorize?: TAuthorize;
37
- context?: PluvContext<TPlatform, TContext>;
38
- crdt?: TCrdt;
39
- debug?: boolean;
40
- limits?: PluvIOLimits;
41
- platform: () => TPlatform;
42
- };
43
-
44
- type ResolvedServerConfig<
45
- TPlatform extends AbstractPlatform<any> = AbstractPlatform<any>,
46
- TAuthorize extends PluvIOAuthorize<TPlatform, any, InferInitContextType<TPlatform>> | null =
47
- any,
48
- TContext extends Record<string, any> = {},
49
- TCrdt extends CrdtLibraryType<any> = CrdtLibraryType<NoopCrdtDocFactory>,
50
- TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext> = {},
51
- > = Partial<PluvIOListeners<TPlatform, TAuthorize, TContext, TEvents>> &
52
- PluvIORouter<TPlatform, TAuthorize, TContext, TEvents> &
53
- (TCrdt extends CrdtLibraryType<NoopCrdtDocFactory>
54
- ? { getInitialStorage?: "[ERROR]: Must specify crdt to use getInitialStorage" }
55
- : { getInitialStorage: GetInitialStorageFn<TContext> });
56
-
57
- export type BaseServerConfig<
58
- TPlatform extends AbstractPlatform<any> = AbstractPlatform<any>,
59
- TAuthorize extends PluvIOAuthorize<TPlatform, any, InferInitContextType<TPlatform>> | null =
60
- any,
61
- TContext extends Record<string, any> = {},
62
- TCrdt extends CrdtLibraryType<any> = CrdtLibraryType<NoopCrdtDocFactory>,
63
- TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext> = {},
64
- > = {
65
- [
66
- P in keyof ResolvedServerConfig<
67
- TPlatform,
68
- TAuthorize,
69
- TContext,
70
- TCrdt,
71
- TEvents
72
- > as ResolvedServerConfig<
73
- TPlatform,
74
- TAuthorize,
75
- TContext,
76
- TCrdt,
77
- TEvents
78
- >[P] extends undefined
79
- ? never
80
- : P
81
- ]: ResolvedServerConfig<TPlatform, TAuthorize, TContext, TCrdt, TEvents>[P];
82
- };
83
-
84
- export type ServerConfig<
85
- TPlatform extends AbstractPlatform<any> = AbstractPlatform<any>,
86
- TAuthorize extends PluvIOAuthorize<TPlatform, any, InferInitContextType<TPlatform>> | null =
87
- any,
88
- TContext extends Record<string, any> = {},
89
- TCrdt extends CrdtLibraryType<any> = CrdtLibraryType<NoopCrdtDocFactory>,
90
- TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext> = {},
91
- > =
92
- HasRequiredProperty<
93
- BaseServerConfig<TPlatform, TAuthorize, TContext, TCrdt, TEvents>
94
- > extends true
95
- ? [BaseServerConfig<TPlatform, TAuthorize, TContext, TCrdt, TEvents>]
96
- : [BaseServerConfig<TPlatform, TAuthorize, TContext, TCrdt, TEvents>?];
97
-
98
- export class PluvIO<
99
- TPlatform extends AbstractPlatform<any> = AbstractPlatform<any>,
100
- TAuthorize extends PluvIOAuthorize<TPlatform, any, InferInitContextType<TPlatform>> | null =
101
- any,
102
- TContext extends Record<string, any> = {},
103
- TCrdt extends CrdtLibraryType<any> = CrdtLibraryType<any>,
104
- > {
105
- public readonly version: string = __PLUV_VERSION as any;
106
-
107
- private readonly _authorize: TAuthorize = null as TAuthorize;
108
- private readonly _context: PluvContext<TPlatform, TContext> = {} as PluvContext<
109
- TPlatform,
110
- TContext
111
- >;
112
- private readonly _crdt: { doc: (value: any) => AbstractCrdtDocFactory<any, any> };
113
- private readonly _debug: boolean;
114
- private readonly _limits: PluvIOLimits;
115
- private readonly _platform: () => TPlatform;
116
-
117
- public get procedure(): PluvProcedure<TPlatform, TAuthorize, TContext, {}, {}> {
118
- return new PluvProcedure();
119
- }
120
-
121
- constructor(options: PluvIOConfig<TPlatform, TAuthorize, TContext, TCrdt>) {
122
- const { authorize, context, crdt = noop, debug = false, limits, platform } = options;
123
-
124
- this._crdt = crdt as CrdtLibraryType<any>;
125
- this._debug = debug;
126
- this._limits = {
127
- presenceMaxSize: MAX_PRESENCE_SIZE_BYTES,
128
- storageMaxSize: MAX_STORAGE_SIZE_BYTES,
129
- userIdMaxLength: MAX_USER_ID_LENGTH,
130
- userMaxSize: MAX_USER_SIZE_BYTES,
131
- ...limits,
132
- };
133
- this._platform = platform;
134
-
135
- if (authorize) this._authorize = authorize;
136
- if (context) this._context = context;
137
- }
138
-
139
- public async createToken(
140
- params: JWTEncodeParams<InferIOAuthorizeUser<TAuthorize>, TPlatform>,
141
- ): Promise<string> {
142
- if (!this._authorize) {
143
- throw new Error("IO does not specify authorize during initialization.");
144
- }
145
-
146
- const ioAuthorize = this._getIOAuthorize(params);
147
- const user = params.user as BaseUser;
148
- const parsed = !!ioAuthorize ? ioAuthorize.user.parse(user) : user;
149
-
150
- if (!!this._limits.userIdMaxLength && user.id.length > this._limits.userIdMaxLength) {
151
- throw new Error(oneLine`
152
- createToken was called with a long user id. User ID must be at
153
- most 128 characters. Current length: ${user.id.length.toLocaleString()}
154
- `);
155
- }
156
-
157
- const bytes = new TextEncoder().encode(JSON.stringify(parsed)).length;
158
-
159
- if (!!this._limits.userMaxSize && bytes > this._limits.userMaxSize) {
160
- throw new Error(oneLine`
161
- createToken called with large payload. User must be at most 512
162
- bytes. Current size: ${bytes.toLocaleString()}
163
- `);
164
- }
165
-
166
- const platform = this._platform();
167
-
168
- /**
169
- * !HACK
170
- * @description Allow the platform to overwrite this behavior as needed. This is introduced
171
- * to support platformPluv
172
- * @date December 15, 2024
173
- */
174
- if (platform._createToken) {
175
- return await platform._createToken({ ...params, authorize: ioAuthorize });
176
- }
177
-
178
- const secret = ioAuthorize?.secret ?? null;
179
-
180
- if (!secret) throw new Error("`authorize` was specified without a valid secret");
181
-
182
- return await authorize({ platform, secret }).encode(
183
- params as JWTEncodeParams<any, TPlatform>,
184
- );
185
- }
186
-
187
- public mergeRouters<TRouters extends PluvRouter<TPlatform, TAuthorize, TContext, any>[]>(
188
- ...routers: TRouters
189
- ): MergedRouter<TRouters> {
190
- return PluvRouter.merge(...routers);
191
- }
192
-
193
- public router<TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext>>(
194
- events: TEvents,
195
- ): PluvRouter<TPlatform, TAuthorize, TContext, TEvents> {
196
- const invalidName = Object.keys(events).find((name) => name.includes("$"));
197
-
198
- if (typeof invalidName === "string") {
199
- throw new Error(oneLine`
200
- Invalid event name. Event names must not contain $: "${invalidName}"
201
- `);
202
- }
203
-
204
- return new PluvRouter<TPlatform, TAuthorize, TContext, TEvents>(events);
205
- }
206
-
207
- public server<TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext> = {}>(
208
- ...config: ServerConfig<TPlatform, TAuthorize, TContext, TCrdt, TEvents>
209
- ): PluvServer<TPlatform, TAuthorize, TContext, TCrdt, TEvents> {
210
- const serverConfig = (config[0] ?? {}) as PluvServerConfig<
211
- TPlatform,
212
- TAuthorize,
213
- TContext,
214
- TCrdt,
215
- TEvents
216
- >;
217
-
218
- return new PluvServer<TPlatform, TAuthorize, TContext, TCrdt, TEvents>({
219
- ...serverConfig,
220
- authorize: this._authorize ?? undefined,
221
- context: this._context,
222
- crdt: this._crdt,
223
- debug: this._debug,
224
- io: this as PluvIO<TPlatform, TAuthorize, TContext, TCrdt>,
225
- limits: this._limits,
226
- platform: this._platform,
227
- });
228
- }
229
-
230
- private _getIOAuthorize(
231
- options: WebSocketRegisterConfig<TPlatform>,
232
- ): ResolvedPluvIOAuthorize<any, any> | null {
233
- if (typeof this._authorize === "function") {
234
- return this._authorize(options);
235
- }
236
-
237
- return this._authorize as ResolvedPluvIOAuthorize<any, any> | null;
238
- }
239
- }
@@ -1,203 +0,0 @@
1
- import type {
2
- EventRecord,
3
- IOAuthorize,
4
- InputZodLike,
5
- JsonObject,
6
- ProcedureLike,
7
- } from "@pluv/types";
8
- import type { AbstractPlatform, InferInitContextType } from "./AbstractPlatform";
9
- import type { EventResolver, EventResolverKind, MergeEventRecords } from "./types";
10
-
11
- export interface PluvProcedureConfig<
12
- TPlatform extends AbstractPlatform<any>,
13
- TAuthorize extends IOAuthorize<any, InferInitContextType<TPlatform>> | null,
14
- TContext extends Record<string, any>,
15
- TInput extends JsonObject = {},
16
- TOutput extends EventRecord<string, any> = {},
17
- > {
18
- broadcast?: EventResolver<
19
- "broadcast",
20
- TPlatform,
21
- TAuthorize,
22
- TContext,
23
- TInput,
24
- Partial<TOutput>
25
- > | null;
26
- input?: InputZodLike<TInput>;
27
- self?: EventResolver<"self", TPlatform, TAuthorize, TContext, TInput, Partial<TOutput>> | null;
28
- sync?: EventResolver<"sync", TPlatform, TAuthorize, TContext, TInput, Partial<TOutput>> | null;
29
- }
30
-
31
- export class PluvProcedure<
32
- TPlatform extends AbstractPlatform<any>,
33
- TAuthorize extends IOAuthorize<any, InferInitContextType<TPlatform>> | null,
34
- TContext extends Record<string, any>,
35
- TInput extends JsonObject = {},
36
- TOutput extends EventRecord<string, any> = {},
37
- TFilled extends "input" | "broadcast" | "self" | "sync" | "" = "",
38
- > {
39
- private _broadcast: EventResolver<
40
- "broadcast",
41
- TPlatform,
42
- TAuthorize,
43
- TContext,
44
- TInput,
45
- Partial<TOutput>
46
- > | null = null;
47
- private _input: InputZodLike<TInput> | null = null;
48
- private _self: EventResolver<
49
- "self",
50
- TPlatform,
51
- TAuthorize,
52
- TContext,
53
- TInput,
54
- Partial<TOutput>
55
- > | null = null;
56
- private _sync: EventResolver<
57
- "sync",
58
- TPlatform,
59
- TAuthorize,
60
- TContext,
61
- TInput,
62
- Partial<TOutput>
63
- > | null = null;
64
-
65
- public get config(): ProcedureLike<TInput, TOutput>["config"] {
66
- return {
67
- broadcast: this._broadcast?.bind(this) ?? null,
68
- input: this._input ?? null,
69
- resolver: this._resolver(),
70
- self: this._self?.bind(this) ?? null,
71
- sync: this._sync?.bind(this) ?? null,
72
- } as ProcedureLike<TInput, TOutput>["config"];
73
- }
74
-
75
- constructor(
76
- config: PluvProcedureConfig<TPlatform, TAuthorize, TContext, TInput, TOutput> = {},
77
- ) {
78
- const { broadcast, input, self, sync } = config;
79
-
80
- this._broadcast = broadcast ?? null;
81
- this._input = input ?? null;
82
- this._self = self ?? null;
83
- this._sync = sync ?? null;
84
- }
85
-
86
- public broadcast<TResult extends EventRecord<string, any> = {}>(
87
- resolver: EventResolver<"broadcast", TPlatform, TAuthorize, TContext, TInput, TResult>,
88
- ): Omit<
89
- PluvProcedure<
90
- TPlatform,
91
- TAuthorize,
92
- TContext,
93
- TInput,
94
- MergeEventRecords<[TOutput, TResult]>,
95
- TFilled | "input" | "broadcast"
96
- >,
97
- TFilled | "input" | "broadcast"
98
- > {
99
- const { broadcast } = this.config;
100
-
101
- if (!!broadcast) throw new Error("Broadcast was already defined for this procedure");
102
-
103
- return new PluvProcedure<
104
- TPlatform,
105
- TAuthorize,
106
- TContext,
107
- TInput,
108
- MergeEventRecords<[TOutput, TResult]>,
109
- TFilled | "input" | "broadcast"
110
- >({
111
- ...(this.config as any),
112
- broadcast: resolver as any,
113
- });
114
- }
115
-
116
- public input<TData extends JsonObject>(
117
- input: InputZodLike<TData>,
118
- ): Omit<
119
- PluvProcedure<TPlatform, TAuthorize, TContext, TData, {}, TFilled | "input">,
120
- TFilled | "input"
121
- > {
122
- return new PluvProcedure<TPlatform, TAuthorize, TContext, TData, {}, TFilled | "input">({
123
- input,
124
- });
125
- }
126
-
127
- public self<TResult extends EventRecord<string, any> = {}>(
128
- resolver: EventResolver<"self", TPlatform, TAuthorize, TContext, TInput, TResult>,
129
- ): Omit<
130
- PluvProcedure<
131
- TPlatform,
132
- TAuthorize,
133
- TContext,
134
- TInput,
135
- MergeEventRecords<[TOutput, TResult]>,
136
- TFilled | "input" | "self"
137
- >,
138
- TFilled | "input" | "self"
139
- > {
140
- const { self } = this.config;
141
-
142
- if (!!self) throw new Error("Self was already defined for this procedure");
143
-
144
- return new PluvProcedure<
145
- TPlatform,
146
- TAuthorize,
147
- TContext,
148
- TInput,
149
- MergeEventRecords<[TOutput, TResult]>,
150
- TFilled | "input" | "self"
151
- >({
152
- ...(this.config as any),
153
- self: resolver as any,
154
- });
155
- }
156
-
157
- public sync<TResult extends EventRecord<string, any> = {}>(
158
- resolver: EventResolver<"sync", TPlatform, TAuthorize, TContext, TInput, TResult>,
159
- ): Omit<
160
- PluvProcedure<
161
- TPlatform,
162
- TAuthorize,
163
- TContext,
164
- TInput,
165
- MergeEventRecords<[TOutput, TResult]>,
166
- TFilled | "input" | "sync"
167
- >,
168
- TFilled | "input" | "sync"
169
- > {
170
- const { sync } = this.config;
171
-
172
- if (!!sync) throw new Error("Sync was already defined for this procedure");
173
-
174
- return new PluvProcedure<
175
- TPlatform,
176
- TAuthorize,
177
- TContext,
178
- TInput,
179
- MergeEventRecords<[TOutput, TResult]>,
180
- TFilled | "input" | "sync"
181
- >({
182
- ...(this.config as any),
183
- sync: resolver as any,
184
- });
185
- }
186
-
187
- private _resolver(): EventResolver<
188
- EventResolverKind,
189
- TPlatform,
190
- TAuthorize,
191
- TContext,
192
- TInput,
193
- TOutput
194
- > {
195
- return (data, context) => {
196
- return {
197
- ...this._broadcast?.(data, context),
198
- ...this._self?.(data, context),
199
- ...this._sync?.(data, context),
200
- } as TOutput;
201
- };
202
- }
203
- }
package/src/PluvRouter.ts DELETED
@@ -1,61 +0,0 @@
1
- import type { IORouterLike } from "@pluv/types";
2
- import type { AbstractPlatform, InferInitContextType } from "./AbstractPlatform";
3
- import type { PluvProcedure } from "./PluvProcedure";
4
- import type { PluvIOAuthorize } from "./types";
5
-
6
- export type PluvRouterEventConfig<
7
- TPlatform extends AbstractPlatform<any> = AbstractPlatform<any>,
8
- TAuthorize extends PluvIOAuthorize<TPlatform, any, InferInitContextType<TPlatform>> | null =
9
- any,
10
- TContext extends Record<string, any> = {},
11
- > = { [P: string]: Pick<PluvProcedure<TPlatform, TAuthorize, TContext, any, any>, "config"> };
12
-
13
- export type MergedRouter<
14
- TRouters extends PluvRouter<TPlatform, TAuthorize, TContext, any>[] = [],
15
- TPlatform extends AbstractPlatform<any> = AbstractPlatform<any>,
16
- TAuthorize extends PluvIOAuthorize<TPlatform, any, InferInitContextType<TPlatform>> = any,
17
- TContext extends Record<string, any> = {},
18
- TRoot extends TRouters[0]["_defs"]["events"] = {},
19
- > = TRouters extends [
20
- infer IHead extends PluvRouter<TPlatform, TAuthorize, TContext, any>,
21
- ...infer ITail extends PluvRouter<TPlatform, TAuthorize, TContext, any>[],
22
- ]
23
- ? MergedRouter<ITail, TPlatform, TAuthorize, TContext, TRoot & IHead["_defs"]["events"]>
24
- : PluvRouter<TPlatform, TAuthorize, TContext, TRoot>;
25
-
26
- export class PluvRouter<
27
- TPlatform extends AbstractPlatform<any> = AbstractPlatform<any>,
28
- TAuthorize extends PluvIOAuthorize<TPlatform, any, InferInitContextType<TPlatform>> | null =
29
- any,
30
- TContext extends Record<string, any> = {},
31
- TEvents extends PluvRouterEventConfig<TPlatform, TAuthorize, TContext> = {},
32
- > implements IORouterLike<TEvents> {
33
- readonly _defs: { events: TEvents } = { events: {} as TEvents };
34
-
35
- constructor(events: TEvents) {
36
- const invalidName = Object.keys(events).find((name) => !this._isValidEventName(name));
37
-
38
- if (typeof invalidName === "string") {
39
- throw new Error(
40
- `Invalid event name. Event names must be formatted as valid JavaScript variable names: "${invalidName}"`,
41
- );
42
- }
43
-
44
- this._defs = { events };
45
- }
46
-
47
- public static merge<TRouters extends PluvRouter<any, any, any, any>[]>(
48
- ...routers: TRouters
49
- ): MergedRouter<TRouters> {
50
- const events = Object.assign(
51
- Object.create(null),
52
- ...routers.map((router) => router._defs.events),
53
- );
54
-
55
- return new PluvRouter<any, any, any, any>(events) as MergedRouter<TRouters>;
56
- }
57
-
58
- private _isValidEventName(name: string): boolean {
59
- return /^[a-z_$][a-z0-9_$]*$/gi.test(name);
60
- }
61
- }