Commit 4f8f9c81 authored by nanahira's avatar nanahira

adapt Koishi v4.8

parent d7df8e31
This diff is collapsed.
...@@ -33,7 +33,7 @@ ...@@ -33,7 +33,7 @@
"peerDependencies": { "peerDependencies": {
"@nestjs/common": "^9.0.3 || ^8.0.0", "@nestjs/common": "^9.0.3 || ^8.0.0",
"@nestjs/core": "^9.0.3 || ^8.0.0", "@nestjs/core": "^9.0.3 || ^8.0.0",
"koishi": "4.7.6", "koishi": "^4.8.1",
"rxjs": "^7.5.5" "rxjs": "^7.5.5"
}, },
"devDependencies": { "devDependencies": {
...@@ -66,9 +66,9 @@ ...@@ -66,9 +66,9 @@
"@types/ws": "^8.5.3", "@types/ws": "^8.5.3",
"koa": "^2.13.4", "koa": "^2.13.4",
"koa-bodyparser": "^4.3.0", "koa-bodyparser": "^4.3.0",
"koishi-decorators": "^2.1.7", "koishi-thirdeye": "^11.0.2",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"typed-reflector": "^1.0.10", "typed-reflector": "^1.0.11",
"ws": "^8.7.0" "ws": "^8.7.0"
}, },
"jest": { "jest": {
......
import { App, Command } from 'koishi'; import { Command, Context } from 'koishi';
import { import {
Inject, Inject,
Injectable, Injectable,
...@@ -16,7 +16,6 @@ import { KoishiMetascanService } from './providers/koishi-metascan.service'; ...@@ -16,7 +16,6 @@ import { KoishiMetascanService } from './providers/koishi-metascan.service';
import { KOISHI_MODULE_OPTIONS, KoishiIpSym } from './utility/koishi.constants'; import { KOISHI_MODULE_OPTIONS, KoishiIpSym } from './utility/koishi.constants';
import { KoishiLoggerService } from './providers/koishi-logger.service'; import { KoishiLoggerService } from './providers/koishi-logger.service';
import { KoishiHttpDiscoveryService } from './koishi-http-discovery/koishi-http-discovery.service'; import { KoishiHttpDiscoveryService } from './koishi-http-discovery/koishi-http-discovery.service';
import { applySelector } from 'koishi-decorators';
import WebSocket from 'ws'; import WebSocket from 'ws';
import { KoishiNestRouter } from './utility/koa-router'; import { KoishiNestRouter } from './utility/koa-router';
import './utility/koishi.workarounds'; import './utility/koishi.workarounds';
...@@ -24,7 +23,7 @@ import './utility/koishi.declares'; ...@@ -24,7 +23,7 @@ import './utility/koishi.declares';
@Injectable() @Injectable()
export class KoishiService export class KoishiService
extends App extends Context
implements OnModuleInit, OnModuleDestroy implements OnModuleInit, OnModuleDestroy
{ {
constructor( constructor(
...@@ -57,15 +56,15 @@ export class KoishiService ...@@ -57,15 +56,15 @@ export class KoishiService
const httpServer: Server = httpAdapter?.getHttpServer(); const httpServer: Server = httpAdapter?.getHttpServer();
if (httpServer && httpServer instanceof Server) { if (httpServer && httpServer instanceof Server) {
this.logger('app').info('App using Nest HTTP Server.'); this.logger('app').info('App using Nest HTTP Server.');
this._httpServer = httpServer; this.router._http = httpServer;
} else { } else {
this.logger('app').info('No http adapters found from Nest application.'); this.logger('app').info('No http adapters found from Nest application.');
this._httpServer = createServer(this._nestKoaTmpInstance.callback()); this.router._http = createServer(this._nestKoaTmpInstance.callback());
this._wsServer = new WebSocket.Server({ this.router._ws = new WebSocket.Server({
server: this._httpServer, server: this.router._http,
}); });
this._wsServer.on('connection', (socket, request) => { this.router._ws.on('connection', (socket, request) => {
for (const manager of this.router.wsStack) { for (const manager of this.router.wsStack) {
if (manager.accept(socket, request)) return; if (manager.accept(socket, request)) return;
} }
...@@ -79,8 +78,7 @@ export class KoishiService ...@@ -79,8 +78,7 @@ export class KoishiService
this.metascan.preRegisterContext(this.any()); this.metascan.preRegisterContext(this.any());
if (this.koishiModuleOptions.usePlugins) { if (this.koishiModuleOptions.usePlugins) {
for (const pluginDef of this.koishiModuleOptions.usePlugins) { for (const pluginDef of this.koishiModuleOptions.usePlugins) {
const ctx = applySelector(this, pluginDef); this.plugin(pluginDef.plugin, pluginDef.options);
ctx.plugin(pluginDef.plugin, pluginDef.options);
} }
} }
await this.metascan.registerContext(this.any()); await this.metascan.registerContext(this.any());
......
...@@ -4,9 +4,10 @@ import { ...@@ -4,9 +4,10 @@ import {
KoishiModuleOptions, KoishiModuleOptions,
KoishiModuleSelection, KoishiModuleSelection,
} from '../utility/koishi.interfaces'; } from '../utility/koishi.interfaces';
import { applySelector, Registrar } from 'koishi-decorators';
import { Context } from 'koishi'; import { Context } from 'koishi';
import { Module } from '@nestjs/core/injector/module'; import { Module } from '@nestjs/core/injector/module';
import { selectContext } from 'koishi-thirdeye';
import { koishiRegistrar } from 'koishi-thirdeye/dist/src/registrar';
@Injectable() @Injectable()
export class KoishiContextService { export class KoishiContextService {
...@@ -24,7 +25,7 @@ export class KoishiContextService { ...@@ -24,7 +25,7 @@ export class KoishiContextService {
getModuleCtx(ctx: Context, module: Module) { getModuleCtx(ctx: Context, module: Module) {
const moduleSelection = this.moduleSelections.get(module.metatype); const moduleSelection = this.moduleSelections.get(module.metatype);
if (moduleSelection) { if (moduleSelection) {
return applySelector(ctx, moduleSelection); return selectContext(ctx, moduleSelection);
} else { } else {
return ctx; return ctx;
} }
...@@ -32,11 +33,9 @@ export class KoishiContextService { ...@@ -32,11 +33,9 @@ export class KoishiContextService {
getProviderCtx(ctx: Context, ...instances: any[]) { getProviderCtx(ctx: Context, ...instances: any[]) {
for (const instance of instances) { for (const instance of instances) {
ctx = new Registrar( ctx = koishiRegistrar
instance, .aspect(instance, this.options.templateParams || {})
undefined, .getScopeContext(ctx);
this.options.templateParams || {},
).getScopeContext(ctx);
} }
return ctx; return ctx;
} }
......
...@@ -17,9 +17,11 @@ import { KoishiContextService } from './koishi-context.service'; ...@@ -17,9 +17,11 @@ import { KoishiContextService } from './koishi-context.service';
import { Module } from '@nestjs/core/injector/module'; import { Module } from '@nestjs/core/injector/module';
import { KoishiMetadataFetcherService } from '../koishi-metadata-fetcher/koishi-metadata-fetcher.service'; import { KoishiMetadataFetcherService } from '../koishi-metadata-fetcher/koishi-metadata-fetcher.service';
import { KoishiInterceptorManagerService } from '../koishi-interceptor-manager/koishi-interceptor-manager.service'; import { KoishiInterceptorManagerService } from '../koishi-interceptor-manager/koishi-interceptor-manager.service';
import { CommandRegisterConfig, Registrar } from 'koishi-decorators';
import { KoishiExceptionHandlerService } from '../koishi-exception-handler/koishi-exception-handler.service'; import { KoishiExceptionHandlerService } from '../koishi-exception-handler/koishi-exception-handler.service';
import { takeFirstValue } from '../utility/take-first-value'; import { registerAtLeastEach } from '../utility/take-first-value';
import { koishiRegistrar } from 'koishi-thirdeye/dist/src/registrar';
import { CommandConfigExtended } from 'koishi-thirdeye/dist/src/def';
import { map } from 'rxjs';
@Injectable() @Injectable()
export class KoishiMetascanService { export class KoishiMetascanService {
...@@ -46,53 +48,6 @@ export class KoishiMetascanService { ...@@ -46,53 +48,6 @@ export class KoishiMetascanService {
return this.intercepterManager.addInterceptors(command, interceptorDefs); return this.intercepterManager.addInterceptors(command, interceptorDefs);
} }
private async handleInstanceRegistration(
ctx: Context,
instance: Record<string, any>,
methodKey: string,
) {
const registrar = new Registrar(instance, undefined, this.templateParams);
const scopeContext = registrar.getScopeContext(ctx, methodKey, false);
return takeFirstValue(
registrar.runLayers(
scopeContext,
async (baseContext) => {
const result = registrar.register(baseContext, methodKey, false);
if (!result) {
return;
}
if (result.type === 'command') {
const command = result.result as Command;
const interceptorDefs: KoishiCommandInterceptorRegistration[] =
_.uniq(
this.metaFetcher.getPropertyMetadataArray(
KoishiCommandInterceptorDef,
instance,
methodKey,
),
);
this.addInterceptors(command, interceptorDefs);
if (!(result.data as CommandRegisterConfig).config?.empty) {
command.action(async (argv) => {
try {
return await argv.next();
} catch (e) {
return this.exceptionHandler.handleActionException(e);
}
}, true);
}
} else if (result.type === 'plugin') {
const mayBePromise = result.result as Promise<any>;
if (mayBePromise instanceof Promise) {
await mayBePromise;
}
}
},
methodKey,
),
);
}
private registerOnService( private registerOnService(
ctx: Context, ctx: Context,
instance: any, instance: any,
...@@ -178,30 +133,51 @@ export class KoishiMetascanService { ...@@ -178,30 +133,51 @@ export class KoishiMetascanService {
); );
} }
private mutateCommandRegistration(
instance: any,
key: string,
command: Command,
) {
const interceptorDefs: KoishiCommandInterceptorRegistration[] = _.uniq(
this.metaFetcher.getPropertyMetadataArray(
KoishiCommandInterceptorDef,
instance,
key,
),
);
this.addInterceptors(command, interceptorDefs);
const config = command.config as CommandConfigExtended;
if (!config?.empty) {
command.action(async (argv) => {
try {
return await argv.next();
} catch (e) {
return this.exceptionHandler.handleActionException(e);
}
}, true);
}
}
registerContext(ctx: Context) { registerContext(ctx: Context) {
return Promise.all( return Promise.all(
this.runForEachProvider(ctx, (providerCtx, instance) => { this.runForEachProvider(ctx, (providerCtx, instance) => {
this.scanInstanceForProvidingContextService(providerCtx, instance); this.scanInstanceForProvidingContextService(providerCtx, instance);
const registrar = new Registrar( const registrar = koishiRegistrar.aspect(instance, this.templateParams);
instance, const allFields = registrar.getAllFieldsToRegister();
undefined, return registerAtLeastEach(
this.templateParams, registrar.register(providerCtx).pipe(
); map((result) => {
return takeFirstValue( if (result.type === 'command') {
registrar.runLayers(providerCtx, (providerInnerCtx) => { this.mutateCommandRegistration(
registrar.performTopActions(providerInnerCtx); instance,
return Promise.all( result.key,
registrar result.result,
.getAllFieldsToRegister() );
.map((methodKey: string) => }
this.handleInstanceRegistration( return result;
providerInnerCtx, }),
instance, ),
methodKey, allFields,
),
),
);
}),
); );
}), }),
); );
......
...@@ -20,7 +20,7 @@ export class KoishiWebsocketGateway ...@@ -20,7 +20,7 @@ export class KoishiWebsocketGateway
afterInit(server: any): any { afterInit(server: any): any {
// console.log('Init ws server', server, server === this.wsServer); // console.log('Init ws server', server, server === this.wsServer);
this.wsServer.path = '__koishi_fallback'; this.wsServer.path = '__koishi_fallback';
this.koishi._wsServer = this.wsServer; this.koishi.router._ws = this.wsServer;
} }
handleConnection(socket: WebSocket, request: IncomingMessage) { handleConnection(socket: WebSocket, request: IncomingMessage) {
......
...@@ -20,7 +20,6 @@ import { ...@@ -20,7 +20,6 @@ import {
ContextScopeTypes, ContextScopeTypes,
getContextProvideToken, getContextProvideToken,
} from './koishi-context.factory'; } from './koishi-context.factory';
import { CallbackLayer } from 'koishi-decorators';
// Injections // Injections
export const InjectContext = () => Inject(KOISHI_CONTEXT); export const InjectContext = () => Inject(KOISHI_CONTEXT);
...@@ -95,8 +94,8 @@ export const ConcatMetadata = <K extends keyof MetadataArrayValueMap>( ...@@ -95,8 +94,8 @@ export const ConcatMetadata = <K extends keyof MetadataArrayValueMap>(
// Export all koishi-decorator decorators // Export all koishi-decorator decorators
export * from 'koishi-decorators/dist/src/decorators'; export * from 'koishi-thirdeye/dist/src/decorators/common';
export { PluginDef } from 'koishi-decorators'; export { PluginDef } from 'koishi-thirdeye';
// Service // Service
...@@ -116,17 +115,11 @@ export function WireContextService(name?: ServiceName): PropertyDecorator { ...@@ -116,17 +115,11 @@ export function WireContextService(name?: ServiceName): PropertyDecorator {
}; };
} }
export function ProvideContextService( export function ProvideContextService(name: ServiceName): ClassDecorator {
name: ServiceName, Context.service(name);
options: Context.ServiceOptions,
): ClassDecorator {
Context.service(name, options);
return AppendMetadata(KoishiServiceProvideSym, name); return AppendMetadata(KoishiServiceProvideSym, name);
} }
export const UsingService = (...services: ServiceName[]) =>
CallbackLayer((ctx, cb) => ctx.using(services, cb));
// Command interceptor // Command interceptor
export const CommandInterceptors = ( export const CommandInterceptors = (
......
import { ModuleMetadata, Provider, Type } from '@nestjs/common'; import { ModuleMetadata, Provider, Type } from '@nestjs/common';
import { App, Channel, Command, Context, User } from 'koishi'; import { App, Channel, Command, Context, User } from 'koishi';
import { MetadataArrayMap, MetadataMap } from './koishi.constants'; import { MetadataArrayMap, MetadataMap } from './koishi.constants';
import { ContextSelector, PluginDefinition } from 'koishi-decorators'; import { PluginRegistrar, Selection } from 'koishi-thirdeye';
export * from 'koishi-decorators/dist/src/def/interfaces'; export * from 'koishi-thirdeye/dist/src/def';
export interface KoishiModuleSelection extends ContextSelector { export interface KoishiModuleSelection extends Selection {
module: Type<any>; module: Type<any>;
} }
...@@ -17,7 +17,7 @@ export interface KoishiModuleTopOptions { ...@@ -17,7 +17,7 @@ export interface KoishiModuleTopOptions {
export interface KoishiModuleOptions export interface KoishiModuleOptions
extends App.Config, extends App.Config,
KoishiModuleTopOptions { KoishiModuleTopOptions {
usePlugins?: PluginDefinition<any>[]; usePlugins?: PluginRegistrar.PluginDefinition<any>[];
loggerPrefix?: string; loggerPrefix?: string;
loggerColor?: number; loggerColor?: number;
moduleSelection?: KoishiModuleSelection[]; moduleSelection?: KoishiModuleSelection[];
......
...@@ -20,6 +20,8 @@ export class IntercepterManagerService { ...@@ -20,6 +20,8 @@ export class IntercepterManagerService {
return this[Context.current] || this.ctx; return this[Context.current] || this.ctx;
} }
static methods = ['withInterceptors'];
withInterceptors( withInterceptors(
interceptors: KoishiCommandInterceptorRegistration[], interceptors: KoishiCommandInterceptorRegistration[],
): Context { ): Context {
...@@ -30,7 +32,4 @@ export class IntercepterManagerService { ...@@ -30,7 +32,4 @@ export class IntercepterManagerService {
} }
} }
Context.service('$interceptorManager', { Context.service('$interceptorManager', IntercepterManagerService);
constructor: IntercepterManagerService,
methods: ['withInterceptors'],
});
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
export function takeFirstValue<T>(obs: Observable<T>): Promise<T> { export function registerAtLeastEach<T extends { key: string }>(
obs: Observable<T>,
keys: string[],
): Promise<T> {
const remainingKeys = new Set(keys);
return new Promise<T>((resolve, reject) => { return new Promise<T>((resolve, reject) => {
let resolved = false; let resolved = false;
let lastValue: T = undefined;
obs.subscribe({ obs.subscribe({
next: (value) => { next: (value) => {
if (!resolved) { lastValue = value;
remainingKeys.delete(value.key);
if (!resolved && remainingKeys.size === 0) {
resolve(value); resolve(value);
resolved = true; resolved = true;
} }
...@@ -18,7 +25,7 @@ export function takeFirstValue<T>(obs: Observable<T>): Promise<T> { ...@@ -18,7 +25,7 @@ export function takeFirstValue<T>(obs: Observable<T>): Promise<T> {
}, },
complete: () => { complete: () => {
if (!resolved) { if (!resolved) {
resolve(undefined); resolve(lastValue);
resolved = true; resolved = true;
} }
}, },
......
...@@ -31,13 +31,13 @@ describe('Koishi module in Fastify adapter', () => { ...@@ -31,13 +31,13 @@ describe('Koishi module in Fastify adapter', () => {
}); });
it('should register http and ws server', () => { it('should register http and ws server', () => {
expect(koishiApp._httpServer).toBeDefined(); expect(koishiApp.router._http).toBeDefined();
expect(koishiApp._wsServer).toBeDefined(); expect(koishiApp.router._ws).toBeDefined();
}); });
it('should be nest http server', () => { it('should be nest http server', () => {
expect(koishiApp._httpServer).toBeInstanceOf(http.Server); expect(koishiApp.router._http).toBeInstanceOf(http.Server);
expect(app.getHttpServer()).toEqual(koishiApp._httpServer); expect(app.getHttpServer()).toEqual(koishiApp.router._http);
}); });
it('should response to koishi routes', () => { it('should response to koishi routes', () => {
......
...@@ -2,10 +2,11 @@ import { KoishiService } from '../src/koishi.service'; ...@@ -2,10 +2,11 @@ import { KoishiService } from '../src/koishi.service';
import { KoishiWsAdapter } from '../src/koishi.ws-adapter'; import { KoishiWsAdapter } from '../src/koishi.ws-adapter';
import http from 'http'; import http from 'http';
import request from 'supertest'; import request from 'supertest';
import { Context, Session } from 'koishi'; import { Context, Events, Session } from 'koishi';
import { testingModule } from './utility/testing-module'; import { testingModule } from './utility/testing-module';
import { NestExpressApplication } from '@nestjs/platform-express'; import { NestExpressApplication } from '@nestjs/platform-express';
import { EventName } from 'koishi-decorators';
type EventName = keyof Events;
describe('Koishi in Nest.js', () => { describe('Koishi in Nest.js', () => {
let app: NestExpressApplication; let app: NestExpressApplication;
...@@ -27,13 +28,13 @@ describe('Koishi in Nest.js', () => { ...@@ -27,13 +28,13 @@ describe('Koishi in Nest.js', () => {
}); });
it('should register http and ws server', () => { it('should register http and ws server', () => {
expect(koishiApp._httpServer).toBeDefined(); expect(koishiApp.router._http).toBeDefined();
expect(koishiApp._wsServer).toBeDefined(); expect(koishiApp.router._ws).toBeDefined();
}); });
it('should be nest http server', () => { it('should be nest http server', () => {
expect(koishiApp._httpServer).toBeInstanceOf(http.Server); expect(koishiApp.router._http).toBeInstanceOf(http.Server);
expect(app.getHttpServer()).toEqual(koishiApp._httpServer); expect(app.getHttpServer()).toEqual(koishiApp.router._http);
}); });
it('should response to koishi routes', () => { it('should response to koishi routes', () => {
......
...@@ -19,6 +19,7 @@ describe('Koishi in Nest.js context', () => { ...@@ -19,6 +19,7 @@ describe('Koishi in Nest.js context', () => {
}); });
it('should register http service', () => { it('should register http service', () => {
expect(koishiApp._httpServer).toBeDefined(); expect(koishiApp.router._http).toBeDefined();
expect(koishiApp.router._ws).toBeDefined();
}); });
}); });
...@@ -9,7 +9,7 @@ import { ...@@ -9,7 +9,7 @@ import {
PutValue, PutValue,
UseCommand, UseCommand,
UseEvent, UseEvent,
} from 'koishi-decorators'; } from 'koishi-thirdeye';
import { import {
CommandInterceptors, CommandInterceptors,
UsingService, UsingService,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment