@rlanz/socket 0.0.1-7 → 0.0.1-9

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 CHANGED
@@ -540,7 +540,10 @@ peer-to-peer. Runtime channel files always come from the generated server manife
540
540
 
541
541
  ### Middleware
542
542
 
543
- Middlewares run before subscription. They may be functions or objects with a `handle` method.
543
+ Middlewares run before subscription. They may be functions, objects with a `handle` method, or
544
+ classes. Middleware classes are resolved through the AdonisJS container for every subscription, so
545
+ constructor injection works with `@inject()`. Classes in the same middleware chain share one
546
+ container resolver, matching AdonisJS HTTP middleware scoping.
544
547
 
545
548
  ```ts
546
549
  import { BaseChannel } from '@rlanz/socket'
@@ -559,6 +562,27 @@ export default class ChatChannel extends BaseChannel<User> {
559
562
  }
560
563
  ```
561
564
 
565
+ ```ts
566
+ import { inject } from '@adonisjs/core'
567
+ import type { MiddlewareContext } from '@rlanz/socket/types'
568
+ import RoomService from '#services/room_service'
569
+
570
+ @inject()
571
+ class EnsureRoomAccess {
572
+ constructor(private rooms: RoomService) {}
573
+
574
+ async handle(ctx: MiddlewareContext<User>, next: () => Promise<void>) {
575
+ await this.rooms.authorize(ctx.socket.getUserOrFail(), ctx.params.roomId)
576
+ await next()
577
+ }
578
+ }
579
+
580
+ export default class ChatChannel extends BaseChannel<User> {
581
+ static pattern = 'chat/:roomId'
582
+ static middlewares = [EnsureRoomAccess]
583
+ }
584
+ ```
585
+
562
586
  Only `SocketResponseError` messages are intentionally exposed to clients. Unexpected middleware,
563
587
  join, and message-handler errors are logged server-side and replaced with a generic protocol error,
564
588
  so internal exception details do not leak. Use `SocketResponseError` only for safe, user-facing
@@ -222,9 +222,16 @@ interface MiddlewareClass<User = unknown> {
222
222
  handle: MiddlewareFn<User>;
223
223
  }
224
224
  /**
225
- * Middleware, either a function or a class.
225
+ * Constructor for a middleware resolved through the AdonisJS container.
226
226
  */
227
- type Middleware<User = unknown> = MiddlewareFn<User> | MiddlewareClass<User>;
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.
233
+ */
234
+ type Middleware<User = unknown> = MiddlewareFn<User> | MiddlewareClass<User> | MiddlewareConstructor<User>;
228
235
  /** Extracts the server-to-client event map declared by a channel instance. */
229
236
  type ChannelServerEventsOf<T> = T extends {
230
237
  readonly $serverEvents: infer Events;
@@ -339,6 +346,8 @@ interface ChannelBroadcaster {
339
346
  };
340
347
  };
341
348
  }
349
+ type ChannelMiddlewareRunner = <User>(middleware: MiddlewareConstructor<User>, ctx: MiddlewareContext<User>, next: () => Promise<void>) => Promise<void>;
350
+ type ChannelMiddlewareRunnerFactory = () => ChannelMiddlewareRunner;
342
351
  /**
343
352
  * Error whose message may intentionally be returned to a socket client.
344
353
  */
