@@ -19,6 +19,7 @@ import {
1919 CsrfTokenError , HydroError , InvalidOperationError ,
2020 MethodNotAllowedError , NotFoundError , UserFacingError ,
2121} from './error' ;
22+ import type { KnownHandlers } from './interface' ;
2223import { Router } from './router' ;
2324import serializer from './serializer' ;
2425
@@ -127,7 +128,7 @@ export interface UserModel {
127128
128129export interface HandlerCommon < C > { } // eslint-disable-line @typescript-eslint/no-unused-vars
129130export class HandlerCommon < C > {
130- static [ kHandler ] : string | boolean = true ;
131+ static [ kHandler ] : string | boolean = 'HandlerCommon' ;
131132 session : Record < string , any > ;
132133 args : Record < string , any > ;
133134 request : HydroRequest ;
@@ -198,6 +199,8 @@ export class HandlerCommon<C> {
198199}
199200
200201export class Handler < C = CordisContext > extends HandlerCommon < C > {
202+ static [ kHandler ] = 'Handler' ;
203+
201204 loginMethods : any ;
202205 noCheckPermView = false ;
203206 notUsage = false ;
@@ -244,6 +247,8 @@ export class Handler<C = CordisContext> extends HandlerCommon<C> {
244247}
245248
246249export class ConnectionHandler < C > extends HandlerCommon < C > {
250+ static [ kHandler ] = 'ConnectionHandler' ;
251+
247252 conn : WebSocket ;
248253 compression : Shorty ;
249254 counter = 0 ;
@@ -472,7 +477,8 @@ ${c.response.status} ${endTime - startTime}ms ${c.response.length}`);
472477 const h = new HandlerClass ( ctx , this . ctx ) ;
473478 ctx . handler = h ;
474479 const method = ctx . method . toLowerCase ( ) ;
475- const name = ( typeof HandlerClass [ kHandler ] === 'string' ? HandlerClass [ kHandler ] : HandlerClass . name ) . replace ( / H a n d l e r $ / , '' ) ;
480+ const name = ( ( Object . hasOwn ( HandlerClass , kHandler ) && typeof HandlerClass [ kHandler ] === 'string' )
481+ ? HandlerClass [ kHandler ] : HandlerClass . name ) . replace ( / H a n d l e r $ / , '' ) ;
476482 try {
477483 const operation = ( method === 'post' && ctx . request . body ?. operation )
478484 ? `_${ ctx . request . body . operation } ` . replace ( / _ ( [ a - z ] ) / gm, ( s ) => s [ 1 ] . toUpperCase ( ) )
@@ -645,7 +651,8 @@ ${c.response.status} ${endTime - startTime}ms ${c.response.length}`);
645651
646652 private register ( type : 'route' | 'conn' , routeName : string , path : string , HandlerClass : any , ...permPrivChecker ) {
647653 if ( ! HandlerClass ?. [ kHandler ] || ! isClass ( HandlerClass ) ) throw new Error ( 'Invalid registration.' ) ;
648- const name = typeof HandlerClass [ kHandler ] === 'string' ? HandlerClass [ kHandler ] : HandlerClass . name ;
654+ const name = ( ( Object . hasOwn ( HandlerClass , kHandler ) && typeof HandlerClass [ kHandler ] === 'string' )
655+ ? HandlerClass [ kHandler ] : HandlerClass . name ) . replace ( / H a n d l e r $ / , '' ) ;
649656 if ( this . registrationCount [ name ] && this . registry [ name ] !== HandlerClass ) {
650657 logger . warn ( 'Route with name %s already exists.' , name ) ;
651658 }
@@ -746,51 +753,46 @@ ${c.response.status} ${endTime - startTime}ms ${c.response.length}`);
746753 this . captureAllRoutes [ prefix ] = cb ;
747754 }
748755
749- public handlerMixin ( MixinClass : Partial < HandlerCommon < C > > ) {
756+ private _applyMixin ( Target : any , MixinClass : Partial < any > ) {
757+ if ( ! ( 'prototype' in Target ) ) throw new Error ( 'Target must be a class.' ) ;
750758 this . ctx . effect ( ( ) => {
759+ let oldValue = null ;
751760 for ( const val of Object . getOwnPropertyNames ( MixinClass ) ) {
752- if ( HandlerCommon . prototype [ val ] ) {
753- logger . warn ( 'HandlerCommon.prototype[%s] already exists.' , val ) ;
761+ if ( Target . prototype [ val ] ) {
762+ logger . warn ( `${ Target . name } .prototype[${ val } ] already exists.` ) ;
763+ oldValue = Target . prototype [ val ] ;
754764 }
755- HandlerCommon . prototype [ val ] = MixinClass [ val ] ;
765+ Target . prototype [ val ] = MixinClass [ val ] ;
756766 }
757767 return ( ) => {
758768 for ( const val of Object . getOwnPropertyNames ( MixinClass ) ) {
759- delete HandlerCommon . prototype [ val ] ;
769+ if ( Target . prototype [ val ] !== MixinClass [ val ] ) {
770+ logger . warn ( `Failed to unload mixin ${ Target . name } .prototype[${ val } ]: not the same as the original value.` ) ;
771+ } else {
772+ delete Target . prototype [ val ] ;
773+ if ( oldValue ) Target . prototype [ val ] = oldValue ;
774+ }
760775 }
761776 } ;
762777 } ) ;
763778 }
764779
765- public httpHandlerMixin ( MixinClass : Partial < Handler < C > > ) {
766- this . ctx . effect ( ( ) => {
767- for ( const val of Object . getOwnPropertyNames ( MixinClass ) ) {
768- if ( Handler . prototype [ val ] ) {
769- logger . warn ( 'Handler.prototype[%s] already exists.' , val ) ;
770- }
771- Handler . prototype [ val ] = MixinClass [ val ] ;
772- }
773- return ( ) => {
774- for ( const val of Object . getOwnPropertyNames ( MixinClass ) ) {
775- delete Handler . prototype [ val ] ;
776- }
777- } ;
780+ public applyMixin < T extends keyof KnownHandlers > ( name : T , MixinClass : any ) {
781+ this . withHandlerClass ( name , ( HandlerClass ) => {
782+ this . _applyMixin ( HandlerClass , MixinClass ) ;
778783 } ) ;
779784 }
780785
786+ public handlerMixin ( MixinClass : Partial < HandlerCommon < C > > ) {
787+ return this . _applyMixin ( HandlerCommon , MixinClass ) ;
788+ }
789+
790+ public httpHandlerMixin ( MixinClass : Partial < Handler < C > > ) {
791+ return this . _applyMixin ( Handler , MixinClass ) ;
792+ }
793+
781794 public wsHandlerMixin ( MixinClass : Partial < ConnectionHandler < C > > ) {
782- this . ctx . effect ( ( ) => {
783- for ( const val of Object . getOwnPropertyNames ( MixinClass ) ) {
784- if ( ConnectionHandler . prototype [ val ] ) {
785- logger . warn ( 'ConnectionHandler.prototype[%s] already exists.' , val ) ;
786- }
787- }
788- return ( ) => {
789- for ( const val of Object . getOwnPropertyNames ( MixinClass ) ) {
790- delete ConnectionHandler . prototype [ val ] ;
791- }
792- } ;
793- } ) ;
795+ return this . _applyMixin ( ConnectionHandler , MixinClass ) ;
794796 }
795797
796798 public registerRenderer ( name : string , func : Renderer ) {
0 commit comments