@rlanz/socket 0.0.1-6 → 0.0.1-8
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/README.md +186 -147
- package/build/{types-DiYaFvgi.d.ts → base_channel-DPOnqned.d.ts} +185 -171
- package/build/chunk-6PJ4ALJD.js +115 -0
- package/build/chunk-6PJ4ALJD.js.map +1 -0
- package/build/chunk-E6ILCRL3.js +182 -0
- package/build/chunk-E6ILCRL3.js.map +1 -0
- package/build/{chunk-SHH6U4CI.js → chunk-G75J233Z.js} +1 -1
- package/build/chunk-G75J233Z.js.map +1 -0
- package/build/{chunk-B4Y3TNDI.js → chunk-IUQ3GYZZ.js} +5 -24
- package/build/chunk-IUQ3GYZZ.js.map +1 -0
- package/build/chunk-K243S3QP.js +131 -0
- package/build/chunk-K243S3QP.js.map +1 -0
- package/build/chunk-QNOOQXYY.js +899 -0
- package/build/chunk-QNOOQXYY.js.map +1 -0
- package/build/{chunk-FVPY6HZW.js → chunk-YVC7IMDN.js} +21 -5
- package/build/chunk-YVC7IMDN.js.map +1 -0
- package/build/framework-B8pJ4cCA.d.ts +13 -0
- package/build/index-DjBYUhFR.d.ts +150 -0
- package/build/index.d.ts +23 -12
- package/build/index.js +12 -14
- package/build/index.js.map +1 -1
- package/build/providers/socket_provider.d.ts +2 -2
- package/build/providers/socket_provider.js +478 -215
- package/build/providers/socket_provider.js.map +1 -1
- package/build/services/socket.d.ts +2 -2
- package/build/{socket_service-D3jKrleE.d.ts → socket_service-BcC9ASCE.d.ts} +8 -7
- package/build/src/assembler_hook.js +33 -59
- package/build/src/assembler_hook.js.map +1 -1
- package/build/src/client/index.d.ts +3 -147
- package/build/src/client/index.js +4 -837
- package/build/src/client/index.js.map +1 -1
- package/build/src/client/react.d.ts +13 -5
- package/build/src/client/react.js +27 -11
- package/build/src/client/react.js.map +1 -1
- package/build/src/client/types.d.ts +30 -36
- package/build/src/client/vue.d.ts +17 -6
- package/build/src/client/vue.js +31 -13
- package/build/src/client/vue.js.map +1 -1
- package/build/src/decorators.d.ts +3 -2
- package/build/src/decorators.js +1 -1
- package/build/src/health_check.d.ts +2 -2
- package/build/src/health_check.js +14 -3
- package/build/src/health_check.js.map +1 -1
- package/build/src/otel.js +0 -1
- package/build/src/otel.js.map +1 -1
- package/build/src/testing.d.ts +3 -12
- package/build/src/testing.js +1 -1
- package/build/src/types/tracing_channels.d.ts +1 -2
- package/build/src/types.d.ts +1 -1
- package/package.json +3 -9
- package/build/chunk-B4Y3TNDI.js.map +0 -1
- package/build/chunk-FVPY6HZW.js.map +0 -1
- package/build/chunk-HK7Z65DA.js +0 -19
- package/build/chunk-HK7Z65DA.js.map +0 -1
- package/build/chunk-SFAY2ZA4.js +0 -28
- package/build/chunk-SFAY2ZA4.js.map +0 -1
- package/build/chunk-SHH6U4CI.js.map +0 -1
- package/build/chunk-YAX5EHHB.js +0 -453
- package/build/chunk-YAX5EHHB.js.map +0 -1
- package/build/framework-DuW6zpPk.d.ts +0 -14
- package/build/src/client/svelte.d.ts +0 -23
- package/build/src/client/svelte.js +0 -82
- package/build/src/client/svelte.js.map +0 -1
|
@@ -72,161 +72,6 @@ declare class PresenceManager {
|
|
|
72
72
|
getLocalSockets(channel: string): PresenceSocket[];
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
interface ChannelBroadcaster {
|
|
76
|
-
to(channel: string): {
|
|
77
|
-
emit(event: string, data: unknown): void;
|
|
78
|
-
except(socketId: string): {
|
|
79
|
-
emit(event: string, data: unknown): void;
|
|
80
|
-
};
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
/**
|
|
84
|
-
* Error whose message may intentionally be returned to a socket client.
|
|
85
|
-
*/
|
|
86
|
-
declare class SocketResponseError extends Error {
|
|
87
|
-
}
|
|
88
|
-
/**
|
|
89
|
-
* Abstract class used to define a channel.
|
|
90
|
-
*
|
|
91
|
-
* @example
|
|
92
|
-
* ```ts
|
|
93
|
-
* export default class ChatChannel extends BaseChannel {
|
|
94
|
-
* static pattern = 'chat/:roomId'
|
|
95
|
-
* static middlewares = [
|
|
96
|
-
* async (ctx, next) => {
|
|
97
|
-
* // Authorize access to the channel through middleware.
|
|
98
|
-
* // if (!Room.isMember(ctx.params.roomId, ctx.socket.user.id)) throw new Error('Forbidden')
|
|
99
|
-
* await next()
|
|
100
|
-
* },
|
|
101
|
-
* ]
|
|
102
|
-
* }
|
|
103
|
-
* ```
|
|
104
|
-
*
|
|
105
|
-
* @example With presence and decorated handlers
|
|
106
|
-
* ```ts
|
|
107
|
-
* import { onMessage } from '@rlanz/socket/decorators'
|
|
108
|
-
*
|
|
109
|
-
* export default class ChatChannel extends BaseChannel {
|
|
110
|
-
* static pattern = 'chat/:roomId'
|
|
111
|
-
* static options = { presence: true }
|
|
112
|
-
*
|
|
113
|
-
* static middlewares = [
|
|
114
|
-
* async (ctx, next) => {
|
|
115
|
-
* if (!ctx.socket.user.isVerified) throw new Error('Not verified')
|
|
116
|
-
* await next()
|
|
117
|
-
* }
|
|
118
|
-
* ]
|
|
119
|
-
*
|
|
120
|
-
* @onMessage('chat:send')
|
|
121
|
-
* async handleSend(socket: AuthenticatedSocket<User>, data: unknown) {
|
|
122
|
-
* // Handle chat:send event
|
|
123
|
-
* }
|
|
124
|
-
*
|
|
125
|
-
* @onMessage('chat:typing')
|
|
126
|
-
* async handleTyping(socket: AuthenticatedSocket<User>, data: unknown) {
|
|
127
|
-
* // Handle chat:typing event
|
|
128
|
-
* }
|
|
129
|
-
* }
|
|
130
|
-
* ```
|
|
131
|
-
*/
|
|
132
|
-
declare class BaseChannel<User = unknown, ServerEvents = unknown> {
|
|
133
|
-
/** Type-only server event declarations consumed by generated browser client types. */
|
|
134
|
-
readonly $serverEvents: ServerEvents;
|
|
135
|
-
/**
|
|
136
|
-
* URL-like channel pattern, for example: 'chat/:roomId'.
|
|
137
|
-
*/
|
|
138
|
-
static pattern: string;
|
|
139
|
-
/**
|
|
140
|
-
* Channel options.
|
|
141
|
-
*/
|
|
142
|
-
static options?: ChannelOptions;
|
|
143
|
-
/**
|
|
144
|
-
* Middlewares to run before subscription.
|
|
145
|
-
*/
|
|
146
|
-
static middlewares?: Middleware<any>[];
|
|
147
|
-
/**
|
|
148
|
-
* Event handler map.
|
|
149
|
-
* Ex: { 'chat:send': 'handleSend' }
|
|
150
|
-
*/
|
|
151
|
-
protected handlers: ChannelHandlerMap<User>;
|
|
152
|
-
/**
|
|
153
|
-
* Injected socket service.
|
|
154
|
-
*/
|
|
155
|
-
protected socket: ChannelBroadcaster;
|
|
156
|
-
/**
|
|
157
|
-
* Full channel name, for example: 'chat/general'.
|
|
158
|
-
*/
|
|
159
|
-
protected channelName: string;
|
|
160
|
-
/**
|
|
161
|
-
* Parameters extracted from the pattern, for example: { roomId: 'general' }.
|
|
162
|
-
*/
|
|
163
|
-
protected params: Record<string, string>;
|
|
164
|
-
/**
|
|
165
|
-
* Injected presence manager when presence is enabled.
|
|
166
|
-
*/
|
|
167
|
-
protected presence: PresenceManager;
|
|
168
|
-
/**
|
|
169
|
-
* Injects dependencies.
|
|
170
|
-
*/
|
|
171
|
-
$setContext(socket: ChannelBroadcaster, channelName: string, params: Record<string, string>, presence?: PresenceManager): void;
|
|
172
|
-
/**
|
|
173
|
-
* Emits to every channel member.
|
|
174
|
-
*/
|
|
175
|
-
protected broadcast<K extends unknown extends ServerEvents ? string : keyof ServerEvents & string>(event: K, data: unknown extends ServerEvents ? unknown : ServerEvents[K & keyof ServerEvents]): void;
|
|
176
|
-
/**
|
|
177
|
-
* Emits to every channel member except one socket.
|
|
178
|
-
*/
|
|
179
|
-
protected broadcastExcept<K extends unknown extends ServerEvents ? string : keyof ServerEvents & string>(socketId: string, event: K, data: unknown extends ServerEvents ? unknown : ServerEvents[K & keyof ServerEvents]): void;
|
|
180
|
-
/**
|
|
181
|
-
* Runs the middleware chain.
|
|
182
|
-
*
|
|
183
|
-
* middleware[0] -> middleware[1] -> ... -> subscribe
|
|
184
|
-
*/
|
|
185
|
-
$runMiddlewares(ctx: MiddlewareContext<User>): Promise<void>;
|
|
186
|
-
/**
|
|
187
|
-
* Returns member information for presence.
|
|
188
|
-
* Implement when options.presence = true.
|
|
189
|
-
*/
|
|
190
|
-
getPresenceInfo?(socket: AuthenticatedSocket<User>): PresenceInfo;
|
|
191
|
-
/**
|
|
192
|
-
* Hook called when a socket joins the channel.
|
|
193
|
-
*/
|
|
194
|
-
onJoin?(socket: AuthenticatedSocket<User>, ...params: string[]): Promise<void>;
|
|
195
|
-
/**
|
|
196
|
-
* Hook called when a socket leaves the channel.
|
|
197
|
-
*/
|
|
198
|
-
onLeave?(socket: AuthenticatedSocket<User>, ...params: string[]): Promise<void>;
|
|
199
|
-
/**
|
|
200
|
-
* Hook called when a message is received on the channel.
|
|
201
|
-
* Used as fallback when no event handler matches.
|
|
202
|
-
*/
|
|
203
|
-
onMessage?(socket: AuthenticatedSocket<User>, event: string, data: unknown): Promise<void>;
|
|
204
|
-
/**
|
|
205
|
-
* Hook called when a member joins a presence channel.
|
|
206
|
-
*/
|
|
207
|
-
onMemberJoin?(socket: AuthenticatedSocket<User>, member: PresenceMember, ...params: string[]): Promise<void>;
|
|
208
|
-
/**
|
|
209
|
-
* Hook called when a member leaves a presence channel.
|
|
210
|
-
*/
|
|
211
|
-
onMemberLeave?(socket: AuthenticatedSocket<User>, member: PresenceMember, ...params: string[]): Promise<void>;
|
|
212
|
-
/**
|
|
213
|
-
* Dispatches a message to the matching handler.
|
|
214
|
-
* Called by SocketService. Do not override.
|
|
215
|
-
*/
|
|
216
|
-
$handleMessage(socket: AuthenticatedSocket<User>, event: string, data: unknown): Promise<unknown>;
|
|
217
|
-
/**
|
|
218
|
-
* Runs the generic join hook. ChannelSubscriptions owns subscription lifecycle.
|
|
219
|
-
*/
|
|
220
|
-
$handleJoin(socket: AuthenticatedSocket<User>, ...params: string[]): Promise<void>;
|
|
221
|
-
/**
|
|
222
|
-
* Runs the generic leave hook. ChannelSubscriptions owns subscription lifecycle.
|
|
223
|
-
*/
|
|
224
|
-
$handleLeave(socket: AuthenticatedSocket<User>, ...params: string[]): Promise<void>;
|
|
225
|
-
$getPresenceInfo(socket: AuthenticatedSocket<User>): PresenceInfo;
|
|
226
|
-
}
|
|
227
|
-
/** Constructor for any BaseChannel specialization. */
|
|
228
|
-
type BaseChannelConstructor = typeof BaseChannel<any, any>;
|
|
229
|
-
|
|
230
75
|
/**
|
|
231
76
|
* Duration can be specified as milliseconds or as a human-readable string.
|
|
232
77
|
*/
|
|
@@ -239,6 +84,19 @@ interface SocketTransportConfig {
|
|
|
239
84
|
* How long to wait for other instances to answer a presence snapshot request.
|
|
240
85
|
*/
|
|
241
86
|
presenceTimeout?: Duration;
|
|
87
|
+
/**
|
|
88
|
+
* In-memory retry policy for transport publications that fail.
|
|
89
|
+
*
|
|
90
|
+
* The queue is enabled and limited to 1000 distinct messages by default.
|
|
91
|
+
* Set maxSize to null only when an unbounded queue is explicitly desired.
|
|
92
|
+
*/
|
|
93
|
+
retryQueue?: SocketRetryQueueConfig;
|
|
94
|
+
}
|
|
95
|
+
interface SocketRetryQueueConfig {
|
|
96
|
+
enabled?: boolean;
|
|
97
|
+
removeDuplicates?: boolean;
|
|
98
|
+
maxSize?: number | null;
|
|
99
|
+
retryInterval?: Duration | false;
|
|
242
100
|
}
|
|
243
101
|
|
|
244
102
|
/**
|
|
@@ -288,14 +146,6 @@ interface AuthenticatedSocket<User = unknown> {
|
|
|
288
146
|
* Emits an event to this socket.
|
|
289
147
|
*/
|
|
290
148
|
emit(event: string, data: unknown): void;
|
|
291
|
-
/**
|
|
292
|
-
* Joins the user target used by SocketService.toUser().
|
|
293
|
-
*/
|
|
294
|
-
joinUserRoom(userId: string | number): Promise<void>;
|
|
295
|
-
/**
|
|
296
|
-
* Leaves the current SocketService.toUser() target.
|
|
297
|
-
*/
|
|
298
|
-
leaveUserRoom(): Promise<void>;
|
|
299
149
|
/**
|
|
300
150
|
* Disconnects the socket.
|
|
301
151
|
*/
|
|
@@ -314,6 +164,8 @@ interface SocketUpgradeContext {
|
|
|
314
164
|
}
|
|
315
165
|
type SocketAuthenticationResult<User = unknown> = User | false | null;
|
|
316
166
|
type SocketAuthenticationHandler<User = unknown> = (ctx: SocketUpgradeContext) => SocketAuthenticationResult<User> | Promise<SocketAuthenticationResult<User>>;
|
|
167
|
+
type SocketOriginValues = boolean | string | string[];
|
|
168
|
+
type SocketOriginResolver = (origin: string, httpContext: HttpContext) => SocketOriginValues;
|
|
317
169
|
/**
|
|
318
170
|
* AdonisJS HTTP middleware executed for the WebSocket upgrade request.
|
|
319
171
|
*/
|
|
@@ -370,15 +222,20 @@ interface MiddlewareClass<User = unknown> {
|
|
|
370
222
|
handle: MiddlewareFn<User>;
|
|
371
223
|
}
|
|
372
224
|
/**
|
|
373
|
-
*
|
|
225
|
+
* Constructor for a middleware resolved through the AdonisJS container.
|
|
226
|
+
*/
|
|
227
|
+
interface MiddlewareConstructor<User = unknown> {
|
|
228
|
+
new (...args: any[]): MiddlewareClass<User>;
|
|
229
|
+
readonly prototype: MiddlewareClass<User>;
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Middleware, either a function, an instance, or a container-resolved class.
|
|
374
233
|
*/
|
|
375
|
-
type Middleware<User = unknown> = MiddlewareFn<User> | MiddlewareClass<User>;
|
|
234
|
+
type Middleware<User = unknown> = MiddlewareFn<User> | MiddlewareClass<User> | MiddlewareConstructor<User>;
|
|
376
235
|
/** Extracts the server-to-client event map declared by a channel instance. */
|
|
377
236
|
type ChannelServerEventsOf<T> = T extends {
|
|
378
237
|
readonly $serverEvents: infer Events;
|
|
379
238
|
} ? Events : unknown;
|
|
380
|
-
type ChannelHandlerFn<User = unknown> = (socket: AuthenticatedSocket<User>, data: any) => unknown | Promise<unknown>;
|
|
381
|
-
type ChannelHandlerMap<User = unknown> = Record<string, ChannelHandlerFn<User>>;
|
|
382
239
|
/**
|
|
383
240
|
* Package configuration.
|
|
384
241
|
*/
|
|
@@ -386,10 +243,14 @@ interface SocketConfig<User = unknown> {
|
|
|
386
243
|
websocket?: {
|
|
387
244
|
path?: string;
|
|
388
245
|
/**
|
|
389
|
-
*
|
|
390
|
-
*
|
|
246
|
+
* Browser origin policy for opening a WebSocket connection.
|
|
247
|
+
*
|
|
248
|
+
* Uses the same value shapes as the AdonisJS CORS `origin` option. A resolver
|
|
249
|
+
* receives the request origin and HTTP context and may return any static value.
|
|
250
|
+
* Same-origin requests are allowed by default when this option is not defined.
|
|
251
|
+
* Clients without an Origin header are always allowed.
|
|
391
252
|
*/
|
|
392
|
-
|
|
253
|
+
origin?: SocketOriginValues | SocketOriginResolver;
|
|
393
254
|
/**
|
|
394
255
|
* HTTP middleware executed only for the WebSocket upgrade request.
|
|
395
256
|
*
|
|
@@ -477,4 +338,157 @@ interface SocketEvents<User = unknown> {
|
|
|
477
338
|
};
|
|
478
339
|
}
|
|
479
340
|
|
|
480
|
-
|
|
341
|
+
interface ChannelBroadcaster {
|
|
342
|
+
to(channel: string): {
|
|
343
|
+
emit(event: string, data: unknown): void;
|
|
344
|
+
except(socketId: string): {
|
|
345
|
+
emit(event: string, data: unknown): void;
|
|
346
|
+
};
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
type ChannelMiddlewareRunner = <User>(middleware: MiddlewareConstructor<User>, ctx: MiddlewareContext<User>, next: () => Promise<void>) => Promise<void>;
|
|
350
|
+
type ChannelMiddlewareRunnerFactory = () => ChannelMiddlewareRunner;
|
|
351
|
+
/**
|
|
352
|
+
* Error whose message may intentionally be returned to a socket client.
|
|
353
|
+
*/
|
|
354
|
+
declare class SocketResponseError extends Error {
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* Abstract class used to define a channel.
|
|
358
|
+
*
|
|
359
|
+
* @example
|
|
360
|
+
* ```ts
|
|
361
|
+
* export default class ChatChannel extends BaseChannel {
|
|
362
|
+
* static pattern = 'chat/:roomId'
|
|
363
|
+
* static middlewares = [
|
|
364
|
+
* async (ctx, next) => {
|
|
365
|
+
* // Authorize access to the channel through middleware.
|
|
366
|
+
* // if (!Room.isMember(ctx.params.roomId, ctx.socket.user.id)) throw new Error('Forbidden')
|
|
367
|
+
* await next()
|
|
368
|
+
* },
|
|
369
|
+
* ]
|
|
370
|
+
* }
|
|
371
|
+
* ```
|
|
372
|
+
*
|
|
373
|
+
* @example With presence and decorated handlers
|
|
374
|
+
* ```ts
|
|
375
|
+
* import { onMessage } from '@rlanz/socket/decorators'
|
|
376
|
+
*
|
|
377
|
+
* export default class ChatChannel extends BaseChannel {
|
|
378
|
+
* static pattern = 'chat/:roomId'
|
|
379
|
+
* static options = { presence: true }
|
|
380
|
+
*
|
|
381
|
+
* static middlewares = [
|
|
382
|
+
* async (ctx, next) => {
|
|
383
|
+
* if (!ctx.socket.user.isVerified) throw new Error('Not verified')
|
|
384
|
+
* await next()
|
|
385
|
+
* }
|
|
386
|
+
* ]
|
|
387
|
+
*
|
|
388
|
+
* @onMessage('chat:send')
|
|
389
|
+
* async handleSend(socket: AuthenticatedSocket<User>, data: unknown) {
|
|
390
|
+
* // Handle chat:send event
|
|
391
|
+
* }
|
|
392
|
+
*
|
|
393
|
+
* @onMessage('chat:typing')
|
|
394
|
+
* async handleTyping(socket: AuthenticatedSocket<User>, data: unknown) {
|
|
395
|
+
* // Handle chat:typing event
|
|
396
|
+
* }
|
|
397
|
+
* }
|
|
398
|
+
* ```
|
|
399
|
+
*/
|
|
400
|
+
declare class BaseChannel<User = unknown, ServerEvents = Record<never, never>> {
|
|
401
|
+
/** Type-only server event declarations consumed by generated browser client types. */
|
|
402
|
+
readonly $serverEvents: ServerEvents;
|
|
403
|
+
/**
|
|
404
|
+
* URL-like channel pattern, for example: 'chat/:roomId'.
|
|
405
|
+
*/
|
|
406
|
+
static pattern: string;
|
|
407
|
+
/**
|
|
408
|
+
* Channel options.
|
|
409
|
+
*/
|
|
410
|
+
static options?: ChannelOptions;
|
|
411
|
+
/**
|
|
412
|
+
* Middlewares to run before subscription.
|
|
413
|
+
*/
|
|
414
|
+
static middlewares?: Middleware<any>[];
|
|
415
|
+
/**
|
|
416
|
+
* Injected socket service.
|
|
417
|
+
*/
|
|
418
|
+
protected socket: ChannelBroadcaster;
|
|
419
|
+
/**
|
|
420
|
+
* Full channel name, for example: 'chat/general'.
|
|
421
|
+
*/
|
|
422
|
+
protected channelName: string;
|
|
423
|
+
/**
|
|
424
|
+
* Parameters extracted from the pattern, for example: { roomId: 'general' }.
|
|
425
|
+
*/
|
|
426
|
+
protected params: Record<string, string>;
|
|
427
|
+
/**
|
|
428
|
+
* Injected presence manager when presence is enabled.
|
|
429
|
+
*/
|
|
430
|
+
protected presence: PresenceManager;
|
|
431
|
+
/**
|
|
432
|
+
* Injects dependencies.
|
|
433
|
+
*/
|
|
434
|
+
$setContext(socket: ChannelBroadcaster, channelName: string, params: Record<string, string>, presence?: PresenceManager): void;
|
|
435
|
+
/**
|
|
436
|
+
* Emits to every channel member.
|
|
437
|
+
*/
|
|
438
|
+
protected broadcast<K extends keyof ServerEvents & string>(event: K, data: ServerEvents[K]): void;
|
|
439
|
+
/**
|
|
440
|
+
* Emits to every channel member except one socket.
|
|
441
|
+
*/
|
|
442
|
+
protected broadcastExcept<K extends keyof ServerEvents & string>(socketId: string, event: K, data: ServerEvents[K]): void;
|
|
443
|
+
/**
|
|
444
|
+
* Runs the middleware chain.
|
|
445
|
+
*
|
|
446
|
+
* middleware[0] -> middleware[1] -> ... -> subscribe
|
|
447
|
+
*/
|
|
448
|
+
$runMiddlewares(ctx: MiddlewareContext<User>, runClassMiddleware?: ChannelMiddlewareRunner): Promise<void>;
|
|
449
|
+
/**
|
|
450
|
+
* Returns member information for presence.
|
|
451
|
+
* Implement when options.presence = true.
|
|
452
|
+
*/
|
|
453
|
+
getPresenceInfo?(socket: AuthenticatedSocket<User>): PresenceInfo;
|
|
454
|
+
/**
|
|
455
|
+
* Hook called when a socket joins the channel.
|
|
456
|
+
*/
|
|
457
|
+
onJoin?(socket: AuthenticatedSocket<User>, ...params: string[]): Promise<void>;
|
|
458
|
+
/**
|
|
459
|
+
* Hook called when a socket leaves the channel.
|
|
460
|
+
*/
|
|
461
|
+
onLeave?(socket: AuthenticatedSocket<User>, ...params: string[]): Promise<void>;
|
|
462
|
+
/**
|
|
463
|
+
* Hook called when a member joins a presence channel.
|
|
464
|
+
*/
|
|
465
|
+
onMemberJoin?(socket: AuthenticatedSocket<User>, member: PresenceMember, ...params: string[]): Promise<void>;
|
|
466
|
+
/**
|
|
467
|
+
* Hook called when a member leaves a presence channel.
|
|
468
|
+
*/
|
|
469
|
+
onMemberLeave?(socket: AuthenticatedSocket<User>, member: PresenceMember, ...params: string[]): Promise<void>;
|
|
470
|
+
/**
|
|
471
|
+
* Dispatches a message to the matching handler.
|
|
472
|
+
* Called by SocketService. Do not override.
|
|
473
|
+
*/
|
|
474
|
+
$handleMessage(socket: AuthenticatedSocket<User>, event: string, data: unknown): Promise<unknown>;
|
|
475
|
+
/**
|
|
476
|
+
* Runs the generic join hook. ChannelSubscriptions owns subscription lifecycle.
|
|
477
|
+
*/
|
|
478
|
+
$handleJoin(socket: AuthenticatedSocket<User>, ...params: string[]): Promise<void>;
|
|
479
|
+
/**
|
|
480
|
+
* Runs the generic leave hook. ChannelSubscriptions owns subscription lifecycle.
|
|
481
|
+
*/
|
|
482
|
+
$handleLeave(socket: AuthenticatedSocket<User>, ...params: string[]): Promise<void>;
|
|
483
|
+
$getPresenceInfo(socket: AuthenticatedSocket<User>): PresenceInfo;
|
|
484
|
+
}
|
|
485
|
+
/** Constructor for any BaseChannel specialization, including container-injected dependencies. */
|
|
486
|
+
interface BaseChannelConstructor {
|
|
487
|
+
new (...args: any[]): BaseChannel<any, any>;
|
|
488
|
+
readonly name: string;
|
|
489
|
+
pattern: string;
|
|
490
|
+
options?: ChannelOptions;
|
|
491
|
+
middlewares?: Middleware<any>[];
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
export { type AuthenticatedSocket as A, BaseChannel as B, type ChannelServerEventsOf as C, type Duration as D, type Middleware as M, PresenceManager as P, type RawSocket as R, type SocketAuthenticationHandler as S, type ChannelAck as a, type ChannelMessage as b, type SocketConfig as c, type ChannelOptions as d, type MiddlewareClass as e, type MiddlewareConstructor as f, type MiddlewareContext as g, type MiddlewareFn as h, type PresenceInfo as i, type PresenceMember as j, type SocketAuthenticationResult as k, type SocketEvents as l, type SocketHealthSnapshot as m, type SocketHttpMiddleware as n, type SocketOriginResolver as o, type SocketOriginValues as p, SocketResponseError as q, type SocketRetryQueueConfig as r, type SocketServiceStatus as s, type SocketTransportConfig as t, type SocketUpgradeContext as u, type BaseChannelConstructor as v, type ChannelMiddlewareRunnerFactory as w, type ChannelMatch as x, type PresenceData as y };
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ChannelPatternSyntax
|
|
3
|
+
} from "./chunk-E6ILCRL3.js";
|
|
4
|
+
|
|
5
|
+
// src/bindable_channel_pattern.ts
|
|
6
|
+
var BindableChannelPattern = class _BindableChannelPattern extends ChannelPatternSyntax {
|
|
7
|
+
constructor(source) {
|
|
8
|
+
super(source);
|
|
9
|
+
this.source = source;
|
|
10
|
+
const bindingSegments = [];
|
|
11
|
+
const parameters = [];
|
|
12
|
+
const parameterNames = /* @__PURE__ */ new Set();
|
|
13
|
+
const serializedSegments = [];
|
|
14
|
+
let bindable = true;
|
|
15
|
+
if (this.canonicalPattern !== "/" && (this.segments.length === 0 || this.segments.length !== this.canonicalPattern.split("/").length)) {
|
|
16
|
+
bindable = false;
|
|
17
|
+
}
|
|
18
|
+
for (const [index, segment] of this.segments.entries()) {
|
|
19
|
+
const final = index === this.segments.length - 1;
|
|
20
|
+
if (segment.kind === "static") {
|
|
21
|
+
if (segment.value.length === 0 || /[:*?]/.test(segment.value)) {
|
|
22
|
+
bindable = false;
|
|
23
|
+
break;
|
|
24
|
+
}
|
|
25
|
+
bindingSegments.push({ kind: "static", value: segment.value });
|
|
26
|
+
serializedSegments.push(segment.value);
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
if (segment.kind === "parameter") {
|
|
30
|
+
const unsupportedParameter = segment.suffix !== "" || !/^[^.:?/*]+$/.test(segment.name) || segment.optional && !final || segment.name === "wildcard" || parameterNames.has(segment.name);
|
|
31
|
+
if (unsupportedParameter) {
|
|
32
|
+
bindable = false;
|
|
33
|
+
break;
|
|
34
|
+
}
|
|
35
|
+
parameterNames.add(segment.name);
|
|
36
|
+
parameters.push({ name: segment.name, optional: segment.optional });
|
|
37
|
+
bindingSegments.push({
|
|
38
|
+
kind: "parameter",
|
|
39
|
+
name: segment.name,
|
|
40
|
+
optional: segment.optional,
|
|
41
|
+
wildcard: false
|
|
42
|
+
});
|
|
43
|
+
serializedSegments.push(`:${segment.name}${segment.optional ? "?" : ""}`);
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
if (!final || segment.name !== "*" || parameterNames.has("wildcard")) {
|
|
47
|
+
bindable = false;
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
50
|
+
parameterNames.add("wildcard");
|
|
51
|
+
parameters.push({ name: "wildcard", optional: false });
|
|
52
|
+
bindingSegments.push({
|
|
53
|
+
kind: "parameter",
|
|
54
|
+
name: "wildcard",
|
|
55
|
+
optional: false,
|
|
56
|
+
wildcard: true
|
|
57
|
+
});
|
|
58
|
+
serializedSegments.push(segment.name);
|
|
59
|
+
}
|
|
60
|
+
if (this.canonicalPattern !== "/" && serializedSegments.join("/") !== this.canonicalPattern) {
|
|
61
|
+
bindable = false;
|
|
62
|
+
}
|
|
63
|
+
this.#bindable = bindable;
|
|
64
|
+
this.parameters = Object.freeze(parameters.map((parameter) => Object.freeze(parameter)));
|
|
65
|
+
this.#bindingSegments = Object.freeze(bindingSegments.map((segment) => Object.freeze(segment)));
|
|
66
|
+
Object.freeze(this);
|
|
67
|
+
}
|
|
68
|
+
parameters;
|
|
69
|
+
#bindable;
|
|
70
|
+
#bindingSegments;
|
|
71
|
+
/**
|
|
72
|
+
* Parse and validate the pattern subset that generated clients can bind.
|
|
73
|
+
*/
|
|
74
|
+
static parse(value) {
|
|
75
|
+
const pattern = new _BindableChannelPattern(value);
|
|
76
|
+
return pattern.#bindable ? pattern : null;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Bind all required parameters and return one concrete channel name.
|
|
80
|
+
*/
|
|
81
|
+
bind(parameters = {}) {
|
|
82
|
+
if (this.canonicalPattern === "/") {
|
|
83
|
+
return "/";
|
|
84
|
+
}
|
|
85
|
+
const values = [];
|
|
86
|
+
for (const segment of this.#bindingSegments) {
|
|
87
|
+
if (segment.kind === "static") {
|
|
88
|
+
values.push(segment.value);
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
const value = Reflect.get(parameters, segment.name);
|
|
92
|
+
if (value === void 0 && segment.optional) {
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
if (value === void 0) {
|
|
96
|
+
const label = segment.wildcard ? "wildcard parameter" : `"${segment.name}" parameter`;
|
|
97
|
+
throw new Error(`Missing ${label} for channel: ${this.source}`);
|
|
98
|
+
}
|
|
99
|
+
if (typeof value !== "string" && typeof value !== "number") {
|
|
100
|
+
throw new Error(`Invalid "${segment.name}" parameter for channel: ${this.source}`);
|
|
101
|
+
}
|
|
102
|
+
const bound = String(value);
|
|
103
|
+
if (bound.length === 0 || !segment.wildcard && bound.includes("/")) {
|
|
104
|
+
throw new Error(`Invalid "${segment.name}" parameter for channel: ${this.source}`);
|
|
105
|
+
}
|
|
106
|
+
values.push(bound);
|
|
107
|
+
}
|
|
108
|
+
return values.join("/") || "/";
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
export {
|
|
113
|
+
BindableChannelPattern
|
|
114
|
+
};
|
|
115
|
+
//# sourceMappingURL=chunk-6PJ4ALJD.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/bindable_channel_pattern.ts"],"sourcesContent":["import { ChannelPatternSyntax } from './channel_pattern_syntax.js'\n\nexport interface BindableChannelParameter {\n readonly name: string\n readonly optional: boolean\n}\n\ntype BindableChannelSegment =\n | { readonly kind: 'static'; readonly value: string }\n | {\n readonly kind: 'parameter'\n readonly name: string\n readonly optional: boolean\n readonly wildcard: boolean\n }\n\n/**\n * The deliberately small subset of channel patterns that generated clients can bind.\n * Runtime routing keeps using the complete grammar through ChannelPattern.\n */\nexport class BindableChannelPattern extends ChannelPatternSyntax {\n readonly parameters: readonly BindableChannelParameter[]\n readonly #bindable: boolean\n readonly #bindingSegments: readonly BindableChannelSegment[]\n\n private constructor(private readonly source: string) {\n super(source)\n\n const bindingSegments: BindableChannelSegment[] = []\n const parameters: BindableChannelParameter[] = []\n const parameterNames = new Set<string>()\n const serializedSegments: string[] = []\n let bindable = true\n\n // Reject syntax that does not map to one complete segment per slash.\n if (\n this.canonicalPattern !== '/' &&\n (this.segments.length === 0 ||\n this.segments.length !== this.canonicalPattern.split('/').length)\n ) {\n bindable = false\n }\n\n for (const [index, segment] of this.segments.entries()) {\n const final = index === this.segments.length - 1\n\n if (segment.kind === 'static') {\n if (segment.value.length === 0 || /[:*?]/.test(segment.value)) {\n bindable = false\n break\n }\n bindingSegments.push({ kind: 'static', value: segment.value })\n serializedSegments.push(segment.value)\n continue\n }\n\n if (segment.kind === 'parameter') {\n // Generated parameters do not support suffixes or intermediate optional values.\n // The wildcard name is reserved for the final wildcard segment.\n const unsupportedParameter =\n segment.suffix !== '' ||\n !/^[^.:?/*]+$/.test(segment.name) ||\n (segment.optional && !final) ||\n segment.name === 'wildcard' ||\n parameterNames.has(segment.name)\n\n if (unsupportedParameter) {\n bindable = false\n break\n }\n\n parameterNames.add(segment.name)\n parameters.push({ name: segment.name, optional: segment.optional })\n bindingSegments.push({\n kind: 'parameter',\n name: segment.name,\n optional: segment.optional,\n wildcard: false,\n })\n serializedSegments.push(`:${segment.name}${segment.optional ? '?' : ''}`)\n continue\n }\n\n // A generated wildcard must be the final segment and must use the reserved key.\n if (!final || segment.name !== '*' || parameterNames.has('wildcard')) {\n bindable = false\n break\n }\n\n parameterNames.add('wildcard')\n parameters.push({ name: 'wildcard', optional: false })\n bindingSegments.push({\n kind: 'parameter',\n name: 'wildcard',\n optional: false,\n wildcard: true,\n })\n serializedSegments.push(segment.name)\n }\n\n // Reject syntax that the parser cannot reproduce without a change.\n if (this.canonicalPattern !== '/' && serializedSegments.join('/') !== this.canonicalPattern) {\n bindable = false\n }\n\n this.#bindable = bindable\n this.parameters = Object.freeze(parameters.map((parameter) => Object.freeze(parameter)))\n this.#bindingSegments = Object.freeze(bindingSegments.map((segment) => Object.freeze(segment)))\n Object.freeze(this)\n }\n\n /**\n * Parse and validate the pattern subset that generated clients can bind.\n */\n static parse(value: string): BindableChannelPattern | null {\n const pattern = new BindableChannelPattern(value)\n return pattern.#bindable ? pattern : null\n }\n\n /**\n * Bind all required parameters and return one concrete channel name.\n */\n bind(parameters: object = {}): string {\n if (this.canonicalPattern === '/') {\n return '/'\n }\n\n const values: string[] = []\n for (const segment of this.#bindingSegments) {\n if (segment.kind === 'static') {\n values.push(segment.value)\n continue\n }\n\n const value = Reflect.get(parameters, segment.name) as unknown\n\n // Omit only an optional parameter. All other segments need one value.\n if (value === undefined && segment.optional) {\n continue\n }\n if (value === undefined) {\n const label = segment.wildcard ? 'wildcard parameter' : `\"${segment.name}\" parameter`\n throw new Error(`Missing ${label} for channel: ${this.source}`)\n }\n if (typeof value !== 'string' && typeof value !== 'number') {\n throw new Error(`Invalid \"${segment.name}\" parameter for channel: ${this.source}`)\n }\n\n const bound = String(value)\n\n // An ordinary parameter must stay in one segment. A wildcard can contain slashes.\n if (bound.length === 0 || (!segment.wildcard && bound.includes('/'))) {\n throw new Error(`Invalid \"${segment.name}\" parameter for channel: ${this.source}`)\n }\n values.push(bound)\n }\n\n return values.join('/') || '/'\n }\n}\n"],"mappings":"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","names":[]}
|