@@ -436,7 +445,7 @@ declare class BaseChannel<User = unknown, ServerEvents = Record<never, never>> {
436
445
  *
437
446
  * middleware[0] -> middleware[1] -> ... -> subscribe
438
447
  */
439
- $runMiddlewares(ctx: MiddlewareContext<User>): Promise<void>;
448
+ $runMiddlewares(ctx: MiddlewareContext<User>, runClassMiddleware?: ChannelMiddlewareRunner): Promise<void>;
440
449
  /**
441
450
  * Returns member information for presence.
442
451
  * Implement when options.presence = true.
@@ -482,4 +491,4 @@ interface BaseChannelConstructor {
482
491
  middlewares?: Middleware<any>[];
483
492
  }
484
493
 
485
- 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 MiddlewareContext as f, type MiddlewareFn as g, type PresenceInfo as h, type PresenceMember as i, type SocketAuthenticationResult as j, type SocketEvents as k, type SocketHealthSnapshot as l, type SocketHttpMiddleware as m, type SocketOriginResolver as n, type SocketOriginValues as o, SocketResponseError as p, type SocketRetryQueueConfig as q, type SocketServiceStatus as r, type SocketTransportConfig as s, type SocketUpgradeContext as t, type BaseChannelConstructor as u, type ChannelMatch as v, type PresenceData as w };
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 };
@@ -3,6 +3,9 @@ import {
3
3
  } from "./chunk-G75J233Z.js";
4
4
 
5
5
  // src/base_channel.ts
6
+ var runMiddlewareWithoutContainer = async (Middleware, ctx, next) => {
7
+ await new Middleware().handle(ctx, next);
8
+ };
6
9
  var SocketResponseError = class extends Error {
7
10
  };
8
11
  var MissingPresenceInfoError = class extends SocketResponseError {
@@ -67,19 +70,27 @@ var BaseChannel = class {
67
70
  *
68
71
  * middleware[0] -> middleware[1] -> ... -> subscribe
69
72
  */
70
- async $runMiddlewares(ctx) {
73
+ async $runMiddlewares(ctx, runClassMiddleware = runMiddlewareWithoutContainer) {
71
74
  const middlewares = this.constructor.middlewares || [];
72
75
  let index = 0;
73
- const next = async () => {
76
+ const dispatch = async () => {
74
77
  if (index >= middlewares.length) return;
75
78
  const middleware = middlewares[index++];
76
- if (typeof middleware === "function") {
79
+ let nextCalled = false;
80
+ const next = async () => {
81
+ if (nextCalled) return;
82
+ nextCalled = true;
83
+ await dispatch();
84
+ };
85
+ if (typeof middleware === "function" && middleware.prototype?.handle) {
86
+ await runClassMiddleware(middleware, ctx, next);
87
+ } else if (typeof middleware === "function") {
77
88
  await middleware(ctx, next);
78
89
  } else {
79
90
  await middleware.handle(ctx, next);
80
91
  }
81
92
  };
82
- await next();
93
+ await dispatch();
83
94
  }
84
95
  /**
85
96
  * Dispatches a message to the matching handler.
@@ -117,4 +128,4 @@ export {
117
128
  SocketResponseError,
118
129
  BaseChannel
119
130
  };
120
- //# sourceMappingURL=chunk-2X6WGCH5.js.map
131
+ //# sourceMappingURL=chunk-K243S3QP.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/base_channel.ts"],"sourcesContent":["import type {\n AuthenticatedSocket,\n ChannelOptions,\n PresenceInfo,\n PresenceMember,\n Middleware,\n MiddlewareConstructor,\n MiddlewareContext,\n MiddlewareFn,\n} from './types.js'\nimport type { PresenceManager } from './presence_manager.js'\nimport { getDecoratedChannelHandlers } from './decorators.js'\n\ninterface ChannelBroadcaster {\n to(channel: string): {\n emit(event: string, data: unknown): void\n except(socketId: string): {\n emit(event: string, data: unknown): void\n }\n }\n}\n\nexport type ChannelMiddlewareRunner = <User>(\n middleware: MiddlewareConstructor<User>,\n ctx: MiddlewareContext<User>,\n next: () => Promise<void>\n) => Promise<void>\n\nexport type ChannelMiddlewareRunnerFactory = () => ChannelMiddlewareRunner\n\nconst runMiddlewareWithoutContainer: ChannelMiddlewareRunner = async (Middleware, ctx, next) => {\n await new Middleware().handle(ctx, next)\n}\n\n/**\n * Error whose message may intentionally be returned to a socket client.\n */\nexport class SocketResponseError extends Error {}\n\nexport class MissingPresenceInfoError extends SocketResponseError {\n constructor() {\n super('Presence channels must implement getPresenceInfo(socket)')\n }\n}\n\n/**\n * Abstract class used to define a channel.\n *\n * @example\n * ```ts\n * export default class ChatChannel extends BaseChannel {\n * static pattern = 'chat/:roomId'\n * static middlewares = [\n * async (ctx, next) => {\n * // Authorize access to the channel through middleware.\n * // if (!Room.isMember(ctx.params.roomId, ctx.socket.user.id)) throw new Error('Forbidden')\n * await next()\n * },\n * ]\n * }\n * ```\n *\n * @example With presence and decorated handlers\n * ```ts\n * import { onMessage } from '@rlanz/socket/decorators'\n *\n * export default class ChatChannel extends BaseChannel {\n * static pattern = 'chat/:roomId'\n * static options = { presence: true }\n *\n * static middlewares = [\n * async (ctx, next) => {\n * if (!ctx.socket.user.isVerified) throw new Error('Not verified')\n * await next()\n * }\n * ]\n *\n * @onMessage('chat:send')\n * async handleSend(socket: AuthenticatedSocket<User>, data: unknown) {\n * // Handle chat:send event\n * }\n *\n * @onMessage('chat:typing')\n * async handleTyping(socket: AuthenticatedSocket<User>, data: unknown) {\n * // Handle chat:typing event\n * }\n * }\n * ```\n */\nexport class BaseChannel<User = unknown, ServerEvents = Record<never, never>> {\n /** Type-only server event declarations consumed by generated browser client types. */\n declare readonly $serverEvents: ServerEvents\n /**\n * URL-like channel pattern, for example: 'chat/:roomId'.\n */\n static pattern: string\n\n /**\n * Channel options.\n */\n static options?: ChannelOptions\n\n /**\n * Middlewares to run before subscription.\n */\n static middlewares?: Middleware<any>[]\n\n /**\n * Injected socket service.\n */\n protected socket!: ChannelBroadcaster\n\n /**\n * Full channel name, for example: 'chat/general'.\n */\n protected channelName!: string\n\n /**\n * Parameters extracted from the pattern, for example: { roomId: 'general' }.\n */\n protected params!: Record<string, string>\n\n /**\n * Injected presence manager when presence is enabled.\n */\n protected presence!: PresenceManager\n\n /**\n * Injects dependencies.\n */\n $setContext(\n socket: ChannelBroadcaster,\n channelName: string,\n params: Record<string, string>,\n presence?: PresenceManager\n ): void {\n this.socket = socket\n this.channelName = channelName\n this.params = params\n if (presence) {\n this.presence = presence\n }\n }\n\n /**\n * Emits to every channel member.\n */\n protected broadcast<K extends keyof ServerEvents & string>(\n event: K,\n data: ServerEvents[K]\n ): void {\n this.socket.to(this.channelName).emit(event, data)\n }\n\n /**\n * Emits to every channel member except one socket.\n */\n protected broadcastExcept<K extends keyof ServerEvents & string>(\n socketId: string,\n event: K,\n data: ServerEvents[K]\n ): void {\n this.socket.to(this.channelName).except(socketId).emit(event, data)\n }\n\n /**\n * Runs the middleware chain.\n *\n * middleware[0] -> middleware[1] -> ... -> subscribe\n */\n async $runMiddlewares(\n ctx: MiddlewareContext<User>,\n runClassMiddleware: ChannelMiddlewareRunner = runMiddlewareWithoutContainer\n ): Promise<void> {\n const middlewares = (this.constructor as typeof BaseChannel).middlewares || []\n\n let index = 0\n\n const dispatch = async (): Promise<void> => {\n if (index >= middlewares.length) return\n\n const middleware = middlewares[index++]\n let nextCalled = false\n const next = async (): Promise<void> => {\n if (nextCalled) return\n nextCalled = true\n await dispatch()\n }\n\n if (typeof middleware === 'function' && middleware.prototype?.handle) {\n await runClassMiddleware(middleware as MiddlewareConstructor<User>, ctx, next)\n } else if (typeof middleware === 'function') {\n await (middleware as MiddlewareFn<User>)(ctx, next)\n } else {\n await middleware.handle(ctx, next)\n }\n }\n\n await dispatch()\n }\n\n /**\n * Returns member information for presence.\n * Implement when options.presence = true.\n */\n getPresenceInfo?(socket: AuthenticatedSocket<User>): PresenceInfo\n\n /**\n * Hook called when a socket joins the channel.\n */\n onJoin?(socket: AuthenticatedSocket<User>, ...params: string[]): Promise<void>\n\n /**\n * Hook called when a socket leaves the channel.\n */\n onLeave?(socket: AuthenticatedSocket<User>, ...params: string[]): Promise<void>\n\n /**\n * Hook called when a member joins a presence channel.\n */\n onMemberJoin?(\n socket: AuthenticatedSocket<User>,\n member: PresenceMember,\n ...params: string[]\n ): Promise<void>\n\n /**\n * Hook called when a member leaves a presence channel.\n */\n onMemberLeave?(\n socket: AuthenticatedSocket<User>,\n member: PresenceMember,\n ...params: string[]\n ): Promise<void>\n\n /**\n * Dispatches a message to the matching handler.\n * Called by SocketService. Do not override.\n */\n async $handleMessage(\n socket: AuthenticatedSocket<User>,\n event: string,\n data: unknown\n ): Promise<unknown> {\n const handlers = getDecoratedChannelHandlers<User>(this)\n const handler = handlers[event]\n\n if (typeof handler === 'function') {\n return Reflect.apply(handler, this, [socket, data])\n }\n\n throw new SocketResponseError(`Unknown channel event: ${event}`)\n }\n\n /**\n * Runs the generic join hook. ChannelSubscriptions owns subscription lifecycle.\n */\n async $handleJoin(socket: AuthenticatedSocket<User>, ...params: string[]): Promise<void> {\n await this.onJoin?.(socket, ...params)\n }\n\n /**\n * Runs the generic leave hook. ChannelSubscriptions owns subscription lifecycle.\n */\n async $handleLeave(socket: AuthenticatedSocket<User>, ...params: string[]): Promise<void> {\n await this.onLeave?.(socket, ...params)\n }\n\n $getPresenceInfo(socket: AuthenticatedSocket<User>): PresenceInfo {\n if (!this.getPresenceInfo) {\n throw new MissingPresenceInfoError()\n }\n\n return this.getPresenceInfo(socket)\n }\n}\n\n/** Constructor for any BaseChannel specialization, including container-injected dependencies. */\nexport interface BaseChannelConstructor {\n new (...args: any[]): BaseChannel<any, any>\n readonly name: string\n pattern: string\n options?: ChannelOptions\n middlewares?: Middleware<any>[]\n}\n"],"mappings":";;;;;AA8BA,IAAM,gCAAyD,OAAO,YAAY,KAAK,SAAS;AAC9F,QAAM,IAAI,WAAW,EAAE,OAAO,KAAK,IAAI;AACzC;AAKO,IAAM,sBAAN,cAAkC,MAAM;AAAC;AAEzC,IAAM,2BAAN,cAAuC,oBAAoB;AAAA,EAChE,cAAc;AACZ,UAAM,0DAA0D;AAAA,EAClE;AACF;AA8CO,IAAM,cAAN,MAAuE;AAAA;AAAA;AAAA;AAAA,EAM5E,OAAO;AAAA;AAAA;AAAA;AAAA,EAKP,OAAO;AAAA;AAAA;AAAA;AAAA,EAKP,OAAO;AAAA;AAAA;AAAA;AAAA,EAKG;AAAA;AAAA;AAAA;AAAA,EAKA;AAAA;AAAA;AAAA;AAAA,EAKA;AAAA;AAAA;AAAA;AAAA,EAKA;AAAA;AAAA;AAAA;AAAA,EAKV,YACE,QACA,aACA,QACA,UACM;AACN,SAAK,SAAS;AACd,SAAK,cAAc;AACnB,SAAK,SAAS;AACd,QAAI,UAAU;AACZ,WAAK,WAAW;AAAA,IAClB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKU,UACR,OACA,MACM;AACN,SAAK,OAAO,GAAG,KAAK,WAAW,EAAE,KAAK,OAAO,IAAI;AAAA,EACnD;AAAA;AAAA;AAAA;AAAA,EAKU,gBACR,UACA,OACA,MACM;AACN,SAAK,OAAO,GAAG,KAAK,WAAW,EAAE,OAAO,QAAQ,EAAE,KAAK,OAAO,IAAI;AAAA,EACpE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,gBACJ,KACA,qBAA8C,+BAC/B;AACf,UAAM,cAAe,KAAK,YAAmC,eAAe,CAAC;AAE7E,QAAI,QAAQ;AAEZ,UAAM,WAAW,YAA2B;AAC1C,UAAI,SAAS,YAAY,OAAQ;AAEjC,YAAM,aAAa,YAAY,OAAO;AACtC,UAAI,aAAa;AACjB,YAAM,OAAO,YAA2B;AACtC,YAAI,WAAY;AAChB,qBAAa;AACb,cAAM,SAAS;AAAA,MACjB;AAEA,UAAI,OAAO,eAAe,cAAc,WAAW,WAAW,QAAQ;AACpE,cAAM,mBAAmB,YAA2C,KAAK,IAAI;AAAA,MAC/E,WAAW,OAAO,eAAe,YAAY;AAC3C,cAAO,WAAkC,KAAK,IAAI;AAAA,MACpD,OAAO;AACL,cAAM,WAAW,OAAO,KAAK,IAAI;AAAA,MACnC;AAAA,IACF;AAEA,UAAM,SAAS;AAAA,EACjB;AAAA;AAAA;AAAA;AAAA;AAAA,EAwCA,MAAM,eACJ,QACA,OACA,MACkB;AAClB,UAAM,WAAW,4BAAkC,IAAI;AACvD,UAAM,UAAU,SAAS,KAAK;AAE9B,QAAI,OAAO,YAAY,YAAY;AACjC,aAAO,QAAQ,MAAM,SAAS,MAAM,CAAC,QAAQ,IAAI,CAAC;AAAA,IACpD;AAEA,UAAM,IAAI,oBAAoB,0BAA0B,KAAK,EAAE;AAAA,EACjE;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,YAAY,WAAsC,QAAiC;AACvF,UAAM,KAAK,SAAS,QAAQ,GAAG,MAAM;AAAA,EACvC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,aAAa,WAAsC,QAAiC;AACxF,UAAM,KAAK,UAAU,QAAQ,GAAG,MAAM;AAAA,EACxC;AAAA,EAEA,iBAAiB,QAAiD;AAChE,QAAI,CAAC,KAAK,iBAAiB;AACzB,YAAM,IAAI,yBAAyB;AAAA,IACrC;AAEA,WAAO,KAAK,gBAAgB,MAAM;AAAA,EACpC;AACF;","names":[]}
@@ -1,4 +1,4 @@
1
- import { C as Channel } from './index-Fg-tkHhP.js';
1
+ import { C as Channel } from './index-DjBYUhFR.js';
2
2
  import { ChannelCallArgs, ChannelContractFor, ChannelPattern, ChannelContract, ChannelParameters } from './src/client/types.js';
3
3
 
4
4
  type ServerEvents<Contract> = Contract extends ChannelContract<unknown, infer Events> ? Events : unknown;
@@ -1,5 +1,5 @@
1
1
  import { SocketClientTransport, PresenceUser, EventHandler, ChannelContract, SubscribeOptions, UnsubscribeOptions, SocketOptions, ConnectionState, ChannelCallArgs, ChannelContractFor } from './src/client/types.js';
2
- import './base_channel-Dr40l7CW.js';
2
+ import './base_channel-DPOnqned.js';
3
3
 
4
4
  /**
5
5
  * Represents a subscribed channel.
package/build/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { S as SocketAuthenticationHandler, c as SocketConfig } from './base_channel-Dr40l7CW.js';
2
- export { A as AuthenticatedSocket, B as BaseChannel, a as ChannelAck, b as ChannelMessage, d as ChannelOptions, C as ChannelServerEventsOf, D as Duration, M as Middleware, e as MiddlewareClass, f as MiddlewareContext, g as MiddlewareFn, h as PresenceInfo, i as PresenceMember, R as RawSocket, j as SocketAuthenticationResult, k as SocketEvents, l as SocketHealthSnapshot, m as SocketHttpMiddleware, n as SocketOriginResolver, o as SocketOriginValues, p as SocketResponseError, q as SocketRetryQueueConfig, r as SocketServiceStatus, s as SocketTransportConfig, t as SocketUpgradeContext } from './base_channel-Dr40l7CW.js';
1
+ import { S as SocketAuthenticationHandler, c as SocketConfig } from './base_channel-DPOnqned.js';
2
+ export { A as AuthenticatedSocket, B as BaseChannel, a as ChannelAck, b as ChannelMessage, d as ChannelOptions, C as ChannelServerEventsOf, D as Duration, M as Middleware, e as MiddlewareClass, f as MiddlewareConstructor, g as MiddlewareContext, h as MiddlewareFn, i as PresenceInfo, j as PresenceMember, R as RawSocket, k as SocketAuthenticationResult, l as SocketEvents, m as SocketHealthSnapshot, n as SocketHttpMiddleware, o as SocketOriginResolver, p as SocketOriginValues, q as SocketResponseError, r as SocketRetryQueueConfig, s as SocketServiceStatus, t as SocketTransportConfig, u as SocketUpgradeContext } from './base_channel-DPOnqned.js';
3
3
  import ConfigureCommand from '@adonisjs/core/commands/configure';
4
4
  import 'ws';
5
5
  import 'node:http';
package/build/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  BaseChannel,
3
3
  SocketResponseError
4
- } from "./chunk-2X6WGCH5.js";
4
+ } from "./chunk-K243S3QP.js";
5
5
  import "./chunk-G75J233Z.js";
6
6
 
7
7
  // src/define_config.ts
@@ -1,6 +1,6 @@
1
1
  import { ApplicationService } from '@adonisjs/core/types';
2
- import { S as SocketService, C as ChannelRouter } from '../socket_service-BYEB8LIN.js';
3
- import { P as PresenceManager } from '../base_channel-Dr40l7CW.js';
2
+ import { S as SocketService, C as ChannelRouter } from '../socket_service-BcC9ASCE.js';
3
+ import { P as PresenceManager } from '../base_channel-DPOnqned.js';
4
4
  import 'node:http';
5
5
  import '@adonisjs/logger';
6
6
  import 'emittery';
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  BaseChannel,
3
3
  SocketResponseError
4
- } from "../chunk-2X6WGCH5.js";
4
+ } from "../chunk-K243S3QP.js";
5
5
  import {
6
6
  ChannelPatternSyntax
7
7
  } from "../chunk-E6ILCRL3.js";
@@ -1895,8 +1895,9 @@ var ChannelPattern = class _ChannelPattern {
1895
1895
 
1896
1896
  // src/channel_router.ts
1897
1897
  var ChannelRouter = class {
1898
- constructor(makeChannel = (channel) => new channel()) {
1898
+ constructor(makeChannel = (channel) => new channel(), createMiddlewareRunner) {
1899
1899
  this.makeChannel = makeChannel;
1900
+ this.createMiddlewareRunner = createMiddlewareRunner;
1900
1901
  }
1901
1902
  /**
1902
1903
  * Registered channels keyed by their pattern.
@@ -1953,7 +1954,7 @@ var ChannelRouter = class {
1953
1954
  }
1954
1955
  };
1955
1956
  try {
1956
- await instance.$runMiddlewares(ctx);
1957
+ await instance.$runMiddlewares(ctx, this.createMiddlewareRunner?.());
1957
1958
  } catch (error) {
1958
1959
  return {
1959
1960
  success: false,
@@ -1997,7 +1998,16 @@ var SocketProvider = class {
1997
1998
  */
1998
1999
  register() {
1999
2000
  this.app.container.singleton("socket.router", () => {
2000
- return new ChannelRouter((channel) => this.app.container.make(channel));
2001
+ return new ChannelRouter(
2002
+ (channel) => this.app.container.make(channel),
2003
+ () => {
2004
+ const resolver = this.app.container.createResolver();
2005
+ return async (Middleware, ctx, next) => {
2006
+ const instance = await resolver.make(Middleware);
2007
+ await resolver.call(instance, "handle", [ctx, next]);
2008
+ };
2009
+ }
2010
+ );
2001
2011
  });
2002
2012
  this.app.container.singleton("socket.presence", () => {
2003
2013
  return new PresenceManager();