Repository: eggjs/tegg Branch: master Commit: 0ece332df200 Files: 1679 Total size: 3.2 MB Directory structure: gitextract_ixpf0f65/ ├── .eslintignore ├── .eslintrc ├── .github/ │ ├── FUNDING.yml │ ├── ISSUE_TEMPLATE/ │ │ ├── bug-report-cn.yml │ │ ├── bug-report.yml │ │ ├── feature-request-cn.yml │ │ ├── feature-request.yml │ │ ├── rfc-cn.yml │ │ └── rfc.yml │ ├── PULL_REQUEST_TEMPLATE.md │ └── workflows/ │ ├── codeql-analysis.yml │ ├── nodejs.yml │ └── release.yml ├── .gitignore ├── .mocharc.yml ├── .nycrc.yml ├── CHANGELOG.md ├── CLAUDE.md ├── LICENSE ├── README.md ├── benchmark/ │ └── http/ │ ├── app/ │ │ ├── controller/ │ │ │ ├── FooTeggController.ts │ │ │ └── template/ │ │ │ └── egg_controller_1.js │ │ └── router.js │ ├── config/ │ │ ├── config.default.js │ │ └── plugin.js │ ├── package.json │ └── tsconfig.json ├── core/ │ ├── agent-runtime/ │ │ ├── CHANGELOG.md │ │ ├── index.ts │ │ ├── package.json │ │ ├── src/ │ │ │ ├── AgentRuntime.ts │ │ │ ├── AgentStoreUtils.ts │ │ │ ├── HttpSSEWriter.ts │ │ │ ├── MessageConverter.ts │ │ │ ├── OSSAgentStore.ts │ │ │ ├── OSSObjectStorageClient.ts │ │ │ ├── RunBuilder.ts │ │ │ └── SSEWriter.ts │ │ ├── test/ │ │ │ ├── AgentRuntime.test.ts │ │ │ ├── HttpSSEWriter.test.ts │ │ │ ├── MessageConverter.test.ts │ │ │ ├── OSSAgentStore.test.ts │ │ │ ├── OSSObjectStorageClient.test.ts │ │ │ ├── RunBuilder.test.ts │ │ │ └── helpers.ts │ │ ├── tsconfig.json │ │ └── tsconfig.pub.json │ ├── agent-tracing/ │ │ ├── CHANGELOG.md │ │ ├── claude.ts │ │ ├── index.ts │ │ ├── langgraph.ts │ │ ├── package.json │ │ ├── src/ │ │ │ ├── AbstractLogServiceClient.ts │ │ │ ├── AbstractOssClient.ts │ │ │ ├── ClaudeAgentTracer.ts │ │ │ ├── LangGraphTracer.ts │ │ │ ├── TracingService.ts │ │ │ └── types.ts │ │ ├── test/ │ │ │ ├── ClaudeAgentTracer.test.ts │ │ │ ├── Configure.test.ts │ │ │ ├── LangGraphTracer.test.ts │ │ │ ├── TestUtils.ts │ │ │ └── TracingService.test.ts │ │ ├── tsconfig.json │ │ └── tsconfig.pub.json │ ├── ajv-decorator/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── index.ts │ │ ├── package.json │ │ ├── src/ │ │ │ ├── enum/ │ │ │ │ └── TransformEnum.ts │ │ │ ├── error/ │ │ │ │ └── AjvInvalidParamError.ts │ │ │ └── type/ │ │ │ └── Ajv.ts │ │ ├── test/ │ │ │ └── TransformEnum.test.ts │ │ ├── tsconfig.json │ │ └── tsconfig.pub.json │ ├── aop-decorator/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── index.ts │ │ ├── package.json │ │ ├── src/ │ │ │ ├── AspectMetaBuilder.ts │ │ │ ├── CrosscutAdviceFactory.ts │ │ │ ├── decorator/ │ │ │ │ ├── Advice.ts │ │ │ │ ├── Crosscut.ts │ │ │ │ └── Pointcut.ts │ │ │ ├── model/ │ │ │ │ ├── Aspect.ts │ │ │ │ └── PointcutInfo.ts │ │ │ └── util/ │ │ │ ├── AdviceInfoUtil.ts │ │ │ ├── AspectInfoUtil.ts │ │ │ ├── CrosscutInfoUtil.ts │ │ │ └── PointcutAdviceInfoUtil.ts │ │ ├── test/ │ │ │ ├── AspectMetaBuilder.test.ts │ │ │ └── fixtures/ │ │ │ ├── CrosscutExample.ts │ │ │ ├── InheritExample.ts │ │ │ └── PointcutExample.ts │ │ ├── tsconfig.json │ │ └── tsconfig.pub.json │ ├── aop-runtime/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── index.ts │ │ ├── package.json │ │ ├── src/ │ │ │ ├── AspectExecutor.ts │ │ │ ├── CrossCutGraphHook.ts │ │ │ ├── EggObjectAopHook.ts │ │ │ ├── EggPrototypeCrossCutHook.ts │ │ │ ├── LoadUnitAopHook.ts │ │ │ └── PointCutGraphHook.ts │ │ ├── test/ │ │ │ ├── aop-runtime.test.ts │ │ │ └── fixtures/ │ │ │ ├── modules/ │ │ │ │ ├── constructor_inject_aop/ │ │ │ │ │ ├── Hello.ts │ │ │ │ │ └── package.json │ │ │ │ ├── hello_cross_cut/ │ │ │ │ │ ├── CallTrace.ts │ │ │ │ │ ├── HelloCrossCut.ts │ │ │ │ │ └── package.json │ │ │ │ ├── hello_point_cut/ │ │ │ │ │ ├── HelloPointCut.ts │ │ │ │ │ └── package.json │ │ │ │ ├── hello_succeed/ │ │ │ │ │ ├── Hello.ts │ │ │ │ │ └── package.json │ │ │ │ └── should_throw/ │ │ │ │ ├── Hello.ts │ │ │ │ └── package.json │ │ │ └── mutli/ │ │ │ ├── a/ │ │ │ │ ├── A.ts │ │ │ │ └── package.json │ │ │ ├── b/ │ │ │ │ ├── B.ts │ │ │ │ └── package.json │ │ │ ├── c/ │ │ │ │ ├── Base.ts │ │ │ │ └── package.json │ │ │ └── cross/ │ │ │ ├── Cross.ts │ │ │ └── package.json │ │ ├── tsconfig.json │ │ └── tsconfig.pub.json │ ├── background-task/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── index.ts │ │ ├── package.json │ │ ├── src/ │ │ │ └── BackgroundTaskHelper.ts │ │ ├── test/ │ │ │ └── BackgroundTaskHelper.test.ts │ │ ├── tsconfig.json │ │ └── tsconfig.pub.json │ ├── common-util/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── index.ts │ │ ├── package.json │ │ ├── src/ │ │ │ ├── FSUtil.ts │ │ │ ├── Graph.ts │ │ │ ├── MapUtil.ts │ │ │ ├── ModuleConfig.ts │ │ │ ├── ModuleConfigs.ts │ │ │ ├── NameUtil.ts │ │ │ ├── ObjectUtils.ts │ │ │ ├── ProxyUtil.ts │ │ │ ├── StackUtil.ts │ │ │ ├── StreamUtil.ts │ │ │ └── TimerUtil.ts │ │ ├── test/ │ │ │ ├── MapUtil.test.ts │ │ │ ├── ModuleConfig.test.ts │ │ │ ├── NameUtil.test.ts │ │ │ ├── ObjectUtil.test.ts │ │ │ ├── ProtoGraph.test.ts │ │ │ ├── TimerUtil.test.ts │ │ │ └── fixtures/ │ │ │ ├── apps/ │ │ │ │ ├── app-with-module-json/ │ │ │ │ │ ├── app/ │ │ │ │ │ │ ├── module-a/ │ │ │ │ │ │ │ └── package.json │ │ │ │ │ │ └── module-b/ │ │ │ │ │ │ └── package.json │ │ │ │ │ ├── config/ │ │ │ │ │ │ └── module.json │ │ │ │ │ └── package.json │ │ │ │ ├── app-with-module-pkg-json/ │ │ │ │ │ ├── config/ │ │ │ │ │ │ └── module.json │ │ │ │ │ ├── node_modules/ │ │ │ │ │ │ └── module-a/ │ │ │ │ │ │ ├── index.js │ │ │ │ │ │ └── package.json │ │ │ │ │ └── package.json │ │ │ │ ├── app-with-modules/ │ │ │ │ │ ├── app/ │ │ │ │ │ │ └── module-a/ │ │ │ │ │ │ └── package.json │ │ │ │ │ └── package.json │ │ │ │ ├── app-with-no-module-json/ │ │ │ │ │ ├── .sff/ │ │ │ │ │ │ ├── .other/ │ │ │ │ │ │ │ └── module-d/ │ │ │ │ │ │ │ └── package.json │ │ │ │ │ │ └── module-c/ │ │ │ │ │ │ └── package.json │ │ │ │ │ ├── app/ │ │ │ │ │ │ ├── module-a/ │ │ │ │ │ │ │ └── package.json │ │ │ │ │ │ └── module-b/ │ │ │ │ │ │ ├── package.json │ │ │ │ │ │ └── test/ │ │ │ │ │ │ └── fixtures/ │ │ │ │ │ │ └── module-e/ │ │ │ │ │ │ └── package.json │ │ │ │ │ ├── node_modules/ │ │ │ │ │ │ ├── dep/ │ │ │ │ │ │ │ ├── index.js │ │ │ │ │ │ │ └── package.json │ │ │ │ │ │ └── module-c/ │ │ │ │ │ │ ├── index.js │ │ │ │ │ │ └── package.json │ │ │ │ │ └── package.json │ │ │ │ ├── app-with-no-module-json-duplicated/ │ │ │ │ │ ├── .sff/ │ │ │ │ │ │ ├── .other/ │ │ │ │ │ │ │ └── module-d/ │ │ │ │ │ │ │ └── package.json │ │ │ │ │ │ └── module-c/ │ │ │ │ │ │ └── package.json │ │ │ │ │ ├── app/ │ │ │ │ │ │ ├── module-a/ │ │ │ │ │ │ │ └── package.json │ │ │ │ │ │ └── module-b/ │ │ │ │ │ │ ├── package.json │ │ │ │ │ │ └── test/ │ │ │ │ │ │ └── fixtures/ │ │ │ │ │ │ └── module-e/ │ │ │ │ │ │ └── package.json │ │ │ │ │ ├── node_modules/ │ │ │ │ │ │ └── module-b/ │ │ │ │ │ │ ├── index.js │ │ │ │ │ │ └── package.json │ │ │ │ │ └── package.json │ │ │ │ └── app-with-symlink/ │ │ │ │ ├── app/ │ │ │ │ │ └── module-a/ │ │ │ │ │ └── package.json │ │ │ │ └── package.json │ │ │ ├── modules/ │ │ │ │ ├── dev-module-config/ │ │ │ │ │ ├── module.dev.yml │ │ │ │ │ └── module.yml │ │ │ │ └── foo-yaml/ │ │ │ │ └── module.yml │ │ │ └── monorepo/ │ │ │ ├── foo/ │ │ │ │ └── .gitkeep │ │ │ └── packages/ │ │ │ ├── a/ │ │ │ │ ├── node_modules/ │ │ │ │ │ └── c/ │ │ │ │ │ └── package.json │ │ │ │ └── package.json │ │ │ ├── b/ │ │ │ │ └── package.json │ │ │ └── d/ │ │ │ ├── node_modules/ │ │ │ │ ├── e/ │ │ │ │ │ └── package.json │ │ │ │ └── f/ │ │ │ │ └── package.json │ │ │ └── package.json │ │ ├── tsconfig.json │ │ └── tsconfig.pub.json │ ├── controller-decorator/ │ │ ├── CHANGELOG.md │ │ ├── README.json │ │ ├── index.ts │ │ ├── package.json │ │ ├── src/ │ │ │ ├── builder/ │ │ │ │ └── ControllerMetaBuilderFactory.ts │ │ │ ├── decorator/ │ │ │ │ ├── Acl.ts │ │ │ │ ├── Context.ts │ │ │ │ ├── Middleware.ts │ │ │ │ ├── agent/ │ │ │ │ │ ├── AgentController.ts │ │ │ │ │ ├── AgentHandler.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── http/ │ │ │ │ │ ├── HTTPController.ts │ │ │ │ │ ├── HTTPMethod.ts │ │ │ │ │ ├── HTTPParam.ts │ │ │ │ │ └── Host.ts │ │ │ │ └── mcp/ │ │ │ │ ├── Extra.ts │ │ │ │ ├── MCPController.ts │ │ │ │ ├── MCPPrompt.ts │ │ │ │ ├── MCPResource.ts │ │ │ │ └── MCPTool.ts │ │ │ ├── impl/ │ │ │ │ ├── http/ │ │ │ │ │ ├── HTTPControllerMetaBuilder.ts │ │ │ │ │ └── HTTPControllerMethodMetaBuilder.ts │ │ │ │ └── mcp/ │ │ │ │ ├── MCPControllerMetaBuilder.ts │ │ │ │ ├── MCPControllerPromptMetaBuilder.ts │ │ │ │ ├── MCPControllerResourceMetaBuilder.ts │ │ │ │ └── MCPControllerToolMetaBuilder.ts │ │ │ ├── model/ │ │ │ │ ├── HTTPControllerMeta.ts │ │ │ │ ├── HTTPCookies.ts │ │ │ │ ├── HTTPMethodMeta.ts │ │ │ │ ├── HTTPRequest.ts │ │ │ │ ├── HTTPResponse.ts │ │ │ │ ├── MCPControllerMeta.ts │ │ │ │ ├── MCPPromptMeta.ts │ │ │ │ ├── MCPResourceMeta.ts │ │ │ │ ├── MCPToolMeta.ts │ │ │ │ └── index.ts │ │ │ └── util/ │ │ │ ├── AgentInfoUtil.ts │ │ │ ├── ControllerInfoUtil.ts │ │ │ ├── ControllerMetadataUtil.ts │ │ │ ├── HTTPInfoUtil.ts │ │ │ ├── HTTPPriorityUtil.ts │ │ │ ├── MCPInfoUtil.ts │ │ │ ├── MethodInfoUtil.ts │ │ │ └── validator/ │ │ │ ├── ControllerValidator.ts │ │ │ └── MethodValidator.ts │ │ ├── test/ │ │ │ ├── Acl.test.ts │ │ │ ├── AgentController.test.ts │ │ │ ├── Context.test.ts │ │ │ ├── MCPMeta.test.ts │ │ │ ├── Middleware.test.ts │ │ │ ├── decorators.test.ts │ │ │ ├── fixtures/ │ │ │ │ ├── AclController.ts │ │ │ │ ├── AgentFooController.ts │ │ │ │ ├── AopMiddlewareController.ts │ │ │ │ ├── ContextController.ts │ │ │ │ ├── HTTPFooController.ts │ │ │ │ ├── HTTPPriorityController.ts │ │ │ │ ├── HostController.ts │ │ │ │ ├── MCPController.ts │ │ │ │ └── MiddlewareController.ts │ │ │ ├── http/ │ │ │ │ ├── HTTPMeta.test.ts │ │ │ │ └── Host.test.ts │ │ │ └── util/ │ │ │ ├── ControllerMetadataUtil.test.ts │ │ │ └── HTTPPriority.test.ts │ │ ├── tsconfig.json │ │ └── tsconfig.pub.json │ ├── core-decorator/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── index.ts │ │ ├── package.json │ │ ├── src/ │ │ │ ├── decorator/ │ │ │ │ ├── ConfigSource.ts │ │ │ │ ├── ContextProto.ts │ │ │ │ ├── EggQualifier.ts │ │ │ │ ├── InitTypeQualifier.ts │ │ │ │ ├── Inject.ts │ │ │ │ ├── ModuleQualifier.ts │ │ │ │ ├── MultiInstanceInfo.ts │ │ │ │ ├── MultiInstanceProto.ts │ │ │ │ ├── Prototype.ts │ │ │ │ └── SingletonProto.ts │ │ │ └── util/ │ │ │ ├── MetadataUtil.ts │ │ │ ├── PrototypeUtil.ts │ │ │ └── QualifierUtil.ts │ │ ├── test/ │ │ │ ├── decorators.test.ts │ │ │ ├── fixtures/ │ │ │ │ └── decators/ │ │ │ │ ├── CacheService.ts │ │ │ │ ├── ChildService.ts │ │ │ │ ├── ConstructorObject.ts │ │ │ │ ├── ContextCache.ts │ │ │ │ ├── FooLogger.ts │ │ │ │ ├── ICache.ts │ │ │ │ ├── OtherService.ts │ │ │ │ ├── QualifierCacheService.ts │ │ │ │ └── SingletonCache.ts │ │ │ └── util/ │ │ │ └── MetadataUtil.test.ts │ │ ├── tsconfig.json │ │ └── tsconfig.pub.json │ ├── dal-decorator/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── index.ts │ │ ├── package.json │ │ ├── src/ │ │ │ ├── decorator/ │ │ │ │ ├── Column.ts │ │ │ │ ├── Dao.ts │ │ │ │ ├── DataSourceQualifier.ts │ │ │ │ ├── Index.ts │ │ │ │ └── Table.ts │ │ │ ├── model/ │ │ │ │ ├── ColumnModel.ts │ │ │ │ ├── IndexModel.ts │ │ │ │ └── TableModel.ts │ │ │ ├── type/ │ │ │ │ ├── MySql.ts │ │ │ │ └── Spatial.ts │ │ │ └── util/ │ │ │ ├── ColumnInfoUtil.ts │ │ │ ├── DaoInfoUtil.ts │ │ │ ├── IndexInfoUtil.ts │ │ │ └── TableInfoUtil.ts │ │ ├── test/ │ │ │ ├── fixtures/ │ │ │ │ └── modules/ │ │ │ │ └── dal/ │ │ │ │ ├── Foo.ts │ │ │ │ └── package.json │ │ │ └── index.test.ts │ │ ├── tsconfig.json │ │ └── tsconfig.pub.json │ ├── dal-runtime/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── index.ts │ │ ├── package.json │ │ ├── src/ │ │ │ ├── BaseSqlMap.ts │ │ │ ├── CodeGenerator.ts │ │ │ ├── DaoLoader.ts │ │ │ ├── DataSource.ts │ │ │ ├── DatabaseForker.ts │ │ │ ├── MySqlDataSource.ts │ │ │ ├── NunjucksConverter.ts │ │ │ ├── NunjucksUtil.ts │ │ │ ├── SqlGenerator.ts │ │ │ ├── SqlMapLoader.ts │ │ │ ├── SqlUtil.ts │ │ │ ├── TableModelInstanceBuilder.ts │ │ │ ├── TableSqlMap.ts │ │ │ ├── TemplateUtil.ts │ │ │ └── templates/ │ │ │ ├── base_dao.njk │ │ │ ├── dao.njk │ │ │ └── extension.njk │ │ ├── test/ │ │ │ ├── CodeGenerator.test.ts │ │ │ ├── DAO.test.ts │ │ │ ├── DataSource.test.ts │ │ │ ├── SqlGenerator.test.ts │ │ │ ├── SqlUtil.test.ts │ │ │ ├── TableSqlMap.test.ts │ │ │ └── fixtures/ │ │ │ └── modules/ │ │ │ ├── dal/ │ │ │ │ ├── AutoUpdateTime.ts │ │ │ │ ├── Foo.ts │ │ │ │ ├── FooIndexName.ts │ │ │ │ └── package.json │ │ │ ├── generate_codes/ │ │ │ │ ├── Foo.ts │ │ │ │ ├── MultiPrimaryKey.ts │ │ │ │ └── package.json │ │ │ ├── generate_codes_not_overwrite_dao/ │ │ │ │ ├── Foo.ts │ │ │ │ └── package.json │ │ │ └── generate_codes_to_src/ │ │ │ ├── package.json │ │ │ └── src/ │ │ │ └── Foo.ts │ │ ├── tsconfig.json │ │ └── tsconfig.pub.json │ ├── dynamic-inject/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── index.ts │ │ ├── package.json │ │ ├── src/ │ │ │ ├── QualifierImplDecoratorUtil.ts │ │ │ └── QualifierImplUtil.ts │ │ ├── test/ │ │ │ ├── fixtures/ │ │ │ │ └── modules/ │ │ │ │ ├── base/ │ │ │ │ │ ├── AbstractContextHello.ts │ │ │ │ │ ├── ContextHello.ts │ │ │ │ │ └── FooType.ts │ │ │ │ ├── wrong-enum-module/ │ │ │ │ │ ├── WrongEnumCase.ts │ │ │ │ │ └── tsconfig.json │ │ │ │ └── wrong-extends-module/ │ │ │ │ ├── WrongExtendsCase.ts │ │ │ │ └── tsconfig.json │ │ │ └── typing.test.ts │ │ ├── tsconfig.json │ │ └── tsconfig.pub.json │ ├── dynamic-inject-runtime/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── index.ts │ │ ├── package.json │ │ ├── src/ │ │ │ ├── EggObjectFactory.ts │ │ │ ├── EggObjectFactoryObject.ts │ │ │ └── EggObjectFactoryPrototype.ts │ │ ├── test/ │ │ │ ├── fixtures/ │ │ │ │ └── modules/ │ │ │ │ └── dynamic-inject-module/ │ │ │ │ ├── AbstractContextHello.ts │ │ │ │ ├── AbstractSingletonHello.ts │ │ │ │ ├── FooType.ts │ │ │ │ ├── HelloService.ts │ │ │ │ ├── decorator/ │ │ │ │ │ ├── ContextHello.ts │ │ │ │ │ └── SingletonHello.ts │ │ │ │ ├── impl/ │ │ │ │ │ ├── BarContextHello.ts │ │ │ │ │ ├── BarSingletonHello.ts │ │ │ │ │ ├── FooContextHello.ts │ │ │ │ │ └── FooSingletonHello.ts │ │ │ │ └── package.json │ │ │ └── index.test.ts │ │ ├── tsconfig.json │ │ └── tsconfig.pub.json │ ├── eventbus-decorator/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── index.ts │ │ ├── package.json │ │ ├── src/ │ │ │ ├── Event.ts │ │ │ ├── EventBus.ts │ │ │ ├── EventContext.ts │ │ │ ├── EventInfoUtil.ts │ │ │ └── type.d.ts │ │ ├── test/ │ │ │ ├── Event.test.ts │ │ │ └── fixtures/ │ │ │ ├── empty-handle.ts │ │ │ ├── event-handle-with-context.ts │ │ │ ├── multiple-events-handle.ts │ │ │ ├── right-event-handle.ts │ │ │ └── wrong-event-handle.ts │ │ ├── tsconfig.json │ │ └── tsconfig.pub.json │ ├── eventbus-runtime/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── index.ts │ │ ├── package.json │ │ ├── src/ │ │ │ ├── EventContextFactory.ts │ │ │ ├── EventHandlerFactory.ts │ │ │ └── SingletonEventBus.ts │ │ ├── test/ │ │ │ ├── EventBus.test.ts │ │ │ └── fixtures/ │ │ │ └── modules/ │ │ │ ├── event/ │ │ │ │ ├── HelloEvent.ts │ │ │ │ ├── MultiEvent.ts │ │ │ │ ├── MultiEventWithContext.ts │ │ │ │ └── package.json │ │ │ └── mock-module/ │ │ │ ├── MockLogger.ts │ │ │ └── package.json │ │ ├── tsconfig.json │ │ └── tsconfig.pub.json │ ├── langchain-decorator/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── index.ts │ │ ├── package.json │ │ ├── src/ │ │ │ ├── builder/ │ │ │ │ ├── BoundModelMetaBuilder.ts │ │ │ │ ├── GraphEdgeMetaBuilder.ts │ │ │ │ ├── GraphMetaBuilder.ts │ │ │ │ ├── GraphNodeMetaBuilder.ts │ │ │ │ └── GraphToolMetaBuilder.ts │ │ │ ├── decorator/ │ │ │ │ ├── BoundModel.ts │ │ │ │ ├── Graph.ts │ │ │ │ ├── GraphEdge.ts │ │ │ │ ├── GraphNode.ts │ │ │ │ └── GraphTool.ts │ │ │ ├── model/ │ │ │ │ ├── BoundModelMetadata.ts │ │ │ │ ├── GraphEdgeMetadata.ts │ │ │ │ ├── GraphMetadata.ts │ │ │ │ ├── GraphNodeMetadata.ts │ │ │ │ └── GraphToolMetadata.ts │ │ │ ├── qualifier/ │ │ │ │ ├── ChatCheckpointSaverQualifier.ts │ │ │ │ └── ChatModelQualifier.ts │ │ │ ├── type/ │ │ │ │ └── metadataKey.ts │ │ │ └── util/ │ │ │ ├── BoundModelInfoUtil.ts │ │ │ ├── GraphEdgeInfoUtil.ts │ │ │ ├── GraphInfoUtil.ts │ │ │ ├── GraphNodeInfoUtil.ts │ │ │ ├── GraphToolInfoUtil.ts │ │ │ └── index.ts │ │ ├── test/ │ │ │ ├── fixtures/ │ │ │ │ └── modules/ │ │ │ │ ├── langchain/ │ │ │ │ │ ├── index.ts │ │ │ │ │ └── package.json │ │ │ │ └── langgraph/ │ │ │ │ └── Graph.ts │ │ │ ├── graph.test.ts │ │ │ ├── index.test.ts │ │ │ ├── package.json │ │ │ └── tsconfig.json │ │ ├── tsconfig.json │ │ └── tsconfig.pub.json │ ├── lifecycle/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── index.ts │ │ ├── package.json │ │ ├── src/ │ │ │ ├── IdenticalObject.ts │ │ │ ├── LifycycleUtil.ts │ │ │ └── decorator/ │ │ │ └── index.ts │ │ ├── test/ │ │ │ └── IdenticalObject.test.ts │ │ ├── tsconfig.json │ │ └── tsconfig.pub.json │ ├── loader/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── index.ts │ │ ├── package.json │ │ ├── src/ │ │ │ ├── LoaderFactory.ts │ │ │ ├── LoaderUtil.ts │ │ │ └── impl/ │ │ │ └── ModuleLoader.ts │ │ ├── test/ │ │ │ ├── Loader.test.ts │ │ │ └── fixtures/ │ │ │ └── modules/ │ │ │ ├── loader-failed/ │ │ │ │ ├── AppRepo.ts │ │ │ │ └── package.json │ │ │ ├── module-for-loader/ │ │ │ │ ├── AppRepo.ts │ │ │ │ ├── SprintRepo.ts │ │ │ │ ├── UserRepo.ts │ │ │ │ └── package.json │ │ │ ├── module-with-extra/ │ │ │ │ ├── .dist/ │ │ │ │ │ └── ThrowError.ts │ │ │ │ ├── AppRepo.ts │ │ │ │ ├── extra/ │ │ │ │ │ └── UserRepo.ts │ │ │ │ └── package.json │ │ │ └── module-with-test/ │ │ │ ├── .gitignore │ │ │ ├── AppRepo.ts │ │ │ ├── coverage/ │ │ │ │ └── fixtures/ │ │ │ │ └── UserRepo.ts │ │ │ ├── package.json │ │ │ └── test/ │ │ │ └── fixtures/ │ │ │ └── UserRepo.ts │ │ ├── tsconfig.json │ │ └── tsconfig.pub.json │ ├── mcp-client/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── index.ts │ │ ├── package.json │ │ ├── src/ │ │ │ ├── HeaderUtil.ts │ │ │ ├── HttpMCPClient.ts │ │ │ └── MCPClientQualifier.ts │ │ ├── test/ │ │ │ ├── HttpMCPClient.test.ts │ │ │ └── fixtures/ │ │ │ ├── sse-mcp-server/ │ │ │ │ └── http.ts │ │ │ └── streamable-mcp-server/ │ │ │ └── http.ts │ │ ├── tsconfig.json │ │ ├── tsconfig.pub.json │ │ └── typings/ │ │ └── index.d.ts │ ├── metadata/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── index.ts │ │ ├── package.json │ │ ├── src/ │ │ │ ├── errors.ts │ │ │ ├── factory/ │ │ │ │ ├── EggPrototypeCreatorFactory.ts │ │ │ │ ├── EggPrototypeFactory.ts │ │ │ │ └── LoadUnitFactory.ts │ │ │ ├── impl/ │ │ │ │ ├── EggPrototypeBuilder.ts │ │ │ │ ├── EggPrototypeImpl.ts │ │ │ │ ├── LoadUnitMultiInstanceProtoHook.ts │ │ │ │ └── ModuleLoadUnit.ts │ │ │ ├── model/ │ │ │ │ ├── AppGraph.ts │ │ │ │ ├── EggPrototype.ts │ │ │ │ ├── LoadUnit.ts │ │ │ │ ├── ModuleDescriptor.ts │ │ │ │ ├── ProtoDescriptor/ │ │ │ │ │ ├── AbstractProtoDescriptor.ts │ │ │ │ │ └── ClassProtoDescriptor.ts │ │ │ │ ├── ProtoDescriptorHelper.ts │ │ │ │ └── graph/ │ │ │ │ ├── GlobalGraph.ts │ │ │ │ ├── GlobalModuleNode.ts │ │ │ │ ├── GlobalModuleNodeBuilder.ts │ │ │ │ ├── ProtoNode.ts │ │ │ │ └── ProtoSelector.ts │ │ │ └── util/ │ │ │ └── ClassUtil.ts │ │ ├── test/ │ │ │ ├── AppGraph.test.ts │ │ │ ├── GlobalGraph.test.ts │ │ │ ├── LoadUnit.test.ts │ │ │ ├── ModuleGraph.test.ts │ │ │ └── fixtures/ │ │ │ ├── LoaderUtil.ts │ │ │ ├── TestLoader.ts │ │ │ └── modules/ │ │ │ ├── app-graph-modules/ │ │ │ │ ├── root/ │ │ │ │ │ ├── Root.ts │ │ │ │ │ ├── RootConstructor.ts │ │ │ │ │ └── package.json │ │ │ │ ├── unused/ │ │ │ │ │ ├── Unused.ts │ │ │ │ │ └── package.json │ │ │ │ └── used/ │ │ │ │ ├── Used.ts │ │ │ │ └── package.json │ │ │ ├── app-multi-inject-multi/ │ │ │ │ ├── app/ │ │ │ │ │ └── modules/ │ │ │ │ │ ├── app/ │ │ │ │ │ │ ├── App.ts │ │ │ │ │ │ ├── module.yml │ │ │ │ │ │ └── package.json │ │ │ │ │ ├── app2/ │ │ │ │ │ │ ├── App.ts │ │ │ │ │ │ ├── module.yml │ │ │ │ │ │ └── package.json │ │ │ │ │ ├── bar/ │ │ │ │ │ │ ├── BizManager.ts │ │ │ │ │ │ └── package.json │ │ │ │ │ └── foo/ │ │ │ │ │ ├── Secret.ts │ │ │ │ │ └── package.json │ │ │ │ └── package.json │ │ │ ├── extends-constructor-module/ │ │ │ │ ├── Base.ts │ │ │ │ └── package.json │ │ │ ├── extends-module/ │ │ │ │ ├── Base.ts │ │ │ │ └── package.json │ │ │ ├── incompatible-proto-inject/ │ │ │ │ ├── package.json │ │ │ │ └── test.ts │ │ │ ├── invalid-multimodule/ │ │ │ │ ├── invalidService.ts │ │ │ │ ├── invalidService2.ts │ │ │ │ ├── package.json │ │ │ │ └── test.ts │ │ │ ├── invalidate-module/ │ │ │ │ ├── InvalidateService.ts │ │ │ │ └── package.json │ │ │ ├── load-unit/ │ │ │ │ ├── AppRepo.ts │ │ │ │ ├── SprintRepo.ts │ │ │ │ ├── UserRepo.ts │ │ │ │ └── package.json │ │ │ ├── multi-callback-instance-module/ │ │ │ │ ├── MultiInstance.ts │ │ │ │ ├── module.yml │ │ │ │ └── package.json │ │ │ ├── multi-instance-module/ │ │ │ │ ├── MultiInstance.ts │ │ │ │ └── package.json │ │ │ ├── optional-inject-module/ │ │ │ │ ├── OptionalInjectService.ts │ │ │ │ └── package.json │ │ │ ├── recursive-load-unit/ │ │ │ │ ├── AppRepo.ts │ │ │ │ ├── SprintRepo.ts │ │ │ │ ├── UserRepo.ts │ │ │ │ └── package.json │ │ │ └── same-name-object/ │ │ │ ├── AppCache.ts │ │ │ ├── ContextAppCache.ts │ │ │ ├── CountService.ts │ │ │ ├── SingletonAppCache.ts │ │ │ └── package.json │ │ ├── tsconfig.json │ │ └── tsconfig.pub.json │ ├── orm-decorator/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── index.ts │ │ ├── package.json │ │ ├── src/ │ │ │ ├── builder/ │ │ │ │ ├── AttributeMetaBuilder.ts │ │ │ │ ├── IndexMetaBuilder.ts │ │ │ │ └── ModelMetaBuilder.ts │ │ │ ├── decorator/ │ │ │ │ ├── Attribute.ts │ │ │ │ ├── DataSource.ts │ │ │ │ ├── Index.ts │ │ │ │ └── Model.ts │ │ │ ├── model/ │ │ │ │ ├── AttributeMeta.ts │ │ │ │ ├── IndexMeta.ts │ │ │ │ └── ModelMetadata.ts │ │ │ └── util/ │ │ │ ├── ModelInfoUtil.ts │ │ │ ├── ModelMetadataUtil.ts │ │ │ └── NameUtil.ts │ │ ├── test/ │ │ │ ├── builder/ │ │ │ │ ├── AttributeMetaBuilder.test.ts │ │ │ │ ├── IndexMetaBuilder.test.ts │ │ │ │ └── ModelMetaBuilder.test.ts │ │ │ ├── decorator.test.ts │ │ │ └── fixtures/ │ │ │ ├── AttributeModel.ts │ │ │ ├── DefaultAttributeModel.ts │ │ │ ├── DefaultIndexModel.ts │ │ │ ├── Foo.ts │ │ │ ├── IndexModel.ts │ │ │ └── InvalidateIndexModel.ts │ │ ├── tsconfig.json │ │ └── tsconfig.pub.json │ ├── runtime/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── index.ts │ │ ├── package.json │ │ ├── src/ │ │ │ ├── factory/ │ │ │ │ ├── EggContainerFactory.ts │ │ │ │ ├── EggObjectFactory.ts │ │ │ │ └── LoadUnitInstanceFactory.ts │ │ │ ├── impl/ │ │ │ │ ├── ContextInitiator.ts │ │ │ │ ├── ContextObjectGraph.ts │ │ │ │ ├── EggAlwaysNewObjectContainer.ts │ │ │ │ ├── EggObjectImpl.ts │ │ │ │ ├── EggObjectUtil.ts │ │ │ │ └── ModuleLoadUnitInstance.ts │ │ │ └── model/ │ │ │ ├── AbstractEggContext.ts │ │ │ ├── ContextHandler.ts │ │ │ ├── EggContext.ts │ │ │ ├── EggObject.ts │ │ │ └── LoadUnitInstance.ts │ │ ├── test/ │ │ │ ├── EggObject.test.ts │ │ │ ├── EggObjectUtil.test.ts │ │ │ ├── LoadUnitInstance.test.ts │ │ │ ├── QualifierLoadUnitInstance.test.ts │ │ │ ├── fixtures/ │ │ │ │ ├── EggContextStorage.ts │ │ │ │ ├── EggTestContext.ts │ │ │ │ └── modules/ │ │ │ │ ├── extends-module/ │ │ │ │ │ ├── Base.ts │ │ │ │ │ └── package.json │ │ │ │ ├── init-type-qualifier-module/ │ │ │ │ │ ├── Cache.ts │ │ │ │ │ ├── CacheService.ts │ │ │ │ │ ├── ContextCache.ts │ │ │ │ │ ├── SingletonCache.ts │ │ │ │ │ └── package.json │ │ │ │ ├── inject-constructor-context-to-singleton/ │ │ │ │ │ ├── object.ts │ │ │ │ │ └── package.json │ │ │ │ ├── inject-context-to-singleton/ │ │ │ │ │ ├── object.ts │ │ │ │ │ └── package.json │ │ │ │ ├── lifecycle-hook/ │ │ │ │ │ ├── object.ts │ │ │ │ │ └── package.json │ │ │ │ ├── module-for-load-unit-instance/ │ │ │ │ │ ├── AppCache.ts │ │ │ │ │ ├── CountController.ts │ │ │ │ │ ├── CountService.ts │ │ │ │ │ ├── TempObj.ts │ │ │ │ │ └── package.json │ │ │ │ ├── multi-instance-module/ │ │ │ │ │ ├── MultiInstance.ts │ │ │ │ │ ├── MultiInstanceConstructor.ts │ │ │ │ │ └── package.json │ │ │ │ └── multi-module/ │ │ │ │ ├── multi-module-common/ │ │ │ │ │ ├── model/ │ │ │ │ │ │ └── App.ts │ │ │ │ │ └── package.json │ │ │ │ ├── multi-module-repo/ │ │ │ │ │ ├── AppRepo.ts │ │ │ │ │ ├── PersistenceService.ts │ │ │ │ │ └── package.json │ │ │ │ └── multi-module-service/ │ │ │ │ ├── AppService.ts │ │ │ │ └── package.json │ │ │ └── util.ts │ │ ├── tsconfig.json │ │ └── tsconfig.pub.json │ ├── schedule-decorator/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── index.ts │ │ ├── package.json │ │ ├── src/ │ │ │ ├── builder/ │ │ │ │ └── ScheduleMetaBuilder.ts │ │ │ ├── decorator/ │ │ │ │ └── Schedule.ts │ │ │ ├── model/ │ │ │ │ └── ScheduleMetadata.ts │ │ │ └── util/ │ │ │ ├── ScheduleInfoUtil.ts │ │ │ └── ScheduleMetadataUtil.ts │ │ ├── tsconfig.json │ │ └── tsconfig.pub.json │ ├── standalone-decorator/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── index.ts │ │ ├── package.json │ │ ├── src/ │ │ │ ├── decorator/ │ │ │ │ └── Runner.ts │ │ │ ├── event/ │ │ │ │ └── EventHandler.ts │ │ │ ├── typing.ts │ │ │ └── util/ │ │ │ └── StandaloneUtil.ts │ │ ├── tsconfig.json │ │ └── tsconfig.pub.json │ ├── tegg/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── agent.ts │ │ ├── ajv.ts │ │ ├── aop.ts │ │ ├── dal.ts │ │ ├── helper.ts │ │ ├── index.ts │ │ ├── orm.ts │ │ ├── package.json │ │ ├── schedule.ts │ │ ├── standalone.ts │ │ ├── test/ │ │ │ ├── helper.test.ts │ │ │ └── index.test.ts │ │ ├── transaction.ts │ │ ├── tsconfig.json │ │ ├── tsconfig.pub.json │ │ └── zod.ts │ ├── test-util/ │ │ ├── CHANGELOG.md │ │ ├── CoreTestHelper.ts │ │ ├── EggTestContext.ts │ │ ├── LoaderUtil.ts │ │ ├── StandaloneTestUtil.ts │ │ ├── TestLoader.ts │ │ ├── index.ts │ │ ├── package.json │ │ ├── tsconfig.json │ │ └── tsconfig.pub.json │ ├── transaction-decorator/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── index.ts │ │ ├── package.json │ │ ├── src/ │ │ │ ├── builder/ │ │ │ │ └── TransactionMetaBuilder.ts │ │ │ ├── decorator/ │ │ │ │ └── Transactional.ts │ │ │ └── util/ │ │ │ └── TransactionMetadataUtil.ts │ │ ├── test/ │ │ │ ├── builder/ │ │ │ │ └── TransactionMetaBuilder.test.ts │ │ │ └── fixtures/ │ │ │ └── transaction.ts │ │ ├── tsconfig.json │ │ └── tsconfig.pub.json │ ├── types/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── agent-runtime/ │ │ │ ├── AgentMessage.ts │ │ │ ├── AgentRuntime.ts │ │ │ ├── AgentStore.ts │ │ │ ├── ObjectStorageClient.ts │ │ │ ├── errors.ts │ │ │ └── index.ts │ │ ├── aop/ │ │ │ ├── Advice.ts │ │ │ ├── Aspect.ts │ │ │ ├── Crosscut.ts │ │ │ ├── Pointcut.ts │ │ │ └── index.ts │ │ ├── common/ │ │ │ ├── Graph.ts │ │ │ ├── Logger.ts │ │ │ ├── ModuleConfig.ts │ │ │ ├── RuntimeConfig.ts │ │ │ └── index.ts │ │ ├── controller-decorator/ │ │ │ ├── HTTPController.ts │ │ │ ├── HTTPMethod.ts │ │ │ ├── HTTPParam.ts │ │ │ ├── MCPController.ts │ │ │ ├── MCPPromptParams.ts │ │ │ ├── MCPResourceParams.ts │ │ │ ├── MCPToolParams.ts │ │ │ ├── MetadataKey.ts │ │ │ ├── builder.ts │ │ │ ├── index.ts │ │ │ └── model/ │ │ │ ├── ControllerMetadata.ts │ │ │ ├── MethodMeta.ts │ │ │ └── types.ts │ │ ├── core-decorator/ │ │ │ ├── ContextProto.ts │ │ │ ├── Inject.ts │ │ │ ├── Metadata.ts │ │ │ ├── MultiInstanceProto.ts │ │ │ ├── Prototype.ts │ │ │ ├── SingletonProto.ts │ │ │ ├── enum/ │ │ │ │ ├── AccessLevel.ts │ │ │ │ ├── EggType.ts │ │ │ │ ├── InjectType.ts │ │ │ │ ├── MultiInstanceType.ts │ │ │ │ ├── ObjectInitType.ts │ │ │ │ └── Qualifier.ts │ │ │ ├── index.ts │ │ │ └── model/ │ │ │ ├── EggMultiInstancePrototypeInfo.ts │ │ │ ├── EggPrototypeInfo.ts │ │ │ ├── InjectConstructorInfo.ts │ │ │ ├── InjectObjectInfo.ts │ │ │ └── QualifierInfo.ts │ │ ├── dal/ │ │ │ ├── Qualifier.ts │ │ │ ├── decorator/ │ │ │ │ ├── Column.ts │ │ │ │ ├── DataSourceQualifier.ts │ │ │ │ ├── Index.ts │ │ │ │ └── Table.ts │ │ │ ├── enum/ │ │ │ │ ├── ColumnFormat.ts │ │ │ │ ├── ColumnType.ts │ │ │ │ ├── CompressionType.ts │ │ │ │ ├── IndexStoreType.ts │ │ │ │ ├── IndexType.ts │ │ │ │ ├── InsertMethod.ts │ │ │ │ ├── RowFormat.ts │ │ │ │ ├── SqlType.ts │ │ │ │ └── Templates.ts │ │ │ ├── index.ts │ │ │ └── type/ │ │ │ ├── BaseDao.ts │ │ │ ├── CodeGenerator.ts │ │ │ ├── ColumnTsType.ts │ │ │ ├── DateSource.ts │ │ │ ├── Spatial.ts │ │ │ └── SqlMap.ts │ │ ├── dynamic-inject.ts │ │ ├── index.ts │ │ ├── lifecycle/ │ │ │ ├── EggObjectLifecycle.ts │ │ │ ├── IdenticalObject.ts │ │ │ ├── LifecycleHook.ts │ │ │ └── index.ts │ │ ├── metadata/ │ │ │ ├── enum/ │ │ │ │ └── ProtoDescriptorType.ts │ │ │ ├── errors.ts │ │ │ ├── index.ts │ │ │ └── model/ │ │ │ ├── EggPrototype.ts │ │ │ ├── LoadUnit.ts │ │ │ ├── Loader.ts │ │ │ └── ProtoDescriptor.ts │ │ ├── orm.ts │ │ ├── package.json │ │ ├── runtime/ │ │ │ ├── Factory.ts │ │ │ ├── index.ts │ │ │ └── model/ │ │ │ ├── EggContainer.ts │ │ │ ├── EggContext.ts │ │ │ ├── EggObject.ts │ │ │ └── LoadUnitInstance.ts │ │ ├── schedule.ts │ │ ├── standalone/ │ │ │ ├── ServiceWorkerContext.ts │ │ │ ├── fetch.ts │ │ │ └── index.ts │ │ ├── transaction.ts │ │ ├── tsconfig.json │ │ └── tsconfig.pub.json │ └── vitest/ │ ├── CHANGELOG.md │ ├── README.md │ ├── index.ts │ ├── package.json │ ├── runner.ts │ ├── src/ │ │ ├── index.ts │ │ ├── runner.ts │ │ └── shared.ts │ ├── test/ │ │ ├── fixture_app.test.ts │ │ ├── fixtures/ │ │ │ └── apps/ │ │ │ └── demo-app/ │ │ │ ├── config/ │ │ │ │ └── module.json │ │ │ ├── modules/ │ │ │ │ └── demo-module/ │ │ │ │ ├── HelloService.ts │ │ │ │ └── package.json │ │ │ └── package.json │ │ ├── get_app_throw.test.ts │ │ ├── get_store_restore.test.ts │ │ ├── hooks.test.ts │ │ └── setup.ts │ ├── tsconfig.json │ ├── tsconfig.pub.json │ └── vitest.config.ts ├── lerna.json ├── package.json ├── plugin/ │ ├── ajv/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── lib/ │ │ │ └── Ajv.ts │ │ ├── package.json │ │ ├── test/ │ │ │ ├── ajv.test.ts │ │ │ └── fixtures/ │ │ │ └── apps/ │ │ │ └── ajv-app/ │ │ │ ├── config/ │ │ │ │ ├── config.default.js │ │ │ │ ├── module.json │ │ │ │ └── plugin.js │ │ │ ├── modules/ │ │ │ │ └── demo/ │ │ │ │ ├── FooController.ts │ │ │ │ ├── module.yml │ │ │ │ └── package.json │ │ │ └── package.json │ │ ├── tsconfig.json │ │ ├── tsconfig.pub.json │ │ └── typings/ │ │ └── index.d.ts │ ├── aop/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── app.ts │ │ ├── lib/ │ │ │ └── AopContextHook.ts │ │ ├── package.json │ │ ├── test/ │ │ │ ├── aop.test.ts │ │ │ └── fixtures/ │ │ │ └── apps/ │ │ │ └── aop-app/ │ │ │ ├── app/ │ │ │ │ ├── controller/ │ │ │ │ │ └── app.ts │ │ │ │ ├── router.ts │ │ │ │ └── typings/ │ │ │ │ └── index.d.ts │ │ │ ├── config/ │ │ │ │ ├── config.default.js │ │ │ │ ├── module.json │ │ │ │ └── plugin.js │ │ │ ├── modules/ │ │ │ │ └── aop-module/ │ │ │ │ ├── Hello.ts │ │ │ │ └── package.json │ │ │ └── package.json │ │ ├── tsconfig.json │ │ ├── tsconfig.pub.json │ │ └── typings/ │ │ └── index.d.ts │ ├── common/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── index.ts │ │ ├── package.json │ │ ├── tsconfig.json │ │ └── tsconfig.pub.json │ ├── config/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── agent.ts │ │ ├── app.ts │ │ ├── lib/ │ │ │ └── ModuleScanner.ts │ │ ├── package.json │ │ ├── test/ │ │ │ ├── DuplicateOptionalModule.test.ts │ │ │ ├── ReadModule.test.ts │ │ │ └── fixtures/ │ │ │ └── apps/ │ │ │ ├── app-with-modules/ │ │ │ │ ├── app/ │ │ │ │ │ └── module-a/ │ │ │ │ │ └── package.json │ │ │ │ ├── config/ │ │ │ │ │ └── config.default.ts │ │ │ │ └── package.json │ │ │ └── duplicate-optional-module/ │ │ │ ├── config/ │ │ │ │ ├── config.default.js │ │ │ │ └── plugin.js │ │ │ ├── node_modules/ │ │ │ │ ├── foo/ │ │ │ │ │ └── package.json │ │ │ │ ├── unused/ │ │ │ │ │ ├── Unused.js │ │ │ │ │ └── package.json │ │ │ │ └── used/ │ │ │ │ ├── Used.js │ │ │ │ └── package.json │ │ │ └── package.json │ │ ├── tsconfig.json │ │ ├── tsconfig.pub.json │ │ └── typings/ │ │ └── index.d.ts │ ├── controller/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── app/ │ │ │ └── middleware/ │ │ │ ├── mcp_body_middleware.ts │ │ │ └── tegg_root_proto.ts │ │ ├── app.ts │ │ ├── config/ │ │ │ └── config.default.ts │ │ ├── lib/ │ │ │ ├── AgentControllerObject.ts │ │ │ ├── AgentControllerProto.ts │ │ │ ├── AppLoadUnitControllerHook.ts │ │ │ ├── ControllerLoadUnit.ts │ │ │ ├── ControllerLoadUnitHandler.ts │ │ │ ├── ControllerLoadUnitInstance.ts │ │ │ ├── ControllerMetadataManager.ts │ │ │ ├── ControllerRegister.ts │ │ │ ├── ControllerRegisterFactory.ts │ │ │ ├── EggControllerLoader.ts │ │ │ ├── EggControllerPrototypeHook.ts │ │ │ ├── MiddlewareGraphHook.ts │ │ │ ├── RootProtoManager.ts │ │ │ ├── errors.ts │ │ │ └── impl/ │ │ │ ├── http/ │ │ │ │ ├── Acl.ts │ │ │ │ ├── HTTPControllerRegister.ts │ │ │ │ ├── HTTPMethodRegister.ts │ │ │ │ └── Req.ts │ │ │ └── mcp/ │ │ │ ├── MCPConfig.ts │ │ │ ├── MCPControllerRegister.ts │ │ │ └── MCPServerHelper.ts │ │ ├── package.json │ │ ├── test/ │ │ │ ├── fixtures/ │ │ │ │ └── apps/ │ │ │ │ ├── acl-app/ │ │ │ │ │ ├── app/ │ │ │ │ │ │ ├── controller/ │ │ │ │ │ │ │ └── AclController.ts │ │ │ │ │ │ └── extend/ │ │ │ │ │ │ └── context.ts │ │ │ │ │ ├── config/ │ │ │ │ │ │ ├── config.default.js │ │ │ │ │ │ └── plugin.js │ │ │ │ │ └── package.json │ │ │ │ ├── controller-app/ │ │ │ │ │ ├── app/ │ │ │ │ │ │ ├── controller/ │ │ │ │ │ │ │ ├── AopMiddlewareController.ts │ │ │ │ │ │ │ ├── AppController.ts │ │ │ │ │ │ │ ├── MiddlewareController.ts │ │ │ │ │ │ │ ├── ParamController.ts │ │ │ │ │ │ │ ├── PriorityController.ts │ │ │ │ │ │ │ ├── RedirectController.ts │ │ │ │ │ │ │ ├── TimeoutController.ts │ │ │ │ │ │ │ └── ViewController.ts │ │ │ │ │ │ └── middleware/ │ │ │ │ │ │ ├── call_module.ts │ │ │ │ │ │ ├── count_mw.ts │ │ │ │ │ │ └── log_mw.ts │ │ │ │ │ ├── config/ │ │ │ │ │ │ ├── config.default.js │ │ │ │ │ │ ├── module.json │ │ │ │ │ │ └── plugin.js │ │ │ │ │ ├── modules/ │ │ │ │ │ │ ├── multi-module-common/ │ │ │ │ │ │ │ ├── advice/ │ │ │ │ │ │ │ │ ├── BarMethodAdvice.ts │ │ │ │ │ │ │ │ ├── CountAdvice.ts │ │ │ │ │ │ │ │ ├── FooControllerAdvice.ts │ │ │ │ │ │ │ │ └── FooMethodAdvice.ts │ │ │ │ │ │ │ ├── model/ │ │ │ │ │ │ │ │ └── App.ts │ │ │ │ │ │ │ └── package.json │ │ │ │ │ │ ├── multi-module-repo/ │ │ │ │ │ │ │ ├── AppRepo.ts │ │ │ │ │ │ │ ├── PersistenceService.ts │ │ │ │ │ │ │ └── package.json │ │ │ │ │ │ └── multi-module-service/ │ │ │ │ │ │ ├── AppService.ts │ │ │ │ │ │ └── package.json │ │ │ │ │ └── package.json │ │ │ │ ├── duplicate-controller-name-app/ │ │ │ │ │ ├── app/ │ │ │ │ │ │ └── controller/ │ │ │ │ │ │ ├── AppController.ts │ │ │ │ │ │ └── AppController2.ts │ │ │ │ │ ├── config/ │ │ │ │ │ │ ├── config.default.js │ │ │ │ │ │ └── plugin.js │ │ │ │ │ └── package.json │ │ │ │ ├── duplicate-proto-name-app/ │ │ │ │ │ ├── app/ │ │ │ │ │ │ └── controller/ │ │ │ │ │ │ ├── AppController.ts │ │ │ │ │ │ └── AppController2.ts │ │ │ │ │ ├── config/ │ │ │ │ │ │ ├── config.default.js │ │ │ │ │ │ └── plugin.js │ │ │ │ │ └── package.json │ │ │ │ ├── host-controller-app/ │ │ │ │ │ ├── app/ │ │ │ │ │ │ └── controller/ │ │ │ │ │ │ ├── AppController.ts │ │ │ │ │ │ ├── AppController2.ts │ │ │ │ │ │ ├── MultiHostController.ts │ │ │ │ │ │ └── MultiMethodHostController.ts │ │ │ │ │ ├── config/ │ │ │ │ │ │ ├── config.default.js │ │ │ │ │ │ └── plugin.js │ │ │ │ │ └── package.json │ │ │ │ ├── http-conflict-app/ │ │ │ │ │ ├── app/ │ │ │ │ │ │ └── controller/ │ │ │ │ │ │ ├── AppController.ts │ │ │ │ │ │ ├── HostController1.ts │ │ │ │ │ │ └── HostController2.ts │ │ │ │ │ ├── config/ │ │ │ │ │ │ ├── config.default.js │ │ │ │ │ │ └── plugin.js │ │ │ │ │ └── package.json │ │ │ │ ├── http-inject-app/ │ │ │ │ │ ├── app/ │ │ │ │ │ │ ├── controller/ │ │ │ │ │ │ │ └── AppController.ts │ │ │ │ │ │ └── middleware/ │ │ │ │ │ │ ├── call_module.ts │ │ │ │ │ │ ├── count_mw.ts │ │ │ │ │ │ └── log_mw.ts │ │ │ │ │ ├── config/ │ │ │ │ │ │ ├── config.default.js │ │ │ │ │ │ ├── module.json │ │ │ │ │ │ └── plugin.js │ │ │ │ │ └── package.json │ │ │ │ ├── mcp-app/ │ │ │ │ │ ├── app/ │ │ │ │ │ │ ├── controller/ │ │ │ │ │ │ │ ├── AppController.ts │ │ │ │ │ │ │ ├── McpController.ts │ │ │ │ │ │ │ ├── TestAppController.ts │ │ │ │ │ │ │ └── TestController.ts │ │ │ │ │ │ └── middleware/ │ │ │ │ │ │ └── tracelog.js │ │ │ │ │ ├── config/ │ │ │ │ │ │ ├── config.default.js │ │ │ │ │ │ └── plugin.js │ │ │ │ │ ├── hook-plugin/ │ │ │ │ │ │ ├── app.ts │ │ │ │ │ │ ├── lib/ │ │ │ │ │ │ │ └── MCPControllerHook.ts │ │ │ │ │ │ └── package.json │ │ │ │ │ └── package.json │ │ │ │ ├── middleware-graph-app/ │ │ │ │ │ ├── app/ │ │ │ │ │ │ └── router.ts │ │ │ │ │ ├── config/ │ │ │ │ │ │ ├── config.default.js │ │ │ │ │ │ ├── module.json │ │ │ │ │ │ └── plugin.js │ │ │ │ │ ├── modules/ │ │ │ │ │ │ ├── advice-module/ │ │ │ │ │ │ │ ├── advice/ │ │ │ │ │ │ │ │ ├── AnotherAdvice.ts │ │ │ │ │ │ │ │ └── TestAdvice.ts │ │ │ │ │ │ │ └── package.json │ │ │ │ │ │ └── controller-module/ │ │ │ │ │ │ ├── TestController.ts │ │ │ │ │ │ └── package.json │ │ │ │ │ └── package.json │ │ │ │ ├── module-app/ │ │ │ │ │ ├── app/ │ │ │ │ │ │ ├── controller/ │ │ │ │ │ │ │ └── AppController2.ts │ │ │ │ │ │ └── router.ts │ │ │ │ │ ├── config/ │ │ │ │ │ │ ├── config.default.js │ │ │ │ │ │ ├── module.json │ │ │ │ │ │ └── plugin.js │ │ │ │ │ ├── modules/ │ │ │ │ │ │ ├── foo-module/ │ │ │ │ │ │ │ ├── AppService.ts │ │ │ │ │ │ │ └── package.json │ │ │ │ │ │ └── http-module/ │ │ │ │ │ │ ├── AppController.ts │ │ │ │ │ │ └── package.json │ │ │ │ │ └── package.json │ │ │ │ └── proto-poisoning/ │ │ │ │ ├── app/ │ │ │ │ │ └── controller/ │ │ │ │ │ └── HelloController.ts │ │ │ │ ├── config/ │ │ │ │ │ ├── config.default.js │ │ │ │ │ └── plugin.js │ │ │ │ └── package.json │ │ │ ├── http/ │ │ │ │ ├── acl.test.ts │ │ │ │ ├── decorator.test.ts │ │ │ │ ├── edgecase.test.ts │ │ │ │ ├── host.test.ts │ │ │ │ ├── middleware-aop.test.ts │ │ │ │ ├── middleware.test.ts │ │ │ │ ├── module.test.ts │ │ │ │ ├── params.test.ts │ │ │ │ ├── priority.test.ts │ │ │ │ ├── proto-poisoning.test.ts │ │ │ │ └── request.test.ts │ │ │ ├── lib/ │ │ │ │ ├── AgentControllerProto.test.ts │ │ │ │ ├── ControllerMetaManager.test.ts │ │ │ │ ├── EggControllerLoader.test.ts │ │ │ │ └── HTTPMethodRegister.test.ts │ │ │ └── mcp/ │ │ │ ├── helper.test.ts │ │ │ ├── mcp.test.ts │ │ │ └── mcpCluster.test.ts │ │ ├── tsconfig.json │ │ ├── tsconfig.pub.json │ │ └── typings/ │ │ └── index.d.ts │ ├── dal/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── app/ │ │ │ └── extend/ │ │ │ └── application.ts │ │ ├── app.ts │ │ ├── index.ts │ │ ├── lib/ │ │ │ ├── DalModuleLoadUnitHook.ts │ │ │ ├── DalTableEggPrototypeHook.ts │ │ │ ├── DataSource.ts │ │ │ ├── MysqlDataSourceManager.ts │ │ │ ├── SqlMapManager.ts │ │ │ ├── TableModelManager.ts │ │ │ ├── TransactionPrototypeHook.ts │ │ │ └── TransactionalAOP.ts │ │ ├── package.json │ │ ├── test/ │ │ │ ├── dal.test.ts │ │ │ ├── fixtures/ │ │ │ │ └── apps/ │ │ │ │ └── dal-app/ │ │ │ │ ├── config/ │ │ │ │ │ ├── config.default.js │ │ │ │ │ ├── module.json │ │ │ │ │ └── plugin.js │ │ │ │ ├── modules/ │ │ │ │ │ └── dal/ │ │ │ │ │ ├── Foo.ts │ │ │ │ │ ├── FooService.ts │ │ │ │ │ ├── dal/ │ │ │ │ │ │ ├── dao/ │ │ │ │ │ │ │ ├── FooDAO.ts │ │ │ │ │ │ │ └── base/ │ │ │ │ │ │ │ └── BaseFooDAO.ts │ │ │ │ │ │ ├── extension/ │ │ │ │ │ │ │ └── FooExtension.ts │ │ │ │ │ │ └── structure/ │ │ │ │ │ │ ├── Foo.json │ │ │ │ │ │ └── Foo.sql │ │ │ │ │ ├── module.yml │ │ │ │ │ └── package.json │ │ │ │ └── package.json │ │ │ └── transaction.test.ts │ │ ├── tsconfig.json │ │ ├── tsconfig.pub.json │ │ └── typings/ │ │ └── index.d.ts │ ├── dns-cache/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── app.ts │ │ ├── config/ │ │ │ └── config.default.ts │ │ ├── lib/ │ │ │ ├── DnsResolver.ts │ │ │ └── index.ts │ │ ├── package.json │ │ ├── test/ │ │ │ ├── dns_cache_lookup.test.ts │ │ │ ├── dns_cache_lookup_http_next.test.ts │ │ │ ├── dns_cache_resolve.test.ts │ │ │ ├── fixtures/ │ │ │ │ └── apps/ │ │ │ │ ├── dns_cache_lookup/ │ │ │ │ │ ├── app/ │ │ │ │ │ │ ├── controller/ │ │ │ │ │ │ │ └── home.js │ │ │ │ │ │ └── router.js │ │ │ │ │ ├── config/ │ │ │ │ │ │ ├── config.js │ │ │ │ │ │ └── plugin.js │ │ │ │ │ └── package.json │ │ │ │ ├── dns_cache_lookup_http_next/ │ │ │ │ │ ├── app/ │ │ │ │ │ │ ├── controller/ │ │ │ │ │ │ │ └── home.js │ │ │ │ │ │ └── router.js │ │ │ │ │ ├── config/ │ │ │ │ │ │ ├── config.js │ │ │ │ │ │ └── plugin.js │ │ │ │ │ └── package.json │ │ │ │ └── dns_cache_resolve/ │ │ │ │ ├── app/ │ │ │ │ │ ├── controller/ │ │ │ │ │ │ └── home.js │ │ │ │ │ └── router.js │ │ │ │ ├── config/ │ │ │ │ │ ├── config.js │ │ │ │ │ └── plugin.js │ │ │ │ └── package.json │ │ │ └── utils.ts │ │ ├── tsconfig.json │ │ ├── tsconfig.pub.json │ │ └── typings/ │ │ └── index.d.ts │ ├── eventbus/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── app/ │ │ │ └── extend/ │ │ │ ├── application.unittest.ts │ │ │ └── context.ts │ │ ├── app.ts │ │ ├── lib/ │ │ │ ├── EggContextEventBus.ts │ │ │ ├── EggEventContext.ts │ │ │ ├── EventHandlerProtoManager.ts │ │ │ ├── EventbusLoadUnitHook.ts │ │ │ └── EventbusProtoHook.ts │ │ ├── package.json │ │ ├── test/ │ │ │ ├── eventbus.test.ts │ │ │ └── fixtures/ │ │ │ └── apps/ │ │ │ └── event-app/ │ │ │ ├── app/ │ │ │ │ └── event-module/ │ │ │ │ ├── HelloLogger.ts │ │ │ │ ├── HelloService.ts │ │ │ │ ├── MultiEventHandler.ts │ │ │ │ └── package.json │ │ │ ├── config/ │ │ │ │ ├── config.default.js │ │ │ │ └── plugin.js │ │ │ └── package.json │ │ ├── tsconfig.json │ │ ├── tsconfig.pub.json │ │ └── typings/ │ │ └── index.d.ts │ ├── langchain/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── app.ts │ │ ├── index.ts │ │ ├── lib/ │ │ │ ├── ChatModelHelper.ts │ │ │ ├── ChatOpenAI.ts │ │ │ ├── boundModel/ │ │ │ │ └── BoundModelObjectHook.ts │ │ │ ├── config/ │ │ │ │ └── QualifierUtil.ts │ │ │ ├── graph/ │ │ │ │ ├── CompiledStateGraphObject.ts │ │ │ │ ├── CompiledStateGraphProto.ts │ │ │ │ ├── GraphBuildHook.ts │ │ │ │ ├── GraphLoadUnitHook.ts │ │ │ │ ├── GraphObjectHook.ts │ │ │ │ └── GraphPrototypeHook.ts │ │ │ ├── tracing/ │ │ │ │ └── LangGraphTracer.ts │ │ │ └── util.ts │ │ ├── package.json │ │ ├── test/ │ │ │ ├── fixtures/ │ │ │ │ ├── apps/ │ │ │ │ │ └── langchain/ │ │ │ │ │ ├── app/ │ │ │ │ │ │ └── modules/ │ │ │ │ │ │ └── bar/ │ │ │ │ │ │ ├── controller/ │ │ │ │ │ │ │ └── AppController.ts │ │ │ │ │ │ ├── module.yml │ │ │ │ │ │ ├── package.json │ │ │ │ │ │ └── service/ │ │ │ │ │ │ ├── BoundChatModel.ts │ │ │ │ │ │ └── Graph.ts │ │ │ │ │ ├── config/ │ │ │ │ │ │ ├── config.default.js │ │ │ │ │ │ ├── module.json │ │ │ │ │ │ └── plugin.js │ │ │ │ │ ├── package.json │ │ │ │ │ └── tsconfig.json │ │ │ │ └── sse-mcp-server/ │ │ │ │ └── http.ts │ │ │ └── llm.test.ts │ │ ├── tsconfig.json │ │ ├── tsconfig.pub.json │ │ └── typings/ │ │ └── index.d.ts │ ├── mcp-client/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── index.ts │ │ ├── lib/ │ │ │ ├── EggHttpMCPClient.ts │ │ │ ├── EggHttpStaticMCPClient.ts │ │ │ ├── HttpMCPClientFactory.ts │ │ │ ├── QualifierUtil.ts │ │ │ └── constants.ts │ │ ├── package.json │ │ ├── test/ │ │ │ ├── fixtures/ │ │ │ │ ├── apps/ │ │ │ │ │ └── mcpclient/ │ │ │ │ │ ├── app/ │ │ │ │ │ │ └── modules/ │ │ │ │ │ │ └── bar/ │ │ │ │ │ │ ├── controller/ │ │ │ │ │ │ │ └── AppController.ts │ │ │ │ │ │ ├── module.yml │ │ │ │ │ │ └── package.json │ │ │ │ │ ├── config/ │ │ │ │ │ │ ├── config.default.js │ │ │ │ │ │ ├── module.json │ │ │ │ │ │ └── plugin.js │ │ │ │ │ └── package.json │ │ │ │ ├── sse-mcp-server/ │ │ │ │ │ └── http.ts │ │ │ │ └── streamable-mcp-server/ │ │ │ │ └── http.ts │ │ │ └── mcpclient.test.ts │ │ ├── tsconfig.json │ │ ├── tsconfig.pub.json │ │ └── typings/ │ │ └── index.d.ts │ ├── mcp-proxy/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── agent.ts │ │ ├── app/ │ │ │ └── extend/ │ │ │ ├── agent.ts │ │ │ └── application.ts │ │ ├── app.ts │ │ ├── config/ │ │ │ └── config.default.ts │ │ ├── index.ts │ │ ├── lib/ │ │ │ └── MCPProxyDataClient.ts │ │ ├── package.json │ │ ├── test/ │ │ │ ├── fixtures/ │ │ │ │ └── apps/ │ │ │ │ └── mcp-proxy/ │ │ │ │ ├── app/ │ │ │ │ │ ├── controller/ │ │ │ │ │ │ └── app.ts │ │ │ │ │ └── router.ts │ │ │ │ ├── config/ │ │ │ │ │ ├── config.default.js │ │ │ │ │ └── plugin.js │ │ │ │ └── package.json │ │ │ └── proxy.test.ts │ │ ├── tsconfig.json │ │ ├── tsconfig.pub.json │ │ └── typings/ │ │ └── index.d.ts │ ├── orm/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── app.ts │ │ ├── lib/ │ │ │ ├── DataSourceManager.ts │ │ │ ├── LeoricRegister.ts │ │ │ ├── ModelProtoHook.ts │ │ │ ├── ModelProtoManager.ts │ │ │ ├── ORMLoadUnitHook.ts │ │ │ ├── SingletonModelObject.ts │ │ │ ├── SingletonModelProto.ts │ │ │ └── SingletonORM.ts │ │ ├── package.json │ │ ├── test/ │ │ │ ├── fixtures/ │ │ │ │ ├── apps/ │ │ │ │ │ └── orm-app/ │ │ │ │ │ ├── app.ts │ │ │ │ │ ├── config/ │ │ │ │ │ │ ├── config.default.js │ │ │ │ │ │ ├── module.json │ │ │ │ │ │ └── plugin.js │ │ │ │ │ ├── modules/ │ │ │ │ │ │ └── orm-module/ │ │ │ │ │ │ ├── AppService.ts │ │ │ │ │ │ ├── CtxService.ts │ │ │ │ │ │ ├── PkgService.ts │ │ │ │ │ │ ├── model/ │ │ │ │ │ │ │ ├── App.ts │ │ │ │ │ │ │ └── Pkg.ts │ │ │ │ │ │ └── package.json │ │ │ │ │ └── package.json │ │ │ │ └── prepare.js │ │ │ └── index.test.ts │ │ ├── tsconfig.json │ │ ├── tsconfig.pub.json │ │ └── typings/ │ │ └── index.d.ts │ ├── schedule/ │ │ ├── CHANGELOG.md │ │ ├── README.md │ │ ├── agent.ts │ │ ├── app.ts │ │ ├── lib/ │ │ │ ├── EggScheduleAdapter.ts │ │ │ ├── EggScheduleMetadataConvertor.ts │ │ │ ├── ScheduleManager.ts │ │ │ ├── SchedulePrototypeHook.ts │ │ │ ├── ScheduleSubscriberRegister.ts │ │ │ ├── ScheduleWorkerLoadUnitHook.ts │ │ │ └── ScheduleWorkerRegister.ts │ │ ├── package.json │ │ ├── test/ │ │ │ ├── ScheduleManager.test.ts │ │ │ ├── fixtures/ │ │ │ │ └── schedule-app/ │ │ │ │ ├── app/ │ │ │ │ │ ├── simple-schedule-module/ │ │ │ │ │ │ ├── SimpleSchedule.ts │ │ │ │ │ │ └── package.json │ │ │ │ │ └── subscriber/ │ │ │ │ │ ├── Subscriber.ts │ │ │ │ │ └── package.json │ │ │ │ ├── config/ │ │ │ │ │ ├── module.json │ │ │ │ │ └── plugin.js │ │ │ │ └── package.json │ │ │ └── schedule.test.ts │ │ ├── tsconfig.json │ │ ├── tsconfig.pub.json │ │ └── typings/ │ │ └── index.d.ts │ └── tegg/ │ ├── CHANGELOG.md │ ├── README.md │ ├── app/ │ │ ├── extend/ │ │ │ ├── application.ts │ │ │ ├── application.unittest.ts │ │ │ └── context.ts │ │ └── middleware/ │ │ └── tegg_ctx_lifecycle_middleware.ts │ ├── app.ts │ ├── lib/ │ │ ├── AppLoadUnit.ts │ │ ├── AppLoadUnitInstance.ts │ │ ├── CompatibleUtil.ts │ │ ├── ConfigSourceLoadUnitHook.ts │ │ ├── EggAppLoader.ts │ │ ├── EggCompatibleObject.ts │ │ ├── EggCompatibleProtoImpl.ts │ │ ├── EggContextCompatibleHook.ts │ │ ├── EggContextHandler.ts │ │ ├── EggContextImpl.ts │ │ ├── EggModuleLoader.ts │ │ ├── EggQualifierProtoHook.ts │ │ ├── ModuleConfigLoader.ts │ │ ├── ModuleHandler.ts │ │ ├── Utils.ts │ │ ├── ctx_lifecycle_middleware.ts │ │ └── run_in_background.ts │ ├── package.json │ ├── test/ │ │ ├── AccessLevelCheck.test.ts │ │ ├── BackgroundTask.test.ts │ │ ├── ConstructorModuleConfig.test.ts │ │ ├── DynamicInject.test.ts │ │ ├── EggCompatible.test.ts │ │ ├── Inject.test.ts │ │ ├── ModuleConfig.test.ts │ │ ├── MultiInstanceInjectMultiInstance.test.ts │ │ ├── NoModuleJson.test.ts │ │ ├── OptionalModule.test.ts │ │ ├── OptionalPluginModule.test.ts │ │ ├── SameProtoName.test.ts │ │ ├── Subscription.test.ts │ │ ├── app/ │ │ │ └── extend/ │ │ │ ├── application.test.ts │ │ │ ├── application.unittest.test.ts │ │ │ └── context.test.ts │ │ ├── close.test.ts │ │ ├── fixtures/ │ │ │ └── apps/ │ │ │ ├── access-level-check/ │ │ │ │ ├── app/ │ │ │ │ │ ├── controller/ │ │ │ │ │ │ └── app.ts │ │ │ │ │ └── router.ts │ │ │ │ ├── config/ │ │ │ │ │ ├── config.default.js │ │ │ │ │ ├── module.json │ │ │ │ │ └── plugin.js │ │ │ │ ├── modules/ │ │ │ │ │ ├── module-a/ │ │ │ │ │ │ ├── BarService.ts │ │ │ │ │ │ ├── FooService.ts │ │ │ │ │ │ └── package.json │ │ │ │ │ ├── module-main/ │ │ │ │ │ │ ├── BarService.ts │ │ │ │ │ │ ├── FooService.ts │ │ │ │ │ │ ├── MainService.ts │ │ │ │ │ │ └── package.json │ │ │ │ │ └── package.json │ │ │ │ ├── package.json │ │ │ │ └── typings/ │ │ │ │ └── index.d.ts │ │ │ ├── app-multi-inject-multi/ │ │ │ │ ├── app/ │ │ │ │ │ └── modules/ │ │ │ │ │ ├── app/ │ │ │ │ │ │ ├── App.ts │ │ │ │ │ │ ├── module.yml │ │ │ │ │ │ └── package.json │ │ │ │ │ ├── app2/ │ │ │ │ │ │ ├── App.ts │ │ │ │ │ │ ├── module.yml │ │ │ │ │ │ └── package.json │ │ │ │ │ ├── bar/ │ │ │ │ │ │ ├── BizManager.ts │ │ │ │ │ │ └── package.json │ │ │ │ │ └── foo/ │ │ │ │ │ ├── Secret.ts │ │ │ │ │ └── package.json │ │ │ │ ├── config/ │ │ │ │ │ ├── config.default.js │ │ │ │ │ └── plugin.js │ │ │ │ └── package.json │ │ │ ├── app-with-no-module-json/ │ │ │ │ ├── app/ │ │ │ │ │ ├── controller/ │ │ │ │ │ │ └── app.ts │ │ │ │ │ ├── extend/ │ │ │ │ │ │ ├── application.unittest.ts │ │ │ │ │ │ └── context.ts │ │ │ │ │ ├── router.ts │ │ │ │ │ └── typings/ │ │ │ │ │ └── index.d.ts │ │ │ │ ├── config/ │ │ │ │ │ ├── config.default.js │ │ │ │ │ └── plugin.js │ │ │ │ ├── modules/ │ │ │ │ │ └── config-module/ │ │ │ │ │ ├── ConfigService.ts │ │ │ │ │ └── package.json │ │ │ │ └── package.json │ │ │ ├── background-app/ │ │ │ │ ├── app/ │ │ │ │ │ ├── controller/ │ │ │ │ │ │ └── app.ts │ │ │ │ │ ├── router.ts │ │ │ │ │ └── typings/ │ │ │ │ │ └── index.d.ts │ │ │ │ ├── config/ │ │ │ │ │ ├── config.default.js │ │ │ │ │ ├── module.json │ │ │ │ │ └── plugin.js │ │ │ │ ├── modules/ │ │ │ │ │ └── multi-module-background/ │ │ │ │ │ ├── BackgroundService.ts │ │ │ │ │ ├── CountService.ts │ │ │ │ │ └── package.json │ │ │ │ └── package.json │ │ │ ├── constructor-module-config/ │ │ │ │ ├── app/ │ │ │ │ │ ├── controller/ │ │ │ │ │ │ └── app.ts │ │ │ │ │ ├── extend/ │ │ │ │ │ │ ├── application.unittest.ts │ │ │ │ │ │ └── context.ts │ │ │ │ │ ├── router.ts │ │ │ │ │ └── typings/ │ │ │ │ │ └── index.d.ts │ │ │ │ ├── app.ts │ │ │ │ ├── config/ │ │ │ │ │ ├── config.default.js │ │ │ │ │ └── plugin.js │ │ │ │ ├── modules/ │ │ │ │ │ └── module-with-config/ │ │ │ │ │ ├── foo.ts │ │ │ │ │ ├── module.unittest.yml │ │ │ │ │ ├── module.yml │ │ │ │ │ └── package.json │ │ │ │ └── package.json │ │ │ ├── dynamic-inject-app/ │ │ │ │ ├── app/ │ │ │ │ │ ├── controller/ │ │ │ │ │ │ └── app.ts │ │ │ │ │ ├── router.ts │ │ │ │ │ └── typings/ │ │ │ │ │ └── index.d.ts │ │ │ │ ├── config/ │ │ │ │ │ ├── config.default.js │ │ │ │ │ ├── module.json │ │ │ │ │ └── plugin.js │ │ │ │ ├── modules/ │ │ │ │ │ └── dynamic-inject-module/ │ │ │ │ │ ├── AbstractContextHello.ts │ │ │ │ │ ├── AbstractSingletonHello.ts │ │ │ │ │ ├── FooType.ts │ │ │ │ │ ├── HelloService.ts │ │ │ │ │ ├── SingletonHelloService.ts │ │ │ │ │ ├── decorator/ │ │ │ │ │ │ ├── ContextHello.ts │ │ │ │ │ │ └── SingletonHello.ts │ │ │ │ │ ├── impl/ │ │ │ │ │ │ ├── BarContextHello.ts │ │ │ │ │ │ ├── BarSingletonHello.ts │ │ │ │ │ │ ├── FooContextHello.ts │ │ │ │ │ │ └── FooSingletonHello.ts │ │ │ │ │ └── package.json │ │ │ │ └── package.json │ │ │ ├── egg-app/ │ │ │ │ ├── app/ │ │ │ │ │ ├── controller/ │ │ │ │ │ │ └── app.ts │ │ │ │ │ ├── extend/ │ │ │ │ │ │ ├── application.ts │ │ │ │ │ │ ├── application.unittest.ts │ │ │ │ │ │ └── context.ts │ │ │ │ │ ├── router.ts │ │ │ │ │ └── typings/ │ │ │ │ │ └── index.d.ts │ │ │ │ ├── config/ │ │ │ │ │ ├── config.default.js │ │ │ │ │ ├── module.json │ │ │ │ │ └── plugin.js │ │ │ │ ├── modules/ │ │ │ │ │ ├── multi-module-common/ │ │ │ │ │ │ ├── model/ │ │ │ │ │ │ │ └── App.ts │ │ │ │ │ │ └── package.json │ │ │ │ │ ├── multi-module-repo/ │ │ │ │ │ │ ├── AppRepo.ts │ │ │ │ │ │ ├── GlobalAppRepo.ts │ │ │ │ │ │ ├── PersistenceService.ts │ │ │ │ │ │ └── package.json │ │ │ │ │ └── multi-module-service/ │ │ │ │ │ ├── AppService.ts │ │ │ │ │ ├── ConfigService.ts │ │ │ │ │ ├── CustomLoggerService.ts │ │ │ │ │ ├── EggTypeService.ts │ │ │ │ │ ├── SingletonFooService.ts │ │ │ │ │ ├── TraceService.ts │ │ │ │ │ └── package.json │ │ │ │ └── package.json │ │ │ ├── inject-module-config/ │ │ │ │ ├── app/ │ │ │ │ │ ├── controller/ │ │ │ │ │ │ └── app.ts │ │ │ │ │ ├── extend/ │ │ │ │ │ │ ├── application.unittest.ts │ │ │ │ │ │ └── context.ts │ │ │ │ │ ├── router.ts │ │ │ │ │ └── typings/ │ │ │ │ │ └── index.d.ts │ │ │ │ ├── app.ts │ │ │ │ ├── config/ │ │ │ │ │ ├── config.default.js │ │ │ │ │ └── plugin.js │ │ │ │ ├── modules/ │ │ │ │ │ ├── module-with-config/ │ │ │ │ │ │ ├── foo.ts │ │ │ │ │ │ ├── module.unittest.yml │ │ │ │ │ │ ├── module.yml │ │ │ │ │ │ └── package.json │ │ │ │ │ └── module-with-overwrite-config/ │ │ │ │ │ ├── bar.ts │ │ │ │ │ ├── module.unittest.yml │ │ │ │ │ ├── module.yml │ │ │ │ │ └── package.json │ │ │ │ └── package.json │ │ │ ├── invalid-inject/ │ │ │ │ ├── app/ │ │ │ │ │ └── modules/ │ │ │ │ │ └── module-a/ │ │ │ │ │ ├── BarService.ts │ │ │ │ │ └── package.json │ │ │ │ ├── config/ │ │ │ │ │ ├── config.default.js │ │ │ │ │ └── plugin.js │ │ │ │ └── package.json │ │ │ ├── optional-inject/ │ │ │ │ ├── app/ │ │ │ │ │ └── modules/ │ │ │ │ │ └── module-a/ │ │ │ │ │ ├── BarService.ts │ │ │ │ │ ├── FooService.ts │ │ │ │ │ └── package.json │ │ │ │ ├── config/ │ │ │ │ │ ├── config.default.js │ │ │ │ │ └── plugin.js │ │ │ │ └── package.json │ │ │ ├── optional-module/ │ │ │ │ ├── app/ │ │ │ │ │ └── modules/ │ │ │ │ │ └── root/ │ │ │ │ │ ├── Root.ts │ │ │ │ │ └── package.json │ │ │ │ ├── config/ │ │ │ │ │ ├── config.default.js │ │ │ │ │ └── plugin.js │ │ │ │ ├── node_modules/ │ │ │ │ │ ├── foo/ │ │ │ │ │ │ └── package.json │ │ │ │ │ ├── unused/ │ │ │ │ │ │ ├── Unused.js │ │ │ │ │ │ └── package.json │ │ │ │ │ └── used/ │ │ │ │ │ ├── Used.js │ │ │ │ │ └── package.json │ │ │ │ └── package.json │ │ │ ├── plugin-module/ │ │ │ │ ├── config/ │ │ │ │ │ ├── config.default.js │ │ │ │ │ └── plugin.js │ │ │ │ ├── node_modules/ │ │ │ │ │ ├── foo/ │ │ │ │ │ │ └── package.json │ │ │ │ │ └── foo-plugin/ │ │ │ │ │ ├── Used.js │ │ │ │ │ └── package.json │ │ │ │ └── package.json │ │ │ ├── recursive-module-app/ │ │ │ │ ├── app/ │ │ │ │ │ ├── controller/ │ │ │ │ │ │ └── app.ts │ │ │ │ │ └── router.ts │ │ │ │ ├── config/ │ │ │ │ │ ├── config.default.js │ │ │ │ │ ├── module.json │ │ │ │ │ └── plugin.js │ │ │ │ ├── modules/ │ │ │ │ │ ├── multi-module-repo/ │ │ │ │ │ │ ├── AppRepo.ts │ │ │ │ │ │ └── package.json │ │ │ │ │ └── multi-module-service/ │ │ │ │ │ ├── AppService.ts │ │ │ │ │ └── package.json │ │ │ │ └── package.json │ │ │ ├── same-name-protos/ │ │ │ │ ├── app/ │ │ │ │ │ └── modules/ │ │ │ │ │ ├── module-a/ │ │ │ │ │ │ ├── BarService.ts │ │ │ │ │ │ └── package.json │ │ │ │ │ ├── module-bar/ │ │ │ │ │ │ ├── FooService.ts │ │ │ │ │ │ └── package.json │ │ │ │ │ └── module-foo/ │ │ │ │ │ ├── FooService.ts │ │ │ │ │ └── package.json │ │ │ │ ├── config/ │ │ │ │ │ ├── config.default.js │ │ │ │ │ └── plugin.js │ │ │ │ └── package.json │ │ │ ├── same-name-singleton-and-context-proto/ │ │ │ │ ├── app/ │ │ │ │ │ └── modules/ │ │ │ │ │ ├── module-bar/ │ │ │ │ │ │ ├── BarConstructorService1.ts │ │ │ │ │ │ ├── BarConstructorService2.ts │ │ │ │ │ │ ├── BarService1.ts │ │ │ │ │ │ ├── BarService2.ts │ │ │ │ │ │ ├── FooService.ts │ │ │ │ │ │ └── package.json │ │ │ │ │ └── module-foo/ │ │ │ │ │ ├── FooService.ts │ │ │ │ │ └── package.json │ │ │ │ ├── config/ │ │ │ │ │ ├── config.default.js │ │ │ │ │ └── plugin.js │ │ │ │ └── package.json │ │ │ ├── schedule-app/ │ │ │ │ ├── app/ │ │ │ │ │ └── schedule/ │ │ │ │ │ └── foo.ts │ │ │ │ ├── config/ │ │ │ │ │ ├── config.default.js │ │ │ │ │ ├── module.json │ │ │ │ │ └── plugin.js │ │ │ │ ├── modules/ │ │ │ │ │ ├── multi-module-repo/ │ │ │ │ │ │ ├── AppRepo.ts │ │ │ │ │ │ └── package.json │ │ │ │ │ └── multi-module-service/ │ │ │ │ │ ├── AppService.ts │ │ │ │ │ └── package.json │ │ │ │ └── package.json │ │ │ └── wrong-order-app/ │ │ │ ├── app/ │ │ │ │ ├── controller/ │ │ │ │ │ └── app.ts │ │ │ │ └── router.ts │ │ │ ├── config/ │ │ │ │ ├── config.default.js │ │ │ │ ├── module.json │ │ │ │ └── plugin.js │ │ │ ├── modules/ │ │ │ │ ├── multi-module-repo/ │ │ │ │ │ ├── AppRepo.ts │ │ │ │ │ └── package.json │ │ │ │ └── multi-module-service/ │ │ │ │ ├── AppService.ts │ │ │ │ └── package.json │ │ │ └── package.json │ │ └── lib/ │ │ └── EggModuleLoader.test.ts │ ├── tsconfig.json │ ├── tsconfig.pub.json │ └── typings/ │ └── index.d.ts ├── standalone/ │ ├── service-worker/ │ │ ├── CHANGELOG.md │ │ ├── index.ts │ │ ├── package.json │ │ ├── src/ │ │ │ ├── ServiceWorkerApp.ts │ │ │ ├── ServiceWorkerRunner.ts │ │ │ ├── StandaloneEggObjectFactory.ts │ │ │ ├── constants.ts │ │ │ ├── controller/ │ │ │ │ ├── ControllerMetadataManager.ts │ │ │ │ ├── ControllerRegister.ts │ │ │ │ ├── ControllerRegisterFactory.ts │ │ │ │ ├── RootProtoManager.ts │ │ │ │ └── ServiceWorkerContext.ts │ │ │ ├── hook/ │ │ │ │ ├── ContextProtoLoadUnitHook.ts │ │ │ │ ├── ControllerLoadUnitHook.ts │ │ │ │ ├── ControllerPrototypeHook.ts │ │ │ │ └── LoadUnitInnerClassHook.ts │ │ │ ├── http/ │ │ │ │ ├── FetchEventHandler.ts │ │ │ │ ├── FetchRouter.ts │ │ │ │ ├── HTTPControllerRegister.ts │ │ │ │ ├── HTTPMethodRegister.ts │ │ │ │ └── ServiceWorkerFetchContext.ts │ │ │ ├── mcp/ │ │ │ │ ├── AbstractControllerAdvice.ts │ │ │ │ ├── MCPControllerRegister.ts │ │ │ │ └── MCPServerHelper.ts │ │ │ ├── types.ts │ │ │ └── utils/ │ │ │ ├── RequestUtils.ts │ │ │ └── ResponseUtils.ts │ │ ├── test/ │ │ │ ├── Utils.ts │ │ │ ├── fixtures/ │ │ │ │ ├── http/ │ │ │ │ │ ├── AopMiddlewareController.ts │ │ │ │ │ ├── GetController.ts │ │ │ │ │ ├── HttpTestAdvice.ts │ │ │ │ │ ├── PostController.ts │ │ │ │ │ └── package.json │ │ │ │ ├── http-builtin/ │ │ │ │ │ ├── BuiltinController.ts │ │ │ │ │ └── package.json │ │ │ │ ├── http-inject/ │ │ │ │ │ ├── UserController.ts │ │ │ │ │ ├── UserService.ts │ │ │ │ │ └── package.json │ │ │ │ ├── http-params/ │ │ │ │ │ ├── ParamController.ts │ │ │ │ │ └── package.json │ │ │ │ ├── http-priority/ │ │ │ │ │ ├── PriorityController.ts │ │ │ │ │ ├── ViewController.ts │ │ │ │ │ └── package.json │ │ │ │ └── mcp/ │ │ │ │ ├── MCPTestController.ts │ │ │ │ ├── McpTestAdvice.ts │ │ │ │ └── package.json │ │ │ ├── http/ │ │ │ │ ├── builtin.test.ts │ │ │ │ ├── inject.test.ts │ │ │ │ ├── params.test.ts │ │ │ │ ├── priority.test.ts │ │ │ │ ├── response.test.ts │ │ │ │ └── router.test.ts │ │ │ └── mcp/ │ │ │ └── mcp.test.ts │ │ ├── tsconfig.json │ │ └── tsconfig.pub.json │ └── standalone/ │ ├── CHANGELOG.md │ ├── README.md │ ├── index.ts │ ├── package.json │ ├── src/ │ │ ├── ConfigSourceLoadUnitHook.ts │ │ ├── EggModuleLoader.ts │ │ ├── ModuleConfig.ts │ │ ├── Runner.ts │ │ ├── StandaloneContext.ts │ │ ├── StandaloneContextHandler.ts │ │ ├── StandaloneContextImpl.ts │ │ ├── StandaloneInnerObject.ts │ │ ├── StandaloneInnerObjectProto.ts │ │ ├── StandaloneLoadUnit.ts │ │ └── main.ts │ ├── test/ │ │ ├── fixtures/ │ │ │ ├── ajv-module/ │ │ │ │ ├── foo.ts │ │ │ │ └── package.json │ │ │ ├── ajv-module-pass/ │ │ │ │ ├── foo.ts │ │ │ │ └── package.json │ │ │ ├── aop-module/ │ │ │ │ ├── Hello.ts │ │ │ │ ├── main.ts │ │ │ │ └── package.json │ │ │ ├── custom-context/ │ │ │ │ ├── foo.ts │ │ │ │ └── package.json │ │ │ ├── dal-module/ │ │ │ │ ├── module.yml │ │ │ │ ├── package.json │ │ │ │ ├── src/ │ │ │ │ │ ├── Foo.ts │ │ │ │ │ ├── dal/ │ │ │ │ │ │ ├── dao/ │ │ │ │ │ │ │ ├── FooDAO.ts │ │ │ │ │ │ │ └── base/ │ │ │ │ │ │ │ └── BaseFooDAO.ts │ │ │ │ │ │ ├── extension/ │ │ │ │ │ │ │ └── FooExtension.ts │ │ │ │ │ │ └── structure/ │ │ │ │ │ │ ├── Foo.json │ │ │ │ │ │ └── Foo.sql │ │ │ │ │ └── main.ts │ │ │ │ └── tsconfig.json │ │ │ ├── dal-transaction-module/ │ │ │ │ ├── module.yml │ │ │ │ ├── package.json │ │ │ │ ├── src/ │ │ │ │ │ ├── Foo.ts │ │ │ │ │ ├── FooService.ts │ │ │ │ │ ├── dal/ │ │ │ │ │ │ ├── dao/ │ │ │ │ │ │ │ ├── FooDAO.ts │ │ │ │ │ │ │ └── base/ │ │ │ │ │ │ │ └── BaseFooDAO.ts │ │ │ │ │ │ ├── extension/ │ │ │ │ │ │ │ └── FooExtension.ts │ │ │ │ │ │ └── structure/ │ │ │ │ │ │ ├── Foo.json │ │ │ │ │ │ └── Foo.sql │ │ │ │ │ └── main.ts │ │ │ │ └── tsconfig.json │ │ │ ├── dependency/ │ │ │ │ ├── foo.ts │ │ │ │ ├── node_modules/ │ │ │ │ │ ├── dependency-1/ │ │ │ │ │ │ └── package.json │ │ │ │ │ └── dependency-2/ │ │ │ │ │ ├── foo.js │ │ │ │ │ ├── module.yml │ │ │ │ │ └── package.json │ │ │ │ └── package.json │ │ │ ├── dynamic-inject-module/ │ │ │ │ ├── AbstractContextHello.ts │ │ │ │ ├── AbstractSingletonHello.ts │ │ │ │ ├── FooType.ts │ │ │ │ ├── HelloService.ts │ │ │ │ ├── decorator/ │ │ │ │ │ ├── ContextHello.ts │ │ │ │ │ └── SingletonHello.ts │ │ │ │ ├── impl/ │ │ │ │ │ ├── BarContextHello.ts │ │ │ │ │ ├── BarSingletonHello.ts │ │ │ │ │ ├── FooContextHello.ts │ │ │ │ │ └── FooSingletonHello.ts │ │ │ │ ├── main.ts │ │ │ │ └── package.json │ │ │ ├── inner-object/ │ │ │ │ ├── foo.ts │ │ │ │ └── package.json │ │ │ ├── invalid-inject/ │ │ │ │ ├── foo.ts │ │ │ │ └── package.json │ │ │ ├── lifecycle/ │ │ │ │ ├── foo.ts │ │ │ │ └── package.json │ │ │ ├── module-with-config/ │ │ │ │ ├── foo.ts │ │ │ │ ├── module.yml │ │ │ │ └── package.json │ │ │ ├── module-with-empty-config/ │ │ │ │ ├── foo.ts │ │ │ │ ├── module.yml │ │ │ │ └── package.json │ │ │ ├── module-with-empty-default-config/ │ │ │ │ ├── foo.ts │ │ │ │ ├── module.dev.yml │ │ │ │ ├── module.yml │ │ │ │ └── package.json │ │ │ ├── module-with-env-config/ │ │ │ │ ├── foo.ts │ │ │ │ ├── module.dev.yml │ │ │ │ ├── module.yml │ │ │ │ └── package.json │ │ │ ├── multi-callback-instance-module/ │ │ │ │ ├── biz/ │ │ │ │ │ ├── biz.ts │ │ │ │ │ ├── module.yml │ │ │ │ │ └── package.json │ │ │ │ ├── logger/ │ │ │ │ │ ├── DynamicLogger.ts │ │ │ │ │ └── package.json │ │ │ │ └── main/ │ │ │ │ ├── foo.ts │ │ │ │ ├── module.yml │ │ │ │ └── package.json │ │ │ ├── multi-modules/ │ │ │ │ ├── bar/ │ │ │ │ │ ├── module.yml │ │ │ │ │ └── package.json │ │ │ │ └── foo/ │ │ │ │ ├── foo.ts │ │ │ │ ├── module.yml │ │ │ │ └── package.json │ │ │ ├── optional-inject/ │ │ │ │ ├── bar.ts │ │ │ │ ├── foo.ts │ │ │ │ └── package.json │ │ │ ├── runtime-config/ │ │ │ │ ├── foo.ts │ │ │ │ └── package.json │ │ │ └── simple/ │ │ │ ├── foo.ts │ │ │ └── package.json │ │ └── index.test.ts │ ├── tsconfig.json │ └── tsconfig.pub.json └── tsconfig.json ================================================ FILE CONTENTS ================================================ ================================================ FILE: .eslintignore ================================================ docs coverage dist node_modules plugin/**/node_modules plugin/**/*.d.ts core/**/node_modules core/**/*.d.ts core/**/test/fixtures integration/**/node_modules integration/**/*.d.ts integration/**/test/fixtures plugin/*/test/fixtures/**/run plugin/*/test/fixtures/**/oneapi plugin/*/test/fixtures/**/logs benchmark !plugin/tegg-compatible/typings/index.d.ts ================================================ FILE: .eslintrc ================================================ { "extends": [ "eslint-config-egg/typescript", "plugin:import/recommended", "plugin:import/typescript" ], "parserOptions": { "project": "./tsconfig.json" }, "rules": { "@typescript-eslint/ban-types": "off", "import/no-unresolved": "off", "import/no-relative-packages": "error" }, "overrides": [ { "files": [ "core/*/test/**/*", "plugin/*/test/**/*", "standalone/*/test/**/*" ], "rules": { "import/no-relative-packages": "off" } } ] } ================================================ FILE: .github/FUNDING.yml ================================================ # These are supported funding model platforms open_collective: eggjs # Replace with a single Open Collective username # github: [ fengmk2, popomore, atian25, dead_horse ] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] # patreon: # Replace with a single Patreon username # ko_fi: # Replace with a single Ko-fi username # tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel # community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry # liberapay: # Replace with a single Liberapay username # issuehunt: # Replace with a single IssueHunt username # otechie: # Replace with a single Otechie username # custom: # Replace with a single custom sponsorship URL ================================================ FILE: .github/ISSUE_TEMPLATE/bug-report-cn.yml ================================================ name: 🐛 TEgg Bug 反馈 description: 如发现 TEgg 框架中的 Bug,请及时在此汇报。 labels: [bug] body: - type: textarea attributes: label: | 在此输入你需要反馈的 Bug 具体信息(Bug in Detail): placeholder: | 1. 我做了什么。 2. 我的预期值。 3. 我实际得到的结果。 4. 可以的话,请提供一些截图、视频作为附件以复现症状。 validations: required: true - type: textarea attributes: label: 可复现问题的仓库地址(Reproduction Repo) description: | 1. 请使用 `npm init egg --type=simple bug` 创建最小可复现问题的代码。 2. 在 GitHub 中上传该代码项目,并在此处粘贴地址(你也可以直接将你的仓库压缩为 zip 文件直接以附件形式提交)。 placeholder: | https://github.com/YOUR_REPOSITORY_URL validations: required: true - type: input attributes: label: Node 版本号: description: | 你的当前复现问题的 Node 版本号: placeholder: | 使用 “node -v” 命令,在控制台得到版本号(例如:v18.14.0)。 validations: required: true - type: input attributes: label: TEgg 版本号: description: | 你的当前复现问题 TEgg 版本号: placeholder: | 请直接在“package.json”中查阅(例如:0.0.1)。 validations: required: true - type: input attributes: label: "相关插件名称与版本号:" description: | 插件名称以及版本号: placeholder: | 请直接在“package.json”中查阅(例如:egg-mysql,3.1.1)。 validations: required: true - type: input attributes: label: "操作平台与版本号:" description: | 你的操作平台与版本号: placeholder: | Windows 10 专业版(21H2) validations: required: true ================================================ FILE: .github/ISSUE_TEMPLATE/bug-report.yml ================================================ name: 🐛 Bug Report For TEgg (in English) description: Report an issue if something isn't working as expected 🤔. labels: [bug] body: - type: textarea attributes: label: | Your detail info about the Bug: placeholder: | 1. What I did. 2. What I expected to happen. 3. What I actually got. 4. If possible, images/videos as attachments are welcomed to show the bug. validations: required: true - type: textarea attributes: label: Reproduction Repo description: | 1. Please use `npm init egg --type=simple bug` to create your smallest repo. 2. Submit it in the GitHub and paste your URL here (you can also attach your zip file directly). placeholder: | https://github.com/YOUR_REPOSITORY_URL or your zip file validations: required: true - type: input attributes: label: Node Version description: | What's your Node's version? placeholder: | Use "node -v" in your console to get it (e.g: v18.14.0). validations: required: true - type: input attributes: label: TEgg Version description: | What's your TEgg version? placeholder: | See it directly in your "package.json" file (e.g: 0.0.1) validations: required: true - type: input attributes: label: Plugin Name and its version description: | What's your plugin's name and version? placeholder: | See them directly in your "package.json" file (e.g: egg-mysql, 3.1.1) validations: required: true - type: input attributes: label: Platform and its version description: | What's your platform and its version? placeholder: | Windows 10 Professional(21H2) validations: required: true ================================================ FILE: .github/ISSUE_TEMPLATE/feature-request-cn.yml ================================================ name: 💡 我有一个新点子 description: 我对 TEgg 框架有一个新的想法(或许我想来实现他)…… labels: [feature request] body: - type: markdown attributes: value: | 对于 TEgg 框架,你有一个新的想法? 不过在提交你的新点子之前,麻烦请检阅一下是否之前的帖子中有类似重复的内容。 - type: textarea attributes: label: 请详细告知你的新点子: placeholder: | 1. 您期望能够实现什么功能。 2. 您的理由(如:我一直被什么问题困扰……)。 如果方便的话,请提供截屏或者视频等详细信息。 3. 我能够做一些什么(最好是能提供一些伪代码帮助实现)。 validations: required: true ================================================ FILE: .github/ISSUE_TEMPLATE/feature-request.yml ================================================ name: 💡 Feature Request For TEgg (in English) description: I have a suggestion (and may want to implement it)! labels: [feature request] body: - type: markdown attributes: value: | You have an idea how to improve the TEgg? Before submitting, please have a look at the existing issues if there's already something related to your suggestion. - type: textarea attributes: label: "Enter your suggestions in details:" placeholder: | 1. What I expected to happen? 2. Your reason (e.g: I'm always frustrated with...). If possible, images or videos are welcome. 3. What I plan to do (Optional but better in pseudo codes). validations: required: true ================================================ FILE: .github/ISSUE_TEMPLATE/rfc-cn.yml ================================================ name: 🚀 RFC 提案 description: 我对 TEgg 框架技术架构功能层面上有重大新增、改进等。 labels: [RFC proposal] body: - type: markdown attributes: value: | 对于 TEgg 框架功能你是否有重大的新增或改进之类的想法? 不过在提交你的新想法或方案之前,麻烦请检阅一下是否之前的帖子中有类似重复的内容。 - type: textarea attributes: label: 请详细告知你的新解决思路: placeholder: | 1. 描述你希望解决的问题的现状,附上相关的 issue 地址。 如果方便的话,请提供截屏或者视频等详细信息。 2. 我能够做一些什么(譬如具体相关的的 API,描述思路,最好是能提供一些伪代码帮助实现)。 validations: required: true - type: checkboxes attributes: label: "跟进类型:" description: 此议案跟进类型情况: options: - label: 这是某个任务 - label: 这是一个具体的 PR 的地址(URL) ================================================ FILE: .github/ISSUE_TEMPLATE/rfc.yml ================================================ name: 🚀 RFC Proposals (in English) description: I've got a major improvement (or idea) on the technical architecture of the TEgg framework. labels: [RFC proposal] body: - type: markdown attributes: value: | Any better new/changable functions for the core technical architecture of the TEgg framework? But please make sure there's no duplicated issues related to your idea before submitting. - type: textarea attributes: label: "Please describe your idea in detail:" placeholder: | 1. Describe the current situation of the problem you want to solve, and attach the related issue address. If it is possible, please provide screenshots or videos in detail. 2. What can I do (related APIs, Your ideas, better to provide some pseudo code to help implementations). validations: required: true - type: checkboxes attributes: label: Follow-up type description: The type of the RFC proposals. options: - label: Some Task - label: PR URL(s) validations: required: true ================================================ FILE: .github/PULL_REQUEST_TEMPLATE.md ================================================ ##### Checklist - [ ] `npm test` passes - [ ] tests and/or benchmarks are included - [ ] documentation is changed or added - [ ] commit message follows commit guidelines ##### Affected core subsystem(s) ##### Description of change ================================================ FILE: .github/workflows/codeql-analysis.yml ================================================ # For most projects, this workflow file will not need changing; you simply need # to commit it to your repository. # # You may wish to alter this file to override the set of languages analyzed, # or to provide custom queries or build logic. # # ******** NOTE ******** # We have attempted to detect the languages in your repository. Please check # the `language` matrix defined below to confirm you have the correct set of # supported CodeQL languages. # name: "CodeQL" on: push: branches: [ "master" ] pull_request: # The branches below must be a subset of the branches above branches: [ "master" ] jobs: analyze: name: Analyze runs-on: ubuntu-latest permissions: actions: read contents: read security-events: write strategy: fail-fast: false matrix: language: [ 'javascript', 'typescript' ] # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support steps: - name: Checkout repository uses: actions/checkout@v4 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. # By default, queries listed here will override any specified in a config file. # Prefix the list here with "+" to use these queries and those in the config file. # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs # queries: security-extended,security-and-quality # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild uses: github/codeql-action/autobuild@v3 # ℹ️ Command-line programs to run using the OS shell. # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun # If the Autobuild fails above, remove it and uncomment the following three lines. # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. # - run: | # echo "Run, Build Application using script" # ./location_of_script_within_repo/buildscript.sh - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v3 ================================================ FILE: .github/workflows/nodejs.yml ================================================ name: Continuous integration on: push: branches: [ master ] pull_request: branches: [ master ] jobs: Runner-ubuntu: runs-on: ubuntu-latest services: mysql: image: mysql:5.7 env: MYSQL_ALLOW_EMPTY_PASSWORD: true MYSQL_DATABASE: test ports: - 3306:3306 options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=5 strategy: fail-fast: false matrix: node-version: [ 16, 18, 20 ] steps: - name: Checkout Git Source uses: actions/checkout@master - name: Setup Node.js uses: actions/setup-node@v1 with: node-version: ${{ matrix.node-version }} - name: Install Utoo uses: utooland/setup-utoo@v1 - name: Install Dependencies run: ut install --ignore-scripts - name: Continuous integration run: | ut ci ut tsc:pub - name: Code Coverage uses: codecov/codecov-action@v1 with: token: ${{ secrets.CODECOV_TOKEN }} Runner-macos: runs-on: macOS-latest strategy: fail-fast: false matrix: node-version: [ 16, 18, 20 ] steps: - name: Checkout Git Source uses: actions/checkout@master - name: Setup Node.js uses: actions/setup-node@v1 with: node-version: ${{ matrix.node-version }} - name: Install Mysql run: brew install mysql - name: Start Mysql ## arm64/x86 homebrew mysql path different run: brew services start mysql - name: Install Utoo uses: utooland/setup-utoo@v1 - name: Install Dependencies run: ut install --ignore-scripts - name: Continuous integration run: ut ci - name: Code Coverage uses: codecov/codecov-action@v1 with: token: ${{ secrets.CODECOV_TOKEN }} # GitHub mysql service not support windows # Runner-windows: # runs-on: windows-latest # # strategy: # fail-fast: false # matrix: # node-version: [ 16, 18, 20 ] # steps: # - name: Checkout Git Source # uses: actions/checkout@master # # - name: Setup Node.js # uses: actions/setup-node@v1 # with: # node-version: ${{ matrix.node-version }} # # - name: Install Npm # run: npm i -g npm@8 # # - name: Install Dependencies # run: npm i # # - name: Continuous integration # run: npm run ci # # - name: Code Coverage # uses: codecov/codecov-action@v1 # with: # token: ${{ secrets.CODECOV_TOKEN }} ================================================ FILE: .github/workflows/release.yml ================================================ name: Manual Release on: workflow_dispatch: inputs: branch: description: 'Branch to release from' required: true default: 'next' type: string version_type: description: 'Version bump type' required: true default: 'prerelease' type: choice options: - patch - minor - major - prerelease - prepatch - preminor - premajor prerelease_tag: description: 'Prerelease tag (alpha, beta, rc) - only used with prerelease/pre* types' required: false default: 'beta' type: choice options: - alpha - beta - rc dry_run: description: 'Dry run (do not publish)' required: false default: false type: boolean jobs: release: name: Manual Release runs-on: ubuntu-latest concurrency: group: release-${{ github.workflow }}-#${{ github.event.pull_request.number || github.head_ref || github.ref }} cancel-in-progress: false permissions: contents: write packages: write id-token: write steps: - name: Checkout uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 with: ref: ${{ github.event.inputs.branch }} fetch-depth: 0 # Use git token for checkout and pushing token: ${{ secrets.GIT_TOKEN }} - name: Setup pnpm uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4 - name: Setup Node.js uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5 with: node-version: '22' cache: 'pnpm' registry-url: 'https://registry.npmjs.org' - name: Re-install npm # TODO: OIDC requires npm >=11.5.1. # Until Node.js v24 is LTS (with npm 11 as the default), we need to bump. run: npm install -g npm@11 - name: Install dependencies run: pnpm install --frozen-lockfile - name: Build packages run: pnpm build - name: Configure Git run: | git config --local user.email "action@github.com" git config --local user.name "GitHub Action" - name: Version bump (dry run) if: ${{ github.event.inputs.dry_run == 'true' }} run: | echo "🧪 Running version bump in dry-run mode..." if [[ "${{ github.event.inputs.version_type }}" == prerelease* ]]; then node scripts/version.js ${{ github.event.inputs.version_type }} --prerelease-tag=${{ github.event.inputs.prerelease_tag }} --dry-run else node scripts/version.js ${{ github.event.inputs.version_type }} --dry-run fi - name: Version bump if: ${{ github.event.inputs.dry_run != 'true' }} run: | echo "🚀 Running version bump..." if [[ "${{ github.event.inputs.version_type }}" == prerelease* ]]; then node scripts/version.js ${{ github.event.inputs.version_type }} --prerelease-tag=${{ github.event.inputs.prerelease_tag }} else node scripts/version.js ${{ github.event.inputs.version_type }} fi # Get the new version tag NEW_TAG=$(git describe --tags --abbrev=0) echo "NEW_TAG=$NEW_TAG" >> $GITHUB_ENV # Push changes and tags git push origin ${{ github.event.inputs.branch }} --tags - name: Publish packages (dry run) if: ${{ github.event.inputs.dry_run == 'true' }} run: | echo "🧪 Running publish in dry-run mode..." if [[ "${{ github.event.inputs.version_type }}" == pre* ]]; then echo "Setting npm tag to: ${{ github.event.inputs.prerelease_tag }}" pnpm -r publish --dry-run --no-git-checks --tag=${{ github.event.inputs.prerelease_tag }} else echo "Setting npm tag to: latest" pnpm -r publish --dry-run --no-git-checks --tag=latest fi - name: Publish packages if: ${{ github.event.inputs.dry_run != 'true' }} run: | echo "📦 Publishing packages..." if [[ "${{ github.event.inputs.version_type }}" == pre* ]]; then echo "Setting npm tag to: ${{ github.event.inputs.prerelease_tag }}" pnpm -r publish --no-git-checks --tag=${{ github.event.inputs.prerelease_tag }} else echo "Setting npm tag to: latest" pnpm -r publish --no-git-checks --tag=latest fi - name: Create GitHub Release (draft) if: ${{ github.event.inputs.dry_run != 'true' }} uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 with: github-token: ${{ secrets.GIT_TOKEN }} script: | const tag = process.env.NEW_TAG; const versionType = '${{ github.event.inputs.version_type }}'; let releaseBody = `## 🎉 ${versionType.charAt(0).toUpperCase() + versionType.slice(1)} Release\n\n`; releaseBody += `This release includes ${versionType} version updates for all packages.\n\n`; releaseBody += `### 📦 Published Packages\n\n`; // Get package versions from the tag const fs = require('fs'); const packagesDirs = ['./packages', './tools', './plugins']; for (const packagesDir of packagesDirs) { const packageFolders = fs.readdirSync(packagesDir); for (const folder of packageFolders) { const packageJsonPath = `${packagesDir}/${folder}/package.json`; if (fs.existsSync(packageJsonPath)) { const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')); if (packageJson.private) { continue; } releaseBody += `- [${packageJson.name}@${packageJson.version}](https://npmjs.com/package/${packageJson.name}/v/${packageJson.version})\n`; } } } releaseBody += `\n### 🔄 What's Changed\n\n`; releaseBody += `\n`; releaseBody += `- Add your changelog items here\n`; releaseBody += `- Remove this placeholder text\n\n`; releaseBody += `**Full Changelog**: https://github.com/${{ github.repository }}/compare/v${tag}...${tag}`; const release = await github.rest.repos.createRelease({ owner: context.repo.owner, repo: context.repo.repo, tag_name: tag, name: tag, body: releaseBody, draft: true, prerelease: versionType.includes('pre') }); core.info(`Created draft release: ${release.data.html_url}`); // Set the release URL as an environment variable for use in summary core.exportVariable('DRAFT_RELEASE_URL', release.data.html_url); - name: Sync to cnpm run: | node scripts/sync-cnpm.js - name: Summary run: | echo "## 🎉 Release Summary" >> $GITHUB_STEP_SUMMARY if [ "${{ github.event.inputs.dry_run }}" == "true" ]; then echo "### 🧪 Dry Run Completed" >> $GITHUB_STEP_SUMMARY echo "- Version bump: **${{ github.event.inputs.version_type }}**" >> $GITHUB_STEP_SUMMARY echo "- Branch: **${{ github.event.inputs.branch }}**" >> $GITHUB_STEP_SUMMARY if [[ "${{ github.event.inputs.version_type }}" == pre* ]]; then echo "- npm tag: **${{ github.event.inputs.prerelease_tag }}**" >> $GITHUB_STEP_SUMMARY else echo "- npm tag: **latest**" >> $GITHUB_STEP_SUMMARY fi echo "- Status: **Dry run - no changes made**" >> $GITHUB_STEP_SUMMARY else echo "### ✅ Release Completed" >> $GITHUB_STEP_SUMMARY echo "- Version bump: **${{ github.event.inputs.version_type }}**" >> $GITHUB_STEP_SUMMARY echo "- Branch: **${{ github.event.inputs.branch }}**" >> $GITHUB_STEP_SUMMARY echo "- New tag: **$NEW_TAG**" >> $GITHUB_STEP_SUMMARY if [[ "${{ github.event.inputs.version_type }}" == pre* ]]; then echo "- npm tag: **${{ github.event.inputs.prerelease_tag }}**" >> $GITHUB_STEP_SUMMARY else echo "- npm tag: **latest**" >> $GITHUB_STEP_SUMMARY fi echo "- Packages published to npm" >> $GITHUB_STEP_SUMMARY echo "- Draft GitHub release created: [View Draft Release]($DRAFT_RELEASE_URL)" >> $GITHUB_STEP_SUMMARY fi ================================================ FILE: .gitignore ================================================ node_modules coverage *.log npm-debug.log .logs logs *.swp run *-run .idea .DS_Store .tmp .vscode codex-logs/ package-lock.json yarn.lock .editorconfig *clinic-flame* *clinic-doctor* .nyc_output/ dist **/*.js **/*.d.ts core/**/dist plugin/**/dist plugin/oneapi/test/fixtures/modules/*/oneapi plugin/dal/test/fixtures/modules/*/dal/ core/dal-runtime/test/fixtures/modules/*/dal core/dal-runtime/test/fixtures/modules/*/src/dal/ !core/dal-runtime/test/fixtures/modules/generate_codes_not_overwrite_dao/dal/extension/FooExtension.ts !core/dal-runtime/test/fixtures/modules/generate_codes_not_overwrite_dao/dal/dao/FooDAO.ts plugin/tegg/test/fixtures/apps/**/*.js !core/common-util/test/fixtures/**/node_modules !core/common-util/test/fixtures/**/node_modules/**/*.js !core/*/typings/*.d.ts !plugin/*/test/fixtures/**/*.js !plugin/*/typings/*.d.ts !plugin/*/test/fixtures/apps/*/config/*.js !plugin/*/test/fixtures/apps/**/typings/*.d.ts !core/eventbus-decorator/src/type.d.ts !plugin/orm/test/fixtures/prepare.js !benchmark/**/*.js !standalone/standalone/test/fixtures/**/node_modules !standalone/standalone/test/fixtures/**/node_modules/**/*.js !plugin/tegg/test/fixtures/**/node_modules !plugin/config/test/fixtures/**/node_modules .node .egg ================================================ FILE: .mocharc.yml ================================================ timeout: "120000" spec: - test/**/*.test.ts recursive: true extension: - ts require: - ts-node/register - source-map-support/register full-trace: true exit: true ================================================ FILE: .nycrc.yml ================================================ extends: - '@istanbuljs/nyc-config-typescript' - 'test-exclude' all: true check-coverage: true temp-directory: './node_modules/.nyc_output' report-dir: './coverage' reporter: - text-summary - json-summary - json - lcov lines: 0 statements: 0 exclude: - core/*/typings - core/*/dist - plugin/*/typings - plugin/*/dist - coverage - core/*/test/**/* - plugin/*/test/**/* - standalone/*/test/**/* - benchmark/**/* - core/test-util/**/* ================================================ FILE: CHANGELOG.md ================================================ # Change Log All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. ## [3.78.15](https://github.com/eggjs/tegg/compare/v3.78.14...v3.78.15) (2026-04-23) ### Bug Fixes * **agent-runtime:** hold cancelRun until executor session is committed ([#441](https://github.com/eggjs/tegg/issues/441)) ([4e02a28](https://github.com/eggjs/tegg/commit/4e02a28bdfe9b924c1190482fd3d85f8cad1fcfa)) ## [3.78.14](https://github.com/eggjs/tegg/compare/v3.78.13...v3.78.14) (2026-04-21) ### Bug Fixes * mcp path ([3835288](https://github.com/eggjs/tegg/commit/3835288e9b78e3d2f422e91e3e56bf4ead0c4372)) * mcp sse connect clear ([#440](https://github.com/eggjs/tegg/issues/440)) ([4c59d6a](https://github.com/eggjs/tegg/commit/4c59d6a13cf066dec70173494bd09c30a5052712)) ## [3.78.13](https://github.com/eggjs/tegg/compare/v3.78.12...v3.78.13) (2026-04-21) ### Bug Fixes * **agent-runtime:** persist thread messages when a run is aborted ([#439](https://github.com/eggjs/tegg/issues/439)) ([384ab1b](https://github.com/eggjs/tegg/commit/384ab1bf3c344177d8eb2593d35dab41361a31dc)) ## [3.78.12](https://github.com/eggjs/tegg/compare/v3.78.11...v3.78.12) (2026-04-18) ### Bug Fixes * del debug log ([a7d8e06](https://github.com/eggjs/tegg/commit/a7d8e0608d709a733ea463c75ca955184ae4c552)) ## [3.78.11](https://github.com/eggjs/tegg/compare/v3.78.10...v3.78.11) (2026-04-18) ### Bug Fixes * limit mcp version ([8627372](https://github.com/eggjs/tegg/commit/86273726bbf4f33e0856dc726aa3f7ff963e9e99)) ## [3.78.10](https://github.com/eggjs/tegg/compare/v3.78.9...v3.78.10) (2026-04-18) ### Bug Fixes * import ([c7ed1b7](https://github.com/eggjs/tegg/commit/c7ed1b78f9c0ee308c85029e79d5187fd7fd1bd4)) ## [3.78.9](https://github.com/eggjs/tegg/compare/v3.78.8...v3.78.9) (2026-04-18) **Note:** Version bump only for package tegg ## [3.78.8](https://github.com/eggjs/tegg/compare/v3.78.7...v3.78.8) (2026-04-18) ### Bug Fixes * remove egg module ([#438](https://github.com/eggjs/tegg/issues/438)) ([c82c26e](https://github.com/eggjs/tegg/commit/c82c26ebfa8272e32477f9b5be51da85e70904a6)) ## [3.78.7](https://github.com/eggjs/tegg/compare/v3.78.6...v3.78.7) (2026-04-18) ### Bug Fixes * ts ([4e9baaa](https://github.com/eggjs/tegg/commit/4e9baaad4a78e98148979fc3336e24f872300c7d)) ## [3.78.6](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.6) (2026-04-18) **Note:** Version bump only for package tegg ## [3.78.5](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.5) (2026-04-18) **Note:** Version bump only for package tegg ## [3.78.4](https://github.com/eggjs/tegg/compare/v3.78.3...v3.78.4) (2026-04-18) **Note:** Version bump only for package tegg ## [3.78.3](https://github.com/eggjs/tegg/compare/v3.78.2...v3.78.3) (2026-04-15) ### Bug Fixes * mcp_client_dispatcher ([#435](https://github.com/eggjs/tegg/issues/435)) ([82cdf41](https://github.com/eggjs/tegg/commit/82cdf41230af10b15758c56ec59756d2c5885e5e)) ## [3.78.2](https://github.com/eggjs/tegg/compare/v3.78.1...v3.78.2) (2026-04-09) ### Bug Fixes * **agent-runtime:** filter stream_event in all appendMessages calls ([#434](https://github.com/eggjs/tegg/issues/434)) ([c3b81bd](https://github.com/eggjs/tegg/commit/c3b81bdb108d07528a40fbf14162fcbeb3338c60)), closes [#433](https://github.com/eggjs/tegg/issues/433) ## [3.78.1](https://github.com/eggjs/tegg/compare/v3.78.0...v3.78.1) (2026-04-09) **Note:** Version bump only for package tegg # [3.78.0](https://github.com/eggjs/tegg/compare/v3.77.2...v3.78.0) (2026-04-09) ### Features * **agent-runtime:** rewrite streamRun with StreamEvent format and reconnection ([#432](https://github.com/eggjs/tegg/issues/432)) ([d03dac2](https://github.com/eggjs/tegg/commit/d03dac2ddd78641acb47e19275488ad9fbfcda2a)) ## [3.77.2](https://github.com/eggjs/tegg/compare/v3.77.1...v3.77.2) (2026-04-07) ### Bug Fixes * streamable get timeout ([#431](https://github.com/eggjs/tegg/issues/431)) ([ebacefa](https://github.com/eggjs/tegg/commit/ebacefaa0512cfe9a0d5e7465a0386041cb9d07c)) ## [3.77.1](https://github.com/eggjs/tegg/compare/v3.77.0...v3.77.1) (2026-04-01) ### Bug Fixes * **agent-runtime:** handle all content_block_start and delta subtypes in normalizeContentBlocks ([#430](https://github.com/eggjs/tegg/issues/430)) ([119ba38](https://github.com/eggjs/tegg/commit/119ba3889a52b3577bf0aa23b6123c4d2fd4a23c)) # [3.77.0](https://github.com/eggjs/tegg/compare/v3.76.1...v3.77.0) (2026-04-01) ### Features * **agent-runtime:** add normalizeContentBlocks for Anthropic SDK stream events ([#429](https://github.com/eggjs/tegg/issues/429)) ([d780fdb](https://github.com/eggjs/tegg/commit/d780fdba3cc243db4811af6733fda737f8c1dc4a)) ## [3.76.1](https://github.com/eggjs/tegg/compare/v3.76.0...v3.76.1) (2026-04-01) ### Bug Fixes * **agent-runtime:** merge content blocks and support accumulate control ([#428](https://github.com/eggjs/tegg/issues/428)) ([f4f904e](https://github.com/eggjs/tegg/commit/f4f904e357497fc5ad9a2c7d2ece4e9b305f5738)) # [3.76.0](https://github.com/eggjs/tegg/compare/v3.75.1...v3.76.0) (2026-04-01) ### Features * **agent-runtime:** support custom SSE event types in streamRun ([#427](https://github.com/eggjs/tegg/issues/427)) ([2efe539](https://github.com/eggjs/tegg/commit/2efe539cd2673e27dc91cb4597751e6e0a9d4b67)) ## [3.75.1](https://github.com/eggjs/tegg/compare/v3.75.0...v3.75.1) (2026-04-01) ### Bug Fixes * **agent-runtime:** preserve non-text content blocks in MessageConverter ([#426](https://github.com/eggjs/tegg/issues/426)) ([8c4382f](https://github.com/eggjs/tegg/commit/8c4382f33f68534218049cfbfadfd4f6800a348c)) # [3.75.0](https://github.com/eggjs/tegg/compare/v3.74.0...v3.75.0) (2026-03-30) ### Features * **agent-tracing:** add typesVersions for moduleResolution:node compatibility ([#424](https://github.com/eggjs/tegg/issues/424)) ([25b282c](https://github.com/eggjs/tegg/commit/25b282c947e5ce567ad72516d9c612cfb949d891)) # [3.74.0](https://github.com/eggjs/tegg/compare/v3.73.0...v3.74.0) (2026-03-30) ### Bug Fixes * **agent-runtime:** set isResume based on thread message history ([#419](https://github.com/eggjs/tegg/issues/419)) ([8a7eacc](https://github.com/eggjs/tegg/commit/8a7eacca79a94815251a0d660f828ebef443d12a)) * **agent-tracing:** add eggModule declaration for tegg module scanning ([#416](https://github.com/eggjs/tegg/issues/416)) ([d169cab](https://github.com/eggjs/tegg/commit/d169cabeb0580b96c320ac6c067d3d9828639a32)) * **agent-tracing:** separate traceId and sessionId in createSession ([#417](https://github.com/eggjs/tegg/issues/417)) ([c760776](https://github.com/eggjs/tegg/commit/c7607761d1e85cbf91cb74878e90af898672db3a)) ### Features * **agent-tracing:** collect assistant messages into root run outputs ([#421](https://github.com/eggjs/tegg/issues/421)) ([8dcebaf](https://github.com/eggjs/tegg/commit/8dcebafa19061d4a91161272409a2ac729d78341)) * **agent-tracing:** rename TraceSession to Trace and support inputs in createTrace ([#420](https://github.com/eggjs/tegg/issues/420)) ([5471bda](https://github.com/eggjs/tegg/commit/5471bda05bd21ed5c60d82182a2807d09f9be097)) # [3.73.0](https://github.com/eggjs/tegg/compare/v3.72.0...v3.73.0) (2026-03-25) ### Bug Fixes * compatibility with @modelcontextprotocol/sdk 1.26 ([#404](https://github.com/eggjs/tegg/issues/404)) ([dbda39a](https://github.com/eggjs/tegg/commit/dbda39ad2f4be5879f1e0540cf26a242cecd05e3)) * mcp helper init ([#415](https://github.com/eggjs/tegg/issues/415)) ([58f15e1](https://github.com/eggjs/tegg/commit/58f15e176edf3e8229d2bcddcf034fd7d96d5f14)) ### Features * add @eggjs/agent-tracing package for AI agent tracing ([#412](https://github.com/eggjs/tegg/issues/412)) ([56f460d](https://github.com/eggjs/tegg/commit/56f460d17007a42b8643d8d5dd25d25c3f52dcc1)) * add agent-runtime package with @AgentController decorator ([#411](https://github.com/eggjs/tegg/issues/411)) ([d4d0006](https://github.com/eggjs/tegg/commit/d4d00061e90230f82c0958bcf5268f8a511395db)) * add tegg vitest workspace ([#401](https://github.com/eggjs/tegg/issues/401)) ([c853090](https://github.com/eggjs/tegg/commit/c853090c8da22c158b684d4e0ccabf0cba4c17b8)) * **agent-runtime:** add isResume flag to CreateRunInput ([#414](https://github.com/eggjs/tegg/issues/414)) ([29ac989](https://github.com/eggjs/tegg/commit/29ac98995c0a37bb34d33f7ad81af7c664a67bce)) # [3.72.0](https://github.com/eggjs/tegg/compare/v3.71.2...v3.72.0) (2026-02-04) ### Features * add structured tool ([#387](https://github.com/eggjs/tegg/issues/387)) ([56c23ad](https://github.com/eggjs/tegg/commit/56c23adb0af25ce0fd3624491eaf7af3fb1570cf)) ## [3.71.2](https://github.com/eggjs/tegg/compare/v3.71.1...v3.71.2) (2026-01-30) ### Bug Fixes * duplicate wrap tracer ([#398](https://github.com/eggjs/tegg/issues/398)) ([c20f55c](https://github.com/eggjs/tegg/commit/c20f55c8ad26ee2236911b3153466cfc17c95c19)) ## [3.71.1](https://github.com/eggjs/tegg/compare/v3.71.0...v3.71.1) (2026-01-28) ### Bug Fixes * mcp proxy header ([#397](https://github.com/eggjs/tegg/issues/397)) ([d79cf73](https://github.com/eggjs/tegg/commit/d79cf735e535fe41756a4a349e1b04a556f81ce9)) # [3.71.0](https://github.com/eggjs/tegg/compare/v3.70.1...v3.71.0) (2026-01-26) ### Features * add trace to log ([#395](https://github.com/eggjs/tegg/issues/395)) ([bcfb895](https://github.com/eggjs/tegg/commit/bcfb89554f1ad0d83acba6e1fc424edbe93ad774)) ## [3.70.1](https://github.com/eggjs/tegg/compare/v3.70.0...v3.70.1) (2026-01-14) ### Bug Fixes * add stream log and fix add node options ([#394](https://github.com/eggjs/tegg/issues/394)) ([9fce038](https://github.com/eggjs/tegg/commit/9fce038b876100f22344ced707a8c8039594aa3b)) # [3.70.0](https://github.com/eggjs/tegg/compare/v3.69.0...v3.70.0) (2026-01-07) ### Features * set default retry time to 3 for dal init ([#390](https://github.com/eggjs/tegg/issues/390)) ([afde48c](https://github.com/eggjs/tegg/commit/afde48c39990f5b000520cb1ba3ba1a336222ce2)) # [3.69.0](https://github.com/eggjs/tegg/compare/v3.68.0...v3.69.0) (2026-01-06) ### Features * dns cache logger ([#388](https://github.com/eggjs/tegg/issues/388)) ([e9c1180](https://github.com/eggjs/tegg/commit/e9c1180424df18c48c73615133bbf6ed2f930e7a)) # [3.68.0](https://github.com/eggjs/tegg/compare/v3.67.2...v3.68.0) (2025-12-29) ### Features * impl dns cache plugin ([#385](https://github.com/eggjs/tegg/issues/385)) ([d319448](https://github.com/eggjs/tegg/commit/d31944827c02cb967f4335b0ac66eeea4e10251c)) * impl iterator of moduleConfigs ([#386](https://github.com/eggjs/tegg/issues/386)) ([ee1f1a2](https://github.com/eggjs/tegg/commit/ee1f1a27a5a7de09bc6ee2c35376211541be409b)) ## [3.67.2](https://github.com/eggjs/tegg/compare/v3.67.1...v3.67.2) (2025-12-24) ### Bug Fixes * langchain version ([#384](https://github.com/eggjs/tegg/issues/384)) ([2bdb3b4](https://github.com/eggjs/tegg/commit/2bdb3b49a1891bdbd9bb24c30ca52295ef4833d4)) ## [3.67.1](https://github.com/eggjs/tegg/compare/v3.67.0...v3.67.1) (2025-12-23) ### Bug Fixes * zod v4 ([#381](https://github.com/eggjs/tegg/issues/381)) ([43614c8](https://github.com/eggjs/tegg/commit/43614c8734084a98b1a25c6e907c9c12ff41cb8f)) # [3.67.0](https://github.com/eggjs/tegg/compare/v3.66.0...v3.67.0) (2025-12-18) ### Features * **schedule:** module schedule unregister on module destroy ([#377](https://github.com/eggjs/tegg/issues/377)) ([098e889](https://github.com/eggjs/tegg/commit/098e889db4f9c1e227d2e8c6390b83e7596b3e69)) # [3.66.0](https://github.com/eggjs/tegg/compare/v3.65.3...v3.66.0) (2025-12-16) ### Features * use langchain for deepagents ([#376](https://github.com/eggjs/tegg/issues/376)) ([0af84c7](https://github.com/eggjs/tegg/commit/0af84c7b143cba9234dceb6675ea14004b8b3c9c)) ## [3.65.3](https://github.com/eggjs/tegg/compare/v3.65.2...v3.65.3) (2025-12-16) **Note:** Version bump only for package tegg ## [3.65.2](https://github.com/eggjs/tegg/compare/v3.65.1...v3.65.2) (2025-12-16) **Note:** Version bump only for package tegg ## [3.65.1](https://github.com/eggjs/tegg/compare/v3.65.0...v3.65.1) (2025-12-16) ### Bug Fixes * hono node v16 ([#374](https://github.com/eggjs/tegg/issues/374)) ([870b5e3](https://github.com/eggjs/tegg/commit/870b5e34f41399a44023756614b8bb5c59efc6ee)) # [3.65.0](https://github.com/eggjs/tegg/compare/v3.64.5...v3.65.0) (2025-12-10) ### Features * add parameterized query ([#366](https://github.com/eggjs/tegg/issues/366)) ([6d7d8d8](https://github.com/eggjs/tegg/commit/6d7d8d8383f4eea574d13e87ee03c57a33a319e7)) ## [3.64.5](https://github.com/eggjs/tegg/compare/v3.64.4...v3.64.5) (2025-12-08) ### Bug Fixes * langchain build bug ([#373](https://github.com/eggjs/tegg/issues/373)) ([3d32355](https://github.com/eggjs/tegg/commit/3d323550cefe950c1b0025296670ea33d7afc242)) ## [3.64.4](https://github.com/eggjs/tegg/compare/v3.64.3...v3.64.4) (2025-12-06) **Note:** Version bump only for package tegg ## [3.64.3](https://github.com/eggjs/tegg/compare/v3.64.2...v3.64.3) (2025-12-06) ### Bug Fixes * update trace logger check ([#372](https://github.com/eggjs/tegg/issues/372)) ([b974762](https://github.com/eggjs/tegg/commit/b974762dfccf1bb7b188c233be33014b6336bfee)) ## [3.64.2](https://github.com/eggjs/tegg/compare/v3.64.1...v3.64.2) (2025-11-27) **Note:** Version bump only for package tegg ## [3.64.1](https://github.com/eggjs/tegg/compare/v3.64.0...v3.64.1) (2025-11-27) **Note:** Version bump only for package tegg # [3.64.0](https://github.com/eggjs/tegg/compare/v3.63.2...v3.64.0) (2025-11-27) ### Bug Fixes * add ping config ([#364](https://github.com/eggjs/tegg/issues/364)) ([c2757fc](https://github.com/eggjs/tegg/commit/c2757fc568599f7da9a65b2b8fd2ebf3f4522f9d)) * mcp zod type and langchain test version ([#369](https://github.com/eggjs/tegg/issues/369)) ([8178168](https://github.com/eggjs/tegg/commit/81781685c392346d21c56b649bfe8bb7a99bc9fb)) ### Features * add langchain decorator ([#356](https://github.com/eggjs/tegg/issues/356)) ([b176c73](https://github.com/eggjs/tegg/commit/b176c7325009c372ce9d17f348b4fc1f1b6d7fb1)) ## [3.63.2](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.2) (2025-11-18) **Note:** Version bump only for package tegg ## [3.63.1](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.1) (2025-11-18) **Note:** Version bump only for package tegg # [3.63.0](https://github.com/eggjs/tegg/compare/v3.62.3...v3.63.0) (2025-11-18) ### Features * add MiddlewareGraphHook to handle controller middleware depende… ([#361](https://github.com/eggjs/tegg/issues/361)) ([7ab3eae](https://github.com/eggjs/tegg/commit/7ab3eae1af20e14101e1df63628a426cb5f6d3db)) ## [3.62.3](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.3) (2025-11-05) ### Bug Fixes * mcp args chinese ([61dad90](https://github.com/eggjs/tegg/commit/61dad903b2ba6140dd52a5b5600a36caef26373f)) * mcp chinese args ([#359](https://github.com/eggjs/tegg/issues/359)) ([b3c6ff1](https://github.com/eggjs/tegg/commit/b3c6ff178e1e3c76c34cedfe936d40475514708e)) * **typings:** fix getEggObjectFromName return type to Promise ([#358](https://github.com/eggjs/tegg/issues/358)) ([919b72b](https://github.com/eggjs/tegg/commit/919b72b48e7966226a5e970c7e297ee6c3d1f081)) ## [3.62.2](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.2) (2025-11-05) ### Bug Fixes * mcp args chinese ([61dad90](https://github.com/eggjs/tegg/commit/61dad903b2ba6140dd52a5b5600a36caef26373f)) * mcp chinese args ([#359](https://github.com/eggjs/tegg/issues/359)) ([b3c6ff1](https://github.com/eggjs/tegg/commit/b3c6ff178e1e3c76c34cedfe936d40475514708e)) * **typings:** fix getEggObjectFromName return type to Promise ([#358](https://github.com/eggjs/tegg/issues/358)) ([919b72b](https://github.com/eggjs/tegg/commit/919b72b48e7966226a5e970c7e297ee6c3d1f081)) ## [3.62.1](https://github.com/eggjs/tegg/compare/v3.62.0...v3.62.1) (2025-11-03) ### Bug Fixes * multi mcp client ([#357](https://github.com/eggjs/tegg/issues/357)) ([f9e4728](https://github.com/eggjs/tegg/commit/f9e47289160dffc5b47e93b60128a25ffd94fb4e)) ### Reverts * Revert "chore: add release scripts (#347)" ([f8bce5c](https://github.com/eggjs/tegg/commit/f8bce5cad484db185f6568bec25a352a9ea94bbe)), closes [#347](https://github.com/eggjs/tegg/issues/347) # [3.62.0](https://github.com/eggjs/tegg/compare/v3.61.0...v3.62.0) (2025-09-24) ### Bug Fixes * deduplicate modules reference ([#343](https://github.com/eggjs/tegg/issues/343)) ([aa5daf7](https://github.com/eggjs/tegg/commit/aa5daf7e8db49c8b273ba2102c127fd14a2de044)) ### Features * add mcp middleware hook ([#344](https://github.com/eggjs/tegg/issues/344)) ([7215645](https://github.com/eggjs/tegg/commit/72156452a2d69ff8f31b4fe76324dd4164761698)) # [3.61.0](https://github.com/eggjs/tegg/compare/v3.60.3...v3.61.0) (2025-08-15) ### Features * allow inject MysqlDataSourceManager ([#342](https://github.com/eggjs/tegg/issues/342)) ([d13b2d7](https://github.com/eggjs/tegg/commit/d13b2d7cd11dd36960647cb40bfc4bf92ce704fd)) ## [3.60.3](https://github.com/eggjs/tegg/compare/v3.60.2...v3.60.3) (2025-08-06) ### Bug Fixes * mcp middleware ([#340](https://github.com/eggjs/tegg/issues/340)) ([a47db22](https://github.com/eggjs/tegg/commit/a47db2295a899113aad46d7f4ca0857d91d44774)) ## [3.60.2](https://github.com/eggjs/tegg/compare/v3.60.1...v3.60.2) (2025-08-06) ### Bug Fixes * csrf type is bool bug ([#338](https://github.com/eggjs/tegg/issues/338)) ([67f69c9](https://github.com/eggjs/tegg/commit/67f69c90f8550f56d4bbf336986a0feabd1d192c)) ## [3.60.1](https://github.com/eggjs/tegg/compare/v3.60.0...v3.60.1) (2025-07-28) **Note:** Version bump only for package tegg # [3.60.0](https://github.com/eggjs/tegg/compare/v3.59.1...v3.60.0) (2025-07-28) ### Features * add multiple mcp server ([#337](https://github.com/eggjs/tegg/issues/337)) ([5b5e233](https://github.com/eggjs/tegg/commit/5b5e233510111b63bbcba14da1703becccebbd2f)) ## [3.59.1](https://github.com/eggjs/tegg/compare/v3.59.0...v3.59.1) (2025-07-16) ### Bug Fixes * concurrent init ([#336](https://github.com/eggjs/tegg/issues/336)) ([bfafeff](https://github.com/eggjs/tegg/commit/bfafeff99f1b7221de85df3890b55145a9fe2b35)) # [3.59.0](https://github.com/eggjs/tegg/compare/v3.58.0...v3.59.0) (2025-07-07) ### Features * add mcp global middleware ([#335](https://github.com/eggjs/tegg/issues/335)) ([7722102](https://github.com/eggjs/tegg/commit/772210298a937b7fbae9fd4fd1e1bc318b754cef)) # [3.58.0](https://github.com/eggjs/tegg/compare/v3.57.14...v3.58.0) (2025-07-01) ### Features * add timeout ([#334](https://github.com/eggjs/tegg/issues/334)) ([6d5d94b](https://github.com/eggjs/tegg/commit/6d5d94b6f319388a94b4adf4d427b95d2b851c17)) ## [3.57.14](https://github.com/eggjs/tegg/compare/v3.57.13...v3.57.14) (2025-06-18) **Note:** Version bump only for package tegg ## [3.57.13](https://github.com/eggjs/tegg/compare/v3.57.12...v3.57.13) (2025-06-18) ### Bug Fixes * add mcp clearInterval ([#332](https://github.com/eggjs/tegg/issues/332)) ([b29d68b](https://github.com/eggjs/tegg/commit/b29d68bd4ec4cdbef5b8246426fa0391208e8ded)) ## [3.57.12](https://github.com/eggjs/tegg/compare/v3.57.11...v3.57.12) (2025-06-16) **Note:** Version bump only for package tegg ## [3.57.11](https://github.com/eggjs/tegg/compare/v3.57.10...v3.57.11) (2025-06-16) ### Bug Fixes * mcp mem leak ([#329](https://github.com/eggjs/tegg/issues/329)) ([bddf8ed](https://github.com/eggjs/tegg/commit/bddf8ed45ea477f59c4d3b7d63bb81ca89484e56)) ## [3.57.10](https://github.com/eggjs/tegg/compare/v3.57.9...v3.57.10) (2025-06-12) ### Bug Fixes * muliti column primary generator code error ([#326](https://github.com/eggjs/tegg/issues/326)) ([7b8e1de](https://github.com/eggjs/tegg/commit/7b8e1de5b990574f7b907f3d7a3f68ecd54f8a86)) ## [3.57.9](https://github.com/eggjs/tegg/compare/v3.57.8...v3.57.9) (2025-05-29) **Note:** Version bump only for package tegg ## [3.57.8](https://github.com/eggjs/tegg/compare/v3.57.7...v3.57.8) (2025-05-29) **Note:** Version bump only for package tegg ## [3.57.7](https://github.com/eggjs/tegg/compare/v3.57.6...v3.57.7) (2025-05-28) ### Bug Fixes * the loading order issue in multi-module mode ([#324](https://github.com/eggjs/tegg/issues/324)) ([c9610bd](https://github.com/eggjs/tegg/commit/c9610bd53dee7bebd069bc6766e869cb2b2f9fc9)) ## [3.57.6](https://github.com/eggjs/tegg/compare/v3.57.5...v3.57.6) (2025-05-27) ### Bug Fixes * sse new ctx ([#323](https://github.com/eggjs/tegg/issues/323)) ([5716e0a](https://github.com/eggjs/tegg/commit/5716e0a33f8f58249ebdc1a3b6fb7959394ef4ee)) ## [3.57.5](https://github.com/eggjs/tegg/compare/v3.57.4...v3.57.5) (2025-05-15) ### Bug Fixes * zod phantom dependency ([#322](https://github.com/eggjs/tegg/issues/322)) ([e92372e](https://github.com/eggjs/tegg/commit/e92372eb884d0f5d8227d340a3d7db01b51267cf)) ## [3.57.4](https://github.com/eggjs/tegg/compare/v3.57.3...v3.57.4) (2025-05-14) **Note:** Version bump only for package tegg ## [3.57.3](https://github.com/eggjs/tegg/compare/v3.57.2...v3.57.3) (2025-05-13) ### Bug Fixes * update sessionIdGenerator ([#320](https://github.com/eggjs/tegg/issues/320)) ([ffc1e19](https://github.com/eggjs/tegg/commit/ffc1e19f69c30aa21a19c8f7ecc920fece21a947)) ## [3.57.2](https://github.com/eggjs/tegg/compare/v3.57.1...v3.57.2) (2025-05-13) ### Bug Fixes * stream mcp wait ([#319](https://github.com/eggjs/tegg/issues/319)) ([47ef28b](https://github.com/eggjs/tegg/commit/47ef28b1fae06c57857a7b340d0703403a907859)) ## [3.57.1](https://github.com/eggjs/tegg/compare/v3.57.0...v3.57.1) (2025-05-13) ### Bug Fixes * mcp context proto ([#318](https://github.com/eggjs/tegg/issues/318)) ([4d8e107](https://github.com/eggjs/tegg/commit/4d8e107fad4414da9593dd07ab2ae888dfd6a335)) # [3.57.0](https://github.com/eggjs/tegg/compare/v3.56.3...v3.57.0) (2025-05-09) ### Features * preserve SQL hint in minify function ([#314](https://github.com/eggjs/tegg/issues/314)) ([145bcf3](https://github.com/eggjs/tegg/commit/145bcf37bcd1ba86084cc304d15f0993abf0ebc8)) ## [3.56.3](https://github.com/eggjs/tegg/compare/v3.56.2...v3.56.3) (2025-05-07) ### Bug Fixes * dep ([#313](https://github.com/eggjs/tegg/issues/313)) ([791feea](https://github.com/eggjs/tegg/commit/791feead91ad48adaa2dee4c0746bea382e61d34)) * mcp teggCtxLifecycleMiddleware ([#312](https://github.com/eggjs/tegg/issues/312)) ([5304384](https://github.com/eggjs/tegg/commit/53043840c3aaab0e485db50b7a2d9362266eef8c)) ## [3.56.2](https://github.com/eggjs/tegg/compare/v3.56.1...v3.56.2) (2025-05-01) ### Bug Fixes * mcp version check ([#311](https://github.com/eggjs/tegg/issues/311)) ([7f16c9c](https://github.com/eggjs/tegg/commit/7f16c9c361cfec8b5ffd4075b23cff317cc5207a)) ## [3.56.1](https://github.com/eggjs/tegg/compare/v3.56.0...v3.56.1) (2025-04-29) ### Bug Fixes * mock init to ready ([#310](https://github.com/eggjs/tegg/issues/310)) ([2bd6fb6](https://github.com/eggjs/tegg/commit/2bd6fb6d5e60945637358140c8d669f78ea923f7)) # [3.56.0](https://github.com/eggjs/tegg/compare/v3.55.0...v3.56.0) (2025-04-29) ### Features * add mcp stateless ([#309](https://github.com/eggjs/tegg/issues/309)) ([b79d313](https://github.com/eggjs/tegg/commit/b79d313aa8f24adc91f88ea0732f2d98c0a8ead9)) # [3.55.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.55.0) (2025-04-29) ### Features * add mcp ([#307](https://github.com/eggjs/tegg/issues/307)) ([a9a57b4](https://github.com/eggjs/tegg/commit/a9a57b4d7102dd552e09d33c3f82fc15a245790a)) # [3.54.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.54.0) (2025-04-29) ### Features * add mcp ([#307](https://github.com/eggjs/tegg/issues/307)) ([a9a57b4](https://github.com/eggjs/tegg/commit/a9a57b4d7102dd552e09d33c3f82fc15a245790a)) # [3.53.0](https://github.com/eggjs/tegg/compare/v3.52.1...v3.53.0) (2025-04-21) ### Bug Fixes * stream end ([#302](https://github.com/eggjs/tegg/issues/302)) ([7f1f4b3](https://github.com/eggjs/tegg/commit/7f1f4b396294af5609c9454f6882d213dc237512)) ### Features * add timeout metadata for http controller ([#301](https://github.com/eggjs/tegg/issues/301)) ([68980c2](https://github.com/eggjs/tegg/commit/68980c23de81dbc9bd86c1d8df7b3952f52aa5ce)) ## [3.52.1](https://github.com/eggjs/tegg/compare/v3.52.0...v3.52.1) (2025-03-14) ### Bug Fixes * add qualifier check ([#295](https://github.com/eggjs/tegg/issues/295)) ([6744088](https://github.com/eggjs/tegg/commit/674408810d77fe0f4b95b25790bcb3975e543e26)) # [3.52.0](https://github.com/eggjs/tegg/compare/v3.51.2...v3.52.0) (2024-12-30) ### Features * dal retry when init failed ([#260](https://github.com/eggjs/tegg/issues/260)) ([74e7c06](https://github.com/eggjs/tegg/commit/74e7c067c3ff7ae0ed705abaaa8a91f804e487e3)) ## [3.51.2](https://github.com/eggjs/tegg/compare/v3.51.1...v3.51.2) (2024-12-09) ### Features * add mgw stream types ([#259](https://github.com/eggjs/tegg/issues/259)) ([1379d38](https://github.com/eggjs/tegg/commit/1379d382635c6bc575ce4acf3d3a7b5168487a3d)) ## [3.51.1](https://github.com/eggjs/tegg/compare/v3.51.0...v3.51.1) (2024-11-06) ### Bug Fixes * remove inner class hook ([#257](https://github.com/eggjs/tegg/issues/257)) ([faffd34](https://github.com/eggjs/tegg/commit/faffd3492f9edd411213034651d6863fb3f1a24d)) # [3.51.0](https://github.com/eggjs/tegg/compare/v3.50.1...v3.51.0) (2024-10-30) ### Features * add default inject init type qualifier ([#255](https://github.com/eggjs/tegg/issues/255)) ([538ae80](https://github.com/eggjs/tegg/commit/538ae8033ff102ac0b1d141c6495058a800e46f1)) * support optional inject ([#254](https://github.com/eggjs/tegg/issues/254)) ([260470b](https://github.com/eggjs/tegg/commit/260470b766d5fdb323c1bd72cc6260a90468a161)) ## [3.50.1](https://github.com/eggjs/tegg/compare/v3.50.0...v3.50.1) (2024-10-23) ### Bug Fixes * disable dump in preload ([#253](https://github.com/eggjs/tegg/issues/253)) ([081912b](https://github.com/eggjs/tegg/commit/081912beb9cb945c863c73d91ef5be112c2940d9)) # [3.50.0](https://github.com/eggjs/tegg/compare/v3.49.0...v3.50.0) (2024-10-22) ### Features * add dump switcher ([#252](https://github.com/eggjs/tegg/issues/252)) ([80c312f](https://github.com/eggjs/tegg/commit/80c312f7862b4021180f3e587f63c6b0dd87202c)) # [3.49.0](https://github.com/eggjs/tegg/compare/v3.48.1...v3.49.0) (2024-10-21) ### Features * expand register add loadUnit ([#251](https://github.com/eggjs/tegg/issues/251)) ([8a1649d](https://github.com/eggjs/tegg/commit/8a1649d5ea539d22c7cfd8881595247a07e3fbd7)) ## [3.48.1](https://github.com/eggjs/tegg/compare/v3.48.0...v3.48.1) (2024-10-14) ### Bug Fixes * fix merge qualifier ([#250](https://github.com/eggjs/tegg/issues/250)) ([d5a8a93](https://github.com/eggjs/tegg/commit/d5a8a93abad570f69881f9fa42f39d7b5cd436be)) # [3.48.0](https://github.com/eggjs/tegg/compare/v3.47.2...v3.48.0) (2024-10-10) ### Features * add rpc stream type ([#249](https://github.com/eggjs/tegg/issues/249)) ([7f3d40b](https://github.com/eggjs/tegg/commit/7f3d40b95d7939534f245b08d9d06a9b10bac350)) ## [3.47.2](https://github.com/eggjs/tegg/compare/v3.47.1...v3.47.2) (2024-10-10) **Note:** Version bump only for package tegg ## [3.47.1](https://github.com/eggjs/tegg/compare/v3.47.0...v3.47.1) (2024-10-10) ### Bug Fixes * fix aop in constructor inject type ([#247](https://github.com/eggjs/tegg/issues/247)) ([d169bb2](https://github.com/eggjs/tegg/commit/d169bb2fbbc86335315619866b4134a25296f552)) # [3.47.0](https://github.com/eggjs/tegg/compare/v3.46.4...v3.47.0) (2024-10-10) ### Features * export ProtoDescriptorHelper ([#245](https://github.com/eggjs/tegg/issues/245)) ([f01fb63](https://github.com/eggjs/tegg/commit/f01fb639b153a907fd9c951d4b1e40ba101b43d0)) * impl GlobalGraph build hook ([#246](https://github.com/eggjs/tegg/issues/246)) ([48fce45](https://github.com/eggjs/tegg/commit/48fce4512e99259ec26a9b032bfcc9f4046ad235)) ## [3.46.4](https://github.com/eggjs/tegg/compare/v3.46.3...v3.46.4) (2024-10-09) **Note:** Version bump only for package tegg ## [3.46.3](https://github.com/eggjs/tegg/compare/v3.46.2...v3.46.3) (2024-10-08) ### Bug Fixes * Prototype should not be inherited ([#243](https://github.com/eggjs/tegg/issues/243)) ([6e7017a](https://github.com/eggjs/tegg/commit/6e7017a48d395fba6525e0b31c848a257eb171ef)) ## [3.46.2](https://github.com/eggjs/tegg/compare/v3.46.1...v3.46.2) (2024-10-07) **Note:** Version bump only for package tegg ## [3.46.1](https://github.com/eggjs/tegg/compare/v3.46.0...v3.46.1) (2024-09-30) ### Bug Fixes * fix miss MultiInstance proper qualifiers ([#241](https://github.com/eggjs/tegg/issues/241)) ([15666d3](https://github.com/eggjs/tegg/commit/15666d36c18b99eccc4f1a11d8e7702503694ee1)) # [3.46.0](https://github.com/eggjs/tegg/compare/v3.45.0...v3.46.0) (2024-09-29) ### Features * impl MultiInstance inject MultiInstance ([#240](https://github.com/eggjs/tegg/issues/240)) ([08e3b0c](https://github.com/eggjs/tegg/commit/08e3b0cc02f3d2dbba767298a6aec6c00147f9ed)) # [3.45.0](https://github.com/eggjs/tegg/compare/v3.44.1...v3.45.0) (2024-09-29) ### Features * impl MultiInstanceInfo decorator ([#239](https://github.com/eggjs/tegg/issues/239)) ([70d4d95](https://github.com/eggjs/tegg/commit/70d4d95bca4a0c3e11d0d7cc4f292b1315e49e81)) ## [3.44.1](https://github.com/eggjs/tegg/compare/v3.44.0...v3.44.1) (2024-09-29) ### Bug Fixes * fix DataSourceQualifier ([#238](https://github.com/eggjs/tegg/issues/238)) ([7b1ebe7](https://github.com/eggjs/tegg/commit/7b1ebe718736d93e548f531bf99c5d2d38b41046)) # [3.44.0](https://github.com/eggjs/tegg/compare/v3.43.2...v3.44.0) (2024-09-29) ### Features * support inject in constructor ([#237](https://github.com/eggjs/tegg/issues/237)) ([e68b1ed](https://github.com/eggjs/tegg/commit/e68b1ed6a90432f1cb35a6f562914b7b04cb5114)) ## [3.43.2](https://github.com/eggjs/tegg/compare/v3.43.1...v3.43.2) (2024-09-14) ### Bug Fixes * add preload loadunit ([#236](https://github.com/eggjs/tegg/issues/236)) ([0e28972](https://github.com/eggjs/tegg/commit/0e2897200a9bc3bc6aa1028c8549bdbf45bbaaa3)) ## [3.43.1](https://github.com/eggjs/tegg/compare/v3.43.0...v3.43.1) (2024-09-14) **Note:** Version bump only for package tegg # [3.43.0](https://github.com/eggjs/tegg/compare/v3.42.0...v3.43.0) (2024-09-13) ### Features * add http cookies ([#235](https://github.com/eggjs/tegg/issues/235)) ([f46efa5](https://github.com/eggjs/tegg/commit/f46efa54b03bad41504bf76f6ed2baa8c48858ce)) # [3.42.0](https://github.com/eggjs/tegg/compare/v3.41.0...v3.42.0) (2024-09-10) ### Features * add LifecyclePreLoad ([#234](https://github.com/eggjs/tegg/issues/234)) ([2b72163](https://github.com/eggjs/tegg/commit/2b7216387f02cd045952447eaa21baa3a7ee04a3)) # [3.41.0](https://github.com/eggjs/tegg/compare/v3.40.1...v3.41.0) (2024-08-26) ### Features * export controller info util for get aop middleware ([#233](https://github.com/eggjs/tegg/issues/233)) ([1d3cca8](https://github.com/eggjs/tegg/commit/1d3cca8fad859ae54fb10c1700dda261e93055b3)) ## [3.40.1](https://github.com/eggjs/tegg/compare/v3.40.0...v3.40.1) (2024-08-23) ### Bug Fixes * use symbol.for instead of symbol ([#232](https://github.com/eggjs/tegg/issues/232)) ([4254ce5](https://github.com/eggjs/tegg/commit/4254ce558d22234f9dfff0ea9bc067075e21c654)) # [3.40.0](https://github.com/eggjs/tegg/compare/v3.39.5...v3.40.0) (2024-08-22) ### Features * @Middleware support Advice ([#231](https://github.com/eggjs/tegg/issues/231)) ([613a89d](https://github.com/eggjs/tegg/commit/613a89da7ea6dd70d50e34aa9f4152358a622625)) ## [3.39.5](https://github.com/eggjs/tegg/compare/v3.39.4...v3.39.5) (2024-08-09) ### Bug Fixes * generate index name with column name ([#230](https://github.com/eggjs/tegg/issues/230)) ([82ec72d](https://github.com/eggjs/tegg/commit/82ec72d4fb8628c847b32d0ddf23a95119ca6ccf)) ## [3.39.4](https://github.com/eggjs/tegg/compare/v3.39.3...v3.39.4) (2024-07-09) ### Bug Fixes * fix total type in paginate ([#228](https://github.com/eggjs/tegg/issues/228)) ([e57b91e](https://github.com/eggjs/tegg/commit/e57b91ee64e89487a3cc1663868d9b819e6e60c0)) ## [3.39.3](https://github.com/eggjs/tegg/compare/v3.39.2...v3.39.3) (2024-04-28) ### Bug Fixes * mount clazzExtension/clazzExtension/tableSql to BaseDao ([#220](https://github.com/eggjs/tegg/issues/220)) ([ac322cf](https://github.com/eggjs/tegg/commit/ac322cfc4100841a1483b04b99e04d553af323eb)) ## [3.39.2](https://github.com/eggjs/tegg/compare/v3.39.1...v3.39.2) (2024-04-28) ### Bug Fixes * use loader to load TableClazzList ([#219](https://github.com/eggjs/tegg/issues/219)) ([15ef977](https://github.com/eggjs/tegg/commit/15ef977806dcb15831d6e906b92134257dd03654)) ## [3.39.1](https://github.com/eggjs/tegg/compare/v3.39.0...v3.39.1) (2024-04-28) ### Bug Fixes * not overwrite extension file ([#218](https://github.com/eggjs/tegg/issues/218)) ([f915f08](https://github.com/eggjs/tegg/commit/f915f08dc61b01f10400ad844496f5b227f377eb)) # [3.39.0](https://github.com/eggjs/tegg/compare/v3.38.0...v3.39.0) (2024-04-19) ### Bug Fixes * not overwrite dao file ([#215](https://github.com/eggjs/tegg/issues/215)) ([0856bf1](https://github.com/eggjs/tegg/commit/0856bf189b160c7209bc24cf7eb911ec2f5875d1)) ### Features * use app.loader.getTypeFiles to generate module config file names ([#213](https://github.com/eggjs/tegg/issues/213)) ([e0656a4](https://github.com/eggjs/tegg/commit/e0656a4d59beef103a5627461d9b9c87996928e3)) # [3.38.0](https://github.com/eggjs/tegg/compare/v3.37.3...v3.38.0) (2024-04-18) ### Features * impl dal transaction ([#214](https://github.com/eggjs/tegg/issues/214)) ([b8b67dd](https://github.com/eggjs/tegg/commit/b8b67dd7e0fb282d78de7e68e68834ff79d30732)) ## [3.37.3](https://github.com/eggjs/tegg/compare/v3.37.2...v3.37.3) (2024-04-17) ### Bug Fixes * tegg-types publish ([#212](https://github.com/eggjs/tegg/issues/212)) ([98a4188](https://github.com/eggjs/tegg/commit/98a4188be2a307722c3df0c82a7af0d0fef685fd)) ## [3.37.2](https://github.com/eggjs/tegg/compare/v3.37.1...v3.37.2) (2024-04-17) ### Bug Fixes * always get extension from Module._extensions ([#211](https://github.com/eggjs/tegg/issues/211)) ([62e9c06](https://github.com/eggjs/tegg/commit/62e9c06f3cbde28d17d0e43797d4080279d7b9fa)) ## [3.37.1](https://github.com/eggjs/tegg/compare/v3.37.0...v3.37.1) (2024-04-16) ### Bug Fixes * fix dal runtime dep ([#210](https://github.com/eggjs/tegg/issues/210)) ([5ad7f45](https://github.com/eggjs/tegg/commit/5ad7f4537114217924ae8dc7445e8fc77eee0b5a)) # [3.37.0](https://github.com/eggjs/tegg/compare/v3.36.3...v3.37.0) (2024-04-16) ### Bug Fixes * @eggjs/dal-runtime deps ([#209](https://github.com/eggjs/tegg/issues/209)) ([ffc8fdf](https://github.com/eggjs/tegg/commit/ffc8fdf342e7ea73400d6e31f82981155c2b0693)) ### Features * add HTTPHeaders decorator ([#208](https://github.com/eggjs/tegg/issues/208)) ([4678c45](https://github.com/eggjs/tegg/commit/4678c450d8b3c632bbdbe2b49b9c02e99f16733c)) ## [3.36.3](https://github.com/eggjs/tegg/compare/v3.36.2...v3.36.3) (2024-04-10) ### Bug Fixes * fix custom sql extension ([#207](https://github.com/eggjs/tegg/issues/207)) ([a405233](https://github.com/eggjs/tegg/commit/a405233d11323cd8a51c6197e855f0c3ff98337d)) ## [3.36.2](https://github.com/eggjs/tegg/compare/v3.36.1...v3.36.2) (2024-04-08) ### Bug Fixes * fix dao extension in prod ([#206](https://github.com/eggjs/tegg/issues/206)) ([0498e9d](https://github.com/eggjs/tegg/commit/0498e9d11bd9e4d186160e8b6af07e627dde6a20)) ## [3.36.1](https://github.com/eggjs/tegg/compare/v3.36.0...v3.36.1) (2024-04-07) ### Bug Fixes * use @eggjs/ajv-keywords and @eggjs/ajv-formats ([#204](https://github.com/eggjs/tegg/issues/204)) ([31b02a0](https://github.com/eggjs/tegg/commit/31b02a08dac8bf27212fdb213a7d93b5b3a685ba)) # [3.36.0](https://github.com/eggjs/tegg/compare/v3.35.1...v3.36.0) (2024-04-02) ### Features * impl ajv + typebox Validator ([#201](https://github.com/eggjs/tegg/issues/201)) ([9fd585d](https://github.com/eggjs/tegg/commit/9fd585de9b613466c96b73494a08a494db34ea57)) * impl dal forkDb ([#202](https://github.com/eggjs/tegg/issues/202)) ([a411f04](https://github.com/eggjs/tegg/commit/a411f04e074425419b5b348a362f120bf8189541)) * impl Date/timestamp on update ([#203](https://github.com/eggjs/tegg/issues/203)) ([e5c7b8d](https://github.com/eggjs/tegg/commit/e5c7b8d529f2854b77de2e99369c781a4ea9e070)) ## [3.35.1](https://github.com/eggjs/tegg/compare/v3.35.0...v3.35.1) (2024-03-26) ### Bug Fixes * fix dal templates build ([#199](https://github.com/eggjs/tegg/issues/199)) ([17afe8c](https://github.com/eggjs/tegg/commit/17afe8c98929c7613739e32e897e881619bbdb2a)) # [3.35.0](https://github.com/eggjs/tegg/compare/v3.34.0...v3.35.0) (2024-03-26) ### Features * dal-runtime templates support pkg alias ([#198](https://github.com/eggjs/tegg/issues/198)) ([cecef78](https://github.com/eggjs/tegg/commit/cecef781bd134b629fc835063a351460aceb340c)) # [3.34.0](https://github.com/eggjs/tegg/compare/v3.33.1...v3.34.0) (2024-03-22) ### Features * impl dal for standalone tegg ([#197](https://github.com/eggjs/tegg/issues/197)) ([56b259d](https://github.com/eggjs/tegg/commit/56b259d7215a9d9542b36e421996623819369846)) ## [3.33.1](https://github.com/eggjs/tegg/compare/v3.33.0...v3.33.1) (2024-03-22) ### Bug Fixes * add dal templates ([#196](https://github.com/eggjs/tegg/issues/196)) ([49ba4f9](https://github.com/eggjs/tegg/commit/49ba4f9db3d9313654674f813c0358dc0774fd10)) # [3.33.0](https://github.com/eggjs/tegg/compare/v3.32.0...v3.33.0) (2024-03-22) ### Bug Fixes * set column canNull default to false ([#195](https://github.com/eggjs/tegg/issues/195)) ([24628ec](https://github.com/eggjs/tegg/commit/24628ec5a3cd167dc44a50017450d0dedec2c9ce)) ### Features * impl dal ([#192](https://github.com/eggjs/tegg/issues/192)) ([1c7d145](https://github.com/eggjs/tegg/commit/1c7d1454bc8c600cd58c3ec7b9cda4e8a98c7287)) # [3.32.0](https://github.com/eggjs/tegg/compare/v3.31.0...v3.32.0) (2024-02-19) ### Bug Fixes * ignore duplicated module ([#191](https://github.com/eggjs/tegg/issues/191)) ([263467f](https://github.com/eggjs/tegg/commit/263467fc43a25eb5a1670de4778de127662a201b)) ### Features * set plugin module optional false if be enabled as a plugin ([#190](https://github.com/eggjs/tegg/issues/190)) ([57a1adc](https://github.com/eggjs/tegg/commit/57a1adcd4f0305cad690be229c59e103e7acf5cd)) # [3.31.0](https://github.com/eggjs/tegg/compare/v3.30.1...v3.31.0) (2024-01-31) ### Features * add getEggObjects API to fetch all instances ([#189](https://github.com/eggjs/tegg/issues/189)) ([f8592c2](https://github.com/eggjs/tegg/commit/f8592c2cd141d01b4f1730b1e3d66e35c3e1ce05)) ## [3.30.1](https://github.com/eggjs/tegg/compare/v3.30.0...v3.30.1) (2024-01-25) ### Bug Fixes * fix modify ctx.args in aop beforeCall not work ([#187](https://github.com/eggjs/tegg/issues/187)) ([7656424](https://github.com/eggjs/tegg/commit/765642414387c8a9940525cd3c519fcb5fd694a0)) # [3.30.0](https://github.com/eggjs/tegg/compare/v3.29.0...v3.30.0) (2024-01-17) ### Bug Fixes * config for env is not merged when default config is empty ([#178](https://github.com/eggjs/tegg/issues/178)) ([9c1de22](https://github.com/eggjs/tegg/commit/9c1de223e9c9befb0a803ac5a1bd843f74aa9493)) ### Features * scan framework dependencies as optional module ([#184](https://github.com/eggjs/tegg/issues/184)) ([a4908c6](https://github.com/eggjs/tegg/commit/a4908c6c640000c7068def57d32052cca15adf47)) # [3.29.0](https://github.com/eggjs/tegg/compare/v3.28.2...v3.29.0) (2023-12-26) ### Features * allow a handler to subscribe to multiple events ([#179](https://github.com/eggjs/tegg/issues/179)) ([1d460a5](https://github.com/eggjs/tegg/commit/1d460a5a6bdcf9a3d61b13d3527633c8b990a38c)) ## [3.28.2](https://github.com/eggjs/tegg/compare/v3.28.1...v3.28.2) (2023-12-12) ### Bug Fixes * clear all hooks after app close ([#175](https://github.com/eggjs/tegg/issues/175)) ([6fe12b9](https://github.com/eggjs/tegg/commit/6fe12b9bd2cc1c250d02ac851a6e2e172ab12514)) ## [3.28.1](https://github.com/eggjs/tegg/compare/v3.28.0...v3.28.1) (2023-12-11) **Note:** Version bump only for package tegg # [3.28.0](https://github.com/eggjs/tegg/compare/v3.27.0...v3.28.0) (2023-12-10) ### Features * inject moduleConfig read from tegg-config app.moduleConfigs config ([#169](https://github.com/eggjs/tegg/issues/169)) ([2d984ef](https://github.com/eggjs/tegg/commit/2d984efad0806b333aa2ea30daac2df859967750)) # [3.27.0](https://github.com/eggjs/tegg/compare/v3.26.0...v3.27.0) (2023-11-23) ### Features * impl getObjectFromName ([#167](https://github.com/eggjs/tegg/issues/167)) ([95843c7](https://github.com/eggjs/tegg/commit/95843c74c201ecdfeb7023e16e3f8348a1cb32ea)) # [3.26.0](https://github.com/eggjs/tegg/compare/v3.25.2...v3.26.0) (2023-11-17) ### Features * Add ControllerType = SCHEDULE ([#166](https://github.com/eggjs/tegg/issues/166)) ([2c22e7d](https://github.com/eggjs/tegg/commit/2c22e7d4943659848ddbae7b552febef38b57a3d)) ## [3.25.2](https://github.com/eggjs/tegg/compare/v3.25.1...v3.25.2) (2023-11-06) ### Bug Fixes * verify isEggMultiInstancePrototype before proto exists ([#164](https://github.com/eggjs/tegg/issues/164)) ([db9a621](https://github.com/eggjs/tegg/commit/db9a62159886829de36b831f49f296fe05f0b228)) ## [3.25.1](https://github.com/eggjs/tegg/compare/v3.25.0...v3.25.1) (2023-11-03) ### Bug Fixes * fix standalone import ConfigSource ([#163](https://github.com/eggjs/tegg/issues/163)) ([6922071](https://github.com/eggjs/tegg/commit/6922071219413a8a11387be3d05f0e3970ce4f48)) # [3.25.0](https://github.com/eggjs/tegg/compare/v3.24.0...v3.25.0) (2023-11-03) ### Features * getObject support MultiInstanceProto ([#161](https://github.com/eggjs/tegg/issues/161)) ([1a24e48](https://github.com/eggjs/tegg/commit/1a24e48cd9a38e906966a21c5f0d1304c4b40d7c)) * tegg plugin support ModuleConfig ([#162](https://github.com/eggjs/tegg/issues/162)) ([58bd9fa](https://github.com/eggjs/tegg/commit/58bd9fafdd0d56aabdde5f7c33f17c45568bada8)) # [3.24.0](https://github.com/eggjs/tegg/compare/v3.23.0...v3.24.0) (2023-10-26) ### Features * support Request decorators for HTTPController ([#159](https://github.com/eggjs/tegg/issues/159)) ([945e1eb](https://github.com/eggjs/tegg/commit/945e1eb18237f40879acdd2e43cd53dd2e8272a9)) # [3.23.0](https://github.com/eggjs/tegg/compare/v3.22.0...v3.23.0) (2023-09-20) ### Bug Fixes * typo `acL` to `acl` ([#156](https://github.com/eggjs/tegg/issues/156)) ([a775d34](https://github.com/eggjs/tegg/commit/a775d34d38c481c5f9e90504224553d31ad728d3)) ### Features * add className property to EggPrototypeInfo ([#158](https://github.com/eggjs/tegg/issues/158)) ([bddac97](https://github.com/eggjs/tegg/commit/bddac97a9f575c9f13b794246a7e8346c58d1a09)) # [3.20.0](https://github.com/eggjs/tegg/compare/v3.19.0...v3.20.0) (2023-09-07) ### Features * support load module config with env ([#151](https://github.com/eggjs/tegg/issues/151)) ([c087226](https://github.com/eggjs/tegg/commit/c087226bd7764242fadce5622fccd9e9fee56322)) # [3.19.0](https://github.com/eggjs/tegg/compare/v3.18.1...v3.19.0) (2023-08-31) ### Features * add LoadUnitMultiInstanceProtoHook for tegg plugin ([#150](https://github.com/eggjs/tegg/issues/150)) ([b938580](https://github.com/eggjs/tegg/commit/b9385803383dceedfc26bd990e5d752cd33f0f67)) ## [3.18.1](https://github.com/eggjs/tegg/compare/v3.18.0...v3.18.1) (2023-08-29) ### Bug Fixes * fix use MultiInstanceProto from other modules ([#147](https://github.com/eggjs/tegg/issues/147)) ([b71af60](https://github.com/eggjs/tegg/commit/b71af60ce6d1da0d778f5e712633b8c15052bd70)) # [3.18.0](https://github.com/eggjs/tegg/compare/v3.17.0...v3.18.0) (2023-08-29) ### Features * add aop runtime/dynamic inject runtime for standalone ([#149](https://github.com/eggjs/tegg/issues/149)) ([6091fc6](https://github.com/eggjs/tegg/commit/6091fc6be885976d72a6920d37ec685927b63d5d)) * add helper to get EggObject from class ([#148](https://github.com/eggjs/tegg/issues/148)) ([77eaf38](https://github.com/eggjs/tegg/commit/77eaf38383ad974b30d13f4c30c489fb7fa7274d)) # [3.17.0](https://github.com/eggjs/tegg/compare/v3.16.0...v3.17.0) (2023-08-24) ### Features * impl MultiInstanceProto ([#145](https://github.com/eggjs/tegg/issues/145)) ([12fd5cf](https://github.com/eggjs/tegg/commit/12fd5cff4004578bcc737dcdf4f7e9d1159f5633)) # [3.16.0](https://github.com/eggjs/tegg/compare/v3.15.0...v3.16.0) (2023-08-24) ### Features * export EggModuleLoader in standalone ([#146](https://github.com/eggjs/tegg/issues/146)) ([9d1da9a](https://github.com/eggjs/tegg/commit/9d1da9a87dbd486930adc50cd43020c2fb478230)) * implement RuntimeConfig ([#144](https://github.com/eggjs/tegg/issues/144)) ([0862655](https://github.com/eggjs/tegg/commit/0862655846f6765349d406ee697c036cec2a37bd)) ## [3.14.3](https://github.com/eggjs/tegg/compare/v3.14.2...v3.14.3) (2023-08-14) ### Bug Fixes * fix aop plugin files ([#140](https://github.com/eggjs/tegg/issues/140)) ([f47eef6](https://github.com/eggjs/tegg/commit/f47eef634efd442ac5a8f68567e36c940247e48b)) ## [3.14.2](https://github.com/eggjs/tegg/compare/v3.14.1...v3.14.2) (2023-08-14) ### Bug Fixes * init all context advice if root proto miss ([#139](https://github.com/eggjs/tegg/issues/139)) ([0602ea8](https://github.com/eggjs/tegg/commit/0602ea81578bf717ee4b4c490ace8c1c133478c5)) ## [3.14.1](https://github.com/eggjs/tegg/compare/v3.14.0...v3.14.1) (2023-08-11) ### Bug Fixes * ensure ContextInitiator be called after ctx ready ([#138](https://github.com/eggjs/tegg/issues/138)) ([79e16da](https://github.com/eggjs/tegg/commit/79e16dae913b6114ac8d13bde8de60164d57dab3)) # [3.14.0](https://github.com/eggjs/tegg/compare/v3.13.0...v3.14.0) (2023-07-31) ### Features * impl ModuleConfigs for standalone ([#136](https://github.com/eggjs/tegg/issues/136)) ([7227492](https://github.com/eggjs/tegg/commit/7227492295b9c84e3660bfc006ca96e7a9652a25)) # [3.13.0](https://github.com/eggjs/tegg/compare/v3.12.0...v3.13.0) (2023-07-25) ### Features * 事务注解增加数据源选项 ([#135](https://github.com/eggjs/tegg/issues/135)) ([c33b3b5](https://github.com/eggjs/tegg/commit/c33b3b5ec9d32a8c6675d986013042f0cb8e4370)) # [3.12.0](https://github.com/eggjs/tegg/compare/v3.11.1...v3.12.0) (2023-07-13) ### Bug Fixes * after call mockModuleContext, hasMockModuleContext should be true ([#134](https://github.com/eggjs/tegg/issues/134)) ([88b3caa](https://github.com/eggjs/tegg/commit/88b3caadd24f08221b8098c42733e26376338cae)) ## [3.11.1](https://github.com/eggjs/tegg/compare/v3.11.0...v3.11.1) (2023-06-29) ### Bug Fixes * export StandaloneInnerObject ([#131](https://github.com/eggjs/tegg/issues/131)) ([e4b87e0](https://github.com/eggjs/tegg/commit/e4b87e0a48e3232adaf43bad75f44d0ae775c984)) # [3.11.0](https://github.com/eggjs/tegg/compare/v3.10.0...v3.11.0) (2023-06-29) ### Features * export transaction decorator from tegg ([8be0521](https://github.com/eggjs/tegg/commit/8be05212b62fe7f111688efaec935be64d623918)) * impl transaction decorator ([#124](https://github.com/eggjs/tegg/issues/124)) ([4896615](https://github.com/eggjs/tegg/commit/4896615af951bbff940cda7abc116df40ed486e5)) # [3.10.0](https://github.com/eggjs/tegg/compare/v3.9.0...v3.10.0) (2023-06-28) ### Bug Fixes * use posix join for package path ([#127](https://github.com/eggjs/tegg/issues/127)) ([53672f4](https://github.com/eggjs/tegg/commit/53672f404edb72c7330e125f72dd356cde0607ad)) ### Features * standalone Runner run support ctx ([#126](https://github.com/eggjs/tegg/issues/126)) ([0788c7d](https://github.com/eggjs/tegg/commit/0788c7dfb57f96c55e94cc6692c0b6e9ac1ee03c)) # [3.9.0](https://github.com/eggjs/tegg/compare/v3.8.0...v3.9.0) (2023-06-20) ### Features * implement advice params ([76ec8ad](https://github.com/eggjs/tegg/commit/76ec8ad7b7170a637e59d74d49c1f00d8a201321)) # [3.8.0](https://github.com/eggjs/tegg/compare/v3.7.0...v3.8.0) (2023-05-30) ### Features * impl EggObjectLifecycle hook in decorator ([#119](https://github.com/eggjs/tegg/issues/119)) ([cced8a2](https://github.com/eggjs/tegg/commit/cced8a2e009c33d5040fa21d00409fddef471b0e)) # [3.7.0](https://github.com/eggjs/tegg/compare/v3.6.3...v3.7.0) (2023-04-03) ### Bug Fixes * don't check eventbus plugin name ([#113](https://github.com/eggjs/tegg/issues/113)) ([2a94a57](https://github.com/eggjs/tegg/commit/2a94a57c58e4fd971400966c15597aace4bb1ecc)) ### Features * The exposed module reads the options. ([#112](https://github.com/eggjs/tegg/issues/112)) ([a52b44b](https://github.com/eggjs/tegg/commit/a52b44b753463bfdef6fbbc39f920be8eccf1567)) ## [3.6.3](https://github.com/eggjs/tegg/compare/v3.6.2...v3.6.3) (2023-03-02) ### Bug Fixes * fix contextEggObjectGetProperty conflict ([#105](https://github.com/eggjs/tegg/issues/105)) ([c570315](https://github.com/eggjs/tegg/commit/c570315ece6ef7443ecf3df2b45aa8c934a5aa38)) ## [3.6.2](https://github.com/eggjs/tegg/compare/v3.6.1...v3.6.2) (2023-02-16) ### Bug Fixes * should not cache ctx object ([#103](https://github.com/eggjs/tegg/issues/103)) ([be54083](https://github.com/eggjs/tegg/commit/be5408375261d98b60fbc97e18de9232581a9547)) ## [3.6.1](https://github.com/eggjs/tegg/compare/v3.6.0...v3.6.1) (2023-02-14) ### Bug Fixes * get app/ctx properties when load unit beforeCreate ([#102](https://github.com/eggjs/tegg/issues/102)) ([76ef679](https://github.com/eggjs/tegg/commit/76ef679d745deb235db9dcc3fa34984b511bd5c6)) # [3.6.0](https://github.com/eggjs/tegg/compare/v3.5.2...v3.6.0) (2023-02-13) ### Bug Fixes * egg qualifier should register after all file loaded ([#100](https://github.com/eggjs/tegg/issues/100)) ([5033b51](https://github.com/eggjs/tegg/commit/5033b51796b8a3329bd79884a8d8f18226193a1b)) ### Features * add backgroundTask.timeout config ([#101](https://github.com/eggjs/tegg/issues/101)) ([0b1eee0](https://github.com/eggjs/tegg/commit/0b1eee00d6feb9c6d4509023dffe85c0ada749c2)) ## [3.5.2](https://github.com/eggjs/tegg/compare/v3.5.1...v3.5.2) (2023-02-10) ### Bug Fixes * eventbus cork should support reentry ([#98](https://github.com/eggjs/tegg/issues/98)) ([077044c](https://github.com/eggjs/tegg/commit/077044c040f8423572605eb2980e3cc6da8c038e)) * not create ctx logger proto ([#97](https://github.com/eggjs/tegg/issues/97)) ([100886b](https://github.com/eggjs/tegg/commit/100886ba90bdc7cccd07fa2f390defb5b0c53e22)) ## [3.5.1](https://github.com/eggjs/tegg/compare/v3.5.0...v3.5.1) (2023-02-10) ### Bug Fixes * remove useless init singleton proto ([#96](https://github.com/eggjs/tegg/issues/96)) ([097ac58](https://github.com/eggjs/tegg/commit/097ac58c675d43088c8785a12cf224b5d6adea17)) # [3.5.0](https://github.com/eggjs/tegg/compare/v3.4.1...v3.5.0) (2023-02-10) ### Bug Fixes * loader should not deps metadata ([#94](https://github.com/eggjs/tegg/issues/94)) ([ff57de4](https://github.com/eggjs/tegg/commit/ff57de4f3e0d0dc33d77d05a887242fcb4c32024)) ### Features * append call stack for runInBackground ([#91](https://github.com/eggjs/tegg/issues/91)) ([ec7bc2c](https://github.com/eggjs/tegg/commit/ec7bc2c60ffb49b4a51feec82e391b1f6a88549a)) * remove context egg object factory ([#93](https://github.com/eggjs/tegg/issues/93)) ([e14bdb2](https://github.com/eggjs/tegg/commit/e14bdb257eaebc0b0a4c37c6073a5c3237718718)) * use SingletonProto for egg ctx object ([#92](https://github.com/eggjs/tegg/issues/92)) ([3385d57](https://github.com/eggjs/tegg/commit/3385d571b076d3148978f252188f29d9cf2c6781)) ## [3.4.1](https://github.com/eggjs/tegg/compare/v3.4.0...v3.4.1) (2023-02-02) ### Bug Fixes * BackgroundTaskHelper should support recursively call ([#90](https://github.com/eggjs/tegg/issues/90)) ([368ac03](https://github.com/eggjs/tegg/commit/368ac0343d0d4e96b3768e7fd169b721551d0e4b)) # [3.4.0](https://github.com/eggjs/tegg/compare/v3.3.4...v3.4.0) (2023-02-01) ### Features * use singleton model insteadof context ([#89](https://github.com/eggjs/tegg/issues/89)) ([cfdfc05](https://github.com/eggjs/tegg/commit/cfdfc05f13048806274de1a35b1207c073a8519d)) ## [3.3.4](https://github.com/eggjs/tegg/compare/v3.3.3...v3.3.4) (2023-01-29) ### Bug Fixes * should not notify backgroundTaskHelper if teggContext not exists ([#88](https://github.com/eggjs/tegg/issues/88)) ([4cab68b](https://github.com/eggjs/tegg/commit/4cab68bfc08a3786bde9a67cd8687f152829d9a0)) ## [3.3.3](https://github.com/eggjs/tegg/compare/v3.3.2...v3.3.3) (2023-01-29) ### Bug Fixes * wait egg background task done before destroy tegg ctx ([#87](https://github.com/eggjs/tegg/issues/87)) ([deea4d8](https://github.com/eggjs/tegg/commit/deea4d8d75c43347c6ee09e0e97f5fa80dd68dd9)) ## [3.3.2](https://github.com/eggjs/tegg/compare/v3.3.1...v3.3.2) (2023-01-29) ### Bug Fixes * beginModuleScope should be reentrant ([#86](https://github.com/eggjs/tegg/issues/86)) ([648aeaf](https://github.com/eggjs/tegg/commit/648aeaf1f20ff5bc217bf6f16fac9d9181eb8447)) ## [3.3.1](https://github.com/eggjs/tegg/compare/v3.3.0...v3.3.1) (2023-01-28) ### Bug Fixes * inject property should be configurable ([#85](https://github.com/eggjs/tegg/issues/85)) ([c13ab55](https://github.com/eggjs/tegg/commit/c13ab55d7b483a5c4a6e4293a6095aa98d070a8b)) # [3.3.0](https://github.com/eggjs/tegg/compare/v3.2.4...v3.3.0) (2023-01-28) ### Bug Fixes * router type ([#83](https://github.com/eggjs/tegg/issues/83)) ([b32d9b8](https://github.com/eggjs/tegg/commit/b32d9b8e94552d27dc0249c9f38e7223b24beff0)) ### Features * add app.eggContextHandler ([#84](https://github.com/eggjs/tegg/issues/84)) ([2772624](https://github.com/eggjs/tegg/commit/277262418143956b2e75bd1db5f2e7dd9b75eb8b)) * export singleton orm client ([#82](https://github.com/eggjs/tegg/issues/82)) ([5320af7](https://github.com/eggjs/tegg/commit/5320af77d7e7c5c73b80560a576f2ce01fc21fff)) ## [3.2.3](https://github.com/eggjs/tegg/compare/v3.2.2...v3.2.3) (2023-01-16) ### Bug Fixes * cork/uncork should can be called multi times in same ctx ([#78](https://github.com/eggjs/tegg/issues/78)) ([269cda6](https://github.com/eggjs/tegg/commit/269cda6327122111c230e6f69abb525ce4ab5be1)) ## [3.2.2](https://github.com/eggjs/tegg/compare/v3.2.1...v3.2.2) (2023-01-06) **Note:** Version bump only for package tegg ## [3.2.1](https://github.com/eggjs/tegg/compare/v3.2.0...v3.2.1) (2022-12-28) ### Bug Fixes * fix nest inject ctx obj to singleton obj ([#74](https://github.com/eggjs/tegg/issues/74)) ([e4b6252](https://github.com/eggjs/tegg/commit/e4b6252aa79925e16185e568bf7b220f367253ab)) # [3.2.0](https://github.com/eggjs/tegg/compare/v3.1.0...v3.2.0) (2022-12-28) ### Features * impl mockModuleContextScope ([#73](https://github.com/eggjs/tegg/issues/73)) ([041881c](https://github.com/eggjs/tegg/commit/041881ca317ad81366172a35ac56b7b2dc0a0488)) # [3.1.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.1.0) (2022-12-27) ### Bug Fixes * add 'globby' as dependencies ([#71](https://github.com/eggjs/tegg/issues/71)) ([76d85d9](https://github.com/eggjs/tegg/commit/76d85d9948527028f926ae0ff5a61111eb1cbd04)) * eventbus runtime should wait all handlers done ([#51](https://github.com/eggjs/tegg/issues/51)) ([0651d30](https://github.com/eggjs/tegg/commit/0651d300f9a18bd97299548f3ebccad1d0382d28)) * fix events type from any to keyof Events ([#54](https://github.com/eggjs/tegg/issues/54)) ([a2551b2](https://github.com/eggjs/tegg/commit/a2551b2d9f9eabf9ed5c87f83489615eefa3e6d1)) * fix file path for advice decorator ([#64](https://github.com/eggjs/tegg/issues/64)) ([d6aa091](https://github.com/eggjs/tegg/commit/d6aa091851b5d1ca63e7e56e081df4d15ab3284e)) * fix miss agent file ([0fa496b](https://github.com/eggjs/tegg/commit/0fa496bdbb4ffa4e911fffa3e176fa7bdf03fb12)) * fix miss agent file ([#56](https://github.com/eggjs/tegg/issues/56)) ([cfb4dcc](https://github.com/eggjs/tegg/commit/cfb4dcc006ee1253733c7122f885a05da94f80b5)) * fix mock prototype in aop not work ([#66](https://github.com/eggjs/tegg/issues/66)) ([16640eb](https://github.com/eggjs/tegg/commit/16640eb751405532b2a1241b17624ce3ac2d1c7a)) * fix rootProtoManager.registerRootProto ([f416ed7](https://github.com/eggjs/tegg/commit/f416ed70af1c46d31ebf712b208205d67337d958)) * fix schedule import ([1fb5481](https://github.com/eggjs/tegg/commit/1fb54816fb3240c641824c2bc2b464c35652b655)) * inject context proto to singleton proto ([#72](https://github.com/eggjs/tegg/issues/72)) ([fcc0b2b](https://github.com/eggjs/tegg/commit/fcc0b2b48fc9bce580c1f2bcfcc38039ae909951)) * none exist node_modules path ([#59](https://github.com/eggjs/tegg/issues/59)) ([77cb068](https://github.com/eggjs/tegg/commit/77cb0687ba8e5d9f20a6df0548de9d55a8771c21)) * optimize backgroud output ([#47](https://github.com/eggjs/tegg/issues/47)) ([6d978c5](https://github.com/eggjs/tegg/commit/6d978c5d7c339c78a90b00d2c2622f0be85ab3ce)) * skip file not exits ([#62](https://github.com/eggjs/tegg/issues/62)) ([10e56d4](https://github.com/eggjs/tegg/commit/10e56d418f359efa5d8909541768082cf068d2a4)) * use getMetaData for ModelMetadataUtil ([#44](https://github.com/eggjs/tegg/issues/44)) ([87a306c](https://github.com/eggjs/tegg/commit/87a306c4fba51fd519a47c0caaa79442643ea107)) * use require.resolve instead path.join to resolve dependencies path ([#63](https://github.com/eggjs/tegg/issues/63)) ([d7f3beb](https://github.com/eggjs/tegg/commit/d7f3beb27a22b95bb54589c5988a68ce2484c089)) ### Features * add new module scan mode ([#58](https://github.com/eggjs/tegg/issues/58)) ([3be6c20](https://github.com/eggjs/tegg/commit/3be6c2047a0241a482aafd0aaa072f51f861b6ea)) * **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) * impl Host decorator ([#48](https://github.com/eggjs/tegg/issues/48)) ([65dc7a8](https://github.com/eggjs/tegg/commit/65dc7a899ba72dd0851c35046562766d7f2b71b6)) * impl Inject Model ([#43](https://github.com/eggjs/tegg/issues/43)) ([ced2ce2](https://github.com/eggjs/tegg/commit/ced2ce2134964dcb410410c0192a34f77507c42d)) * impl Schedule decorator ([#52](https://github.com/eggjs/tegg/issues/52)) ([7f95005](https://github.com/eggjs/tegg/commit/7f950050b548ca542addbd7b466675da4e81ce3f)) * implement cork/uncork for eventbus ([#60](https://github.com/eggjs/tegg/issues/60)) ([38114bd](https://github.com/eggjs/tegg/commit/38114bd7ea3b46cc4a79556a005ef18b2ae11ec2)) * middleware decorator allow multi middleware function ([#46](https://github.com/eggjs/tegg/issues/46)) ([a4b55f7](https://github.com/eggjs/tegg/commit/a4b55f7065c3d78e2c98c4b05f01871f666542ef)) * multi host decorator ([#68](https://github.com/eggjs/tegg/issues/68)) ([f6679de](https://github.com/eggjs/tegg/commit/f6679de1495024ecb9182168843300aa91288508)) * standalone support context ([#65](https://github.com/eggjs/tegg/issues/65)) ([b35dc2d](https://github.com/eggjs/tegg/commit/b35dc2d40fff1331145abd3f04917dc64f80010b)) * support leoric hooks ([#41](https://github.com/eggjs/tegg/issues/41)) ([9ecdbd2](https://github.com/eggjs/tegg/commit/9ecdbd2fe434445c698cd2140ae97f76b6bb6ddf)) # [3.0.0](https://github.com/eggjs/tegg/compare/v3.0.0-alpha.0...v3.0.0) (2022-12-26) ### Features * delete controller root hook ([bbb68f4](https://github.com/eggjs/tegg/commit/bbb68f43a1a9fcfd86c05581b10c56eeb77d4053)) # [3.0.0-alpha.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.0.0-alpha.0) (2022-12-22) ### Bug Fixes * eventbus runtime should wait all handlers done ([#51](https://github.com/eggjs/tegg/issues/51)) ([0651d30](https://github.com/eggjs/tegg/commit/0651d300f9a18bd97299548f3ebccad1d0382d28)) * fix events type from any to keyof Events ([#54](https://github.com/eggjs/tegg/issues/54)) ([a2551b2](https://github.com/eggjs/tegg/commit/a2551b2d9f9eabf9ed5c87f83489615eefa3e6d1)) * fix file path for advice decorator ([#64](https://github.com/eggjs/tegg/issues/64)) ([d6aa091](https://github.com/eggjs/tegg/commit/d6aa091851b5d1ca63e7e56e081df4d15ab3284e)) * fix miss agent file ([0fa496b](https://github.com/eggjs/tegg/commit/0fa496bdbb4ffa4e911fffa3e176fa7bdf03fb12)) * fix miss agent file ([#56](https://github.com/eggjs/tegg/issues/56)) ([cfb4dcc](https://github.com/eggjs/tegg/commit/cfb4dcc006ee1253733c7122f885a05da94f80b5)) * fix mock prototype in aop not work ([#66](https://github.com/eggjs/tegg/issues/66)) ([16640eb](https://github.com/eggjs/tegg/commit/16640eb751405532b2a1241b17624ce3ac2d1c7a)) * fix rootProtoManager.registerRootProto ([f416ed7](https://github.com/eggjs/tegg/commit/f416ed70af1c46d31ebf712b208205d67337d958)) * fix schedule import ([1fb5481](https://github.com/eggjs/tegg/commit/1fb54816fb3240c641824c2bc2b464c35652b655)) * none exist node_modules path ([#59](https://github.com/eggjs/tegg/issues/59)) ([77cb068](https://github.com/eggjs/tegg/commit/77cb0687ba8e5d9f20a6df0548de9d55a8771c21)) * optimize backgroud output ([#47](https://github.com/eggjs/tegg/issues/47)) ([6d978c5](https://github.com/eggjs/tegg/commit/6d978c5d7c339c78a90b00d2c2622f0be85ab3ce)) * skip file not exits ([#62](https://github.com/eggjs/tegg/issues/62)) ([10e56d4](https://github.com/eggjs/tegg/commit/10e56d418f359efa5d8909541768082cf068d2a4)) * use getMetaData for ModelMetadataUtil ([#44](https://github.com/eggjs/tegg/issues/44)) ([87a306c](https://github.com/eggjs/tegg/commit/87a306c4fba51fd519a47c0caaa79442643ea107)) * use require.resolve instead path.join to resolve dependencies path ([#63](https://github.com/eggjs/tegg/issues/63)) ([d7f3beb](https://github.com/eggjs/tegg/commit/d7f3beb27a22b95bb54589c5988a68ce2484c089)) ### Features * add new module scan mode ([#58](https://github.com/eggjs/tegg/issues/58)) ([3be6c20](https://github.com/eggjs/tegg/commit/3be6c2047a0241a482aafd0aaa072f51f861b6ea)) * **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) * impl Host decorator ([#48](https://github.com/eggjs/tegg/issues/48)) ([65dc7a8](https://github.com/eggjs/tegg/commit/65dc7a899ba72dd0851c35046562766d7f2b71b6)) * impl Inject Model ([#43](https://github.com/eggjs/tegg/issues/43)) ([ced2ce2](https://github.com/eggjs/tegg/commit/ced2ce2134964dcb410410c0192a34f77507c42d)) * impl Schedule decorator ([#52](https://github.com/eggjs/tegg/issues/52)) ([7f95005](https://github.com/eggjs/tegg/commit/7f950050b548ca542addbd7b466675da4e81ce3f)) * implement cork/uncork for eventbus ([#60](https://github.com/eggjs/tegg/issues/60)) ([38114bd](https://github.com/eggjs/tegg/commit/38114bd7ea3b46cc4a79556a005ef18b2ae11ec2)) * middleware decorator allow multi middleware function ([#46](https://github.com/eggjs/tegg/issues/46)) ([a4b55f7](https://github.com/eggjs/tegg/commit/a4b55f7065c3d78e2c98c4b05f01871f666542ef)) * multi host decorator ([#68](https://github.com/eggjs/tegg/issues/68)) ([f6679de](https://github.com/eggjs/tegg/commit/f6679de1495024ecb9182168843300aa91288508)) * standalone support context ([#65](https://github.com/eggjs/tegg/issues/65)) ([b35dc2d](https://github.com/eggjs/tegg/commit/b35dc2d40fff1331145abd3f04917dc64f80010b)) * support leoric hooks ([#41](https://github.com/eggjs/tegg/issues/41)) ([9ecdbd2](https://github.com/eggjs/tegg/commit/9ecdbd2fe434445c698cd2140ae97f76b6bb6ddf)) # [1.3.0](https://github.com/eggjs/tegg/compare/v1.2.0...v1.3.0) (2022-07-01) ### Features * allow inject proto and name ([#40](https://github.com/eggjs/tegg/issues/40)) ([abd1766](https://github.com/eggjs/tegg/commit/abd17665af2528c4c2e33f4c6b0fceddd8a4e76b)) * support leoric hooks ([#41](https://github.com/eggjs/tegg/issues/41)) ([9bdbc2c](https://github.com/eggjs/tegg/commit/9bdbc2cbe96df9f66f96b4f8e208883e99957946)) # [0.2.0](https://github.com/eggjs/tegg/compare/v0.1.19...v0.2.0) (2022-01-20) ### Bug Fixes * invalid value of main ([#27](https://github.com/eggjs/tegg/issues/27)) ([47f22d6](https://github.com/eggjs/tegg/commit/47f22d60f7ab01cf3c0e68bd078cdd0bb75169d5)) ### Features * impl aop ([c53df00](https://github.com/eggjs/tegg/commit/c53df001d1455a0a105689694775d880541d9d2f)) ================================================ FILE: CLAUDE.md ================================================ # CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Project Overview tegg is a TypeScript dependency injection framework and plugin system for [Egg.js](https://eggjs.org/). It provides decorator-based dependency injection, lifecycle management, and modular architecture for building scalable Node.js applications. ## Common Commands ```bash # Install dependencies npm install # Run all tests across workspaces npm test # Run tests for a single package npm test --workspace=core/metadata npm test --workspace=plugin/tegg # Run a single test file (from package directory) cd core/metadata && npm test -- --grep "test name" # Lint npm run lint npm run lint:fix # Build TypeScript npm run tsc # Full CI (prepare + lint + test) npm run ci # Publish (requires permissions) npm run bump # version bump with lerna npm run pub # publish to npm ``` ## Architecture ### Monorepo Structure This is a Lerna monorepo with three workspace categories: - **core/**: Core packages containing decorators, runtime, and utilities - **plugin/**: Egg.js plugins that integrate tegg features - **standalone/**: Standalone runtime without Egg.js dependency ### Key Packages | Package | Description | |---------|-------------| | `@eggjs/tegg` | Main entry point, re-exports all decorator packages | | `@eggjs/tegg-plugin` | Main Egg.js plugin for tegg integration | | `@eggjs/core-decorator` | Core decorators: `@ContextProto`, `@SingletonProto`, `@Inject` | | `@eggjs/tegg-types` | TypeScript type definitions | | `@eggjs/tegg-metadata` | Metadata storage and graph building | | `@eggjs/tegg-runtime` | Runtime container and object instantiation | | `@eggjs/tegg-lifecycle` | Object lifecycle hooks | ### Core Concepts **Prototype Types (instantiation modes):** - `@ContextProto`: One instance per request context - `@SingletonProto`: One instance for entire application lifecycle - `@MultiInstanceProto`: Multiple instances with different qualifiers **Access Levels:** - `AccessLevel.PRIVATE`: Only accessible within the same module - `AccessLevel.PUBLIC`: Accessible from other modules **Object Init Types:** - `ObjectInitType.CONTEXT`: New instance per request - `ObjectInitType.SINGLETON`: Single instance for app lifetime - `ObjectInitType.ALWAYS_NEW`: New instance on every injection ### Dependency Injection Flow 1. Decorators (`@ContextProto`, `@SingletonProto`, etc.) register metadata on classes 2. `@eggjs/tegg-loader` scans and loads modules 3. `@eggjs/tegg-metadata` builds a dependency graph (GlobalGraph, ModuleGraph) 4. `@eggjs/tegg-runtime` instantiates objects based on the graph 5. `@Inject` decorator triggers dependency resolution at runtime ### Lifecycle Hooks Objects can implement `EggObjectLifecycle` interface or use decorators: - `@LifecyclePostConstruct` / `postConstruct()` - `@LifecyclePreInject` / `preInject()` - `@LifecyclePostInject` / `postInject()` - `@LifecycleInit` / `init()` - `@LifecyclePreDestroy` / `preDestroy()` - `@LifecycleDestroy` / `destroy()` ## Import Guidelines ### Application Code (Egg.js apps using tegg) Always import from `@eggjs/tegg` - it re-exports everything needed: ```typescript // Core decorators and enums import { ContextProto, SingletonProto, Inject, AccessLevel, EggQualifier, EggType, } from '@eggjs/tegg'; // Subpath imports for specific features import { Advice, Crosscut, Pointcut } from '@eggjs/tegg/aop'; import { DataSource } from '@eggjs/tegg/orm'; import { Schedule } from '@eggjs/tegg/schedule'; import { Transactional } from '@eggjs/tegg/transaction'; ``` Available subpaths: `aop`, `orm`, `dal`, `schedule`, `transaction`, `ajv`, `helper`, `standalone` ### Advanced Usage (custom loaders, lifecycle hooks) Use `@eggjs/tegg/helper` for internal APIs: ```typescript import { ModuleConfigUtil, LoaderFactory, EggObjectLifeCycleContext, EggObject, } from '@eggjs/tegg/helper'; ``` ### Framework Internal Code (packages within this monorepo) **For type-only imports**, use `@eggjs/tegg-types`: ```typescript import type { EggPrototype, EggObject, EggObjectLifecycle } from '@eggjs/tegg-types'; import { AccessLevel, ObjectInitType } from '@eggjs/tegg-types'; ``` **For decorator utilities**, import from `@eggjs/core-decorator`: ```typescript import { PrototypeUtil, QualifierUtil, MetadataUtil } from '@eggjs/core-decorator'; ``` ### Package Dependency Rules | If you're in... | Import decorators from | Import types from | |-----------------|----------------------|-------------------| | Application code | `@eggjs/tegg` | `@eggjs/tegg` | | `plugin/*` packages | `@eggjs/tegg` | `@eggjs/tegg-types` | | `core/*` packages | `@eggjs/core-decorator` | `@eggjs/tegg-types` | ## Best Practices ### Prefer SingletonProto for Performance Use `@SingletonProto` by default unless you need request-scoped state. Singleton objects are created once at startup, avoiding repeated instantiation overhead per request. ```typescript // Good: Stateless service as singleton @SingletonProto() export class UserRepository { async findById(id: string) { /* ... */ } } // Use ContextProto only when you need request-specific state @ContextProto() export class RequestTracer { @Inject() ctx: EggContext; // needs access to current request } ``` ### Minimize AccessLevel Scope Default is `AccessLevel.PRIVATE`. Only use `AccessLevel.PUBLIC` when the prototype genuinely needs to be accessed from other modules. ```typescript // Good: explicitly public for cross-module access @SingletonProto({ accessLevel: AccessLevel.PUBLIC }) export class SharedConfigService { } // Good: private by default, only used within module @SingletonProto() export class InternalHelper { } ``` ### Use Lifecycle Hooks for Async Initialization Constructors cannot be async. Use `init()` lifecycle hook for async setup like database connections or external service initialization. ```typescript @SingletonProto() export class DatabaseClient implements EggObjectLifecycle { private connection: Connection; async init() { // Async initialization here this.connection = await createConnection(); } async destroy() { // Cleanup resources await this.connection.close(); } } ``` ### Use BackgroundTaskHelper for Async Tasks Never use raw `setTimeout`, `setInterval`, or `process.nextTick` in `@ContextProto` objects. The context may be destroyed before the task completes. Use `BackgroundTaskHelper` instead. ```typescript @ContextProto() export class OrderService { @Inject() backgroundTaskHelper: BackgroundTaskHelper; async createOrder(data: OrderData) { const order = await this.saveOrder(data); // Safe async task that survives request lifecycle this.backgroundTaskHelper.run(async () => { await this.sendNotification(order); }); return order; } } ``` ### Inject What You Need Avoid injecting `ctx` or `app` directly. Inject specific services or objects instead for better testability and clearer dependencies. ```typescript // Avoid @ContextProto() export class BadService { @Inject() ctx: Context; // too broad async doSomething() { return this.ctx.service.userService.find(); } } // Better @ContextProto() export class GoodService { @Inject() userService: UserService; // explicit dependency async doSomething() { return this.userService.find(); } } ``` ### Module Organization - One module per bounded context or feature domain - Keep modules focused and cohesive - Use `AccessLevel.PUBLIC` sparingly to define clear module boundaries - Prefer injecting prototypes over direct module-to-module references ## Important Constraints - `@ContextProto` objects can inject any prototype type - `@SingletonProto` objects **can** inject `@ContextProto` objects via Proxy mechanism: - tegg uses `AsyncLocalStorage` to track the current request context - When SingletonProto injects ContextProto, a Proxy object is injected - The Proxy resolves to the correct ContextProto instance from current context on each access - **Caveat**: Accessing ContextProto from SingletonProto outside a request context will throw an error - Circular dependencies between prototypes are not allowed - Circular dependencies between modules are not allowed ## Code Conventions - TypeScript strict mode with `experimentalDecorators` and `emitDecoratorMetadata` enabled - ESLint with `eslint-config-egg/typescript` - Tests use Mocha and are located in `test/` directories within each package - Test fixtures are in `test/fixtures/` directories --- ## Framework Development Guide This section is for contributors developing the tegg framework itself. ### Development Workflow Before committing code, always run: ```bash # 1. Lint check (required) npm run lint # 2. Run full CI: prepare-test + lint + test (required) npm run ci # 3. Verify TypeScript compilation for publishing (required for core packages) npm run tsc:pub ``` **CI Pipeline** (`npm run ci`) executes: 1. `ut prepare-test` - Prepare test fixtures 2. `ut lint` - ESLint check 3. `ut test` - Run all tests across workspaces **Note**: Some tests require MySQL. CI runs against MySQL 5.7. To run locally: - macOS: `brew install mysql && brew services start mysql` - Linux: Use Docker or native MySQL installation ### Package Development Order When developing a new feature for tegg, create packages in this order: ``` 1. core/types → Type definitions (interfaces, enums) 2. core/*-decorator → Decorators and metadata utilities 3. core/*-runtime → Runtime logic (if needed) 4. core/tegg → Re-export from main package (add to index.ts or create subpath) 5. plugin/* → Egg.js plugin integration ``` **Dependency direction**: `types` ← `decorator` ← `runtime` ← `plugin` ### Creating a New Decorator Package 1. Create package in `core/` directory: ``` core/my-decorator/ ├── package.json ├── tsconfig.json ├── tsconfig.pub.json ├── src/ │ ├── decorator/ │ │ └── MyDecorator.ts │ └── util/ │ └── MyDecoratorInfoUtil.ts ├── index.ts # exports └── test/ └── MyDecorator.test.ts ``` 2. Key dependencies in `package.json`: ```json { "dependencies": { "@eggjs/core-decorator": "^3.x", "@eggjs/tegg-types": "^3.x" } } ``` 3. Decorator implementation pattern: ```typescript // src/decorator/MyDecorator.ts import { PrototypeUtil } from '@eggjs/core-decorator'; import type { EggProtoImplClass } from '@eggjs/tegg-types'; export const MY_DECORATOR_META = Symbol('MY_DECORATOR_META'); export interface MyDecoratorParams { name?: string; } export function MyDecorator(params?: MyDecoratorParams) { return function(target: EggProtoImplClass) { PrototypeUtil.setMetaData(target, MY_DECORATOR_META, params || {}); }; } ``` 4. Add export to `core/tegg/`: - For core features: add to `index.ts` - For optional features: create new subpath file (e.g., `myfeature.ts`) ### Creating a New Plugin 1. Create package in `plugin/` directory: ``` plugin/my-plugin/ ├── package.json ├── tsconfig.json ├── tsconfig.pub.json ├── app.ts # Plugin entry point ├── config/ │ └── config.default.ts ├── lib/ │ ├── MyService.ts # Injectable services │ └── MyHook.ts # Lifecycle hooks ├── typings/ │ └── index.d.ts # Type extensions for egg └── test/ └── my-plugin.test.ts ``` 2. Key fields in `package.json`: ```json { "name": "@eggjs/tegg-my-plugin", "eggPlugin": { "name": "myPlugin", "dependencies": ["tegg"] }, "eggModule": { "name": "teggMyPlugin" }, "dependencies": { "@eggjs/tegg": "^3.x", "@eggjs/my-decorator": "^3.x" // if you have decorator package } } ``` - `eggPlugin`: Configuration for Egg.js plugin mode - `eggModule`: Configuration for standalone mode (`@eggjs/standalone`), where the plugin runs as a module without Egg.js 3. Plugin entry point (`app.ts`): ```typescript import { Application } from 'egg'; export default class MyPluginAppHook { private readonly app: Application; constructor(app: Application) { this.app = app; } configWillLoad() { // Register hooks before module loading } async didLoad() { // After modules are loaded await this.app.moduleHandler.ready(); } beforeClose() { // Cleanup resources } } ``` ### Wrapping Third-Party SDK as Plugin Pattern for wrapping external SDKs (e.g., database clients, API clients). See `mcp-client` plugin for a complete example. **Step 1: Create core wrapper package** (`core/my-client`) Wrap the third-party SDK with tegg-compatible interface (no tegg runtime dependency): ```typescript // core/my-client/src/MyClient.ts import { ThirdPartySDK } from 'third-party-sdk'; import type { Logger } from '@eggjs/tegg'; export interface MyClientOptions { logger: Logger; // ... other options } // Extend the third-party SDK class directly export class MyClient extends ThirdPartySDK { protected logger: Logger; constructor(options: MyClientOptions) { super(options); this.logger = options.logger; } async init() { // Initialize connection await super.connect(); } } ``` **Step 2: Create Egg-specific subclass in plugin** (`plugin/my-plugin`) Extend the core wrapper to add tegg-specific features (e.g., context tracking): ```typescript // plugin/my-plugin/lib/EggMyClient.ts import { Logger } from '@eggjs/tegg'; import { ContextHandler } from '@eggjs/tegg-runtime'; import { MyClient, MyClientOptions } from '@eggjs/my-client'; export interface EggMyClientOptions extends MyClientOptions { // additional egg-specific options } // Extend the core wrapper class export class EggMyClient extends MyClient { constructor(options: EggMyClientOptions) { super(options); } // Override methods to add tegg-specific behavior async doSomething() { const context = ContextHandler.getContext(); if (context) { // Set context-specific data for tracing, logging, etc. context.set('myClient.method', 'doSomething'); } return super.doSomething(); } } ``` **Step 3: Create factory for instantiation** Factory injects tegg dependencies and creates the client: ```typescript // plugin/my-plugin/lib/MyClientFactory.ts import { AccessLevel, Inject, Logger, SingletonProto, } from '@eggjs/tegg'; import { EggMyClient, EggMyClientOptions } from './EggMyClient'; @SingletonProto({ accessLevel: AccessLevel.PUBLIC, name: 'myClientFactory', }) export class MyClientFactory { @Inject() private readonly logger: Logger; async build(options: Omit): Promise { const client = new EggMyClient({ ...options, logger: this.logger, }); await client.init(); return client; } } ``` **Summary of the pattern:** ``` third-party-sdk → core/my-client (MyClient extends SDK) → plugin/my-plugin (EggMyClient extends MyClient) → plugin/my-plugin (MyClientFactory creates EggMyClient) ``` This layered approach: - Keeps core wrapper independent of tegg runtime - Allows Egg-specific extensions (context tracking, tracing) - Factory handles dependency injection (logger, config) **Step 4: Register lifecycle hooks in plugin** (if needed): ```typescript // plugin/my-plugin/app.ts import { Application } from 'egg'; import { MyPrototypeHook } from './lib/MyPrototypeHook'; export default class MyPluginAppHook { private readonly app: Application; private myHook: MyPrototypeHook; constructor(app: Application) { this.app = app; } configDidLoad() { // Register prototype lifecycle hooks this.myHook = new MyPrototypeHook(); this.app.eggPrototypeLifecycleUtil.registerLifecycle(this.myHook); } beforeClose() { this.app.eggPrototypeLifecycleUtil.deleteLifecycle(this.myHook); } } ``` ### Available Lifecycle Hooks Plugins can register hooks at different levels: | Hook Registry | Purpose | |--------------|---------| | `app.loadUnitLifecycleUtil` | Module loading/unloading | | `app.eggPrototypeLifecycleUtil` | Prototype creation/destruction | | `app.eggObjectLifecycleUtil` | Object instance creation/destruction | | `app.eggContextLifecycleUtil` | Request context creation/destruction | ### Testing Plugins Use `egg-mock` for plugin testing: ```typescript import { app } from 'egg-mock/bootstrap'; describe('my-plugin', () => { it('should work', async () => { await app.mockModuleContextScope(async ctx => { const myClient = await ctx.getEggObject(MyClient); const result = await myClient.doSomething(); assert(result); }); }); }); ``` ### Core Package Internals #### @eggjs/core-decorator (`core/core-decorator`) Provides the fundamental decorators for dependency injection. ``` src/ ├── decorator/ │ ├── ContextProto.ts # @ContextProto decorator │ ├── SingletonProto.ts # @SingletonProto decorator │ ├── MultiInstanceProto.ts # @MultiInstanceProto decorator │ ├── Inject.ts # @Inject decorator │ ├── Prototype.ts # Base prototype decorator logic │ ├── InitTypeQualifier.ts # @InitTypeQualifier for disambiguation │ ├── ModuleQualifier.ts # @ModuleQualifier for cross-module injection │ └── EggQualifier.ts # @EggQualifier for egg ctx/app disambiguation └── util/ ├── PrototypeUtil.ts # Read/write prototype metadata ├── QualifierUtil.ts # Qualifier metadata operations └── MetadataUtil.ts # General metadata utilities ``` #### @eggjs/tegg-metadata (`core/metadata`) Builds and manages the dependency graph for all modules and prototypes. ``` src/ ├── model/ │ ├── graph/ │ │ ├── GlobalGraph.ts # Main dependency graph (modules + protos) │ │ ├── GlobalModuleNode.ts # Module node in graph │ │ ├── ProtoNode.ts # Prototype node in graph │ │ └── ProtoSelector.ts # Logic for finding matching prototypes │ ├── ModuleDescriptor.ts # Module metadata container │ ├── LoadUnit.ts # LoadUnit interface (module instance) │ └── EggPrototype.ts # Prototype metadata model ├── impl/ │ ├── EggPrototypeBuilder.ts # Builds EggPrototype from class │ ├── EggPrototypeImpl.ts # EggPrototype implementation │ └── ModuleLoadUnit.ts # LoadUnit implementation for modules └── factory/ ├── EggPrototypeFactory.ts # Creates prototypes from classes └── LoadUnitFactory.ts # Manages LoadUnit instances ``` **Key class: `GlobalGraph`** - Manages two graphs: - `moduleGraph`: Vertices are modules, edges are module dependencies - `protoGraph`: Vertices are prototypes, edges are injection dependencies The `build()` method resolves all injection edges, `sort()` produces instantiation order. #### @eggjs/tegg-runtime (`core/runtime`) Handles actual object instantiation and lifecycle management. ``` src/ ├── model/ │ ├── EggObject.ts # EggObject interface and lifecycle utils │ ├── EggContext.ts # Request context interface │ ├── AbstractEggContext.ts # Base context implementation │ └── LoadUnitInstance.ts # Runtime instance of a LoadUnit ├── impl/ │ ├── EggObjectImpl.ts # Creates and initializes object instances │ ├── EggObjectUtil.ts # Injection and lifecycle utilities │ ├── ModuleLoadUnitInstance.ts # LoadUnitInstance for modules │ └── ContextObjectGraph.ts # Per-request object graph └── factory/ ├── EggObjectFactory.ts # Creates EggObject instances ├── EggContainerFactory.ts # Manages object containers └── LoadUnitInstanceFactory.ts # Creates LoadUnitInstance ``` #### @eggjs/tegg-types (`core/types`) TypeScript type definitions organized by domain: ``` ├── core-decorator/ # Types for core decorators │ ├── enum/ # AccessLevel, ObjectInitType, EggType, etc. │ └── model/ # EggPrototypeInfo, InjectObjectInfo, etc. ├── metadata/ # Types for metadata layer ├── runtime/ # Types for runtime layer ├── lifecycle/ # Lifecycle hook interfaces └── controller-decorator/ # HTTP controller types ``` #### @eggjs/tegg-plugin (`plugin/tegg`) Main Egg.js plugin that integrates tegg into an Egg application. ``` ├── app.ts # Plugin entry: lifecycle hooks (didLoad, beforeClose) ├── app/ │ ├── extend/ │ │ ├── application.ts # Extends app with getEggObject, moduleHandler │ │ └── context.ts # Extends ctx with getEggObject, beginModuleScope │ └── middleware/ │ └── tegg_ctx_lifecycle_middleware.ts # Creates EggContext per request └── lib/ ├── ModuleHandler.ts # Orchestrates module loading and initialization ├── EggModuleLoader.ts # Loads modules using tegg-loader ├── EggContextHandler.ts # Manages EggContext lifecycle ├── EggContextImpl.ts # EggContext implementation for Egg ├── EggCompatibleObject.ts # Wraps egg app/ctx objects as prototypes └── EggCompatibleProtoImpl.ts # Makes egg objects injectable ``` **Initialization flow in `app.ts`:** 1. `configWillLoad`: Register middleware 2. `configDidLoad`: Setup EggContextHandler, ModuleHandler 3. `didLoad`: Register hooks, call `moduleHandler.init()` to load all modules 4. `beforeClose`: Cleanup and destroy #### @eggjs/tegg-standalone (`standalone/standalone`) Provides a standalone runtime for tegg that works without Egg.js. This enables using tegg's dependency injection in CLI tools, background workers, or any Node.js application. ``` ├── src/ │ ├── Runner.ts # Main orchestrator: loads modules, manages lifecycle │ ├── main.ts # Simple entry point: main() function │ ├── EggModuleLoader.ts # Loads modules using tegg-loader │ ├── StandaloneContext.ts # EggContext implementation for standalone │ ├── StandaloneContextHandler.ts # AsyncLocalStorage-based context management │ ├── StandaloneLoadUnit.ts # LoadUnit for inner objects (logger, config, etc.) │ └── StandaloneInnerObjectProto.ts # EggPrototype for inner objects └── index.ts # Exports all public APIs ``` ### Standalone Mode: How tegg Runs Without Egg.js The standalone package replicates Egg.js plugin functionality using pure Node.js primitives: **Key Differences from Egg.js Mode:** | Aspect | Egg.js Mode | Standalone Mode | |--------|------------|-----------------| | Context storage | Egg.js request context | `AsyncLocalStorage` via `StandaloneContextHandler` | | Context object | `EggContextImpl` (wraps Egg ctx) | `StandaloneContext` (extends `AbstractEggContext`) | | Inner objects | From `app` and `ctx` | Manually provided via `innerObjectHandlers` | | HTTP handling | Egg.js middleware | Not provided (bring your own) | | Config loading | Egg.js config system | `ModuleConfigUtil` reads `module.yml` files | **Initialization Flow:** ``` 1. new Runner(cwd, options) ├── Read module references from cwd ├── Load module configs (module.default.yml, module.{env}.yml) ├── Register lifecycle hooks (AOP, DAL, etc.) └── Create GlobalGraph instance 2. runner.init() ├── Register StandaloneContextHandler (AsyncLocalStorage) ├── Register StandaloneLoadUnit factory (for inner objects) ├── Load all modules via EggModuleLoader │ ├── LoaderFactory.loadApp() → ModuleDescriptors │ ├── GlobalGraph.create() → build dependency graph │ ├── GlobalGraph.build() → resolve all injections │ └── GlobalGraph.sort() → topological sort for init order ├── Create LoadUnitInstance for each LoadUnit └── Find @Runner decorated class 3. runner.run() ├── Create StandaloneContext ├── ContextHandler.run(ctx, async () => {...}) │ └── Uses AsyncLocalStorage.run() internally ├── Get @Runner class instance via EggContainerFactory ├── Call runner.main() └── Cleanup context on completion 4. runner.destroy() ├── Destroy all LoadUnitInstances ├── Destroy all LoadUnits └── Unregister lifecycle hooks ``` **Context Management Without Egg.js:** In Egg.js, each HTTP request has a context object (`ctx`). Standalone mode simulates this using `AsyncLocalStorage`: ```typescript // StandaloneContextHandler.ts export class StandaloneContextHandler { static storage = new AsyncLocalStorage(); static register() { // Tell ContextHandler how to get/run context ContextHandler.getContextCallback = () => { return StandaloneContextHandler.storage.getStore(); }; ContextHandler.runInContextCallback = (context, fn) => { return StandaloneContextHandler.storage.run(context, fn); }; } } ``` This allows `@ContextProto` objects to work correctly - they are stored per-context in the `AsyncLocalStorage`. **Inner Objects (Replacing Egg.js app/ctx):** In Egg.js, objects like `logger`, `config` come from `app` or `ctx`. Standalone mode uses `innerObjectHandlers`: ```typescript // Runner creates these automatically: this.innerObjects = { moduleConfigs: [{ obj: new ModuleConfigs(...) }], moduleConfig: [...], // per-module configs with qualifiers runtimeConfig: [{ obj: { baseDir, name, env } }], mysqlDataSourceManager: [{ obj: MysqlDataSourceManager.instance }], }; // Users can add custom inner objects: const runner = new Runner(cwd, { innerObjectHandlers: { logger: [{ obj: myLogger }], customService: [{ obj: myService, qualifiers: [{ attribute: 'type', value: 'production' }], }], }, }); ``` These inner objects are wrapped as `StandaloneInnerObjectProto` and registered in a special `StandaloneLoadUnit`, making them injectable via `@Inject()`. ### Using Standalone Mode **Basic Usage:** ```typescript // main.ts import { ContextProto, Inject, SingletonProto } from '@eggjs/tegg'; import { Runner, MainRunner } from '@eggjs/tegg/standalone'; @SingletonProto() export class MyService { async doSomething() { return 'hello'; } } @ContextProto() @Runner() // Mark as entry point export class MyApp implements MainRunner { @Inject() myService: MyService; async main(): Promise { return await this.myService.doSomething(); } } // Run it import { main } from '@eggjs/tegg-standalone'; const result = await main(__dirname); console.log(result); // 'hello' ``` **With Dependencies and Custom Objects:** ```typescript import { main, Runner } from '@eggjs/tegg-standalone'; const result = await main(__dirname, { // Environment name (affects config loading) env: 'production', // Application name name: 'my-app', // Additional module paths to load dependencies: [ '/path/to/other/module', { baseDir: '/path/to/module', extraFilePattern: ['!**/test'] }, ], // Custom injectable objects innerObjectHandlers: { logger: [{ obj: myLogger }], config: [{ obj: myConfig, qualifiers: [{ attribute: 'env', value: 'production' }], }], }, // Disable module descriptor dump (default: true) dump: false, }); ``` **Advanced: Manual Runner Control:** ```typescript import { Runner, StandaloneContext } from '@eggjs/tegg-standalone'; const runner = new Runner(__dirname, options); await runner.init(); // Custom context with pre-set values const ctx = new StandaloneContext(); ctx.set('requestId', '12345'); // Run multiple times with different contexts const result1 = await runner.run(ctx); const result2 = await runner.run(new StandaloneContext()); // Cleanup when done await runner.destroy(); ``` **PreLoad for Faster Startup:** Use `preLoad()` to run lifecycle `preLoad` hooks before `main()`. This is useful for warming up caches or validating configuration: ```typescript import { preLoad, main } from '@eggjs/tegg-standalone'; // Run preLoad hooks (e.g., validate config, warm caches) await preLoad(__dirname, dependencies); // Then run main const result = await main(__dirname, { dependencies }); ``` ### Creating Standalone-Compatible Plugins Plugins that support standalone mode need an `eggModule` field in `package.json`: ```json { "name": "@eggjs/tegg-my-plugin", "eggPlugin": { "name": "myPlugin", "dependencies": ["tegg"] }, "eggModule": { "name": "teggMyPlugin" } } ``` The `eggModule.name` is used by standalone mode to identify the module. The plugin's prototypes will be loaded automatically when the plugin path is included in `dependencies`. Plugins should avoid depending on Egg.js-specific APIs (`app`, `ctx`) directly. Instead: - Use `@Inject()` for dependencies - Use lifecycle hooks (`init()`, `destroy()`) instead of Egg.js lifecycle - Provide factories that accept configuration via injection ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2017-present Alibaba Group Holding Limited and other contributors. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # `@eggjs/tegg` ## Install ```shell # tegg 注解 npm i --save @eggjs/tegg # tegg 插件 npm i --save @eggjs/tegg-plugin ``` ## Config ```js // config/plugin.js exports.tegg = { package: '@eggjs/tegg-plugin', enable: true, }; ``` ```js // config/config.default.js { tegg: { // 读取模块支持自定义配置,可用于扩展或过滤不需要的模块文件 readModuleOptions: { extraFilePattern: ['!**/dist', '!**/release'], }, }; } ``` ## Usage ### 原型 module 中的对象基本信息,提供了 - 实例化方式:每个请求实例化/全局单例/每次注入都实例化 - 访问级别:module 外是否可访问 #### ContextProto 每次请求都会实例化一个 ContextProto,并且只会实例化一次 ##### 定义 ```typescript @ContextProto(params: { // 原型的实例化名称 // 默认行为:会把 Proto 的首字母转为小写 // 如 UserAdapter 会转换为 userAdapter // 如果有不符合预期的可以手动指定,比如 // @ContextProto({ name: 'mistAdapter' }) // MISTAdapter // MISTAdapter 的实例名称即为 mistAdapter name?: string; // 对象是在 module 内可访问还是全局可访问 // PRIVATE: 仅 module 内可访问 // PUBLIC: 全局可访问 // 默认值为 PRIVATE accessLevel?: AccessLevel; }) ``` ##### 示例 ###### 简单示例 ```typescript import { ContextProto } from '@eggjs/tegg'; @ContextProto() export class HelloService { async hello(): Promise { return 'hello, module!'; } } ``` ###### 复杂示例 ```typescript import { ContextProto, AccessLevel } from '@eggjs/tegg'; @ContextProto({ accessLevel: AccessLevel.PUBLIC, name: 'helloInterface', }) export default class HelloService { async hello(user: User): Promise { const echoResponse = await this.echoAdapter.echo({ name: user.name }); return `hello, ${echoResponse.name}`; } } ``` #### SingletonProto 整个应用声明周期只会实例化一个 SingletonProto ##### 定义 ```typescript @SingletonProto(params: { // 原型的实例化名称 // 默认行为:会把 Proto 的首字母转为小写 // 如 UserAdapter 会转换为 userAdapter // 如果有不符合预期的可以手动指定,比如 // @SingletonProto({ name: 'mistAdapter' }) // MISTAdapter // MISTAdapter 的实例名称即为 mistAdapter name?: string; // 对象是在 module 内可访问还是全局可访问 // PRIVATE: 仅 module 内可访问 // PUBLIC: 全局可访问 // 默认值为 PRIVATE accessLevel?: AccessLevel; }) ``` ##### 示例 ###### 简单示例 ```typescript import { SingletonProto } from '@eggjs/tegg'; @SingletonProto() export class HelloService { async hello(): Promise { return 'hello, module!'; } } ``` ###### 复杂示例 ```typescript import { SingletonProto, AccessLevel } from '@eggjs/tegg'; @SingletonProto({ accessLevel: AccessLevel.PUBLIC, name: 'helloInterface', }) export class HelloService { async hello(user: User): Promise { const echoResponse = await this.echoAdapter.echo({ name: user.name }); return `hello, ${echoResponse.name}`; } } ``` #### MultiInstanceProto 支持一个类有多个实例。比如说 logger 可能会初始化多个,用来输出到不同文件,db 可能会初始化多个,用来连接不同的数据库。 使用这个注解可以方便的对接外部资源。 ##### 定义 ```ts // 静态定义 @MultiInstanceProto(params: { // 对象的生命周期 // CONTEXT: 每个上下文都有一个实例 // SINGLETON: 整个应用生命周期只有一个实例 initType?: ObjectInitTypeLike; // 对象是在 module 内可访问还是全局可访问 // PRIVATE: 仅 module 内可访问 // PUBLIC: 全局可访问 // 默认值为 PRIVATE accessLevel?: AccessLevel; // 高阶参数,指定类型的实现原型 protoImplType?: string; // 对象元信息 objects: ObjectInfo[]; }) // 动态定义 @MultiInstanceProto(params: { // 对象的生命周期 // CONTEXT: 每个上下文都有一个实例 // SINGLETON: 整个应用生命周期只有一个实例 initType?: ObjectInitTypeLike; // 对象是在 module 内可访问还是全局可访问 // PRIVATE: 仅 module 内可访问 // PUBLIC: 全局可访问 // 默认值为 PRIVATE accessLevel?: AccessLevel; // 高阶参数,指定类型的实现原型 protoImplType?: string; // 动态调用,获取对象元信息 // 仅会调用一次,调用后结果会被缓存 getObjects(ctx: MultiInstancePrototypeGetObjectsContext): ObjectInfo[]; }) ``` ##### 示例 首先定义一个自定义 Qualifier 注解 ```ts import { QualifierUtil, EggProtoImplClass, } from '@eggjs/tegg'; export const LOG_PATH_ATTRIBUTE = Symbol.for('LOG_PATH_ATTRIBUTE'); export function LogPath(name: string) { return function(target: any, propertyKey: PropertyKey) { QualifierUtil.addProperQualifier(target.constructor as EggProtoImplClass, propertyKey, LOG_PATH_ATTRIBUTE, name); }; } ``` 定一个具体实现 ```typescript import { MultiInstanceProto, MultiInstancePrototypeGetObjectsContext, LifecycleInit, LifecycleDestroy, QualifierUtil, EggProtoImplClass, } from '@eggjs/tegg'; import { EggObject, ModuleConfigUtil, EggObjectLifeCycleContext } from '@eggjs/tegg/helper'; import fs from 'node:fs'; import { Writable } from 'node:stream'; import path from 'node:path'; import { EOL } from 'node:os'; export const LOG_PATH_ATTRIBUTE = Symbol.for('LOG_PATH_ATTRIBUTE'); @MultiInstanceProto({ // 从 module.yml 中动态获取配置来决定需要初始化几个对象 getObjects(ctx: MultiInstancePrototypeGetObjectsContext) { const config = ModuleConfigUtil.loadModuleConfigSync(ctx.unitPath); return (config as any).features.logger.map(name => { return { name: 'dynamicLogger', qualifiers: [{ attribute: LOG_PATH_ATTRIBUTE, value: name, }], } }); }, }) export class DynamicLogger { stream: Writable; loggerName: string; @LifecycleInit() async init(ctx: EggObjectLifeCycleContext, obj: EggObject) { // 获取需要实例化对象的 Qualifieri const loggerName = obj.proto.getQualifier(LOG_PATH_ATTRIBUTE); this.loggerName = loggerName as string; this.stream = fs.createWriteStream(path.join(ctx.loadUnit.unitPath, `${loggerName}.log`)); } @LifecycleDestroy() async destroy() { return new Promise((resolve, reject) => { this.stream.end(err => { if (err) { return reject(err); } return resolve(); }); }); } info(msg: string) { return new Promise((resolve, reject) => { this.stream.write(msg + EOL,err => { if (err) { return reject(err); } return resolve(); }); }); } } ``` 使用 DynamicLogger. ```ts @SingletonProto() export class Foo { @Inject({ name: 'dynamicLogger', }) // 通过自定义注解来指定 Qualifier @LogPath('foo') fooDynamicLogger: DynamicLogger; @Inject({ name: 'dynamicLogger', }) @LogPath('bar') barDynamicLogger: DynamicLogger; async hello(): Promise { await this.fooDynamicLogger.info('hello, foo'); await this.barDynamicLogger.info('hello, bar'); } } ``` #### 生命周期 hook 由于对象的生命周期交给了容器来托管,代码中无法感知什么时候对象初始化,什么时候依赖被注入了。所以提供了生命周期 hook 来实现这些通知的功能。 ##### 定义 ```typescript /** * lifecycle hook interface for egg object */ interface EggObjectLifecycle { /** * call after construct */ postConstruct?(): Promise; /** * call before inject deps */ preInject?(): Promise; /** * call after inject deps */ postInject?(): Promise; /** * before object is ready */ init?(): Promise; /** * call before destroy */ preDestroy?(): Promise; /** * destroy the object */ destroy?(): Promise; } ``` ##### 示例 ```typescript import { EggObjectLifecycle } from '@eggjs/tegg'; @ContextProto() export class Foo implements EggObjectLifecycle { // 构造函数 constructor() { } async postConstruct(): Promise { console.log('对象构造完成'); } async preInject(): Promise { console.log('依赖将要注入'); } async postInject(): Promise { console.log('依赖注入完成'); } async init(): Promise { console.log('执行一些异步的初始化过程'); } async preDestroy(): Promise { console.log('对象将要释放了'); } async destroy(): Promise { console.log('执行一些释放资源的操作'); } } ``` ##### 生命周期方法装饰器 上面展示的 hook 是通过方法命名约定来实现生命周期 hook,我们还提供了更加可读性更强的装饰器模式。 ```ts import { LifecyclePostConstruct, LifecyclePreInject, LifecyclePostInject, LifecycleInit, LifecyclePreDestroy, LifecycleDestroy, } from '@eggjs/tegg'; @SingletonProto({ accessLevel: AccessLevel.PUBLIC, name: 'helloInterface', }) export class HelloService { @LifecyclePostConstruct() protected async _postConstruct() { console.log('对象构造完成'); } @LifecyclePreInject() protected async _preInject() { console.log('依赖将要注入'); } @LifecyclePostInject() protected async _postInject() { console.log('依赖注入完成'); } @LifecycleInit() protected async _init() { console.log('执行一些异步的初始化过程'); } @LifecyclePreDestroy() protected async _preDestroy() { console.log('对象将要释放了'); } @LifecycleDestroy() protected async _destroy() { console.log('执行一些释放资源的操作'); } async hello(user: User) { const echoResponse = await this.echoAdapter.echo({ name: user.name }); return `hello, ${echoResponse.name}`; } } ``` ### 注入 Proto 中可以依赖其他的 Proto,或者 egg 中的对象。 #### 定义 ```typescript @Inject(param?: { // 注入对象的名称,在某些情况下一个原型可能有多个实例 // 比如说 egg 的 logger // 默认为属性名称 name?: string; // 注入原型的名称 // 在某些情况不希望注入的原型和属性使用一个名称 // 默认为属性名称 proto?: string; // 注入对象是否为可选,默认为 false // 若为 false,当不存在该对象时,启动阶段将会抛出异常 // 若为 true,且未找到对象时,该属性值为 undefined optional?: boolean; }) ``` 对于 optional 为 true 的情况,也提供了 InjectOptional 的 alias 装饰器 ```typescript // 等价于 @Inject({ ...params, optional: true }) @InjectOptional(params: { // 注入对象的名称,在某些情况下一个原型可能有多个实例 // 比如说 egg 的 logger // 默认为属性名称 name?: string; // 注入原型的名称 // 在某些情况不希望注入的原型和属性使用一个名称 // 默认为属性名称 proto?: string; }) ``` #### 示例 ##### 简单示例 ```typescript import { EggLogger } from 'egg'; import { Inject } from '@eggjs/tegg'; @ContextProto() export class HelloService { @Inject() logger: EggLogger; // 等价于 @Inject({ optional: true }) @InjectOptional() maybeUndefinedLogger?: EggLogger; async hello(user: User): Promise { this.logger.info(`[HelloService] hello ${user.name}`); // optional inject 使用时,需要判断是否有值 if (this.maybeUndefinedLogger) { this.maybeUndefinedLogger.info(`[HelloService] hello ${user.name}`); } const echoResponse = await this.echoAdapter.echo({ name: user.name }); return `hello, ${echoResponse.name}`; } } ``` 也可在构造函数中使用 `Inject` 注解。注意 property 和 构造函数两种模式只能选一种,不能混用。 ```typescript import { EggLogger } from 'egg'; import { Inject } from '@eggjs/tegg'; @ContextProto() export class HelloService { constructor( @Inject() readonly logger: EggLogger, @InjectOptional() readonly maybeUndefinedLogger?: EggLogger, ) {} async hello(user: User): Promise { this.logger.info(`[HelloService] hello ${user.name}`); // optional inject 使用时,需要判断是否有值 if (this.maybeUndefinedLogger) { this.maybeUndefinedLogger.info(`[HelloService] hello ${user.name}`); } const echoResponse = await this.echoAdapter.echo({ name: user.name }); return `hello, ${echoResponse.name}`; } } ``` ##### 复杂示例 ```typescript import { EggLogger } from 'egg'; import { Inject } from '@eggjs/tegg'; @ContextProto() export class HelloService { // 在 config.default.js 中 // 配置了 customLogger, // 名称为 bizLogger @Inject({ name: 'bizLogger' }) logger: EggLogger; async hello(user: User): Promise { this.logger.info(`[HelloService] hello ${user.name}`); const echoResponse = await this.echoAdapter.echo({ name: user.name }); return `hello, ${echoResponse.name}`; } } ``` #### 限制 - ContextProto 可以注入任何 Proto 但是 SingletonProto 不能注入 ContextProto - 原型之间不允许有循环依赖,比如 Proto A - inject -> Proto B - inject- > Proto A,这种是不行的 - 类似原型之间不允许有循环依赖,module 直接也不能有循环依赖 - 一个 module 内不能有实例化方式和名称同时相同的原型 - **不可以注入 ctx/app,用什么注入什么** #### 兼容 egg egg 中有 extend 方式,可以将对象扩展到 Context/Application 上,这些对象均可注入。Context 上的对象类比为 ContextProto,Application 上的对象类比为 SingletonProto。 #### 进阶 ### module 内原型名称冲突 一个 module 内,有两个原型,原型名相同,实例化不同,这时直接 Inject 是不行的,module 无法理解具体需要哪个对象。这时就需要告知 module 需要注入的对象实例化方式是哪种。 ###### 定义 ```typescript @InitTypeQualifier(initType: ObjectInitType) ``` ###### 示例 ```typescript import { EggLogger } from 'egg'; import { Inject, InitTypeQualifier, ObjectInitType } from '@eggjs/tegg'; @ContextProto() export class HelloService { @Inject() // 明确指定实例化方式为 CONTEXT 的 logger @InitTypeQualifier(ObjectInitType.CONTEXT) logger: EggLogger; } ``` ##### module 间原型名称冲突 可能多个 module 都实现了名称为 `HelloAdapter` 的原型, 且 `accessLevel = AccessLevel.PUBLIC`,需要明确的告知 module 需要注入的原型来自哪个 module. ###### 定义 ```typescript @ModuleQualifier(moduleName: string) ``` ###### 示例 ```typescript import { EggLogger } from 'egg'; import { Inject, InitTypeQualifier, ObjectInitType } from '@eggjs/tegg'; @ContextProto() export class HelloService { @Inject() // 明确指定使用来自 foo module 的 HelloAdapter @ModuleQualifier('foo') helloAdapter: HelloAdapter; } ``` ### egg 内 ctx/app 命名冲突 egg 内可能出现 ctx 和 app 上有同名对象的存在,我们可以通过使用 `EggQualifier` 来明确指定注入的对象来自 ctx 还是 app。不指定时,默认注入 app 上的对象。 ###### 定义 ```typescript @EggQualifier(eggType: EggType) ``` ###### 示例 ```typescript import { EggLogger } from 'egg'; import { Inject, EggQualifier, EggType } from '@eggjs/tegg'; @ContextProto() export class HelloService { @Inject() // 明确指定注入 ctx 上的 foo 而不是 app 上的 foo @EggQualifier(EggType.CONTEXT) foo: Foo; } ``` ### 单测 #### 单测 Context 在单测中需要获取 egg Context 时,可以使用以下 API。 ```typescript export interface Application { /** * 创建 module 上下文 scope */ mockModuleContextScope(this: MockApplication, fn: (ctx: Context) => Promise, data?: any): Promise; } ``` #### 获取对象实例 在单测中需要获取 module 中的对象实例时,可以使用以下 API。 ```typescript export interface Application { /** * 通过一个类来获取实例 */ getEggObject (clazz: EggProtoImplClass ): Promise ; /** * 通过对象名称来获取实例 */ getEggObjectFromName(name: string, qualifiers?: QualifierInfo | QualifierInfo[]): Promise; } export interface Context { /** * 通过一个类来获取实例 */ getEggObject (clazz: EggProtoImplClass ): Promise ; /** * 通过对象名称来获取实例 */ getEggObjectFromName(name: string, qualifiers?: QualifierInfo | QualifierInfo[]): Promise; } ``` ### Egg 兼容性 目前 module 尚未实现所有 egg 的特性,如果需要使用 egg 的功能,可以通过一些方式来兼容。 ##### Schedule 目前 Schedule 尚未实现注解,仍然需要使用 egg 的目录和继承方式,在这种场景下如果需要使用 module 的实现,需要使用 `ctx.beginModuleScope`。举个例子: ```typescript // notify/EC_FOO.js import { Subscription, Context } from 'egg; class FooSubscriber extends Subscription { private readonly ctx: Context; constructor(ctx: Context) { super(ctx); } async subscribe(msg) { await ctx.beginModuleScope(async () => { await ctx.module.fooService.hello(msg); }); } } module.exports = Subscription; ``` #### 注入 egg 对象 module 会自动去遍历 egg 的 Application 和 Context 对象,获取其所有的属性,所有的属性都可以进行无缝的注入。举个例子,如何注入现在的 egg proxy: ```typescript import { IProxy } from 'egg' @ContextProto() class FooService { @Inject() private readonly proxy: IProxy; get fooFacade() { return this.proxy.fooFacade; } } ``` ##### 注入 logger 专为 logger 做了优化,可以直接注入 custom logger。 举个例子: 有一个自定义的 fooLogger ```javascript // config.default.js exports.customLogger = { fooLogger: { file: 'foo.log', }, }; ``` 代码中可以直接注入: ```typescript import { EggLogger } from 'egg'; class FooService { @Inject() private fooLogger: EggLogger; } ``` #### 注入 egg 方法 由于 module 注入时,只可以注入对象,不能注入方法,如果需要使用现有 egg 的方法,就需要对方法进行一定的封装。 举个例子:假设 context 上有一个方法是 `getHeader`,在 module 中需要使用这个方法需要进行封装。 ```typescript // extend/context.ts export default { getHeader() { return '23333'; } } ``` 先将方法封装成一个对象。 ```typescript // HeaderHelper.ts class HeaderHelper { constructor(ctx) { this.ctx = ctx; } getHeader(): string { return this.ctx.getHeader(); } } ``` 再将对象放到 Context 扩展上即可。 ```typescript // extend/context.ts const HEADER_HELPER = Symbol('context#headerHelper'); export default { get headerHelper() { if (!this[HEADER_HELPER]) { this[HEADER_HELPER] = new HeaderHelper(this); } return this[HEADER_HELPER]; } } ``` ### 生命周期 在 module 中,每个对象实例都有自己的生命周期,开发者可以对每个对象进行细致的控制。只要为对象实现 module 定义好的接口即可。所有生命周期 hook 均为可选方法,不需要的可以不实现。 #### 接口定义 ```typescript interface EggObjectLifecycle { /** * 在对象的构造函数执行完成之后执行 */ async postConstruct?(); /** * 在注入对象依赖之前执行 */ async preInject?(); /** * 在注入对象依赖之后执行 */ async postInject?(); /** * 执行对象自定义异步初始化函数 */ async init?(); /** * 在对象释放前执行 */ async preDestroy?(); /** * 释放对象依赖的底层资源 */ async destroy?(); } ``` #### 实现 ```typescript import { EggObjectLifecycle } from '@eggjs/tegg'; @SingletonProto() class FooService implement EggObjectLifecycle { @Inject() cacheService: CacheService; cache: Record; async init() { this.cache = await this.cacheService.get(key); } } ``` ### 异步任务 module 在请求结束后会把请求相关的对象释放,所以在请求中使用 `process.nextTick`、 `setTimeout`、 `setInterval`这类接口并不安全,可能导致一些错误。因此需要使用框架提供的接口,以便框架了解当前请求有哪些异步任务在执行,等异步任务执行完成后再释放对象。 #### 安装 ```shell npm i --save @eggjs/tegg-background-task ``` #### 使用 ```typescript import { BackgroundTaskHelper } from '@eggjs/tegg-background-task'; @ContextProto() export default class BackgroundService { @Inject() private readonly backgroundTaskHelper: BackgroundTaskHelper; async backgroundAdd() { this.backgroundTaskHelper.run(async () => { // do the background task }); } } ``` #### 超时时间 框架不会无限的等待异步任务执行,在默认 5s 之后如果异步任务还没有完成则会放弃等待开始执行释放过程。如果需要等待更长的时间,建议有两种方式: - **推荐方式:将异步任务转发给单例对象(SingletonProto)来执行,单例对象永远不会释放** - 调整超时时间,对 `backgroundTaskHelper.timeout` 进行赋值即可 - 如果将超时时间设置为 `Infinity`,框架将不会超时 - 可以在 config 文件中指定 `backgroundTask.timeout` 来全局覆盖默认的超时时间 ### 动态注入 #### 使用 定义一个抽象类和一个类型枚举。 ```ts export enum HelloType { FOO = 'FOO', BAR = 'BAR', } export abstract class AbstractHello { abstract hello(): string; } ``` 定义一个自定义枚举。 ```ts import { ImplDecorator, QualifierImplDecoratorUtil } from '@eggjs/tegg'; import { ContextHelloType } from '../FooType'; import { AbstractContextHello } from '../AbstractHello'; export const HELLO_ATTRIBUTE = 'HELLO_ATTRIBUTE'; export const Hello: ImplDecorator = QualifierImplDecoratorUtil.generatorDecorator(AbstractHello, HELLO_ATTRIBUTE); ``` 实现抽象类。 ```ts import { ContextProto } from '@eggjs/tegg'; import { ContextHello } from '../decorator/Hello'; import { ContextHelloType } from '../FooType'; import { AbstractContextHello } from '../AbstractHello'; @ContextProto() @Hello(HelloType.BAR) export class BarHello extends AbstractHello { hello(): string { return `hello, bar`; } } ``` 动态获取实现。 ```ts import { EggObjectFactory } from '@eggjs/tegg'; @ContextProto() export class HelloService { @Inject() private readonly eggObjectFactory: EggObjectFactory; async hello(): Promise { const helloImpl = await this.eggObjectFactory.getEggObject(AbstractHello, HelloType.BAR); return helloImpl.hello(); } } ``` 动态获取多个实现,通过 for/await 循环获得实例。 ```ts import { EggObjectFactory } from '@eggjs/tegg'; @ContextProto() export class HelloService { @Inject() private readonly eggObjectFactory: EggObjectFactory; async hello(): Promise { const helloImpls = await this.eggObjectFactory.getEggObjects(AbstractHello); const messages = []; for await (const helloImpl of helloImpls) { messages.push(helloImpl.hello()); } return messages; } } ``` ### 配置项目 module 一般情况下,无需在项目中手动声明包含了哪些 module,tegg 会自动在项目目录下进行扫描。但是会存在一些特殊的情况,tegg 无法扫描到,比如说 module 是通过 npm 包的方式来发布。因此 tegg 支持通过 `config/module.json` 的方式来声明包含了哪些 module。 支持通过 `path` 引用 `app/modules/foo` 目录下的 module。 ```json [ {"path": "../app/modules/foo"} ] ``` 支持通过 `package` 引用使用 npm 发布的 module。 ```json [ {"package": "foo"} ] ``` ================================================ FILE: benchmark/http/app/controller/FooTeggController.ts ================================================ import { HTTPController, HTTPMethod, HTTPMethodEnum } from '@eggjs/tegg'; @HTTPController() export default class FooTeggController { @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/hello', }) async hello() { return 'hello, tegg'; } } ================================================ FILE: benchmark/http/app/controller/template/egg_controller_1.js ================================================ 'use strict'; const { Controller } = require('egg'); module.exports = class EggController1 extends Controller { async hello() { this.ctx.body = 'hello,egg'; } } ================================================ FILE: benchmark/http/app/router.js ================================================ 'use strict'; module.exports = function (app) { const { router, controller } = app; app.get('/egg1', controller.template.eggController_1.hello); }; ================================================ FILE: benchmark/http/config/config.default.js ================================================ 'use strict'; module.exports = { keys: 'tegg_benchmark', }; ================================================ FILE: benchmark/http/config/plugin.js ================================================ 'use strict' module.exports = { tegg: { enable: true, package: '@eggjs/tegg-plugin', }, teggController: { enable: true, package: '@eggjs/tegg-controller-plugin', }, teggConfig: { enable: true, package: '@eggjs/tegg-config', }, }; ================================================ FILE: benchmark/http/package.json ================================================ { "name": "tegg_benchmark", "egg": { }, "scripts": { "dev": "egg-bin dev" }, "dependencies": { "@eggjs/tegg": "^3.7.0", "@eggjs/tegg-config": "^3.7.0", "@eggjs/tegg-controller-plugin": "^3.7.0", "@eggjs/tegg-plugin": "^3.7.0", "@eggjs/tsconfig": "^1.1.0", "egg": "^3.9.1" }, "devDependencies": { "egg-bin": "6" } } ================================================ FILE: benchmark/http/tsconfig.json ================================================ { "extends": "@eggjs/tsconfig" } ================================================ FILE: core/agent-runtime/CHANGELOG.md ================================================ # Change Log All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. ## [3.78.15](https://github.com/eggjs/tegg/compare/v3.78.14...v3.78.15) (2026-04-23) ### Bug Fixes * **agent-runtime:** hold cancelRun until executor session is committed ([#441](https://github.com/eggjs/tegg/issues/441)) ([4e02a28](https://github.com/eggjs/tegg/commit/4e02a28bdfe9b924c1190482fd3d85f8cad1fcfa)) ## [3.78.14](https://github.com/eggjs/tegg/compare/v3.78.13...v3.78.14) (2026-04-21) **Note:** Version bump only for package @eggjs/agent-runtime ## [3.78.13](https://github.com/eggjs/tegg/compare/v3.78.12...v3.78.13) (2026-04-21) ### Bug Fixes * **agent-runtime:** persist thread messages when a run is aborted ([#439](https://github.com/eggjs/tegg/issues/439)) ([384ab1b](https://github.com/eggjs/tegg/commit/384ab1bf3c344177d8eb2593d35dab41361a31dc)) ## [3.78.12](https://github.com/eggjs/tegg/compare/v3.78.11...v3.78.12) (2026-04-18) **Note:** Version bump only for package @eggjs/agent-runtime ## [3.78.11](https://github.com/eggjs/tegg/compare/v3.78.10...v3.78.11) (2026-04-18) **Note:** Version bump only for package @eggjs/agent-runtime ## [3.78.10](https://github.com/eggjs/tegg/compare/v3.78.9...v3.78.10) (2026-04-18) **Note:** Version bump only for package @eggjs/agent-runtime ## [3.78.9](https://github.com/eggjs/tegg/compare/v3.78.8...v3.78.9) (2026-04-18) **Note:** Version bump only for package @eggjs/agent-runtime ## [3.78.8](https://github.com/eggjs/tegg/compare/v3.78.7...v3.78.8) (2026-04-18) **Note:** Version bump only for package @eggjs/agent-runtime ## [3.78.7](https://github.com/eggjs/tegg/compare/v3.78.6...v3.78.7) (2026-04-18) **Note:** Version bump only for package @eggjs/agent-runtime ## [3.78.6](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.6) (2026-04-18) **Note:** Version bump only for package @eggjs/agent-runtime ## [3.78.5](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.5) (2026-04-18) **Note:** Version bump only for package @eggjs/agent-runtime ## [3.78.4](https://github.com/eggjs/tegg/compare/v3.78.3...v3.78.4) (2026-04-18) **Note:** Version bump only for package @eggjs/agent-runtime ## [3.78.3](https://github.com/eggjs/tegg/compare/v3.78.2...v3.78.3) (2026-04-15) **Note:** Version bump only for package @eggjs/agent-runtime ## [3.78.2](https://github.com/eggjs/tegg/compare/v3.78.1...v3.78.2) (2026-04-09) ### Bug Fixes * **agent-runtime:** filter stream_event in all appendMessages calls ([#434](https://github.com/eggjs/tegg/issues/434)) ([c3b81bd](https://github.com/eggjs/tegg/commit/c3b81bdb108d07528a40fbf14162fcbeb3338c60)), closes [#433](https://github.com/eggjs/tegg/issues/433) ## [3.78.1](https://github.com/eggjs/tegg/compare/v3.78.0...v3.78.1) (2026-04-09) **Note:** Version bump only for package @eggjs/agent-runtime # [3.78.0](https://github.com/eggjs/tegg/compare/v3.77.2...v3.78.0) (2026-04-09) ### Features * **agent-runtime:** rewrite streamRun with StreamEvent format and reconnection ([#432](https://github.com/eggjs/tegg/issues/432)) ([d03dac2](https://github.com/eggjs/tegg/commit/d03dac2ddd78641acb47e19275488ad9fbfcda2a)) ## [3.77.2](https://github.com/eggjs/tegg/compare/v3.77.1...v3.77.2) (2026-04-07) **Note:** Version bump only for package @eggjs/agent-runtime ## [3.77.1](https://github.com/eggjs/tegg/compare/v3.77.0...v3.77.1) (2026-04-01) ### Bug Fixes * **agent-runtime:** handle all content_block_start and delta subtypes in normalizeContentBlocks ([#430](https://github.com/eggjs/tegg/issues/430)) ([119ba38](https://github.com/eggjs/tegg/commit/119ba3889a52b3577bf0aa23b6123c4d2fd4a23c)) # [3.77.0](https://github.com/eggjs/tegg/compare/v3.76.1...v3.77.0) (2026-04-01) ### Features * **agent-runtime:** add normalizeContentBlocks for Anthropic SDK stream events ([#429](https://github.com/eggjs/tegg/issues/429)) ([d780fdb](https://github.com/eggjs/tegg/commit/d780fdba3cc243db4811af6733fda737f8c1dc4a)) ## [3.76.1](https://github.com/eggjs/tegg/compare/v3.76.0...v3.76.1) (2026-04-01) ### Bug Fixes * **agent-runtime:** merge content blocks and support accumulate control ([#428](https://github.com/eggjs/tegg/issues/428)) ([f4f904e](https://github.com/eggjs/tegg/commit/f4f904e357497fc5ad9a2c7d2ece4e9b305f5738)) # [3.76.0](https://github.com/eggjs/tegg/compare/v3.75.1...v3.76.0) (2026-04-01) ### Features * **agent-runtime:** support custom SSE event types in streamRun ([#427](https://github.com/eggjs/tegg/issues/427)) ([2efe539](https://github.com/eggjs/tegg/commit/2efe539cd2673e27dc91cb4597751e6e0a9d4b67)) ## [3.75.1](https://github.com/eggjs/tegg/compare/v3.75.0...v3.75.1) (2026-04-01) ### Bug Fixes * **agent-runtime:** preserve non-text content blocks in MessageConverter ([#426](https://github.com/eggjs/tegg/issues/426)) ([8c4382f](https://github.com/eggjs/tegg/commit/8c4382f33f68534218049cfbfadfd4f6800a348c)) # [3.75.0](https://github.com/eggjs/tegg/compare/v3.74.0...v3.75.0) (2026-03-30) **Note:** Version bump only for package @eggjs/agent-runtime # [3.74.0](https://github.com/eggjs/tegg/compare/v3.73.0...v3.74.0) (2026-03-30) ### Bug Fixes * **agent-runtime:** set isResume based on thread message history ([#419](https://github.com/eggjs/tegg/issues/419)) ([8a7eacc](https://github.com/eggjs/tegg/commit/8a7eacca79a94815251a0d660f828ebef443d12a)) # [3.73.0](https://github.com/eggjs/tegg/compare/v3.72.0...v3.73.0) (2026-03-25) ### Features * add agent-runtime package with @AgentController decorator ([#411](https://github.com/eggjs/tegg/issues/411)) ([d4d0006](https://github.com/eggjs/tegg/commit/d4d00061e90230f82c0958bcf5268f8a511395db)) * **agent-runtime:** add isResume flag to CreateRunInput ([#414](https://github.com/eggjs/tegg/issues/414)) ([29ac989](https://github.com/eggjs/tegg/commit/29ac98995c0a37bb34d33f7ad81af7c664a67bce)) ================================================ FILE: core/agent-runtime/index.ts ================================================ // Re-export types from @eggjs/tegg-types (backward compatible) export * from '@eggjs/tegg-types/agent-runtime'; // Implementation code export * from './src/OSSObjectStorageClient'; export * from './src/OSSAgentStore'; export * from './src/AgentStoreUtils'; export * from './src/MessageConverter'; export * from './src/RunBuilder'; export * from './src/SSEWriter'; export * from './src/HttpSSEWriter'; export { AgentRuntime, AGENT_RUNTIME } from './src/AgentRuntime'; export type { AgentExecutor, AgentRuntimeOptions } from './src/AgentRuntime'; ================================================ FILE: core/agent-runtime/package.json ================================================ { "name": "@eggjs/agent-runtime", "version": "3.78.15", "description": "Agent runtime with store abstraction for Egg.js tegg", "keywords": [ "agent", "egg", "runtime", "store", "tegg" ], "homepage": "https://github.com/eggjs/tegg/tree/master/core/agent-runtime", "bugs": { "url": "https://github.com/eggjs/tegg/issues" }, "license": "MIT", "repository": { "type": "git", "url": "git+https://github.com/eggjs/tegg.git", "directory": "core/agent-runtime" }, "files": [ "dist", "index.js", "index.d.ts" ], "main": "dist/index.js", "types": "dist/index.d.ts", "scripts": { "tsc": "tsc -p tsconfig.json", "tsc:pub": "tsc -p tsconfig.pub.json", "prepublishOnly": "npm run tsc:pub", "test": "mocha" }, "dependencies": { "@eggjs/tegg-types": "^3.78.15", "egg-logger": "^3.0.1", "oss-client": "^2.5.1" }, "devDependencies": { "@types/mocha": "^10.0.0", "@types/node": "^20.0.0", "mocha": "^10.0.0", "typescript": "^5.0.0" }, "engines": { "node": ">= 16.0.0" } } ================================================ FILE: core/agent-runtime/src/AgentRuntime.ts ================================================ import { EventEmitter } from 'node:events'; import { appendFileSync, createReadStream, mkdirSync, existsSync } from 'node:fs'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; import { createInterface } from 'node:readline'; import type { CreateRunInput, GetThreadOptions, ThreadObject, ThreadObjectWithMessages, RunObject, AgentMessage, AgentStore, StreamEvent, } from '@eggjs/tegg-types/agent-runtime'; import { RunStatus, AgentObjectType, AgentConflictError, AgentNotFoundError, AgentTimeoutError, } from '@eggjs/tegg-types/agent-runtime'; import type { EggLogger } from 'egg-logger'; import { MessageConverter } from './MessageConverter'; import { RunBuilder } from './RunBuilder'; import type { SSEWriter } from './SSEWriter'; const HEARTBEAT_INTERVAL_MS = 10_000; const EVENT_DIR = join(tmpdir(), 'agent-runtime-events'); const DEFAULT_CANCEL_COMMIT_TIMEOUT_MS = 30_000; interface RunEventBuffer { filePath: string; lastSeq: number; done: boolean; emitter: EventEmitter; } export const AGENT_RUNTIME: unique symbol = Symbol('agentRuntime'); /** * The executor interface — execRun is required so the runtime can delegate * execution back through the controller's prototype chain (AOP/mock friendly). * * `isSessionCommitted` is an optional hook that lets the executor tell the * runtime when its underlying session has been persisted to storage (e.g. the * Claude Code SDK jsonl file). The runtime uses this to decide when a pending * `cancelRun` can safely abort and persist the thread. See AgentHandler.ts * for the semantics and the default heuristic used when this hook is absent. */ export interface AgentExecutor { execRun(input: CreateRunInput, signal?: AbortSignal): AsyncGenerator; isSessionCommitted?(msg: AgentMessage, history: AgentMessage[]): boolean | Promise; } export interface AgentRuntimeOptions { executor: AgentExecutor; store: AgentStore; logger: EggLogger; /** * How long cancelRun should wait for the executor's session to become * committed before giving up and marking the run as failed. Defaults to * 30 seconds. */ cancelCommitTimeoutMs?: number; } interface RunTaskState { promise: Promise; abortController: AbortController; /** True once the executor has reported (or the heuristic has detected) that * its session is safely persisted and the run can be cancelled cleanly. */ committed: boolean; /** Emits 'commit' the first time committed flips to true, and 'end' when the * task's execution finally finishes (success, failure, or abort). */ emitter: EventEmitter; } export class AgentRuntime { private static readonly TERMINAL_RUN_STATUSES = new Set([ RunStatus.Completed, RunStatus.Failed, RunStatus.Cancelled, RunStatus.Expired, ]); // Statuses that must short-circuit the "write Completed" path in the // execution loops. Covers a TOCTOU window where another actor (most // notably cancelRun's commit-timeout watchdog, which writes Failed) sets // a terminal state while this worker has just exited the for-await loop // but hasn't yet written rb.complete(usage). Completed is intentionally // excluded so the normal success path is not routed through here. private static readonly POST_LOOP_TERMINAL_STATUSES = new Set([ RunStatus.Cancelling, RunStatus.Cancelled, RunStatus.Failed, RunStatus.Expired, ]); private store: AgentStore; private runningTasks: Map; private runBuffers: Map; private executor: AgentExecutor; private logger: EggLogger; private cancelCommitTimeoutMs: number; constructor(options: AgentRuntimeOptions) { this.executor = options.executor; this.store = options.store; if (!options.logger) { throw new Error('AgentRuntimeOptions.logger is required'); } this.logger = options.logger; this.cancelCommitTimeoutMs = options.cancelCommitTimeoutMs ?? DEFAULT_CANCEL_COMMIT_TIMEOUT_MS; this.runningTasks = new Map(); this.runBuffers = new Map(); } async createThread(): Promise { const thread = await this.store.createThread(); return { id: thread.id, object: AgentObjectType.Thread, createdAt: thread.createdAt, metadata: thread.metadata ?? {}, }; } async getThread(threadId: string, options?: GetThreadOptions): Promise { const thread = await this.store.getThread(threadId, options); return { id: thread.id, object: AgentObjectType.Thread, createdAt: thread.createdAt, metadata: thread.metadata ?? {}, messages: thread.messages, }; } private async ensureThread(input: CreateRunInput): Promise<{ threadId: string; input: CreateRunInput }> { if (input.threadId) { const thread = await this.store.getThread(input.threadId); const isResume = thread.messages.length > 0; return { threadId: input.threadId, input: { ...input, isResume } }; } const thread = await this.store.createThread(); return { threadId: thread.id, input: { ...input, threadId: thread.id, isResume: false } }; } async syncRun(input: CreateRunInput, signal?: AbortSignal): Promise { const { threadId, input: resolvedInput } = await this.ensureThread(input); input = resolvedInput; const run = await this.store.createRun(input.input.messages, threadId, input.config, input.metadata); const rb = RunBuilder.create(run, threadId); // Bridge external signal to an internal AbortController so cancelRun can abort syncRun const abortController = new AbortController(); if (signal) { if (signal.aborted) { abortController.abort(); } else { signal.addEventListener('abort', () => abortController.abort(), { once: true }); } } // Register in runningTasks so cancelRun can find and await this run. let resolveTask!: () => void; const taskPromise = new Promise(r => { resolveTask = r; }); const task: RunTaskState = { promise: taskPromise, abortController, committed: false, emitter: new EventEmitter(), }; this.runningTasks.set(run.id, task); const streamMessages: AgentMessage[] = []; try { await this.store.updateRun(run.id, rb.start()); for await (const msg of this.executor.execRun(input, abortController.signal)) { if (abortController.signal.aborted) { if (task.committed) { await this.persistMessagesOnAbort(threadId, input, streamMessages); } await this.finaliseAbortedRun(run.id); const latest = await this.store.getRun(run.id); return RunBuilder.fromRecord(latest).snapshot(); } streamMessages.push(msg); await this.markCommittedIfNeeded(task, msg, streamMessages); } // TOCTOU: another worker (e.g. cancelRun, or its commit-timeout // watchdog which writes Failed) may have terminated this run while // we were finishing the last iterator.next(). Respect the already-set // terminal state instead of overwriting it with Completed. const currentRun = await this.store.getRun(run.id); if (AgentRuntime.POST_LOOP_TERMINAL_STATUSES.has(currentRun.status)) { if (task.committed) { await this.persistMessagesOnAbort(threadId, input, streamMessages); } await this.finaliseAbortedRun(run.id); const latest = await this.store.getRun(run.id); return RunBuilder.fromRecord(latest).snapshot(); } const usage = MessageConverter.extractUsage(streamMessages); // Append input messages + stream messages to thread (excluding stream_event deltas) await this.store.appendMessages(threadId, [ ...MessageConverter.toAgentMessages(input.input.messages), ...MessageConverter.filterForStorage(streamMessages), ]); await this.store.updateRun(run.id, rb.complete(usage)); return rb.snapshot(); } catch (err: unknown) { if (abortController.signal.aborted) { if (task.committed) { await this.persistMessagesOnAbort(threadId, input, streamMessages); } await this.finaliseAbortedRun(run.id); const latest = await this.store.getRun(run.id); return RunBuilder.fromRecord(latest).snapshot(); } try { await this.store.updateRun(run.id, rb.fail(err as Error)); } catch (storeErr) { this.logger.error('[AgentRuntime] failed to update run status after syncRun error:', storeErr); } throw err; } finally { task.emitter.emit('end'); resolveTask(); this.runningTasks.delete(run.id); } } async asyncRun(input: CreateRunInput): Promise { const { threadId, input: resolvedInput } = await this.ensureThread(input); input = resolvedInput; const run = await this.store.createRun(input.input.messages, threadId, input.config, input.metadata); const rb = RunBuilder.create(run, threadId); const abortController = new AbortController(); // Capture queued snapshot before background task mutates state const queuedSnapshot = rb.snapshot(); // Register in runningTasks before the IIFE starts executing to avoid a race let resolveTask!: () => void; const taskPromise = new Promise(r => { resolveTask = r; }); const task: RunTaskState = { promise: taskPromise, abortController, committed: false, emitter: new EventEmitter(), }; this.runningTasks.set(run.id, task); (async () => { const streamMessages: AgentMessage[] = []; try { await this.store.updateRun(run.id, rb.start()); for await (const msg of this.executor.execRun(input, abortController.signal)) { if (abortController.signal.aborted) { if (task.committed) { await this.persistMessagesOnAbort(threadId, input, streamMessages); } await this.finaliseAbortedRun(run.id); return; } streamMessages.push(msg); await this.markCommittedIfNeeded(task, msg, streamMessages); } // TOCTOU: respect any terminal-ish status set by another worker // (cancelRun, its commit-timeout watchdog which writes Failed, or // an external expiration) instead of overwriting it with Completed. const currentRun = await this.store.getRun(run.id); if (AgentRuntime.POST_LOOP_TERMINAL_STATUSES.has(currentRun.status)) { return; } const usage = MessageConverter.extractUsage(streamMessages); // Append input messages + stream messages to thread (excluding stream_event deltas) await this.store.appendMessages(threadId, [ ...MessageConverter.toAgentMessages(input.input.messages), ...MessageConverter.filterForStorage(streamMessages), ]); await this.store.updateRun(run.id, rb.complete(usage)); } catch (err: unknown) { if (!abortController.signal.aborted) { try { const currentRun = await this.store.getRun(run.id); if (currentRun.status !== RunStatus.Cancelling && currentRun.status !== RunStatus.Cancelled) { await this.store.updateRun(run.id, rb.fail(err as Error)); } } catch (storeErr) { this.logger.error('[AgentRuntime] failed to update run status after error:', storeErr); } } else { if (task.committed) { await this.persistMessagesOnAbort(threadId, input, streamMessages); } await this.finaliseAbortedRun(run.id); this.logger.error('[AgentRuntime] execRun error during abort:', err); } } finally { task.emitter.emit('end'); resolveTask(); this.runningTasks.delete(run.id); } })(); return queuedSnapshot; } /** * Start a streaming run with background execution. * The task continues running even if the SSE client disconnects. * Events are persisted to a JSONL file for reconnection support. */ async streamRun(input: CreateRunInput, writer: SSEWriter): Promise { const { threadId, input: resolvedInput } = await this.ensureThread(input); input = resolvedInput; const run = await this.store.createRun(input.input.messages, threadId, input.config, input.metadata); const rb = RunBuilder.create(run, threadId); // Create event buffer for this run (events persisted to JSONL file) if (!existsSync(EVENT_DIR)) { mkdirSync(EVENT_DIR, { recursive: true }); } const buffer: RunEventBuffer = { filePath: join(EVENT_DIR, `${run.id}.jsonl`), lastSeq: 0, done: false, emitter: new EventEmitter(), }; this.runBuffers.set(run.id, buffer); // Emit initial lifecycle event this.pushEvent(buffer, 'run_created', { runId: run.id, threadId }); // Start background execution (not tied to SSE connection) const abortController = new AbortController(); let resolveTask!: () => void; const taskPromise = new Promise(r => { resolveTask = r; }); const task: RunTaskState = { promise: taskPromise, abortController, committed: false, emitter: new EventEmitter(), }; this.runningTasks.set(run.id, task); this.executeStreamBackground(input, run.id, threadId, rb, buffer, task) .finally(() => { task.emitter.emit('end'); resolveTask(); this.runningTasks.delete(run.id); this.runBuffers.delete(run.id); buffer.emitter.removeAllListeners(); }); // Stream events to the current client await this.streamEventsToWriter(buffer, writer, 0); } /** * Reconnect to a running or completed run's event stream. * Replays events after lastSeq, then continues real-time if still running. */ async getRunStream(runId: string, writer: SSEWriter, lastSeq = 0): Promise { const buffer = this.runBuffers.get(runId); if (buffer) { await this.streamEventsToWriter(buffer, writer, lastSeq); return; } // Task already finished — replay from JSONL file directly const filePath = join(EVENT_DIR, `${runId}.jsonl`); if (!existsSync(filePath)) { throw new AgentNotFoundError(`Run event stream not found: ${runId}`); } for await (const event of this.readEventsFromFile(filePath, lastSeq)) { if (writer.closed) return; writer.writeEvent(event.type, event); } if (!writer.closed) writer.end(); } private pushEvent(buffer: RunEventBuffer, type: string, data: unknown): void { const event: StreamEvent = { seq: ++buffer.lastSeq, type, data, ts: Date.now(), }; appendFileSync(buffer.filePath, JSON.stringify(event) + '\n'); buffer.emitter.emit('event', event); } /** * Execute the run in the background, persisting events to JSONL file. * AgentMessage objects are passed through directly as event data. */ private async executeStreamBackground( input: CreateRunInput, runId: string, threadId: string, rb: RunBuilder, buffer: RunEventBuffer, task: RunTaskState, ): Promise { const abortController = task.abortController; const streamMessages: AgentMessage[] = []; try { await this.store.updateRun(runId, rb.start()); for await (const msg of this.executor.execRun(input, abortController.signal)) { if (abortController.signal.aborted) { if (task.committed) { await this.persistMessagesOnAbort(threadId, input, streamMessages); } await this.finaliseAbortedRun(runId); this.pushEvent(buffer, 'error', { message: 'cancelled', runId }); return; } streamMessages.push(msg); // Pass through SDK message directly as event data const eventType = msg.type || 'message'; this.pushEvent(buffer, eventType, msg); await this.markCommittedIfNeeded(task, msg, streamMessages); } // TOCTOU: respect any terminal-ish status set by another worker // (cancelRun, its commit-timeout watchdog which writes Failed, or // an external expiration) instead of overwriting it with Completed. const currentRun = await this.store.getRun(runId); if (AgentRuntime.POST_LOOP_TERMINAL_STATUSES.has(currentRun.status)) { if (task.committed) { await this.persistMessagesOnAbort(threadId, input, streamMessages); } this.pushEvent(buffer, 'error', { message: currentRun.status, runId }); return; } // Persist to store (excluding stream_event deltas) const usage = MessageConverter.extractUsage(streamMessages); await this.store.appendMessages(threadId, [ ...MessageConverter.toAgentMessages(input.input.messages), ...MessageConverter.filterForStorage(streamMessages), ]); await this.store.updateRun(runId, rb.complete(usage)); this.pushEvent(buffer, 'done', { result: 'success', runId }); } catch (err: unknown) { if (!abortController.signal.aborted) { try { const currentRun = await this.store.getRun(runId); if (currentRun.status !== RunStatus.Cancelling && currentRun.status !== RunStatus.Cancelled) { await this.store.updateRun(runId, rb.fail(err as Error)); } } catch (storeErr) { this.logger.error('[AgentRuntime] failed to update run status after error:', storeErr); } this.pushEvent(buffer, 'error', { message: (err as Error).message, runId }); } else { if (task.committed) { await this.persistMessagesOnAbort(threadId, input, streamMessages); } await this.finaliseAbortedRun(runId); this.logger.error('[AgentRuntime] execRun error during abort:', err); this.pushEvent(buffer, 'error', { message: 'cancelled', runId }); } } finally { buffer.done = true; buffer.emitter.emit('event'); } } /** * Flip the task's `committed` flag the first time the executor's current * message indicates its session has been persisted to storage. Uses the * executor's `isSessionCommitted` hook when available, otherwise a default * heuristic where any message with `type !== 'system'` counts as committed * (the Claude Code SDK writes the jsonl around the first non-system event). */ private async markCommittedIfNeeded( task: RunTaskState, msg: AgentMessage, history: AgentMessage[], ): Promise { if (task.committed) return; let committed: boolean; try { committed = typeof this.executor.isSessionCommitted === 'function' ? await this.executor.isSessionCommitted(msg, history) : msg.type !== 'system'; } catch (err) { this.logger.error('[AgentRuntime] isSessionCommitted threw, treating as not committed:', err); committed = false; } if (committed) { task.committed = true; task.emitter.emit('commit'); } } /** * Wait until the task reports that its session is committed, or the task * finishes on its own, or the timeout elapses. Rejects with * AgentTimeoutError on timeout. Resolves without error when the task ends * before committing — in that case the caller should re-read the run's * terminal status rather than trying to cancel further. */ private waitForCommitted(task: RunTaskState, timeoutMs: number): Promise { if (task.committed) return Promise.resolve(); return new Promise((resolve, reject) => { // Handlers need to reference each other (cleanup must off() all of // commit / end / timer), which would force a forward reference if the // arrow functions referred to each other by name. Stash them on a // shared container so cleanup can read them by property access and the // source order stays linear. const refs: { timer?: ReturnType; onCommit?: () => void; onEnd?: () => void; } = {}; const cleanup = (): void => { if (refs.timer) clearTimeout(refs.timer); if (refs.onCommit) task.emitter.off('commit', refs.onCommit); if (refs.onEnd) task.emitter.off('end', refs.onEnd); }; refs.onCommit = () => { cleanup(); resolve(); }; refs.onEnd = () => { cleanup(); resolve(); }; refs.timer = globalThis.setTimeout(() => { cleanup(); reject(new AgentTimeoutError( `Timed out waiting ${timeoutMs}ms for executor session to be committed before cancel`, )); }, timeoutMs); task.emitter.once('commit', refs.onCommit); task.emitter.once('end', refs.onEnd); }); } /** * Persist input + collected stream messages to the thread when a run is * aborted. Keeping the thread in sync with any partial state that the * executor has already written (e.g. Claude CLI session file) is what * allows subsequent resume requests to continue from a consistent history * instead of diverging and failing at executor startup. * * Callers must check `task.committed` before invoking this; if the * executor never reached a committed state the thread should be left * untouched so the next run starts fresh instead of trying to resume a * session that was never created on disk. * * Errors are swallowed here so a store failure cannot mask the abort or * prevent cancelRun from finalising the run status. */ private async persistMessagesOnAbort( threadId: string, input: CreateRunInput, streamMessages: AgentMessage[], ): Promise { try { await this.store.appendMessages(threadId, [ ...MessageConverter.toAgentMessages(input.input.messages), ...MessageConverter.filterForStorage(streamMessages), ]); } catch (err) { this.logger.error('[AgentRuntime] failed to persist messages on abort:', err); } } /** * Push an aborted run to a terminal `cancelled` state when nobody else * will. Abort can be driven either by `cancelRun` — which already owns * the `in_progress → cancelling → cancelled` transition — or by an * external `AbortSignal` / `destroy()`, where the run would otherwise * stay stuck in `in_progress` forever. * * Behaviour: * - terminal status (completed/failed/cancelled/expired): no-op. * - `cancelling`: no-op, let `cancelRun` finish the transition. * - `in_progress` / `queued`: write `cancelling` then `cancelled`. * * Errors are swallowed so a store failure cannot mask the abort. */ private async finaliseAbortedRun(runId: string): Promise { try { const current = await this.store.getRun(runId); if (AgentRuntime.TERMINAL_RUN_STATUSES.has(current.status)) return; if (current.status === RunStatus.Cancelling) return; const rb = RunBuilder.fromRecord(current); await this.store.updateRun(runId, rb.cancelling()); await this.store.updateRun(runId, rb.cancel()); } catch (err) { this.logger.error('[AgentRuntime] failed to finalise aborted run:', err); } } private async* readEventsFromFile(filePath: string, afterSeq: number): AsyncGenerator { if (!existsSync(filePath)) return; const rl = createInterface({ input: createReadStream(filePath) }); for await (const line of rl) { if (!line.trim()) continue; try { const event = JSON.parse(line) as StreamEvent; if (event.seq > afterSeq) { yield event; } } catch { // skip malformed lines } } } private async streamEventsToWriter( buffer: RunEventBuffer, writer: SSEWriter, lastSeq: number, ): Promise { // Phase 1: Replay from JSONL file let lastWrittenSeq = lastSeq; for await (const event of this.readEventsFromFile(buffer.filePath, lastSeq)) { if (writer.closed) return; writer.writeEvent(event.type, event); lastWrittenSeq = event.seq; } if (buffer.done) { if (!writer.closed) writer.end(); return; } // Phase 2: Real-time events via EventEmitter + heartbeat const queue: StreamEvent[] = []; let waitResolve: (() => void) | null = null; function onEvent(event?: StreamEvent): void { if (event) queue.push(event); waitResolve?.(); } buffer.emitter.on('event', onEvent); try { // Catch-up: drain any events that arrived during Phase 1 file read for await (const event of this.readEventsFromFile(buffer.filePath, lastWrittenSeq)) { if (writer.closed) return; if (event.seq > lastWrittenSeq) { writer.writeEvent(event.type, event); lastWrittenSeq = event.seq; } } const waitForEvent = () => new Promise<'event' | 'heartbeat'>(resolve => { waitResolve = () => resolve('event'); setTimeout(() => resolve('heartbeat'), HEARTBEAT_INTERVAL_MS); }); while (!buffer.done || queue.length > 0) { while (queue.length > 0) { const event = queue.shift()!; if (event.seq > lastWrittenSeq) { if (writer.closed) return; writer.writeEvent(event.type, event); lastWrittenSeq = event.seq; } } if (buffer.done) break; if (writer.closed) return; const reason = await waitForEvent(); waitResolve = null; if (reason === 'heartbeat' && queue.length === 0 && !buffer.done) { if (writer.closed) return; writer.writeComment('keepalive'); } } } finally { buffer.emitter.off('event', onEvent); } if (!writer.closed) writer.end(); } async getRun(runId: string): Promise { const run = await this.store.getRun(runId); return RunBuilder.fromRecord(run).snapshot(); } /** * Cancel a running task. The call blocks until either (a) the executor * reports its session is safely committed to storage, and the task has * been aborted and the thread persisted, or (b) the commit watchdog times * out, in which case the run is marked `failed` (not `cancelled`) and * AgentTimeoutError is thrown to the caller. * * The hold is there to guarantee that whatever user input the thread * records on abort is also present in the executor's own persistent * session (e.g. Claude Code SDK jsonl), so a subsequent resume request * on the same thread doesn't diverge from a session that was never * actually written. */ async cancelRun(runId: string): Promise { const run = await this.store.getRun(runId); if (AgentRuntime.TERMINAL_RUN_STATUSES.has(run.status)) { throw new AgentConflictError(`Cannot cancel run with status '${run.status}'`); } const rb = RunBuilder.fromRecord(run); await this.store.updateRun(runId, rb.cancelling()); const task = this.runningTasks.get(runId); if (task) { if (!task.committed) { this.logger.info( '[AgentRuntime] cancelRun %s holding up to %dms for executor session to commit', runId, this.cancelCommitTimeoutMs, ); try { await this.waitForCommitted(task, this.cancelCommitTimeoutMs); } catch (err) { // Commit watchdog timed out. Mark the run as failed *before* // aborting so the execution path's finaliseAbortedRun sees a // terminal status and skips the cancelled transition. The thread // is left untouched because task.committed is still false. this.logger.error( '[AgentRuntime] cancelRun %s timed out after %dms waiting for executor to commit; marking run failed and leaving thread untouched', runId, this.cancelCommitTimeoutMs, ); try { await this.store.updateRun(runId, rb.fail(err as Error)); } catch (storeErr) { this.logger.error('[AgentRuntime] failed to mark run failed after cancel timeout:', storeErr); } task.abortController.abort(); await task.promise.catch(() => { /* ignore */ }); throw err; } } task.abortController.abort(); await task.promise.catch(() => { /* ignore */ }); } const freshRun = await this.store.getRun(runId); if (AgentRuntime.TERMINAL_RUN_STATUSES.has(freshRun.status)) { return RunBuilder.fromRecord(freshRun).snapshot(); } try { await this.store.updateRun(runId, rb.cancel()); } catch (err) { this.logger.error('[AgentRuntime] failed to write cancelled state after cancelling:', err); const fallback = await this.store.getRun(runId); return RunBuilder.fromRecord(fallback).snapshot(); } return rb.snapshot(); } async waitForPendingTasks(): Promise { if (this.runningTasks.size) { const pending = Array.from(this.runningTasks.values()).map(t => t.promise); await Promise.allSettled(pending); } } async destroy(): Promise { for (const task of this.runningTasks.values()) { task.abortController.abort(); } await this.waitForPendingTasks(); for (const buffer of this.runBuffers.values()) { buffer.emitter.removeAllListeners(); } this.runBuffers.clear(); if (this.store.destroy) { await this.store.destroy(); } } static create(options: AgentRuntimeOptions): AgentRuntime { return new AgentRuntime(options); } } ================================================ FILE: core/agent-runtime/src/AgentStoreUtils.ts ================================================ import crypto from 'node:crypto'; export function nowUnix(): number { return Math.floor(Date.now() / 1000); } export function newMsgId(): string { return `msg_${crypto.randomUUID()}`; } export function newThreadId(): string { return `thread_${crypto.randomUUID()}`; } export function newRunId(): string { return `run_${crypto.randomUUID()}`; } ================================================ FILE: core/agent-runtime/src/HttpSSEWriter.ts ================================================ import type { ServerResponse } from 'node:http'; import type { SSEWriter } from './SSEWriter'; export class HttpSSEWriter implements SSEWriter { private res: ServerResponse; private _closed = false; private closeCallbacks: Array<() => void> = []; private headersSent = false; private readonly onResClose: () => void; constructor(res: ServerResponse) { this.res = res; this.onResClose = () => { this._closed = true; for (const cb of this.closeCallbacks) cb(); this.closeCallbacks.length = 0; }; res.on('close', this.onResClose); } /** Lazily write headers on first event — avoids sending corrupt headers if constructor throws. */ private ensureHeaders(): void { if (this.headersSent) return; this.headersSent = true; this.res.writeHead(200, { 'content-type': 'text/event-stream', 'cache-control': 'no-cache', connection: 'keep-alive', }); } writeEvent(event: string, data: unknown): void { if (this._closed) return; this.ensureHeaders(); this.res.write(`event: ${event}\ndata: ${JSON.stringify(data)}\n\n`); } writeComment(text: string): void { if (this._closed) return; this.ensureHeaders(); this.res.write(`: ${text}\n\n`); } get closed(): boolean { return this._closed; } end(): void { if (!this._closed) { this._closed = true; this.res.off('close', this.onResClose); this.closeCallbacks.length = 0; this.res.end(); } } onClose(callback: () => void): void { this.closeCallbacks.push(callback); } } ================================================ FILE: core/agent-runtime/src/MessageConverter.ts ================================================ import type { AgentMessage, InputMessage, SDKResultMessage, } from '@eggjs/tegg-types/agent-runtime'; import type { RunUsage } from './RunBuilder'; export class MessageConverter { /** * Extract accumulated usage from AgentMessage objects. * Only `result` type messages carry usage information. */ static extractUsage(messages: AgentMessage[]): RunUsage | undefined { let promptTokens = 0; let completionTokens = 0; let hasUsage = false; for (const msg of messages) { if (msg.type === 'result') { const resultMsg = msg as SDKResultMessage; if (resultMsg.usage) { hasUsage = true; promptTokens += resultMsg.usage.input_tokens ?? 0; completionTokens += resultMsg.usage.output_tokens ?? 0; } } } if (!hasUsage) return undefined; return { promptTokens, completionTokens, totalTokens: promptTokens + completionTokens, }; } /** * Filter out stream_event messages before persisting to thread storage. * Stream events are incremental deltas (one per token) only useful during * real-time streaming; the final assistant message already contains the * complete response. */ static filterForStorage(messages: AgentMessage[]): AgentMessage[] { return messages.filter(m => m.type !== 'stream_event'); } /** * Convert input messages to AgentMessage format for thread history. * System messages are filtered out — they are transient instructions, * not conversation history. */ static toAgentMessages(messages: InputMessage[]): AgentMessage[] { return messages .filter(m => m.role !== 'system') .map(m => ({ type: m.role as 'user' | 'assistant', message: { role: m.role, content: m.content }, })); } } ================================================ FILE: core/agent-runtime/src/OSSAgentStore.ts ================================================ import type { AgentMessage, AgentRunConfig, AgentStore, GetThreadOptions, InputMessage, RunRecord, ThreadRecord, ObjectStorageClient } from '@eggjs/tegg-types/agent-runtime'; import { AgentObjectType, RunStatus, AgentNotFoundError } from '@eggjs/tegg-types/agent-runtime'; import { nowUnix, newThreadId, newRunId } from './AgentStoreUtils'; export interface OSSAgentStoreOptions { client: ObjectStorageClient; prefix?: string; } /** * Thread metadata stored as a JSON object (excludes messages). * Messages are stored separately in a JSONL file for append-friendly writes. */ type ThreadMetadata = Omit; /** * AgentStore implementation backed by an ObjectStorageClient (OSS, S3, etc.). * * ## Storage layout * * ``` * {prefix}threads/{id}/meta.json — Thread metadata (JSON) * {prefix}threads/{id}/messages.jsonl — Messages (JSONL, one AgentMessage per line) * {prefix}runs/{id}.json — Run record (JSON) * ``` */ export class OSSAgentStore implements AgentStore { private readonly client: ObjectStorageClient; private readonly prefix: string; constructor(options: OSSAgentStoreOptions) { this.client = options.client; const raw = options.prefix ?? ''; this.prefix = raw && !raw.endsWith('/') ? raw + '/' : raw; } private threadMetaKey(threadId: string): string { return `${this.prefix}threads/${threadId}/meta.json`; } private threadMessagesKey(threadId: string): string { return `${this.prefix}threads/${threadId}/messages.jsonl`; } private runKey(runId: string): string { return `${this.prefix}runs/${runId}.json`; } async init(): Promise { await this.client.init?.(); } async destroy(): Promise { await this.client.destroy?.(); } async createThread(metadata?: Record): Promise { const threadId = newThreadId(); const meta: ThreadMetadata = { id: threadId, object: AgentObjectType.Thread, metadata: metadata ?? {}, createdAt: nowUnix(), }; await this.client.put(this.threadMetaKey(threadId), JSON.stringify(meta)); return { ...meta, messages: [] }; } async getThread(threadId: string, options?: GetThreadOptions): Promise { const [ metaData, messagesData ] = await Promise.all([ this.client.get(this.threadMetaKey(threadId)), this.client.get(this.threadMessagesKey(threadId)), ]); if (!metaData) { throw new AgentNotFoundError(`Thread ${threadId} not found`); } const meta = JSON.parse(metaData) as ThreadMetadata; let messages: AgentMessage[] = messagesData ? messagesData .trim() .split('\n') .filter(line => line.length > 0) .map(line => JSON.parse(line) as AgentMessage) : []; // By default only return conversation messages (user + assistant), // aligned with SDK's getSessionMessages behavior. // The JSONL file stores all message types as a complete event log; // this filter provides the application-level conversation view. if (!options?.includeAllMessages) { messages = messages.filter(m => m.type === 'user' || m.type === 'assistant'); } return { ...meta, messages }; } async appendMessages(threadId: string, messages: AgentMessage[]): Promise { const metaData = await this.client.get(this.threadMetaKey(threadId)); if (!metaData) { throw new AgentNotFoundError(`Thread ${threadId} not found`); } if (messages.length === 0) return; const lines = messages.map(m => JSON.stringify(m)).join('\n') + '\n'; const messagesKey = this.threadMessagesKey(threadId); if (this.client.append) { await this.client.append(messagesKey, lines); } else { const existing = (await this.client.get(messagesKey)) ?? ''; await this.client.put(messagesKey, existing + lines); } } async createRun( input: InputMessage[], threadId?: string, config?: AgentRunConfig, metadata?: Record, ): Promise { const runId = newRunId(); const record: RunRecord = { id: runId, object: AgentObjectType.ThreadRun, threadId, status: RunStatus.Queued, input, config, metadata, createdAt: nowUnix(), }; await this.client.put(this.runKey(runId), JSON.stringify(record)); return record; } async getRun(runId: string): Promise { const data = await this.client.get(this.runKey(runId)); if (!data) { throw new AgentNotFoundError(`Run ${runId} not found`); } return JSON.parse(data) as RunRecord; } async updateRun(runId: string, updates: Partial): Promise { const run = await this.getRun(runId); const safeUpdates = { ...updates }; delete safeUpdates.id; delete (safeUpdates as any).object; Object.assign(run, safeUpdates); await this.client.put(this.runKey(runId), JSON.stringify(run)); } } ================================================ FILE: core/agent-runtime/src/OSSObjectStorageClient.ts ================================================ import type { ObjectStorageClient } from '@eggjs/tegg-types/agent-runtime'; import type { OSSObject } from 'oss-client'; function isOSSError(err: unknown, code: string): boolean { return err != null && typeof err === 'object' && 'code' in err && (err as { code: unknown }).code === code; } /** * ObjectStorageClient backed by Alibaba Cloud OSS (via oss-client). * * Supports both `put`/`get` for normal objects and `append` for * OSS Appendable Objects. The append path uses a local position cache * to avoid extra HEAD requests; on position mismatch it falls back to * HEAD + retry automatically. * * The OSSObject instance should be constructed and injected by the caller, * following the IoC/DI principle. */ export class OSSObjectStorageClient implements ObjectStorageClient { private readonly client: OSSObject; /** * In-memory cache of next-append positions. * * After each successful `append()`, OSS returns `nextAppendPosition`. * We cache it here so the next append can skip a HEAD round-trip. * If the cached position is stale (e.g., process restarted or another * writer appended), the append will fail with PositionNotEqualToLength * and we fall back to HEAD + retry. */ private readonly appendPositions = new Map(); constructor(client: OSSObject) { this.client = client; } async put(key: string, value: string): Promise { await this.client.put(key, Buffer.from(value, 'utf-8')); } async get(key: string): Promise { try { const result = await this.client.get(key); if (result.content) { return Buffer.isBuffer(result.content) ? result.content.toString('utf-8') : String(result.content); } return null; } catch (err: unknown) { if (isOSSError(err, 'NoSuchKey')) { return null; } throw err; } } /** * Append data to an OSS Appendable Object. * * OSS AppendObject requires a `position` parameter that must equal the * current object size. We use a three-step strategy: * * 1. Use the cached position (0 for new objects, or the value from the * last successful append). * 2. If OSS returns PositionNotEqualToLength (cache is stale), issue a * HEAD request to learn the current object size, then retry once. * 3. Update the cache with `nextAppendPosition` from the response. * * This gives us single-round-trip performance in the common case (single * writer, no restarts) while still being self-healing when the cache is * stale. */ async append(key: string, value: string): Promise { const buf = Buffer.from(value, 'utf-8'); const position = this.appendPositions.get(key) ?? 0; try { const result = await this.client.append(key, buf, { position }); this.appendPositions.set(key, Number(result.nextAppendPosition)); } catch (err: unknown) { // Position mismatch — the object grew since our last cached position. // Fall back to HEAD to learn the actual size, then retry. if (isOSSError(err, 'PositionNotEqualToLength')) { const head = await this.client.head(key); const currentPos = Number(head.res.headers['content-length'] ?? 0); const result = await this.client.append(key, buf, { position: currentPos }); this.appendPositions.set(key, Number(result.nextAppendPosition)); } else { throw err; } } } } ================================================ FILE: core/agent-runtime/src/RunBuilder.ts ================================================ import type { RunObject, RunRecord, AgentRunConfig } from '@eggjs/tegg-types/agent-runtime'; import { RunStatus, AgentErrorCode, AgentObjectType, InvalidRunStateTransitionError } from '@eggjs/tegg-types/agent-runtime'; import { nowUnix } from './AgentStoreUtils'; /** Accumulated token usage — same shape as non-null RunRecord['usage']. */ export type RunUsage = NonNullable; /** * Encapsulates run state transitions. * * Mutation methods (`start`, `complete`, `fail`, `cancel`) update internal * state and return `Partial` for the store. * * `snapshot()` produces a `RunObject` suitable for API responses and SSE events. */ export class RunBuilder { private readonly id: string; private readonly threadId: string; private readonly createdAt: number; private readonly metadata?: Record; private readonly config?: AgentRunConfig; private status: RunStatus; private startedAt?: number; private completedAt?: number; private cancelledAt?: number; private failedAt?: number; private lastError?: { code: string; message: string } | null; private usage?: RunUsage; private constructor( id: string, threadId: string, createdAt: number, status: RunStatus, metadata?: Record, config?: AgentRunConfig, ) { this.id = id; this.threadId = threadId; this.createdAt = createdAt; this.status = status; this.metadata = metadata; this.config = config; } /** Create a RunBuilder from a store RunRecord, using its own threadId. */ static fromRecord(run: RunRecord): RunBuilder { return RunBuilder.create(run, run.threadId ?? ''); } /** Create a RunBuilder from a store RunRecord, restoring all mutable state. */ static create(run: RunRecord, threadId: string): RunBuilder { const rb = new RunBuilder(run.id, threadId, run.createdAt, run.status, run.metadata, run.config); rb.startedAt = run.startedAt ?? undefined; rb.completedAt = run.completedAt ?? undefined; rb.cancelledAt = run.cancelledAt ?? undefined; rb.failedAt = run.failedAt ?? undefined; rb.lastError = run.lastError ?? undefined; if (run.usage) { rb.usage = { ...run.usage }; } return rb; } /** queued -> in_progress. Returns store update. */ start(): Partial { if (this.status !== RunStatus.Queued) { throw new InvalidRunStateTransitionError(this.status, RunStatus.InProgress); } this.status = RunStatus.InProgress; this.startedAt = nowUnix(); return { status: this.status, startedAt: this.startedAt }; } /** in_progress -> completed. Returns store update. */ complete(usage?: RunUsage): Partial { if (this.status !== RunStatus.InProgress) { throw new InvalidRunStateTransitionError(this.status, RunStatus.Completed); } this.status = RunStatus.Completed; this.completedAt = nowUnix(); this.usage = usage; return { status: this.status, usage, completedAt: this.completedAt, }; } /** * queued/in_progress/cancelling -> failed. Returns store update. * * `cancelling -> failed` covers the case where AgentRuntime has initiated * a cancel but the watchdog times out before the executor commits — the * run is treated as a failed startup rather than a successful cancel. */ fail(error: Error): Partial { if ( this.status !== RunStatus.InProgress && this.status !== RunStatus.Queued && this.status !== RunStatus.Cancelling ) { throw new InvalidRunStateTransitionError(this.status, RunStatus.Failed); } this.status = RunStatus.Failed; this.failedAt = nowUnix(); this.lastError = { code: AgentErrorCode.ExecError, message: error.message }; return { status: this.status, lastError: this.lastError, failedAt: this.failedAt, }; } /** in_progress/queued -> cancelling (idempotent if already cancelling). Returns store update. */ cancelling(): Partial { if (this.status === RunStatus.Cancelling) { return { status: this.status }; } if (this.status !== RunStatus.InProgress && this.status !== RunStatus.Queued) { throw new InvalidRunStateTransitionError(this.status, RunStatus.Cancelling); } this.status = RunStatus.Cancelling; return { status: this.status }; } /** cancelling -> cancelled. Returns store update. */ cancel(): Partial { if (this.status !== RunStatus.Cancelling) { throw new InvalidRunStateTransitionError(this.status, RunStatus.Cancelled); } this.status = RunStatus.Cancelled; this.cancelledAt = nowUnix(); return { status: this.status, cancelledAt: this.cancelledAt, }; } /** Produce a RunObject snapshot for API / SSE. */ snapshot(): RunObject { return { id: this.id, object: AgentObjectType.ThreadRun, createdAt: this.createdAt, threadId: this.threadId, status: this.status, lastError: this.lastError, startedAt: this.startedAt ?? null, completedAt: this.completedAt ?? null, cancelledAt: this.cancelledAt ?? null, failedAt: this.failedAt ?? null, usage: this.usage ?? null, metadata: this.metadata, config: this.config, }; } } ================================================ FILE: core/agent-runtime/src/SSEWriter.ts ================================================ /** * Abstract interface for writing SSE events. * Decouples AgentRuntime from HTTP transport details. */ export interface SSEWriter { /** Write an SSE event with the given name and JSON-serializable data. */ writeEvent(event: string, data: unknown): void; /** Write an SSE comment (e.g. `: keepalive`). Used for heartbeat signals. */ writeComment(text: string): void; /** Whether the underlying connection has been closed. */ readonly closed: boolean; /** End the SSE stream. */ end(): void; /** Register a callback for when the client disconnects. */ onClose(callback: () => void): void; } ================================================ FILE: core/agent-runtime/test/AgentRuntime.test.ts ================================================ import assert from 'node:assert'; import { setTimeout } from 'node:timers/promises'; import type { RunRecord, CreateRunInput, AgentMessage, SDKResultMessage, StreamEvent, } from '@eggjs/tegg-types/agent-runtime'; import { RunStatus, AgentObjectType, AgentNotFoundError, AgentConflictError, AgentTimeoutError, } from '@eggjs/tegg-types/agent-runtime'; import { AgentRuntime } from '../src/AgentRuntime'; import type { AgentExecutor, AgentRuntimeOptions } from '../src/AgentRuntime'; import { OSSAgentStore } from '../src/OSSAgentStore'; import type { SSEWriter } from '../src/SSEWriter'; import { MapStorageClient } from './helpers'; class MockSSEWriter implements SSEWriter { events: Array<{ event: string; data: unknown }> = []; comments: string[] = []; closed = false; private closeCallbacks: Array<() => void> = []; writeEvent(event: string, data: unknown): void { this.events.push({ event, data }); } writeComment(text: string): void { this.comments.push(text); } end(): void { this.closed = true; } onClose(callback: () => void): void { this.closeCallbacks.push(callback); } simulateClose(): void { this.closed = true; for (const cb of this.closeCallbacks) cb(); } } async function waitForRunStatus( agentStore: OSSAgentStore, runId: string, expectedStatus: RunStatus, timeoutMs = 2000, ): Promise { const start = Date.now(); while (Date.now() - start < timeoutMs) { const run = await agentStore.getRun(runId); if (run.status === expectedStatus) return; await setTimeout(10); } throw new Error(`Run ${runId} did not reach status '${expectedStatus}' within ${timeoutMs}ms`); } function createSlowExecRun(chunks: AgentMessage[], onYielded?: () => void): AgentExecutor['execRun'] { return async function* (_input: CreateRunInput, signal?: AbortSignal): AsyncGenerator { for (const chunk of chunks) { yield chunk; } onYielded?.(); await new Promise((resolve, reject) => { const timer = globalThis.setTimeout(resolve, 5000); if (signal) { signal.addEventListener( 'abort', () => { clearTimeout(timer); reject(new Error('aborted')); }, { once: true }, ); } }); }; } function createBlockingExecRun( resolveRef: { resolve?: () => void }, chunks: AgentMessage[], ): AgentExecutor['execRun'] { return async function* (_input: CreateRunInput, signal?: AbortSignal): AsyncGenerator { await new Promise((resolve, reject) => { resolveRef.resolve = resolve; if (signal) { signal.addEventListener('abort', () => reject(new Error('aborted')), { once: true }); } }); for (const chunk of chunks) { yield chunk; } }; } describe('test/AgentRuntime.test.ts', () => { let runtime: AgentRuntime; let store: OSSAgentStore; let executor: AgentExecutor; beforeEach(() => { store = new OSSAgentStore({ client: new MapStorageClient() }); executor = { async* execRun(input: CreateRunInput): AsyncGenerator { const messages = input.input.messages; yield { type: 'assistant', message: { role: 'assistant', content: [{ type: 'text', text: `Hello ${messages.length} messages` }], }, }; yield { type: 'result', subtype: 'success', usage: { input_tokens: 10, output_tokens: 5 }, } as SDKResultMessage; }, }; runtime = new AgentRuntime({ executor, store, logger: { info() { /* noop */ }, error() { /* noop */ }, } as unknown as AgentRuntimeOptions['logger'], }); }); afterEach(async () => { await runtime.destroy(); }); describe('createThread', () => { it('should create a thread and return ThreadObject', async () => { const result = await runtime.createThread(); assert(result.id.startsWith('thread_')); assert.equal(result.object, AgentObjectType.Thread); assert(typeof result.createdAt === 'number'); // Unix seconds assert(result.createdAt <= Math.floor(Date.now() / 1000)); assert(typeof result.metadata === 'object'); }); }); describe('getThread', () => { it('should get a thread by id', async () => { const created = await runtime.createThread(); const result = await runtime.getThread(created.id); assert.equal(result.id, created.id); assert.equal(result.object, AgentObjectType.Thread); assert(Array.isArray(result.messages)); }); it('should throw AgentNotFoundError for non-existent thread', async () => { await assert.rejects( () => runtime.getThread('thread_xxx'), (err: unknown) => { assert(err instanceof AgentNotFoundError); assert.equal(err.status, 404); return true; }, ); }); }); describe('syncRun', () => { it('should collect all chunks and return completed RunObject', async () => { const result = await runtime.syncRun({ input: { messages: [{ role: 'user', content: 'Hi' }] }, }); assert(result.id.startsWith('run_')); assert.equal(result.object, AgentObjectType.ThreadRun); assert.equal(result.status, RunStatus.Completed); assert(result.threadId); assert(result.threadId.startsWith('thread_')); assert.equal(result.usage!.promptTokens, 10); assert.equal(result.usage!.completionTokens, 5); assert.equal(result.usage!.totalTokens, 15); assert(result.startedAt! >= result.createdAt, 'startedAt should be >= createdAt'); }); it('should pass metadata through to store and return it', async () => { const meta = { user_id: 'u_1', trace: 'xyz' }; const result = await runtime.syncRun({ input: { messages: [{ role: 'user', content: 'Hi' }] }, metadata: meta, }); assert.deepStrictEqual(result.metadata, meta); const run = await store.getRun(result.id); assert.deepStrictEqual(run.metadata, meta); }); it('should store the run in the store', async () => { const result = await runtime.syncRun({ input: { messages: [{ role: 'user', content: 'Hi' }] }, }); const run = await store.getRun(result.id); assert.equal(run.status, RunStatus.Completed); assert(run.completedAt); }); it('should append messages to thread when threadId provided', async () => { const thread = await runtime.createThread(); await runtime.syncRun({ threadId: thread.id, input: { messages: [{ role: 'user', content: 'Hi' }] }, }); const updated = await runtime.getThread(thread.id); // Should have: user input message + assistant message + result message assert.equal(updated.messages.length, 2); assert.equal(updated.messages[0].type, 'user'); assert.equal(updated.messages[1].type, 'assistant'); }); it('should auto-create thread and append messages when threadId not provided', async () => { const result = await runtime.syncRun({ input: { messages: [{ role: 'user', content: 'Hi' }] }, }); assert(result.threadId); assert(result.threadId.startsWith('thread_')); const thread = await runtime.getThread(result.threadId); assert.equal(thread.messages.length, 2); assert.equal(thread.messages[0].type, 'user'); assert.equal(thread.messages[1].type, 'assistant'); }); it('should set isResume=false when no threadId provided (auto-create)', async () => { let capturedInput: CreateRunInput | undefined; executor.execRun = async function* (input: CreateRunInput): AsyncGenerator { capturedInput = input; yield { type: 'assistant', message: { role: 'assistant', content: [{ type: 'text', text: 'hi' }] } }; }; await runtime.syncRun({ input: { messages: [{ role: 'user', content: 'Hi' }] }, }); assert.equal(capturedInput!.isResume, false); }); it('should set isResume=false when threadId provided but thread has no messages', async () => { let capturedInput: CreateRunInput | undefined; executor.execRun = async function* (input: CreateRunInput): AsyncGenerator { capturedInput = input; yield { type: 'assistant', message: { role: 'assistant', content: [{ type: 'text', text: 'hi' }] } }; }; const thread = await runtime.createThread(); await runtime.syncRun({ threadId: thread.id, input: { messages: [{ role: 'user', content: 'Hi' }] }, }); assert.equal(capturedInput!.isResume, false); }); it('should set isResume=true when threadId provided and thread has messages', async () => { let capturedInput: CreateRunInput | undefined; executor.execRun = async function* (input: CreateRunInput): AsyncGenerator { capturedInput = input; yield { type: 'assistant', message: { role: 'assistant', content: [{ type: 'text', text: 'hi' }] } }; }; // First run creates thread with messages const result = await runtime.syncRun({ input: { messages: [{ role: 'user', content: 'Hi' }] }, }); // Second run on the same thread — now it has history await runtime.syncRun({ threadId: result.threadId, input: { messages: [{ role: 'user', content: 'Hello again' }] }, }); assert.equal(capturedInput!.isResume, true); }); it('should not throw when store.updateRun fails in catch block', async () => { executor.execRun = async function* (): AsyncGenerator { throw new Error('exec failed'); }; let callCount = 0; const origUpdateRun = store.updateRun.bind(store); store.updateRun = async (runId: string, updates: Partial) => { callCount++; if (callCount === 2) { throw new Error('store down'); } return origUpdateRun(runId, updates); }; await assert.rejects( () => runtime.syncRun({ input: { messages: [{ role: 'user', content: 'Hi' }] } }), (err: unknown) => { assert(err instanceof Error); assert.equal(err.message, 'exec failed'); return true; }, ); }); }); describe('asyncRun', () => { it('should return queued status immediately with auto-created threadId', async () => { const result = await runtime.asyncRun({ input: { messages: [{ role: 'user', content: 'Hi' }] }, }); assert(result.id.startsWith('run_')); assert.equal(result.object, AgentObjectType.ThreadRun); assert.equal(result.status, RunStatus.Queued); assert(result.threadId); assert(result.threadId.startsWith('thread_')); }); it('should complete the run in the background', async () => { const result = await runtime.asyncRun({ input: { messages: [{ role: 'user', content: 'Hi' }] }, }); await runtime.waitForPendingTasks(); const run = await store.getRun(result.id); assert.equal(run.status, RunStatus.Completed); }); it('should auto-create thread and append messages when threadId not provided', async () => { const result = await runtime.asyncRun({ input: { messages: [{ role: 'user', content: 'Hi' }] }, }); assert(result.threadId); await runtime.waitForPendingTasks(); const thread = await store.getThread(result.threadId); assert.equal(thread.messages.length, 2); assert.equal(thread.messages[0].type, 'user'); assert.equal(thread.messages[1].type, 'assistant'); }); it('should pass metadata through to store and return it', async () => { const meta = { session: 'sess_1' }; const result = await runtime.asyncRun({ input: { messages: [{ role: 'user', content: 'Hi' }] }, metadata: meta, }); assert.deepStrictEqual(result.metadata, meta); await runtime.waitForPendingTasks(); const run = await store.getRun(result.id); assert.deepStrictEqual(run.metadata, meta); }); }); describe('streamRun', () => { it('should emit StreamEvent sequence: run_created, message events, done', async () => { const writer = new MockSSEWriter(); await runtime.streamRun({ input: { messages: [{ role: 'user', content: 'Hi' }] } }, writer); const eventTypes = writer.events.map(e => e.event); assert(eventTypes.includes('run_created'), 'should have run_created event'); assert(eventTypes.includes('done'), 'should have done event'); assert(writer.closed, 'writer should be closed'); // Verify order: run_created comes first, done comes last const createdIdx = eventTypes.indexOf('run_created'); const doneIdx = eventTypes.indexOf('done'); assert(createdIdx === 0, 'run_created should be first'); assert(doneIdx === eventTypes.length - 1, 'done should be last'); // Verify StreamEvent format: { seq, type, data, ts } for (const ev of writer.events) { const streamEvent = ev.data as StreamEvent; assert(typeof streamEvent.seq === 'number', 'seq should be a number'); assert(typeof streamEvent.type === 'string', 'type should be a string'); assert(typeof streamEvent.ts === 'number', 'ts should be a number'); assert('data' in streamEvent, 'should have data field'); } // Verify sequential seq numbers const seqs = writer.events.map(e => (e.data as StreamEvent).seq); for (let i = 1; i < seqs.length; i++) { assert(seqs[i] === seqs[i - 1] + 1, `seq should be sequential: ${seqs[i]} after ${seqs[i - 1]}`); } // Verify run_created data has runId and threadId const runCreatedEvent = writer.events[0].data as StreamEvent; assert((runCreatedEvent.data as any).runId, 'run_created should have runId'); assert((runCreatedEvent.data as any).threadId, 'run_created should have threadId'); // Verify messages persisted to thread const threadId = (runCreatedEvent.data as any).threadId; const thread = await runtime.getThread(threadId); assert.equal(thread.messages.length, 2); assert.equal(thread.messages[0].type, 'user'); assert.equal(thread.messages[1].type, 'assistant'); }); it('should continue background execution on client disconnect', async () => { let resolveYielded!: () => void; const yieldedPromise = new Promise(r => { resolveYielded = r; }); executor.execRun = async function* ( _input: CreateRunInput, signal?: AbortSignal, ): AsyncGenerator { yield { type: 'assistant', message: { role: 'assistant', content: [{ type: 'text', text: 'start' }] } }; resolveYielded(); // Simulate more work after client disconnects await setTimeout(50); if (!signal?.aborted) { yield { type: 'assistant', message: { role: 'assistant', content: [{ type: 'text', text: ' end' }] } }; } }; const writer = new MockSSEWriter(); const streamPromise = runtime.streamRun( { input: { messages: [{ role: 'user', content: 'Hi' }] } }, writer, ); // Wait for first chunk to be yielded, then give the writer time to receive it await yieldedPromise; await setTimeout(50); writer.simulateClose(); await streamPromise; // Writer should have received at least run_created before disconnect assert(writer.events.length >= 1, 'should have at least run_created event'); // Background task should complete — wait for it await runtime.waitForPendingTasks(); // Verify the run completed in the store (not cancelled) const runCreatedEvent = writer.events[0].data as StreamEvent; const runId = (runCreatedEvent.data as any).runId; const run = await runtime.getRun(runId); assert.equal(run.status, RunStatus.Completed, 'run should complete despite client disconnect'); }); it('should forward SDK message types as event types', async () => { executor.execRun = async function* (): AsyncGenerator { yield { type: 'system', subtype: 'init', session_id: 'sess-1' }; yield { type: 'stream_event', event: { type: 'content_block_delta' } }; yield { type: 'assistant', message: { role: 'assistant', content: [{ type: 'text', text: 'Hello' }] } }; }; const writer = new MockSSEWriter(); await runtime.streamRun({ input: { messages: [{ role: 'user', content: 'Hi' }] } }, writer); const eventTypes = writer.events.map(e => e.event); assert(eventTypes.includes('system'), 'should forward system event'); assert(eventTypes.includes('stream_event'), 'should forward stream_event'); assert(eventTypes.includes('assistant'), 'should forward assistant event'); }); it('should pass through SDK message directly as event data', async () => { const sdkMsg: AgentMessage = { type: 'assistant', message: { role: 'assistant', content: [{ type: 'text', text: 'raw hello' }] }, }; executor.execRun = async function* (): AsyncGenerator { yield sdkMsg; }; const writer = new MockSSEWriter(); await runtime.streamRun({ input: { messages: [{ role: 'user', content: 'Hi' }] } }, writer); const assistantEvent = writer.events.find(e => e.event === 'assistant'); assert.ok(assistantEvent); const streamEvent = assistantEvent.data as StreamEvent; assert.deepStrictEqual(streamEvent.data, sdkMsg, 'should pass SDK message directly as data'); }); it('should use "message" as default event type when type is not set', async () => { executor.execRun = async function* (): AsyncGenerator { yield { type: '', message: { content: 'Hello' } } as unknown as AgentMessage; }; const writer = new MockSSEWriter(); await runtime.streamRun({ input: { messages: [{ role: 'user', content: 'Hi' }] } }, writer); const eventTypes = writer.events.map(e => e.event); assert(eventTypes.includes('message'), 'should fallback to "message" for empty type'); }); it('should emit error event when execRun throws', async () => { executor.execRun = async function* (): AsyncGenerator { throw new Error('model unavailable'); }; const writer = new MockSSEWriter(); await runtime.streamRun({ input: { messages: [{ role: 'user', content: 'Hi' }] } }, writer); const errorEvent = writer.events.find(e => e.event === 'error'); assert.ok(errorEvent, 'should have error event'); const streamEvent = errorEvent.data as StreamEvent; assert.equal((streamEvent.data as any).message, 'model unavailable'); assert(writer.closed); // Verify run is marked as failed in store const runCreatedEvent = writer.events[0].data as StreamEvent; const runId = (runCreatedEvent.data as any).runId; const run = await runtime.getRun(runId); assert.equal(run.status, RunStatus.Failed); }); it('should persist usage to store on completion', async () => { executor.execRun = async function* (): AsyncGenerator { yield { type: 'assistant', message: { content: 'Hi' } }; yield { type: 'result', subtype: 'success', usage: { input_tokens: 10, output_tokens: 8 }, } as SDKResultMessage; }; const writer = new MockSSEWriter(); await runtime.streamRun({ input: { messages: [{ role: 'user', content: 'Hi' }] } }, writer); const runCreatedEvent = writer.events[0].data as StreamEvent; const runId = (runCreatedEvent.data as any).runId; const run = await runtime.getRun(runId); assert.equal(run.status, RunStatus.Completed); assert.equal(run.usage!.promptTokens, 10); assert.equal(run.usage!.completionTokens, 8); assert.equal(run.usage!.totalTokens, 18); }); }); describe('getRunStream', () => { it('should replay all events on reconnect with lastSeq=0', async () => { const writer1 = new MockSSEWriter(); await runtime.streamRun({ input: { messages: [{ role: 'user', content: 'Hi' }] } }, writer1); // Get runId from the first event const runCreatedEvent = writer1.events[0].data as StreamEvent; const runId = (runCreatedEvent.data as any).runId; // Reconnect and get all events const writer2 = new MockSSEWriter(); await runtime.getRunStream(runId, writer2, 0); // Should get the same events assert.equal(writer2.events.length, writer1.events.length); for (let i = 0; i < writer1.events.length; i++) { const se1 = writer1.events[i].data as StreamEvent; const se2 = writer2.events[i].data as StreamEvent; assert.equal(se1.seq, se2.seq); assert.equal(se1.type, se2.type); } }); it('should replay only events after lastSeq', async () => { const writer1 = new MockSSEWriter(); await runtime.streamRun({ input: { messages: [{ role: 'user', content: 'Hi' }] } }, writer1); const runCreatedEvent = writer1.events[0].data as StreamEvent; const runId = (runCreatedEvent.data as any).runId; const totalEvents = writer1.events.length; // Reconnect from seq 2 (skip first 2 events) const writer2 = new MockSSEWriter(); await runtime.getRunStream(runId, writer2, 2); assert.equal(writer2.events.length, totalEvents - 2); const firstReplayedSeq = (writer2.events[0].data as StreamEvent).seq; assert.equal(firstReplayedSeq, 3); }); it('should throw AgentNotFoundError for unknown runId', async () => { const writer = new MockSSEWriter(); await assert.rejects( () => runtime.getRunStream('run_nonexistent', writer), (err: unknown) => { assert(err instanceof AgentNotFoundError); return true; }, ); }); it('should stream real-time events during reconnect to running task', async () => { let resolveExec!: () => void; const execPromise = new Promise(r => { resolveExec = r; }); executor.execRun = async function* ( _input: CreateRunInput, signal?: AbortSignal, ): AsyncGenerator { yield { type: 'assistant', message: { content: 'chunk1' } }; // Wait for reconnect to happen await execPromise; if (!signal?.aborted) { yield { type: 'assistant', message: { content: 'chunk2' } }; } }; // Start streaming and disconnect immediately after first events const writer1 = new MockSSEWriter(); const streamPromise = runtime.streamRun( { input: { messages: [{ role: 'user', content: 'Hi' }] } }, writer1, ); // Wait a bit for the first chunk to be buffered await setTimeout(50); writer1.simulateClose(); await streamPromise; const runId = ((writer1.events[0].data as StreamEvent).data as any).runId; // Reconnect — should get replayed events then real-time const writer2 = new MockSSEWriter(); const reconnectPromise = runtime.getRunStream(runId, writer2, 0); // Let the background task continue await setTimeout(20); resolveExec(); await runtime.waitForPendingTasks(); // Give streamEventsToWriter time to process the final events await setTimeout(20); // Close writer2 to end reconnect writer2.simulateClose(); await reconnectPromise; // writer2 should have received all events including chunk2 and done const eventTypes = writer2.events.map(e => e.event); assert(eventTypes.includes('done'), 'reconnected stream should receive done event'); }); }); describe('getRun', () => { it('should get a run by id', async () => { const syncResult = await runtime.syncRun({ input: { messages: [{ role: 'user', content: 'Hi' }] }, }); const result = await runtime.getRun(syncResult.id); assert.equal(result.id, syncResult.id); assert.equal(result.object, AgentObjectType.ThreadRun); assert.equal(result.status, RunStatus.Completed); assert(typeof result.createdAt === 'number'); }); it('should return metadata from getRun', async () => { const meta = { source: 'api' }; const syncResult = await runtime.syncRun({ input: { messages: [{ role: 'user', content: 'Hi' }] }, metadata: meta, }); const result = await runtime.getRun(syncResult.id); assert.deepStrictEqual(result.metadata, meta); }); }); describe('cancelRun', () => { it('should cancel a run', async () => { executor.execRun = createSlowExecRun([ { type: 'assistant', message: { role: 'assistant', content: [{ type: 'text', text: 'start' }] } }, ]); const result = await runtime.asyncRun({ input: { messages: [{ role: 'user', content: 'Hi' }] }, }); await waitForRunStatus(store, result.id, RunStatus.InProgress); const cancelResult = await runtime.cancelRun(result.id); assert.equal(cancelResult.id, result.id); assert.equal(cancelResult.object, AgentObjectType.ThreadRun); assert.equal(cancelResult.status, RunStatus.Cancelled); const run = await store.getRun(result.id); assert.equal(run.status, RunStatus.Cancelled); assert(run.cancelledAt); }); it('should write cancelling then cancelled to store', async () => { executor.execRun = createSlowExecRun([ { type: 'assistant', message: { role: 'assistant', content: [{ type: 'text', text: 'start' }] } }, ]); const statusHistory: string[] = []; const origUpdateRun = store.updateRun.bind(store); store.updateRun = async (runId: string, updates: Partial) => { if (updates.status) { statusHistory.push(updates.status); } return origUpdateRun(runId, updates); }; const asyncResult = await runtime.asyncRun({ input: { messages: [{ role: 'user', content: 'Hello' }] }, }); await waitForRunStatus(store, asyncResult.id, RunStatus.InProgress); statusHistory.length = 0; await runtime.cancelRun(asyncResult.id); const cancellingIdx = statusHistory.indexOf(RunStatus.Cancelling); const cancelledIdx = statusHistory.indexOf(RunStatus.Cancelled); assert(cancellingIdx >= 0, 'cancelling should have been written'); assert(cancelledIdx > cancellingIdx, 'cancelled should come after cancelling'); }); it('should throw AgentConflictError when cancelling a completed run', async () => { const result = await runtime.syncRun({ input: { messages: [{ role: 'user', content: 'Hi' }] }, }); assert.equal(result.status, RunStatus.Completed); await assert.rejects( () => runtime.cancelRun(result.id), (err: unknown) => { assert(err instanceof AgentConflictError); return true; }, ); }); it('should not overwrite cancelling status with completed (cross-worker scenario)', async () => { const resolveRef: { resolve?: () => void } = {}; executor.execRun = createBlockingExecRun(resolveRef, [ { type: 'assistant', message: { role: 'assistant', content: [{ type: 'text', text: 'done' }] } }, { type: 'result', subtype: 'success', usage: { input_tokens: 1, output_tokens: 1 }, } as SDKResultMessage, ]); const result = await runtime.asyncRun({ input: { messages: [{ role: 'user', content: 'Hi' }] }, }); await waitForRunStatus(store, result.id, RunStatus.InProgress); await store.updateRun(result.id, { status: RunStatus.Cancelling }); resolveRef.resolve!(); await runtime.waitForPendingTasks(); const run = await store.getRun(result.id); assert.equal(run.status, RunStatus.Cancelling); }); it('should hold until the executor commits before aborting and persisting', async () => { // Simulates the Claude Code SDK startup window: the executor emits a // non-committing `system/init` right away and then a committing // `assistant` message later. cancelRun is called in between and must // wait for the assistant chunk before it aborts, otherwise it would // persist a user message against a session file that does not yet // exist on disk. let resolveGate!: () => void; const gate = new Promise(r => { resolveGate = r; }); executor.execRun = async function* (_input: CreateRunInput, signal?: AbortSignal): AsyncGenerator { yield { type: 'system', subtype: 'init' } as AgentMessage; await gate; yield { type: 'assistant', message: { role: 'assistant', content: [{ type: 'text', text: 'now committed' }] } }; await new Promise((_resolve, reject) => { if (signal?.aborted) { reject(new Error('aborted')); return; } signal?.addEventListener('abort', () => reject(new Error('aborted')), { once: true }); }); }; const thread = await runtime.createThread(); const result = await runtime.asyncRun({ threadId: thread.id, input: { messages: [{ role: 'user', content: 'Hi' }] }, }); await waitForRunStatus(store, result.id, RunStatus.InProgress); // Give the executor time to yield the first system message; the task // must still be considered uncommitted because type === 'system'. await setTimeout(30); const cancelPromise = runtime.cancelRun(result.id); let cancelResolved = false; cancelPromise.then(() => { cancelResolved = true; }, () => { cancelResolved = true; }); // Hold for a bit — cancel should not resolve while we are blocked in // the gate, because no committing message has been yielded yet. await setTimeout(100); assert.equal(cancelResolved, false, 'cancelRun must block until executor commits'); // Release the gate so the executor yields a non-system message. resolveGate(); const cancelled = await cancelPromise; assert.equal(cancelled.status, RunStatus.Cancelled); // Thread should contain both the user input and the partial assistant // reply — i.e. it is in sync with what the SDK jsonl would contain. const updated = await runtime.getThread(thread.id); assert.equal(updated.messages.length, 2); assert.equal(updated.messages[0].type, 'user'); assert.equal(updated.messages[1].type, 'assistant'); }); it('should fail the run and leave the thread empty when cancel waits past the commit timeout', async () => { // Rebuild the runtime with a short commit timeout so the watchdog // fires before the blocking executor ever yields. await runtime.destroy(); const resolveRef: { resolve?: () => void } = {}; executor.execRun = createBlockingExecRun(resolveRef, []); runtime = new AgentRuntime({ executor, store, logger: { info() { /* noop */ }, error() { /* noop */ } } as unknown as AgentRuntimeOptions['logger'], cancelCommitTimeoutMs: 50, }); const thread = await runtime.createThread(); const result = await runtime.asyncRun({ threadId: thread.id, input: { messages: [{ role: 'user', content: 'Hi' }] }, }); await waitForRunStatus(store, result.id, RunStatus.InProgress); await assert.rejects( () => runtime.cancelRun(result.id), (err: unknown) => { assert(err instanceof AgentTimeoutError, `expected AgentTimeoutError, got ${err}`); return true; }, ); const persisted = await store.getRun(result.id); assert.equal(persisted.status, RunStatus.Failed); const updated = await runtime.getThread(thread.id); assert.equal(updated.messages.length, 0, 'thread must stay empty when executor never committed'); // Unblock the executor so runtime.destroy() in afterEach completes cleanly. resolveRef.resolve?.(); }); it('should not overwrite watchdog-set Failed with Completed when executor finishes naturally without committing', async () => { // Regression test for a TOCTOU race on the commit-timeout path: // 1. cancelRun's watchdog fires at cancelCommitTimeoutMs and writes Failed. // 2. The executor does not listen to the abort signal and finishes // naturally (no committing message ever yielded). // 3. asyncRun's IIFE then enters its post-loop status check; without // the Failed/Expired guard it would fall through to rb.complete() // and overwrite the watchdog-set Failed with Completed. await runtime.destroy(); let resolveGate!: () => void; const gate = new Promise(r => { resolveGate = r; }); executor = { async* execRun(): AsyncGenerator { yield { type: 'system', subtype: 'init' } as AgentMessage; // Intentionally does NOT listen to the abort signal — simulates an // executor that keeps running to natural completion after the // runtime has already declared the run Failed. await gate; }, }; runtime = new AgentRuntime({ executor, store, logger: { info() { /* noop */ }, error() { /* noop */ } } as unknown as AgentRuntimeOptions['logger'], cancelCommitTimeoutMs: 50, }); const thread = await runtime.createThread(); const result = await runtime.asyncRun({ threadId: thread.id, input: { messages: [{ role: 'user', content: 'Hi' }] }, }); await waitForRunStatus(store, result.id, RunStatus.InProgress); const cancelPromise = runtime.cancelRun(result.id); // Wait well past the 50ms watchdog so Failed has definitely been // written, then release the gate to let the executor finish naturally. await setTimeout(150); resolveGate(); await assert.rejects( cancelPromise, (err: unknown) => { assert(err instanceof AgentTimeoutError, `expected AgentTimeoutError, got ${err}`); return true; }, ); const persisted = await store.getRun(result.id); assert.equal( persisted.status, RunStatus.Failed, 'watchdog-set Failed must not be overwritten by post-loop rb.complete', ); const updated = await runtime.getThread(thread.id); assert.equal(updated.messages.length, 0, 'thread must stay empty when executor never committed'); }); it('should not hold cancelRun when the executor has already committed', async () => { executor.execRun = createSlowExecRun([ { type: 'system', subtype: 'init' } as AgentMessage, { type: 'assistant', message: { role: 'assistant', content: [{ type: 'text', text: 'committed' }] } }, ]); const result = await runtime.asyncRun({ input: { messages: [{ role: 'user', content: 'Hi' }] }, }); await waitForRunStatus(store, result.id, RunStatus.InProgress); // Wait long enough for the assistant chunk to land and mark committed. await setTimeout(50); const start = Date.now(); const cancelled = await runtime.cancelRun(result.id); const elapsed = Date.now() - start; assert.equal(cancelled.status, RunStatus.Cancelled); assert(elapsed < 1000, `cancelRun should return quickly when already committed, took ${elapsed}ms`); }); it('should consult a custom isSessionCommitted hook instead of the default heuristic', async () => { // The executor yields three assistant messages; the hook treats only // the third as committing. cancelRun must wait for the third. const decisions: Array<{ text: string; committed: boolean }> = []; let resolveGate!: () => void; const gate = new Promise(r => { resolveGate = r; }); executor.execRun = async function* (_input: CreateRunInput, signal?: AbortSignal): AsyncGenerator { yield { type: 'assistant', message: { role: 'assistant', content: [{ type: 'text', text: 'first' }] } }; yield { type: 'assistant', message: { role: 'assistant', content: [{ type: 'text', text: 'second' }] } }; await gate; yield { type: 'assistant', message: { role: 'assistant', content: [{ type: 'text', text: 'committed' }] } }; await new Promise((_resolve, reject) => { if (signal?.aborted) { reject(new Error('aborted')); return; } signal?.addEventListener('abort', () => reject(new Error('aborted')), { once: true }); }); }; executor.isSessionCommitted = (msg: AgentMessage) => { const text = ((msg as { message?: { content?: Array<{ text?: string }> } }).message?.content?.[0]?.text) ?? ''; const committed = text === 'committed'; decisions.push({ text, committed }); return committed; }; const result = await runtime.asyncRun({ input: { messages: [{ role: 'user', content: 'Hi' }] }, }); await waitForRunStatus(store, result.id, RunStatus.InProgress); // Let the first two non-committing chunks land before cancelling. await setTimeout(30); const cancelPromise = runtime.cancelRun(result.id); let cancelResolved = false; cancelPromise.then(() => { cancelResolved = true; }, () => { cancelResolved = true; }); await setTimeout(80); assert.equal(cancelResolved, false, 'cancelRun must wait for the hook to accept a message as committed'); resolveGate(); const cancelled = await cancelPromise; assert.equal(cancelled.status, RunStatus.Cancelled); assert(decisions.some(d => d.committed), 'hook must have observed the committing chunk'); }); it('should not overwrite terminal state when run completes during cancellation (TOCTOU)', async () => { const resolveRef: { resolve?: () => void } = {}; executor.execRun = createBlockingExecRun(resolveRef, [ { type: 'assistant', message: { role: 'assistant', content: [{ type: 'text', text: 'done' }] } }, { type: 'result', subtype: 'success', usage: { input_tokens: 1, output_tokens: 1 }, } as SDKResultMessage, ]); const result = await runtime.asyncRun({ input: { messages: [{ role: 'user', content: 'Hi' }] }, }); await waitForRunStatus(store, result.id, RunStatus.InProgress); const origUpdateRun = store.updateRun.bind(store); store.updateRun = async (runId: string, updates: Partial) => { await origUpdateRun(runId, updates); if (updates.status === RunStatus.Cancelling) { await origUpdateRun(runId, { status: RunStatus.Completed, completedAt: Math.floor(Date.now() / 1000) }); store.updateRun = origUpdateRun; } }; resolveRef.resolve!(); const cancelResult = await runtime.cancelRun(result.id); assert.equal(cancelResult.status, RunStatus.Completed); }); }); describe('abort message persistence', () => { // Rationale: when an executor supports resuming from a session file // (e.g. Claude CLI session), aborts leave partial state in that session. // If the thread is NOT updated with the same partial state, any // subsequent resume request diverges from the executor's view of history // and can fail at executor startup. These tests pin down that abort // writes the same messages to the thread that the executor has already // observed. async function waitUntil(cond: () => boolean, timeoutMs = 2000): Promise { const start = Date.now(); while (!cond()) { if (Date.now() - start > timeoutMs) throw new Error('waitUntil timeout'); await setTimeout(10); } } it('syncRun: should persist user + partial assistant messages when aborted via signal', async () => { let yielded = false; executor.execRun = createSlowExecRun( [{ type: 'assistant', message: { role: 'assistant', content: [{ type: 'text', text: 'partial' }] } }], () => { yielded = true; }, ); const thread = await runtime.createThread(); const ac = new AbortController(); const syncPromise = runtime.syncRun( { threadId: thread.id, input: { messages: [{ role: 'user', content: 'Hi' }] } }, ac.signal, ); await waitUntil(() => yielded); ac.abort(); const result = await syncPromise; assert.equal(result.threadId, thread.id); const updated = await runtime.getThread(thread.id); assert.equal(updated.messages.length, 2); assert.equal(updated.messages[0].type, 'user'); assert.equal(updated.messages[1].type, 'assistant'); assert.deepStrictEqual( (updated.messages[1].message as { content: unknown }).content, [{ type: 'text', text: 'partial' }], ); }); it('syncRun: should NOT persist the user message when aborted before the executor commits', async () => { // When an external signal aborts before the executor has yielded any // non-system message, the executor's underlying session (e.g. the // Claude Code SDK jsonl file) was never created on disk. Persisting // the user message to the thread in that state would cause the next // run to diverge from a session that does not exist, so the thread // must be left empty instead. const resolveRef: { resolve?: () => void } = {}; executor.execRun = createBlockingExecRun(resolveRef, [ { type: 'assistant', message: { role: 'assistant', content: [{ type: 'text', text: 'never' }] } }, ]); const thread = await runtime.createThread(); const ac = new AbortController(); const syncPromise = runtime.syncRun( { threadId: thread.id, input: { messages: [{ role: 'user', content: 'Hi' }] } }, ac.signal, ); // Give syncRun time to enter the await on the executor await setTimeout(20); ac.abort(); await syncPromise; const updated = await runtime.getThread(thread.id); assert.equal(updated.messages.length, 0); }); it('asyncRun: should persist user + partial assistant messages when aborted via cancelRun', async () => { let yielded = false; executor.execRun = createSlowExecRun( [{ type: 'assistant', message: { role: 'assistant', content: [{ type: 'text', text: 'partial' }] } }], () => { yielded = true; }, ); const thread = await runtime.createThread(); const result = await runtime.asyncRun({ threadId: thread.id, input: { messages: [{ role: 'user', content: 'Hi' }] }, }); await waitUntil(() => yielded); await runtime.cancelRun(result.id); const updated = await runtime.getThread(thread.id); assert.equal(updated.messages.length, 2); assert.equal(updated.messages[0].type, 'user'); assert.equal(updated.messages[1].type, 'assistant'); }); it('streamRun: should persist user + partial assistant messages when aborted via cancelRun', async () => { let yielded = false; executor.execRun = createSlowExecRun( [{ type: 'assistant', message: { role: 'assistant', content: [{ type: 'text', text: 'partial' }] } }], () => { yielded = true; }, ); const thread = await runtime.createThread(); const writer = new MockSSEWriter(); const streamPromise = runtime.streamRun( { threadId: thread.id, input: { messages: [{ role: 'user', content: 'Hi' }] } }, writer, ); await waitUntil(() => yielded); // Wait for run_created event to land so we can read the runId await waitUntil(() => writer.events.some(e => e.event === 'run_created')); const runCreated = writer.events.find(e => e.event === 'run_created')!; const runId = ((runCreated.data as StreamEvent).data as { runId: string }).runId; await runtime.cancelRun(runId); writer.simulateClose(); await streamPromise; const updated = await runtime.getThread(thread.id); assert.equal(updated.messages.length, 2); assert.equal(updated.messages[0].type, 'user'); assert.equal(updated.messages[1].type, 'assistant'); }); it('should set isResume=true on the next run after an abort (regression: abort+continue)', async () => { // Reproduces the bug: user aborts turn N, then sends turn N+1. Before the fix, turn N's // messages were never persisted, so the second call's isResume depended only on earlier // completed turns — and any divergence from the executor's session caused subsequent // resume attempts to fail. let yielded = false; executor.execRun = createSlowExecRun( [{ type: 'assistant', message: { role: 'assistant', content: [{ type: 'text', text: 'partial' }] } }], () => { yielded = true; }, ); const thread = await runtime.createThread(); const ac = new AbortController(); const firstPromise = runtime.syncRun( { threadId: thread.id, input: { messages: [{ role: 'user', content: 'Hi' }] } }, ac.signal, ); await waitUntil(() => yielded); ac.abort(); await firstPromise; let capturedInput: CreateRunInput | undefined; executor.execRun = async function* (input: CreateRunInput): AsyncGenerator { capturedInput = input; yield { type: 'assistant', message: { role: 'assistant', content: [{ type: 'text', text: 'ok' }] } }; }; const secondResult = await runtime.syncRun({ threadId: thread.id, input: { messages: [{ role: 'user', content: 'continue' }] }, }); assert.equal(capturedInput!.isResume, true); assert.equal(capturedInput!.input.messages[0].content, 'continue'); assert.equal(secondResult.status, RunStatus.Completed); }); it('syncRun: should finalise run status to Cancelled when aborted via external signal (no cancelRun)', async () => { let yielded = false; executor.execRun = createSlowExecRun( [{ type: 'assistant', message: { role: 'assistant', content: [{ type: 'text', text: 'partial' }] } }], () => { yielded = true; }, ); const thread = await runtime.createThread(); const ac = new AbortController(); const syncPromise = runtime.syncRun( { threadId: thread.id, input: { messages: [{ role: 'user', content: 'Hi' }] } }, ac.signal, ); await waitUntil(() => yielded); ac.abort(); const snapshot = await syncPromise; // Snapshot reflects the live store state after finalisation assert.equal(snapshot.status, RunStatus.Cancelled); const persisted = await store.getRun(snapshot.id); assert.equal(persisted.status, RunStatus.Cancelled); assert(persisted.cancelledAt); }); it('asyncRun: should finalise run status to Cancelled when destroy() aborts in-flight runs', async () => { const resolveRef: { resolve?: () => void } = {}; executor.execRun = createBlockingExecRun(resolveRef, [ { type: 'assistant', message: { role: 'assistant', content: [{ type: 'text', text: 'never' }] } }, ]); const result = await runtime.asyncRun({ input: { messages: [{ role: 'user', content: 'Hi' }] }, }); await waitForRunStatus(store, result.id, RunStatus.InProgress); await runtime.destroy(); const persisted = await store.getRun(result.id); assert.equal(persisted.status, RunStatus.Cancelled); }); it('should preserve cancelling → cancelled ordering when cancelRun drives the abort', async () => { // Regression guard for the finaliseAbortedRun addition: our in-branch // finaliser must NOT write when cancelRun has already set `cancelling`, // otherwise cancelRun's own `cancel()` transition would throw. executor.execRun = createSlowExecRun([ { type: 'assistant', message: { role: 'assistant', content: [{ type: 'text', text: 'partial' }] } }, ]); const statusHistory: string[] = []; const origUpdateRun = store.updateRun.bind(store); store.updateRun = async (runId: string, updates: Partial) => { if (updates.status) statusHistory.push(updates.status); return origUpdateRun(runId, updates); }; const asyncResult = await runtime.asyncRun({ input: { messages: [{ role: 'user', content: 'Hi' }] }, }); await waitForRunStatus(store, asyncResult.id, RunStatus.InProgress); statusHistory.length = 0; await runtime.cancelRun(asyncResult.id); const cancellingWrites = statusHistory.filter(s => s === RunStatus.Cancelling).length; const cancelledWrites = statusHistory.filter(s => s === RunStatus.Cancelled).length; assert.equal(cancellingWrites, 1, 'cancelling should be written exactly once (by cancelRun)'); assert.equal(cancelledWrites, 1, 'cancelled should be written exactly once (by cancelRun)'); }); it('should swallow store.appendMessages errors during abort cleanup', async () => { let yielded = false; executor.execRun = createSlowExecRun( [{ type: 'assistant', message: { role: 'assistant', content: [{ type: 'text', text: 'partial' }] } }], () => { yielded = true; }, ); const thread = await runtime.createThread(); const origAppend = store.appendMessages.bind(store); store.appendMessages = async () => { throw new Error('store down'); }; const ac = new AbortController(); const syncPromise = runtime.syncRun( { threadId: thread.id, input: { messages: [{ role: 'user', content: 'Hi' }] } }, ac.signal, ); await waitUntil(() => yielded); ac.abort(); // Should resolve with a snapshot instead of rejecting const result = await syncPromise; assert(result.id.startsWith('run_')); // Thread append failed — state is empty, but the abort path completed cleanly store.appendMessages = origAppend; }); }); }); ================================================ FILE: core/agent-runtime/test/HttpSSEWriter.test.ts ================================================ import assert from 'node:assert'; import { EventEmitter } from 'node:events'; import { HttpSSEWriter } from '../src/HttpSSEWriter'; /** * Minimal mock of Node.js ServerResponse for testing HttpSSEWriter. * Captures writeHead/write/end calls and emits 'close' on demand. */ class MockServerResponse extends EventEmitter { writtenHead: { statusCode: number; headers: Record } | null = null; chunks: string[] = []; ended = false; writeHead(statusCode: number, headers: Record): void { this.writtenHead = { statusCode, headers }; } write(chunk: string): boolean { this.chunks.push(chunk); return true; } end(): void { this.ended = true; } } describe('test/HttpSSEWriter.test.ts', () => { let res: MockServerResponse; beforeEach(() => { res = new MockServerResponse(); }); it('should delay headers until first writeEvent', () => { const writer = new HttpSSEWriter(res as any); // Headers not sent yet after construction assert.equal(res.writtenHead, null); assert.equal(res.chunks.length, 0); writer.writeEvent('test', { foo: 'bar' }); // Now headers should be sent assert.ok(res.writtenHead); assert.equal(res.writtenHead.statusCode, 200); }); it('should use lowercase header keys', () => { const writer = new HttpSSEWriter(res as any); writer.writeEvent('ping', {}); assert.ok(res.writtenHead); assert.equal(res.writtenHead.headers['content-type'], 'text/event-stream'); assert.equal(res.writtenHead.headers['cache-control'], 'no-cache'); assert.equal(res.writtenHead.headers.connection, 'keep-alive'); }); it('should format SSE events correctly', () => { const writer = new HttpSSEWriter(res as any); writer.writeEvent('message', { text: 'hello' }); assert.equal(res.chunks.length, 1); assert.equal(res.chunks[0], 'event: message\ndata: {"text":"hello"}\n\n'); }); it('should not write after connection closes', () => { const writer = new HttpSSEWriter(res as any); // Simulate client disconnect res.emit('close'); assert.equal(writer.closed, true); writer.writeEvent('late', { data: 'ignored' }); // No headers sent, no chunks written assert.equal(res.writtenHead, null); assert.equal(res.chunks.length, 0); }); it('should trigger onClose callbacks when connection closes', () => { const writer = new HttpSSEWriter(res as any); const calls: number[] = []; writer.onClose(() => calls.push(1)); writer.onClose(() => calls.push(2)); res.emit('close'); assert.deepStrictEqual(calls, [ 1, 2 ]); }); it('should handle end() idempotently', () => { const writer = new HttpSSEWriter(res as any); assert.equal(writer.closed, false); writer.end(); assert.equal(writer.closed, true); assert.equal(res.ended, true); // Reset flag to verify second end() doesn't call res.end() again res.ended = false; writer.end(); assert.equal(res.ended, false); // Not called again }); it('should write multiple events sequentially', () => { const writer = new HttpSSEWriter(res as any); writer.writeEvent('event1', { n: 1 }); writer.writeEvent('event2', { n: 2 }); writer.writeEvent('event3', { n: 3 }); assert.equal(res.chunks.length, 3); assert.equal(res.chunks[0], 'event: event1\ndata: {"n":1}\n\n'); assert.equal(res.chunks[1], 'event: event2\ndata: {"n":2}\n\n'); assert.equal(res.chunks[2], 'event: event3\ndata: {"n":3}\n\n'); // Headers sent only once assert.ok(res.writtenHead); }); it('should start with closed=false', () => { const writer = new HttpSSEWriter(res as any); assert.equal(writer.closed, false); }); it('should format SSE comments correctly', () => { const writer = new HttpSSEWriter(res as any); writer.writeComment('keepalive'); assert.equal(res.chunks.length, 1); assert.equal(res.chunks[0], ': keepalive\n\n'); }); it('should not write comment after connection closes', () => { const writer = new HttpSSEWriter(res as any); res.emit('close'); writer.writeComment('keepalive'); assert.equal(res.chunks.length, 0); }); it('should send headers on first writeComment', () => { const writer = new HttpSSEWriter(res as any); assert.equal(res.writtenHead, null); writer.writeComment('ping'); assert.ok(res.writtenHead); assert.equal(res.writtenHead.statusCode, 200); assert.equal(res.writtenHead.headers['content-type'], 'text/event-stream'); }); }); ================================================ FILE: core/agent-runtime/test/MessageConverter.test.ts ================================================ import assert from 'node:assert'; import type { AgentMessage, InputMessage, SDKResultMessage } from '@eggjs/tegg-types/agent-runtime'; import { MessageConverter } from '../src/MessageConverter'; describe('test/MessageConverter.test.ts', () => { describe('extractUsage', () => { it('should return undefined when no result messages', () => { const messages: AgentMessage[] = [ { type: 'assistant', message: { role: 'assistant', content: [{ type: 'text', text: 'hi' }] } }, { type: 'user', message: { role: 'user', content: 'hello' } }, ]; const usage = MessageConverter.extractUsage(messages); assert.equal(usage, undefined); }); it('should extract usage from a single result message', () => { const messages: AgentMessage[] = [ { type: 'assistant', message: { role: 'assistant', content: [{ type: 'text', text: 'hi' }] } }, { type: 'result', subtype: 'success', usage: { input_tokens: 10, output_tokens: 5 }, } as SDKResultMessage, ]; const usage = MessageConverter.extractUsage(messages); assert.ok(usage); assert.equal(usage.promptTokens, 10); assert.equal(usage.completionTokens, 5); assert.equal(usage.totalTokens, 15); }); it('should accumulate usage from multiple result messages', () => { const messages: AgentMessage[] = [ { type: 'result', subtype: 'success', usage: { input_tokens: 10, output_tokens: 5 }, } as SDKResultMessage, { type: 'result', subtype: 'success', usage: { input_tokens: 20, output_tokens: 8 }, } as SDKResultMessage, ]; const usage = MessageConverter.extractUsage(messages); assert.ok(usage); assert.equal(usage.promptTokens, 30); assert.equal(usage.completionTokens, 13); assert.equal(usage.totalTokens, 43); }); it('should handle result message without usage field', () => { const messages: AgentMessage[] = [ { type: 'result', subtype: 'success' } as SDKResultMessage, ]; const usage = MessageConverter.extractUsage(messages); assert.equal(usage, undefined); }); it('should handle empty messages array', () => { const usage = MessageConverter.extractUsage([]); assert.equal(usage, undefined); }); it('should handle partial usage fields (missing output_tokens)', () => { const messages: AgentMessage[] = [ { type: 'result', subtype: 'success', usage: { input_tokens: 10 }, } as SDKResultMessage, ]; const usage = MessageConverter.extractUsage(messages); assert.ok(usage); assert.equal(usage.promptTokens, 10); assert.equal(usage.completionTokens, 0); assert.equal(usage.totalTokens, 10); }); it('should handle cache-related usage fields', () => { const messages: AgentMessage[] = [ { type: 'result', subtype: 'success', usage: { input_tokens: 100, output_tokens: 50, cache_creation_input_tokens: 200, cache_read_input_tokens: 80, }, } as SDKResultMessage, ]; const usage = MessageConverter.extractUsage(messages); assert.ok(usage); assert.equal(usage.promptTokens, 100); assert.equal(usage.completionTokens, 50); assert.equal(usage.totalTokens, 150); }); }); describe('filterForStorage', () => { it('should filter out stream_event messages', () => { const messages: AgentMessage[] = [ { type: 'system', subtype: 'init', session_id: 'sess-1' }, { type: 'user', message: { role: 'user', content: 'hello' } }, { type: 'stream_event', event: { type: 'content_block_delta' }, session_id: 'sess-1' }, { type: 'stream_event', event: { type: 'content_block_delta' }, session_id: 'sess-1' }, { type: 'assistant', message: { role: 'assistant', content: [{ type: 'text', text: 'hi' }] } }, { type: 'result', subtype: 'success', usage: { input_tokens: 10, output_tokens: 5 } } as SDKResultMessage, ]; const result = MessageConverter.filterForStorage(messages); assert.equal(result.length, 4); assert.equal(result[0].type, 'system'); assert.equal(result[1].type, 'user'); assert.equal(result[2].type, 'assistant'); assert.equal(result[3].type, 'result'); }); it('should return all messages when no stream_event present', () => { const messages: AgentMessage[] = [ { type: 'user', message: { role: 'user', content: 'hello' } }, { type: 'assistant', message: { role: 'assistant', content: [{ type: 'text', text: 'hi' }] } }, ]; const result = MessageConverter.filterForStorage(messages); assert.equal(result.length, 2); }); it('should handle empty array', () => { const result = MessageConverter.filterForStorage([]); assert.deepStrictEqual(result, []); }); }); describe('toAgentMessages', () => { it('should convert user messages to AgentMessage format', () => { const messages: InputMessage[] = [ { role: 'user', content: 'hello' }, ]; const result = MessageConverter.toAgentMessages(messages); assert.equal(result.length, 1); assert.equal(result[0].type, 'user'); assert.deepStrictEqual((result[0] as any).message, { role: 'user', content: 'hello' }); }); it('should filter out system messages', () => { const messages: InputMessage[] = [ { role: 'system', content: 'you are a bot' }, { role: 'user', content: 'hello' }, ]; const result = MessageConverter.toAgentMessages(messages); assert.equal(result.length, 1); assert.equal(result[0].type, 'user'); }); it('should handle array content', () => { const messages: InputMessage[] = [ { role: 'user', content: [ { type: 'text', text: 'part1' }, { type: 'text', text: 'part2' }, ], }, ]; const result = MessageConverter.toAgentMessages(messages); assert.equal(result.length, 1); assert.deepStrictEqual((result[0] as any).message.content, [ { type: 'text', text: 'part1' }, { type: 'text', text: 'part2' }, ]); }); it('should handle empty array', () => { const result = MessageConverter.toAgentMessages([]); assert.deepStrictEqual(result, []); }); it('should preserve assistant role messages with correct type', () => { const messages: InputMessage[] = [ { role: 'assistant', content: 'I said something' }, ]; const result = MessageConverter.toAgentMessages(messages); assert.equal(result.length, 1); assert.equal(result[0].type, 'assistant'); assert.deepStrictEqual((result[0] as any).message.role, 'assistant'); }); }); }); ================================================ FILE: core/agent-runtime/test/OSSAgentStore.test.ts ================================================ import assert from 'node:assert'; import type { AgentMessage } from '@eggjs/tegg-types/agent-runtime'; import { AgentNotFoundError, OSSAgentStore } from '../index'; import { MapStorageClient, MapStorageClientWithoutAppend } from './helpers'; describe('test/OSSAgentStore.test.ts', () => { let store: OSSAgentStore; beforeEach(() => { store = new OSSAgentStore({ client: new MapStorageClient() }); }); describe('threads', () => { it('should create a thread', async () => { const thread = await store.createThread(); assert(thread.id.startsWith('thread_')); assert.equal(thread.object, 'thread'); assert(Array.isArray(thread.messages)); assert.equal(thread.messages.length, 0); assert(typeof thread.createdAt === 'number'); assert(thread.createdAt <= Math.floor(Date.now() / 1000)); }); it('should create a thread with metadata', async () => { const thread = await store.createThread({ key: 'value' }); assert.deepEqual(thread.metadata, { key: 'value' }); }); it('should create a thread with empty metadata by default', async () => { const thread = await store.createThread(); assert.deepEqual(thread.metadata, {}); }); it('should get a thread by id', async () => { const created = await store.createThread(); const fetched = await store.getThread(created.id); assert.equal(fetched.id, created.id); assert.equal(fetched.object, 'thread'); assert.equal(fetched.createdAt, created.createdAt); }); it('should return empty messages for a new thread', async () => { const thread = await store.createThread(); const fetched = await store.getThread(thread.id); assert.deepEqual(fetched.messages, []); }); it('should throw AgentNotFoundError for non-existent thread', async () => { await assert.rejects( () => store.getThread('thread_non_existent'), (err: unknown) => { assert(err instanceof AgentNotFoundError); assert.equal(err.status, 404); assert.match(err.message, /Thread thread_non_existent not found/); return true; }, ); }); it('should append messages to a thread', async () => { const thread = await store.createThread(); const messages: AgentMessage[] = [ { type: 'user', message: { role: 'user', content: 'Hello' } }, { type: 'assistant', message: { role: 'assistant', content: [{ type: 'text', text: 'Hi!' }] } }, ]; await store.appendMessages(thread.id, messages); const fetched = await store.getThread(thread.id); assert.equal(fetched.messages.length, 2); assert.equal(fetched.messages[0].type, 'user'); assert.equal(fetched.messages[1].type, 'assistant'); }); it('should append messages incrementally', async () => { const thread = await store.createThread(); await store.appendMessages(thread.id, [ { type: 'user', message: { role: 'user', content: 'First' } }, ]); await store.appendMessages(thread.id, [ { type: 'assistant', message: { role: 'assistant', content: [{ type: 'text', text: 'Second' }] } }, ]); const fetched = await store.getThread(thread.id); assert.equal(fetched.messages.length, 2); assert.equal(fetched.messages[0].type, 'user'); assert.equal(fetched.messages[1].type, 'assistant'); }); it('should throw AgentNotFoundError when appending to non-existent thread', async () => { await assert.rejects( () => store.appendMessages('thread_non_existent', [ { type: 'user', message: { role: 'user', content: 'Hello' } }, ]), (err: unknown) => { assert(err instanceof AgentNotFoundError); return true; }, ); }); it('should only return user and assistant messages by default', async () => { const thread = await store.createThread(); await store.appendMessages(thread.id, [ { type: 'system', subtype: 'init', session_id: 'sess-1' }, { type: 'user', message: { role: 'user', content: 'Hello' } }, { type: 'assistant', message: { role: 'assistant', content: [{ type: 'text', text: 'Hi!' }] } }, { type: 'result', subtype: 'success', usage: { input_tokens: 10, output_tokens: 5 } }, ]); const fetched = await store.getThread(thread.id); assert.equal(fetched.messages.length, 2); assert.equal(fetched.messages[0].type, 'user'); assert.equal(fetched.messages[1].type, 'assistant'); }); it('should return all message types when includeAllMessages is true', async () => { const thread = await store.createThread(); await store.appendMessages(thread.id, [ { type: 'system', subtype: 'init', session_id: 'sess-1' }, { type: 'user', message: { role: 'user', content: 'Hello' } }, { type: 'result', subtype: 'success', usage: { input_tokens: 10, output_tokens: 5 } }, ]); const fetched = await store.getThread(thread.id, { includeAllMessages: true }); assert.equal(fetched.messages.length, 3); assert.equal(fetched.messages[0].type, 'system'); assert.equal(fetched.messages[1].type, 'user'); assert.equal(fetched.messages[2].type, 'result'); }); }); describe('threads (without append)', () => { it('should fall back to get-concat-put when client has no append', async () => { const fallbackStore = new OSSAgentStore({ client: new MapStorageClientWithoutAppend() }); const thread = await fallbackStore.createThread(); await fallbackStore.appendMessages(thread.id, [ { type: 'user', message: { role: 'user', content: 'Hello' } }, ]); await fallbackStore.appendMessages(thread.id, [ { type: 'assistant', message: { role: 'assistant', content: [{ type: 'text', text: 'Hi!' }] } }, ]); const fetched = await fallbackStore.getThread(thread.id); assert.equal(fetched.messages.length, 2); assert.equal(fetched.messages[0].type, 'user'); assert.equal(fetched.messages[1].type, 'assistant'); }); }); describe('runs', () => { it('should create a run', async () => { const run = await store.createRun([{ role: 'user', content: 'Hello' }]); assert(run.id.startsWith('run_')); assert.equal(run.object, 'thread.run'); assert.equal(run.status, 'queued'); assert.equal(run.input.length, 1); assert(typeof run.createdAt === 'number'); assert(run.createdAt <= Math.floor(Date.now() / 1000)); }); it('should create a run with threadId and config', async () => { const run = await store.createRun([{ role: 'user', content: 'Hello' }], 'thread_123', { timeoutMs: 5000 }); assert.equal(run.threadId, 'thread_123'); assert.deepEqual(run.config, { timeoutMs: 5000 }); }); it('should create a run with metadata', async () => { const meta = { user_id: 'u_1', session: 'abc' }; const run = await store.createRun([{ role: 'user', content: 'Hello' }], 'thread_123', undefined, meta); assert.deepEqual(run.metadata, meta); const fetched = await store.getRun(run.id); assert.deepEqual(fetched.metadata, meta); }); it('should preserve metadata across updateRun', async () => { const meta = { tag: 'test' }; const run = await store.createRun([{ role: 'user', content: 'Hello' }], undefined, undefined, meta); await store.updateRun(run.id, { status: 'in_progress', startedAt: Math.floor(Date.now() / 1000) }); const fetched = await store.getRun(run.id); assert.equal(fetched.status, 'in_progress'); assert.deepEqual(fetched.metadata, meta); }); it('should get a run by id', async () => { const created = await store.createRun([{ role: 'user', content: 'Hello' }]); const fetched = await store.getRun(created.id); assert.equal(fetched.id, created.id); assert.equal(fetched.status, 'queued'); }); it('should throw AgentNotFoundError for non-existent run', async () => { await assert.rejects( () => store.getRun('run_non_existent'), (err: unknown) => { assert(err instanceof AgentNotFoundError); assert.equal(err.status, 404); assert.match(err.message, /Run run_non_existent not found/); return true; }, ); }); it('should update a run', async () => { const run = await store.createRun([{ role: 'user', content: 'Hello' }]); await store.updateRun(run.id, { status: 'completed', completedAt: Math.floor(Date.now() / 1000), usage: { promptTokens: 10, completionTokens: 5, totalTokens: 15 }, }); const fetched = await store.getRun(run.id); assert.equal(fetched.status, 'completed'); assert(typeof fetched.completedAt === 'number'); assert.deepStrictEqual(fetched.usage, { promptTokens: 10, completionTokens: 5, totalTokens: 15 }); }); it('should not allow overwriting id or object via updateRun', async () => { const run = await store.createRun([{ role: 'user', content: 'Hello' }]); await store.updateRun(run.id, { id: 'run_hacked', object: 'thread' as never, status: 'completed', }); const fetched = await store.getRun(run.id); assert.equal(fetched.id, run.id); assert.equal(fetched.object, 'thread.run'); assert.equal(fetched.status, 'completed'); }); }); describe('init / destroy', () => { it('should call client init when present', async () => { let initCalled = false; const client = new MapStorageClient(); client.init = async () => { initCalled = true; }; const s = new OSSAgentStore({ client }); await s.init(); assert.equal(initCalled, true); }); it('should call client destroy when present', async () => { let destroyCalled = false; const client = new MapStorageClient(); client.destroy = async () => { destroyCalled = true; }; const s = new OSSAgentStore({ client }); await s.destroy(); assert.equal(destroyCalled, true); }); it('should not throw when client has no init/destroy', async () => { const s = new OSSAgentStore({ client: new MapStorageClient() }); await s.init(); await s.destroy(); }); }); describe('prefix', () => { it('should use prefix in storage keys', async () => { const client = new MapStorageClient(); const prefixedStore = new OSSAgentStore({ client, prefix: 'myapp/' }); const thread = await prefixedStore.createThread(); // Verify we can get it back (proves the prefix is used consistently) const fetched = await prefixedStore.getThread(thread.id); assert.equal(fetched.id, thread.id); const run = await prefixedStore.createRun([{ role: 'user', content: 'Hello' }]); const fetchedRun = await prefixedStore.getRun(run.id); assert.equal(fetchedRun.id, run.id); }); it('should normalize prefix without trailing slash', async () => { const client = new MapStorageClient(); const withSlash = new OSSAgentStore({ client, prefix: 'myapp/' }); const withoutSlash = new OSSAgentStore({ client, prefix: 'myapp' }); // Both stores should write to the same keys const thread = await withSlash.createThread(); const fetched = await withoutSlash.getThread(thread.id); assert.equal(fetched.id, thread.id); }); it('should isolate data between different prefixes', async () => { const client = new MapStorageClient(); const store1 = new OSSAgentStore({ client, prefix: 'app1/' }); const store2 = new OSSAgentStore({ client, prefix: 'app2/' }); const thread = await store1.createThread(); await assert.rejects( () => store2.getThread(thread.id), (err: unknown) => { assert(err instanceof AgentNotFoundError); return true; }, ); }); }); }); ================================================ FILE: core/agent-runtime/test/OSSObjectStorageClient.test.ts ================================================ import assert from 'node:assert'; import type { OSSObject } from 'oss-client'; import { OSSObjectStorageClient } from '../src/OSSObjectStorageClient'; /** Simple mock function helper for mocha tests. */ function mockFn() { const calls: any[][] = []; let nextResults: Array<{ type: 'resolve' | 'reject'; value: any }> = []; const fn = (...args: any[]) => { calls.push(args); const result = nextResults.shift(); if (result) { return result.type === 'resolve' ? Promise.resolve(result.value) : Promise.reject(result.value); } return Promise.resolve({}); }; fn.mock = { calls }; fn.mockResolvedValue = (val: any) => { nextResults = []; fn.mockResolvedValueOnce(val); nextResults = nextResults.map(() => ({ type: 'resolve' as const, value: val })); (fn as any)._defaultResult = { type: 'resolve', value: val }; return fn; }; fn.mockResolvedValueOnce = (val: any) => { nextResults.push({ type: 'resolve', value: val }); return fn; }; fn.mockRejectedValue = (val: any) => { (fn as any)._defaultResult = { type: 'reject', value: val }; return fn; }; fn.mockRejectedValueOnce = (val: any) => { nextResults.push({ type: 'reject', value: val }); return fn; }; // Override fn to use default result when nextResults is empty const wrappedFn: any = (...args: any[]) => { calls.push(args); const result = nextResults.shift(); if (result) { return result.type === 'resolve' ? Promise.resolve(result.value) : Promise.reject(result.value); } const def = (wrappedFn as any)._defaultResult; if (def) { return def.type === 'resolve' ? Promise.resolve(def.value) : Promise.reject(def.value); } return Promise.resolve({}); }; wrappedFn.mock = { calls }; wrappedFn.mockResolvedValue = (val: any) => { (wrappedFn as any)._defaultResult = { type: 'resolve', value: val }; return wrappedFn; }; wrappedFn.mockResolvedValueOnce = (val: any) => { nextResults.push({ type: 'resolve', value: val }); return wrappedFn; }; wrappedFn.mockRejectedValue = (val: any) => { (wrappedFn as any)._defaultResult = { type: 'reject', value: val }; return wrappedFn; }; wrappedFn.mockRejectedValueOnce = (val: any) => { nextResults.push({ type: 'reject', value: val }); return wrappedFn; }; return wrappedFn; } describe('test/OSSObjectStorageClient.test.ts', () => { let client: OSSObjectStorageClient; let mockOSS: { put: ReturnType; get: ReturnType; append: ReturnType; head: ReturnType; }; beforeEach(() => { mockOSS = { put: mockFn(), get: mockFn(), append: mockFn(), head: mockFn(), }; client = new OSSObjectStorageClient(mockOSS as unknown as OSSObject); }); describe('put', () => { it('should pass Buffer to SDK put', async () => { mockOSS.put.mockResolvedValue({}); await client.put('threads/t1.json', '{"id":"t1"}'); assert.equal(mockOSS.put.mock.calls.length, 1); const [ key, body ] = mockOSS.put.mock.calls[0]; assert.equal(key, 'threads/t1.json'); assert(Buffer.isBuffer(body)); assert.equal(body.toString('utf-8'), '{"id":"t1"}'); }); }); describe('get', () => { it('should return string when content is Buffer', async () => { mockOSS.get.mockResolvedValue({ content: Buffer.from('{"id":"t1"}', 'utf-8'), }); const result = await client.get('threads/t1.json'); assert.equal(result, '{"id":"t1"}'); }); it('should return string when content is non-Buffer', async () => { mockOSS.get.mockResolvedValue({ content: '{"id":"t1"}', }); const result = await client.get('threads/t1.json'); assert.equal(result, '{"id":"t1"}'); }); it('should return null when content is empty', async () => { mockOSS.get.mockResolvedValue({ content: null }); const result = await client.get('threads/t1.json'); assert.equal(result, null); }); it('should return null for NoSuchKey error', async () => { const err = new Error('Object not exists'); (err as Error & { code: string }).code = 'NoSuchKey'; mockOSS.get.mockRejectedValue(err); const result = await client.get('threads/nonexistent.json'); assert.equal(result, null); }); it('should re-throw non-NoSuchKey errors', async () => { const err = new Error('Network failure'); mockOSS.get.mockRejectedValue(err); await assert.rejects( () => client.get('threads/t1.json'), (thrown: unknown) => { assert(thrown instanceof Error); assert.equal(thrown.message, 'Network failure'); return true; }, ); }); }); describe('append', () => { it('should create new object with position 0 on first append', async () => { mockOSS.append.mockResolvedValue({ nextAppendPosition: '13' }); await client.append('msgs.jsonl', '{"id":"m1"}\n'); assert.equal(mockOSS.append.mock.calls.length, 1); const [ key, buf, opts ] = mockOSS.append.mock.calls[0]; assert.equal(key, 'msgs.jsonl'); assert(Buffer.isBuffer(buf)); assert.equal(buf.toString('utf-8'), '{"id":"m1"}\n'); assert.equal(opts.position, 0); }); it('should use cached nextAppendPosition on subsequent appends', async () => { mockOSS.append.mockResolvedValueOnce({ nextAppendPosition: '13' }); await client.append('msgs.jsonl', '{"id":"m1"}\n'); mockOSS.append.mockResolvedValueOnce({ nextAppendPosition: '26' }); await client.append('msgs.jsonl', '{"id":"m2"}\n'); assert.equal(mockOSS.append.mock.calls.length, 2); assert.equal(mockOSS.append.mock.calls[1][2].position, 13); }); it('should fall back to HEAD + retry on PositionNotEqualToLength', async () => { const posErr = new Error('Position mismatch'); (posErr as Error & { code: string }).code = 'PositionNotEqualToLength'; mockOSS.append.mockRejectedValueOnce(posErr); mockOSS.head.mockResolvedValue({ res: { headers: { 'content-length': '50' } } }); mockOSS.append.mockResolvedValueOnce({ nextAppendPosition: '63' }); await client.append('msgs.jsonl', '{"id":"m1"}\n'); // First attempt failed, then HEAD, then retry assert.equal(mockOSS.append.mock.calls.length, 2); assert.equal(mockOSS.head.mock.calls.length, 1); assert.equal(mockOSS.append.mock.calls[1][2].position, 50); }); it('should re-throw non-position errors', async () => { const err = new Error('Network failure'); mockOSS.append.mockRejectedValue(err); await assert.rejects( () => client.append('msgs.jsonl', '{"id":"m1"}\n'), (thrown: unknown) => { assert(thrown instanceof Error); assert.equal(thrown.message, 'Network failure'); return true; }, ); }); }); }); ================================================ FILE: core/agent-runtime/test/RunBuilder.test.ts ================================================ import assert from 'node:assert'; import type { RunRecord } from '@eggjs/tegg-types/agent-runtime'; import { RunStatus, AgentObjectType, AgentErrorCode, InvalidRunStateTransitionError } from '@eggjs/tegg-types/agent-runtime'; import { RunBuilder } from '../src/RunBuilder'; import type { RunUsage } from '../src/RunBuilder'; function makeRunRecord(overrides?: Partial): RunRecord { return { id: 'run_1', object: AgentObjectType.ThreadRun, threadId: 'thread_1', status: RunStatus.Queued, input: [{ role: 'user', content: 'hello' }], createdAt: 1000, ...overrides, }; } describe('test/RunBuilder.test.ts', () => { describe('create and snapshot', () => { it('should create from a queued RunRecord and produce a valid snapshot', () => { const record = makeRunRecord(); const rb = RunBuilder.create(record, 'thread_1'); const snap = rb.snapshot(); assert.equal(snap.id, 'run_1'); assert.equal(snap.object, AgentObjectType.ThreadRun); assert.equal(snap.createdAt, 1000); assert.equal(snap.threadId, 'thread_1'); assert.equal(snap.status, RunStatus.Queued); assert.equal(snap.startedAt, null); assert.equal(snap.completedAt, null); assert.equal(snap.cancelledAt, null); assert.equal(snap.failedAt, null); assert.equal(snap.usage, null); assert.equal(snap.lastError, undefined); }); it('should restore all mutable fields from a completed RunRecord', () => { const record = makeRunRecord({ status: RunStatus.Completed, startedAt: 1001, completedAt: 1002, usage: { promptTokens: 10, completionTokens: 5, totalTokens: 15 }, metadata: { key: 'value' }, config: { maxIterations: 10 }, }); const snap = RunBuilder.create(record, 'thread_1').snapshot(); assert.equal(snap.status, RunStatus.Completed); assert.equal(snap.startedAt, 1001); assert.equal(snap.completedAt, 1002); assert.deepStrictEqual(snap.usage, { promptTokens: 10, completionTokens: 5, totalTokens: 15 }); assert.deepStrictEqual(snap.metadata, { key: 'value' }); assert.deepStrictEqual(snap.config, { maxIterations: 10 }); }); it('should restore failed state with lastError', () => { const record = makeRunRecord({ status: RunStatus.Failed, startedAt: 1001, failedAt: 1003, lastError: { code: 'EXEC_ERROR', message: 'boom' }, }); const snap = RunBuilder.create(record, 'thread_1').snapshot(); assert.equal(snap.status, RunStatus.Failed); assert.equal(snap.failedAt, 1003); assert.deepStrictEqual(snap.lastError, { code: 'EXEC_ERROR', message: 'boom' }); }); }); describe('start', () => { it('should transition queued → in_progress', () => { const rb = RunBuilder.create(makeRunRecord(), 'thread_1'); const update = rb.start(); assert.equal(update.status, RunStatus.InProgress); assert.equal(typeof update.startedAt, 'number'); assert.equal(rb.snapshot().status, RunStatus.InProgress); }); it('should throw for non-queued status', () => { const rb = RunBuilder.create(makeRunRecord({ status: RunStatus.InProgress }), 'thread_1'); assert.throws(() => rb.start(), InvalidRunStateTransitionError); }); }); describe('complete', () => { it('should transition in_progress → completed with usage', () => { const rb = RunBuilder.create(makeRunRecord(), 'thread_1'); rb.start(); const usage: RunUsage = { promptTokens: 10, completionTokens: 5, totalTokens: 15 }; const update = rb.complete(usage); assert.equal(update.status, RunStatus.Completed); assert.equal(typeof update.completedAt, 'number'); assert.deepStrictEqual(update.usage, { promptTokens: 10, completionTokens: 5, totalTokens: 15, }); const snap = rb.snapshot(); assert.equal(snap.status, RunStatus.Completed); assert.deepStrictEqual(snap.usage, { promptTokens: 10, completionTokens: 5, totalTokens: 15, }); }); it('should complete without usage', () => { const rb = RunBuilder.create(makeRunRecord(), 'thread_1'); rb.start(); const update = rb.complete(); assert.equal(update.status, RunStatus.Completed); assert.equal(update.usage, undefined); const snap = rb.snapshot(); assert.equal(snap.usage, null); }); it('should throw for non-in_progress status', () => { const rb = RunBuilder.create(makeRunRecord(), 'thread_1'); assert.throws(() => rb.complete(), InvalidRunStateTransitionError); }); }); describe('fail', () => { it('should transition in_progress → failed with error', () => { const rb = RunBuilder.create(makeRunRecord(), 'thread_1'); rb.start(); const update = rb.fail(new Error('something broke')); assert.equal(update.status, RunStatus.Failed); assert.equal(typeof update.failedAt, 'number'); assert.deepStrictEqual(update.lastError, { code: AgentErrorCode.ExecError, message: 'something broke', }); const snap = rb.snapshot(); assert.equal(snap.status, RunStatus.Failed); }); it('should allow failing from queued status', () => { const rb = RunBuilder.create(makeRunRecord(), 'thread_1'); const update = rb.fail(new Error('early failure')); assert.equal(update.status, RunStatus.Failed); }); it('should allow failing from cancelling status (cancel watchdog timeout)', () => { const rb = RunBuilder.create(makeRunRecord({ status: RunStatus.Cancelling }), 'thread_1'); const update = rb.fail(new Error('commit timeout')); assert.equal(update.status, RunStatus.Failed); }); it('should throw for terminal status', () => { const rb = RunBuilder.create(makeRunRecord({ status: RunStatus.Completed }), 'thread_1'); assert.throws(() => rb.fail(new Error('nope')), InvalidRunStateTransitionError); }); }); describe('cancelling', () => { it('should transition in_progress → cancelling', () => { const rb = RunBuilder.create(makeRunRecord(), 'thread_1'); rb.start(); const update = rb.cancelling(); assert.equal(update.status, RunStatus.Cancelling); assert.equal(rb.snapshot().status, RunStatus.Cancelling); }); it('should transition queued → cancelling', () => { const rb = RunBuilder.create(makeRunRecord(), 'thread_1'); const update = rb.cancelling(); assert.equal(update.status, RunStatus.Cancelling); }); it('should be idempotent when already cancelling', () => { const rb = RunBuilder.create(makeRunRecord(), 'thread_1'); rb.start(); rb.cancelling(); const update = rb.cancelling(); assert.equal(update.status, RunStatus.Cancelling); }); it('should throw for terminal status', () => { const rb = RunBuilder.create(makeRunRecord({ status: RunStatus.Completed }), 'thread_1'); assert.throws(() => rb.cancelling(), InvalidRunStateTransitionError); }); }); describe('cancel', () => { it('should transition cancelling → cancelled', () => { const rb = RunBuilder.create(makeRunRecord(), 'thread_1'); rb.start(); rb.cancelling(); const update = rb.cancel(); assert.equal(update.status, RunStatus.Cancelled); assert.equal(typeof update.cancelledAt, 'number'); const snap = rb.snapshot(); assert.equal(snap.status, RunStatus.Cancelled); assert.equal(typeof snap.cancelledAt, 'number'); }); it('should throw when not in cancelling status', () => { const rb = RunBuilder.create(makeRunRecord(), 'thread_1'); rb.start(); assert.throws(() => rb.cancel(), InvalidRunStateTransitionError); }); }); describe('full lifecycle', () => { it('should support queued → in_progress → completed', () => { const rb = RunBuilder.create(makeRunRecord(), 'thread_1'); assert.equal(rb.snapshot().status, RunStatus.Queued); rb.start(); assert.equal(rb.snapshot().status, RunStatus.InProgress); rb.complete({ promptTokens: 1, completionTokens: 2, totalTokens: 3 }); const snap = rb.snapshot(); assert.equal(snap.status, RunStatus.Completed); assert.ok(snap.startedAt); assert.ok(snap.completedAt); }); it('should support queued → in_progress → cancelling → cancelled', () => { const rb = RunBuilder.create(makeRunRecord(), 'thread_1'); rb.start(); rb.cancelling(); rb.cancel(); assert.equal(rb.snapshot().status, RunStatus.Cancelled); }); it('should support queued → in_progress → failed', () => { const rb = RunBuilder.create(makeRunRecord(), 'thread_1'); rb.start(); rb.fail(new Error('err')); assert.equal(rb.snapshot().status, RunStatus.Failed); }); }); }); ================================================ FILE: core/agent-runtime/test/helpers.ts ================================================ import type { ObjectStorageClient } from '@eggjs/tegg-types/agent-runtime'; /** In-memory ObjectStorageClient backed by a Map — for testing only. */ export class MapStorageClient implements ObjectStorageClient { private readonly store = new Map(); init?(): Promise; destroy?(): Promise; async put(key: string, value: string): Promise { this.store.set(key, value); } async get(key: string): Promise { return this.store.get(key) ?? null; } async append(key: string, value: string): Promise { const existing = this.store.get(key) ?? ''; this.store.set(key, existing + value); } } /** MapStorageClient variant without append — tests the get-concat-put fallback path. */ export class MapStorageClientWithoutAppend implements ObjectStorageClient { private readonly store = new Map(); init?(): Promise; destroy?(): Promise; async put(key: string, value: string): Promise { this.store.set(key, value); } async get(key: string): Promise { return this.store.get(key) ?? null; } } ================================================ FILE: core/agent-runtime/tsconfig.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "dist", "baseUrl": "./" }, "exclude": [ "dist", "node_modules", "test" ] } ================================================ FILE: core/agent-runtime/tsconfig.pub.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "dist", "baseUrl": "./" }, "exclude": [ "dist", "node_modules", "test" ] } ================================================ FILE: core/agent-tracing/CHANGELOG.md ================================================ # Change Log All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. ## [3.78.15](https://github.com/eggjs/tegg/compare/v3.78.14...v3.78.15) (2026-04-23) **Note:** Version bump only for package @eggjs/agent-tracing ## [3.78.14](https://github.com/eggjs/tegg/compare/v3.78.13...v3.78.14) (2026-04-21) **Note:** Version bump only for package @eggjs/agent-tracing ## [3.78.13](https://github.com/eggjs/tegg/compare/v3.78.12...v3.78.13) (2026-04-21) **Note:** Version bump only for package @eggjs/agent-tracing ## [3.78.12](https://github.com/eggjs/tegg/compare/v3.78.11...v3.78.12) (2026-04-18) **Note:** Version bump only for package @eggjs/agent-tracing ## [3.78.11](https://github.com/eggjs/tegg/compare/v3.78.10...v3.78.11) (2026-04-18) **Note:** Version bump only for package @eggjs/agent-tracing ## [3.78.10](https://github.com/eggjs/tegg/compare/v3.78.9...v3.78.10) (2026-04-18) **Note:** Version bump only for package @eggjs/agent-tracing ## [3.78.9](https://github.com/eggjs/tegg/compare/v3.78.8...v3.78.9) (2026-04-18) **Note:** Version bump only for package @eggjs/agent-tracing ## [3.78.8](https://github.com/eggjs/tegg/compare/v3.78.7...v3.78.8) (2026-04-18) **Note:** Version bump only for package @eggjs/agent-tracing ## [3.78.7](https://github.com/eggjs/tegg/compare/v3.78.6...v3.78.7) (2026-04-18) **Note:** Version bump only for package @eggjs/agent-tracing ## [3.78.6](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.6) (2026-04-18) **Note:** Version bump only for package @eggjs/agent-tracing ## [3.78.5](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.5) (2026-04-18) **Note:** Version bump only for package @eggjs/agent-tracing ## [3.78.4](https://github.com/eggjs/tegg/compare/v3.78.3...v3.78.4) (2026-04-18) **Note:** Version bump only for package @eggjs/agent-tracing ## [3.78.3](https://github.com/eggjs/tegg/compare/v3.78.2...v3.78.3) (2026-04-15) **Note:** Version bump only for package @eggjs/agent-tracing ## [3.78.2](https://github.com/eggjs/tegg/compare/v3.78.1...v3.78.2) (2026-04-09) **Note:** Version bump only for package @eggjs/agent-tracing ## [3.78.1](https://github.com/eggjs/tegg/compare/v3.78.0...v3.78.1) (2026-04-09) **Note:** Version bump only for package @eggjs/agent-tracing # [3.78.0](https://github.com/eggjs/tegg/compare/v3.77.2...v3.78.0) (2026-04-09) **Note:** Version bump only for package @eggjs/agent-tracing ## [3.77.2](https://github.com/eggjs/tegg/compare/v3.77.1...v3.77.2) (2026-04-07) **Note:** Version bump only for package @eggjs/agent-tracing ## [3.77.1](https://github.com/eggjs/tegg/compare/v3.77.0...v3.77.1) (2026-04-01) **Note:** Version bump only for package @eggjs/agent-tracing # [3.77.0](https://github.com/eggjs/tegg/compare/v3.76.1...v3.77.0) (2026-04-01) **Note:** Version bump only for package @eggjs/agent-tracing ## [3.76.1](https://github.com/eggjs/tegg/compare/v3.76.0...v3.76.1) (2026-04-01) **Note:** Version bump only for package @eggjs/agent-tracing # [3.76.0](https://github.com/eggjs/tegg/compare/v3.75.1...v3.76.0) (2026-04-01) **Note:** Version bump only for package @eggjs/agent-tracing ## [3.75.1](https://github.com/eggjs/tegg/compare/v3.75.0...v3.75.1) (2026-04-01) **Note:** Version bump only for package @eggjs/agent-tracing # [3.75.0](https://github.com/eggjs/tegg/compare/v3.74.0...v3.75.0) (2026-03-30) ### Features * **agent-tracing:** add typesVersions for moduleResolution:node compatibility ([#424](https://github.com/eggjs/tegg/issues/424)) ([25b282c](https://github.com/eggjs/tegg/commit/25b282c947e5ce567ad72516d9c612cfb949d891)) # [3.74.0](https://github.com/eggjs/tegg/compare/v3.73.0...v3.74.0) (2026-03-30) ### Bug Fixes * **agent-tracing:** add eggModule declaration for tegg module scanning ([#416](https://github.com/eggjs/tegg/issues/416)) ([d169cab](https://github.com/eggjs/tegg/commit/d169cabeb0580b96c320ac6c067d3d9828639a32)) * **agent-tracing:** separate traceId and sessionId in createSession ([#417](https://github.com/eggjs/tegg/issues/417)) ([c760776](https://github.com/eggjs/tegg/commit/c7607761d1e85cbf91cb74878e90af898672db3a)) ### Features * **agent-tracing:** collect assistant messages into root run outputs ([#421](https://github.com/eggjs/tegg/issues/421)) ([8dcebaf](https://github.com/eggjs/tegg/commit/8dcebafa19061d4a91161272409a2ac729d78341)) * **agent-tracing:** rename TraceSession to Trace and support inputs in createTrace ([#420](https://github.com/eggjs/tegg/issues/420)) ([5471bda](https://github.com/eggjs/tegg/commit/5471bda05bd21ed5c60d82182a2807d09f9be097)) # [3.73.0](https://github.com/eggjs/tegg/compare/v3.72.0...v3.73.0) (2026-03-25) ### Features * add @eggjs/agent-tracing package for AI agent tracing ([#412](https://github.com/eggjs/tegg/issues/412)) ([56f460d](https://github.com/eggjs/tegg/commit/56f460d17007a42b8643d8d5dd25d25c3f52dcc1)) ================================================ FILE: core/agent-tracing/claude.ts ================================================ export * from './index'; export { ClaudeAgentTracer } from './src/ClaudeAgentTracer'; ================================================ FILE: core/agent-tracing/index.ts ================================================ export * from './src/types'; export { AbstractOssClient } from './src/AbstractOssClient'; export { AbstractLogServiceClient } from './src/AbstractLogServiceClient'; ================================================ FILE: core/agent-tracing/langgraph.ts ================================================ export * from './index'; export { LangGraphTracer } from './src/LangGraphTracer'; ================================================ FILE: core/agent-tracing/package.json ================================================ { "name": "@eggjs/agent-tracing", "version": "3.78.15", "description": "Tracing support for AI agents (LangGraph, Claude Agent SDK)", "keywords": [ "agent", "claude", "egg", "langchain", "langgraph", "tegg", "tracing", "typescript" ], "main": "dist/index.js", "files": [ "dist/**/*.js", "dist/**/*.d.ts" ], "typings": "dist/index.d.ts", "typesVersions": { "*": { "claude": [ "./dist/claude.d.ts" ], "langgraph": [ "./dist/langgraph.d.ts" ] } }, "exports": { ".": { "types": "./dist/index.d.ts", "default": "./dist/index.js" }, "./claude": { "types": "./dist/claude.d.ts", "default": "./dist/claude.js" }, "./langgraph": { "types": "./dist/langgraph.d.ts", "default": "./dist/langgraph.js" }, "./package.json": "./package.json" }, "scripts": { "test": "node --eval \"process.exit(parseInt(process.versions.node) < 18 ? 0 : 1)\" || cross-env NODE_ENV=test NODE_OPTIONS='--no-deprecation' mocha", "clean": "tsc -b --clean", "tsc": "ut run clean && tsc -p ./tsconfig.json", "tsc:pub": "ut run clean && tsc -p ./tsconfig.pub.json", "prepublishOnly": "ut tsc:pub" }, "homepage": "https://github.com/eggjs/tegg", "bugs": { "url": "https://github.com/eggjs/tegg/issues" }, "repository": { "type": "git", "url": "git@github.com:eggjs/tegg.git", "directory": "core/agent-tracing" }, "engines": { "node": ">=18.0.0" }, "author": "killagu ", "license": "MIT", "dependencies": { "@eggjs/core-decorator": "^3.78.15", "@eggjs/tegg-background-task": "^3.78.15", "@eggjs/tegg-types": "^3.78.15", "onelogger": "^1.0.1" }, "peerDependencies": { "@anthropic-ai/claude-agent-sdk": ">=0.2.52", "@langchain/core": ">=1.1.1" }, "peerDependenciesMeta": { "@anthropic-ai/claude-agent-sdk": { "optional": true }, "@langchain/core": { "optional": true } }, "devDependencies": { "@anthropic-ai/claude-agent-sdk": "^0.2.52", "@anthropic-ai/sdk": "^0.78.0", "@eggjs/tegg-common-util": "^3.78.15", "@langchain/core": "^1.1.29", "@langchain/langgraph": "^0.2.74", "@types/mocha": "^10.0.1", "@types/node": "^20.2.4", "cross-env": "^7.0.3", "mocha": "^10.2.0", "ts-node": "^10.9.1", "typescript": "^5.0.4" }, "eggModule": { "name": "teggAgentTracing" }, "publishConfig": { "access": "public" } } ================================================ FILE: core/agent-tracing/src/AbstractLogServiceClient.ts ================================================ /** * Abstract log service client for dependency injection. * * To enable log service syncing in TracingService, implement this class in your application * and register it with Tegg IoC. The implementation class MUST be named `LogServiceClient` * (or use `@SingletonProto({ name: 'logServiceClient' })`) so the container can resolve it. * * @example * ```typescript * import { SingletonProto } from '@eggjs/core-decorator'; * import { AccessLevel } from '@eggjs/tegg-types'; * import { AbstractLogServiceClient } from '@eggjs/agent-tracing'; * * // Class name must be LogServiceClient (registers as 'logServiceClient' in the IoC container) * @SingletonProto({ accessLevel: AccessLevel.PUBLIC }) * export class LogServiceClient extends AbstractLogServiceClient { * async send(log: string): Promise { * await fetch('https://log.example.com/api', { * method: 'POST', * headers: { 'content-type': 'application/json' }, * body: JSON.stringify({ log }), * }); * } * } * ``` * * If no implementation is registered, log service syncing is silently skipped. */ export abstract class AbstractLogServiceClient { abstract send(log: string): Promise; } ================================================ FILE: core/agent-tracing/src/AbstractOssClient.ts ================================================ /** * Abstract OSS client for dependency injection. * * To enable OSS uploads in TracingService, implement this class in your application * and register it with Tegg IoC. The implementation class MUST be named `OssClient` * (or use `@SingletonProto({ name: 'ossClient' })`) so the container can resolve it. * * @example * ```typescript * import { SingletonProto } from '@eggjs/core-decorator'; * import { AccessLevel } from '@eggjs/tegg-types'; * import { AbstractOssClient } from '@eggjs/agent-tracing'; * * // Class name must be OssClient (registers as 'ossClient' in the IoC container) * @SingletonProto({ accessLevel: AccessLevel.PUBLIC }) * export class OssClient extends AbstractOssClient { * async put(key: string, content: string | Buffer): Promise { * // your OSS implementation here * } * } * ``` * * If no implementation is registered, OSS uploads are silently skipped. */ export abstract class AbstractOssClient { abstract put(key: string, content: string | Buffer): Promise; } ================================================ FILE: core/agent-tracing/src/ClaudeAgentTracer.ts ================================================ import { randomUUID } from 'node:crypto'; import type { SDKMessage, SDKResultMessage } from '@anthropic-ai/claude-agent-sdk'; import { SingletonProto, Inject } from '@eggjs/core-decorator'; import { AccessLevel } from '@eggjs/tegg-types'; import type { Logger } from '@eggjs/tegg-types'; import type { Run } from '@langchain/core/tracers/base'; import type { TracingService } from './TracingService'; import { type ClaudeMessage, type ClaudeContentBlock, type ClaudeTokenUsage, type IRunCost, type CreateTraceOptions, RunStatus, type TracerConfig, applyTracerConfig, } from './types'; /** * Manages state for a single agent execution with streaming support. * Allows processing messages one-by-one and logging them immediately. */ class Trace { private traceId: string; private threadId?: string; private inputs?: Record; private rootRun: Run | null = null; private rootRunId: string; private startTime: number; private executionOrder = 2; // Start at 2, root is 1 private pendingToolUses = new Map(); private outputMessages: Array<{ role: string; content: ClaudeContentBlock[] }> = []; private tracer: ClaudeAgentTracer; constructor(tracer: ClaudeAgentTracer, options?: CreateTraceOptions) { this.tracer = tracer; this.traceId = options?.traceId || randomUUID(); this.threadId = options?.threadId; this.inputs = options?.inputs; this.rootRunId = randomUUID(); this.startTime = Date.now(); } /** * Process a single SDK message and log it immediately. * Non-tracing message types (tool_progress, stream_event, status, etc.) are automatically ignored. */ async processMessage(message: SDKMessage): Promise { try { const converted = this.tracer.convertSDKMessage(message); if (!converted) return; if (converted.type === 'system' && converted.subtype === 'init') { this.handleInit(converted); } else if (converted.type === 'assistant') { this.handleAssistant(converted); } else if (converted.type === 'user') { this.handleUser(converted); } else if (converted.type === 'result') { this.handleResult(converted); } } catch (e) { this.tracer.logger.warn('[ClaudeAgentTracer] processMessage error:', e); } } private handleInit(message: ClaudeMessage): void { // threadId: prefer constructor option, fallback to init message's session_id if (!this.threadId) { this.threadId = message.session_id; } this.rootRun = this.tracer.createRootRunInternal(message, this.startTime, this.traceId, this.rootRunId, this.threadId); if (this.inputs) { Object.assign(this.rootRun.inputs, this.inputs); } this.tracer.logTrace(this.rootRun, RunStatus.START); } private handleAssistant(message: ClaudeMessage): void { if (!this.rootRun) { this.tracer.logger.warn('[ClaudeAgentTracer] Received assistant message before init'); return; } const content = message.message?.content || []; const hasToolUse = content.some(c => c.type === 'tool_use'); const hasText = content.some(c => c.type === 'text'); // Collect assistant message for outputs.messages if (content.length > 0) { this.outputMessages.push({ role: 'assistant', content }); } if (hasToolUse) { const eventTime = Date.now(); // Create LLM run that initiated tool calls const llmRun = this.tracer.createLLMRunInternal( message, this.rootRunId, this.traceId, this.executionOrder++, eventTime, true, ); this.rootRun.child_runs.push(llmRun); this.tracer.logTrace(llmRun, RunStatus.END); // Create tool runs (will be completed when tool_result arrives) for (const block of content) { if (block.type === 'tool_use') { const toolRun = this.tracer.createToolRunStartInternal( block, this.rootRunId, this.traceId, this.executionOrder++, eventTime, ); this.rootRun.child_runs.push(toolRun); this.pendingToolUses.set(block.id, toolRun); this.tracer.logTrace(toolRun, RunStatus.START); } } } else if (hasText) { // Text-only response const llmRun = this.tracer.createLLMRunInternal( message, this.rootRunId, this.traceId, this.executionOrder++, Date.now(), false, ); this.rootRun.child_runs.push(llmRun); this.tracer.logTrace(llmRun, RunStatus.END); } } private handleUser(message: ClaudeMessage): void { if (!message.message?.content) return; for (const block of message.message.content) { if (block.type === 'tool_result') { const toolRun = this.pendingToolUses.get(block.tool_use_id); if (toolRun) { this.tracer.completeToolRunInternal(toolRun, block, Date.now()); const status = block.is_error ? RunStatus.ERROR : RunStatus.END; this.tracer.logTrace(toolRun, status); this.pendingToolUses.delete(block.tool_use_id); } } } } private handleResult(message: ClaudeMessage): void { if (!this.rootRun) { this.tracer.logger.warn('[ClaudeAgentTracer] Received result message before init'); return; } // Complete any pending tool runs for (const [ toolUseId, toolRun ] of this.pendingToolUses) { this.tracer.logger.warn(`[ClaudeAgentTracer] Tool run ${toolUseId} did not receive result`); toolRun.end_time = Date.now(); this.tracer.logTrace(toolRun, RunStatus.ERROR); } this.pendingToolUses.clear(); // Update and log root run end this.rootRun.end_time = this.startTime + (message.duration_ms || 0); this.rootRun.outputs = { messages: this.outputMessages, result: message.result, is_error: message.is_error, num_turns: message.num_turns, }; if (message.usage || message.modelUsage) { const cost = this.tracer.createRunCostInternal(message); if (this.rootRun.outputs) { (this.rootRun.outputs as any).llmOutput = cost; } } if (message.is_error) { this.rootRun.error = message.result; } this.rootRun.child_execution_order = this.executionOrder - 1; const status = message.is_error ? RunStatus.ERROR : RunStatus.END; this.tracer.logTrace(this.rootRun, status); } /** * Get current trace ID */ getTraceId(): string { return this.traceId; } } /** * ClaudeAgentTracer - Converts Claude SDK messages to LangChain Run format * and logs them to the same remote logging system as LangGraphTracer. * * Supports both batch processing (processMessages) and streaming (createTrace). */ @SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) export class ClaudeAgentTracer { /** @internal */ @Inject() readonly logger: Logger; @Inject() private tracingService: TracingService; name = 'ClaudeAgentTracer'; agentName = ''; /** * Configure the tracer with agent name and service credentials. */ configure(config: TracerConfig): void { applyTracerConfig(this, config); } /** * Create a new trace for one agent execution. * Returns a Trace object for streaming message processing. * * @param options.traceId - Server-side trace ID for call chain linking. Defaults to a random UUID. * @param options.threadId - Thread ID (conversation/session identifier), recorded in metadata. * @param options.inputs - Additional inputs to merge into root run's inputs (e.g. user messages). * * @example * const trace = claudeTracer.createTrace({ * traceId: ctx.tracer.traceId, * threadId, * inputs: { messages: [{ role: 'user', content: 'hello' }] }, * }); * for await (const message of agent.run('task')) { * await trace.processMessage(message); * } */ public createTrace(options?: CreateTraceOptions): Trace { return new Trace(this, options); } /** * Main entry point - convert SDK messages to Run trees and log them. * Use this when you have all messages collected (batch processing). * For real-time streaming, use createTrace() instead. * * Non-tracing message types (tool_progress, stream_event, status, etc.) are automatically filtered out. */ public async processMessages(sdkMessages: SDKMessage[]): Promise { try { if (!sdkMessages || sdkMessages.length === 0) { this.logger.warn('[ClaudeAgentTracer] No messages to process'); return; } // Pre-validate: ensure there is an init message before creating trace const hasInit = sdkMessages.some(m => m.type === 'system' && 'subtype' in m && m.subtype === 'init'); if (!hasInit) { this.logger.warn('[ClaudeAgentTracer] No system/init message found'); return; } // Delegate to Trace for message processing const trace = this.createTrace(); for (const msg of sdkMessages) { await trace.processMessage(msg); } } catch (e) { this.logger.warn('[ClaudeAgentTracer] processMessages error:', e); } } /** * @internal * Convert an SDKMessage to internal ClaudeMessage format. * Returns null for message types that are not relevant to tracing. */ convertSDKMessage(msg: SDKMessage): ClaudeMessage | null { // SDKSystemMessage (init) if (msg.type === 'system' && 'subtype' in msg && msg.subtype === 'init') { return msg as unknown as ClaudeMessage; } // SDKAssistantMessage if (msg.type === 'assistant' && 'message' in msg && 'parent_tool_use_id' in msg) { return { type: 'assistant', uuid: msg.uuid, session_id: msg.session_id, message: msg.message as any, parent_tool_use_id: msg.parent_tool_use_id, }; } // SDKUserMessage (tool results, not replay) if (msg.type === 'user' && 'message' in msg && !('isReplay' in msg && (msg as any).isReplay)) { return { type: 'user', uuid: msg.uuid || randomUUID(), session_id: msg.session_id, message: msg.message as any, parent_tool_use_id: (msg as any).parent_tool_use_id, }; } // SDKResultMessage (success or error) if (msg.type === 'result') { const resultMsg = msg as SDKResultMessage; const isSuccess = resultMsg.subtype === 'success'; return { type: 'result', subtype: isSuccess ? 'success' : 'error', is_error: resultMsg.is_error, duration_ms: resultMsg.duration_ms, duration_api_ms: resultMsg.duration_api_ms, num_turns: resultMsg.num_turns, result: isSuccess ? (resultMsg as any).result : (resultMsg as any).errors?.join('; ') || 'Unknown error', session_id: resultMsg.session_id, total_cost_usd: resultMsg.total_cost_usd, usage: resultMsg.usage as any, modelUsage: resultMsg.modelUsage as any, uuid: resultMsg.uuid, }; } // Ignore all other SDK message types (tool_progress, stream_event, status, hook, etc.) return null; } /** * @internal * Create root run from init message (used by Trace) */ createRootRunInternal(initMsg: ClaudeMessage, startTime: number, traceId: string, rootRunId?: string, threadId?: string): Run { const runId = rootRunId || initMsg.uuid || randomUUID(); const resolvedThreadId = threadId || initMsg.session_id; return { id: runId, name: this.name, run_type: 'chain', inputs: {}, outputs: undefined, start_time: startTime, end_time: undefined, execution_order: 1, child_execution_order: 1, child_runs: [], events: [], trace_id: traceId, parent_run_id: undefined, tags: [], extra: { metadata: { thread_id: resolvedThreadId, }, tools: initMsg.tools || [], model: initMsg.model, session_id: resolvedThreadId, mcp_servers: initMsg.mcp_servers, agents: initMsg.agents, slash_commands: initMsg.slash_commands, apiKeySource: initMsg.apiKeySource, claude_code_version: initMsg.claude_code_version, output_style: initMsg.output_style, permissionMode: initMsg.permissionMode, }, } as Run; } /** * @internal * Create LLM run from assistant message (used by Trace) */ createLLMRunInternal( msg: ClaudeMessage, rootRunId: string, traceId: string, order: number, startTime: number, isToolCall: boolean, ): Run { const runId = msg.uuid || randomUUID(); const content = msg.message?.content || []; const textBlocks = content.filter(c => c.type === 'text'); const toolBlocks = content.filter(c => c.type === 'tool_use'); const inputs = { messages: textBlocks.map(c => (c as any).text).filter(Boolean), }; const outputs: any = {}; if (isToolCall) { outputs.tool_calls = toolBlocks.map(c => ({ id: (c as any).id, name: (c as any).name, input: (c as any).input, })); } else { outputs.content = textBlocks.map(c => (c as any).text).join(''); } if (msg.message?.usage) { outputs.llmOutput = this.extractTokenUsage(msg.message.usage); } return { id: runId, name: 'LLM', run_type: 'llm', inputs, outputs, start_time: startTime, end_time: startTime, execution_order: order, child_execution_order: order, child_runs: [], events: [], trace_id: traceId, parent_run_id: rootRunId, tags: [], extra: { model: msg.message?.model, }, } as Run; } /** * @internal * Create tool run at start (before result, used by Trace) */ createToolRunStartInternal( toolUseBlock: ClaudeContentBlock, rootRunId: string, traceId: string, order: number, startTime: number, ): Run { const toolUse = toolUseBlock as any; const runId = randomUUID(); return { id: runId, name: toolUse.name || 'Tool', run_type: 'tool', inputs: { tool_use_id: toolUse.id, ...toolUse.input, }, outputs: undefined, start_time: startTime, end_time: undefined, execution_order: order, child_execution_order: order, child_runs: [], events: [], trace_id: traceId, parent_run_id: rootRunId, tags: [], extra: { tool_use_id: toolUse.id, }, } as Run; } /** * @internal * Complete tool run with result (used by Trace) */ completeToolRunInternal(toolRun: Run, toolResultBlock: ClaudeContentBlock, startTime: number): void { const result = toolResultBlock as any; toolRun.end_time = startTime; toolRun.outputs = { content: result.content, }; if (result.is_error) { toolRun.error = typeof result.content === 'string' ? result.content : JSON.stringify(result.content); } } /** * Extract token usage from Claude SDK usage object into IRunCost format. */ private extractTokenUsage(usage: ClaudeTokenUsage): IRunCost { const result: IRunCost = {}; if (usage.input_tokens !== undefined) { result.promptTokens = usage.input_tokens; } if (usage.output_tokens !== undefined) { result.completionTokens = usage.output_tokens; } if (usage.cache_creation_input_tokens !== undefined) { result.cacheCreationInputTokens = usage.cache_creation_input_tokens; } if (usage.cache_read_input_tokens !== undefined) { result.cacheReadInputTokens = usage.cache_read_input_tokens; } const totalTokens = (usage.input_tokens || 0) + (usage.output_tokens || 0); if (totalTokens > 0) { result.totalTokens = totalTokens; } return result; } /** * @internal * Create run cost from result message (used by Trace) */ createRunCostInternal(resultMsg: ClaudeMessage): IRunCost { const cost: IRunCost = resultMsg.usage ? this.extractTokenUsage(resultMsg.usage) : {}; if (resultMsg.total_cost_usd !== undefined) { cost.totalCost = resultMsg.total_cost_usd; } return cost; } /** * @internal * Log trace - delegates to TracingService (used by Trace) */ logTrace(run: Run, status: RunStatus): void { this.tracingService.logTrace(run, status, this.name, this.agentName); } } ================================================ FILE: core/agent-tracing/src/LangGraphTracer.ts ================================================ import { SingletonProto, Inject } from '@eggjs/core-decorator'; import { AccessLevel } from '@eggjs/tegg-types'; import { BaseTracer } from '@langchain/core/tracers/base'; import type { Run } from '@langchain/core/tracers/base'; import type { TracingService } from './TracingService'; import { RunStatus, type TracerConfig, applyTracerConfig } from './types'; @SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) export class LangGraphTracer extends BaseTracer { @Inject() private tracingService: TracingService; name = 'LangGraphTracer'; agentName = ''; /** * Configure the tracer with agent name and service credentials. */ configure(config: TracerConfig): void { applyTracerConfig(this, config); } // eslint-disable-next-line @typescript-eslint/no-unused-vars protected persistRun(_: Run): Promise { return Promise.resolve(undefined); } private logTrace(run: Run, status: RunStatus): void { this.tracingService.logTrace(run, status, this.name, this.agentName); } onChainStart(run: Run): void | Promise { this.logTrace(run, RunStatus.START); } onChainEnd(run: Run): void | Promise { this.logTrace(run, RunStatus.END); } onChainError(run: Run): void | Promise { this.logTrace(run, RunStatus.ERROR); } onToolStart(run: Run): void | Promise { this.logTrace(run, RunStatus.START); } onToolEnd(run: Run): void | Promise { this.logTrace(run, RunStatus.END); } onToolError(run: Run): void | Promise { this.logTrace(run, RunStatus.ERROR); } onLLMStart(run: Run): void | Promise { this.logTrace(run, RunStatus.START); } onLLMEnd(run: Run): void | Promise { this.logTrace(run, RunStatus.END); } onLLMError(run: Run): void | Promise { this.logTrace(run, RunStatus.ERROR); } onRetrieverStart(run: Run): void | Promise { this.logTrace(run, RunStatus.START); } onRetrieverEnd(run: Run): void | Promise { this.logTrace(run, RunStatus.END); } onRetrieverError(run: Run): void | Promise { this.logTrace(run, RunStatus.ERROR); } onAgentAction(run: Run): void | Promise { this.logTrace(run, RunStatus.START); } onAgentEnd(run: Run): void | Promise { this.logTrace(run, RunStatus.END); } } ================================================ FILE: core/agent-tracing/src/TracingService.ts ================================================ import type { BackgroundTaskHelper } from '@eggjs/tegg-background-task'; import { SingletonProto, Inject, InjectOptional } from '@eggjs/core-decorator'; import { AccessLevel } from '@eggjs/tegg-types'; import type { Logger } from '@eggjs/tegg-types'; import type { Run } from '@langchain/core/tracers/base'; import { getCustomLogger } from 'onelogger'; import { AbstractLogServiceClient } from './AbstractLogServiceClient'; import { AbstractOssClient } from './AbstractOssClient'; import { FIELDS_TO_OSS, type IResource, RunStatus } from './types'; /** * TracingService - Shared service for common tracing operations. * Used by both LangGraphTracer and ClaudeAgentTracer to avoid code duplication. */ @SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) export class TracingService { @Inject() public readonly logger: Logger; @Inject() private backgroundTaskHelper: BackgroundTaskHelper; @InjectOptional() private readonly ossClient: AbstractOssClient; @InjectOptional() private readonly logServiceClient: AbstractLogServiceClient; /** * Get the current environment (local, pre, prod, gray) */ getEnv(): string { const env = process.env.FAAS_ENV || process.env.SERVER_ENV || 'local'; if (env === 'prepub') { return 'pre'; } return env; } /** * Check if running in online environment (prod, pre, gray) */ isOnlineEnv(): boolean { const env = this.getEnv(); return [ 'prod', 'pre', 'gray' ].includes(env); } /** * Generate log info prefix for a run */ getLogInfoPrefix(run: Run, status: RunStatus, name: string): string { const env = this.getEnv(); const envSegment = process.env.FAAS_ENV || env === 'local' ? '' : `env=${env},`; const threadId = (run.extra as Record)?.metadata?.thread_id ?? 'unknown'; return ( `[agent_run][${name}]:` + `traceId=${run.trace_id},` + `threadId=${threadId},` + `type=${run.parent_run_id ? 'child_run' : 'root_run'},` + `status=${status},` + `${envSegment}` + `run_id=${run.id},` + `parent_run_id=${run.parent_run_id ?? ''}` ); } /** * Upload content to OSS using the injected AbstractOssClient implementation. * Gracefully skips if no AbstractOssClient is provided. */ async uploadToOss(key: string, fileContent: string): Promise { if (!this.ossClient) { this.logger.warn('[TracingService] OSS client not configured. Provide an AbstractOssClient implementation.'); return; } this.logger.info(`Uploading to OSS with key: ${key}`); await this.ossClient.put(key, Buffer.from(fileContent)); this.logger.info(`Upload completed for key: ${key}`); } /** * Sync local tracing logs to the injected AbstractLogServiceClient implementation. * Silently skips if no AbstractLogServiceClient is registered. */ async syncLocalToLogService(log: string, agentName: string): Promise { if (!this.logServiceClient) { return; } if (!agentName) { this.logger.warn('[TraceLogErr] syncLocalToLogService: agentName is empty'); return; } try { await this.logServiceClient.send(`[${agentName}]${log}`); } catch (e) { this.logger.warn('[TraceLogErr] syncLocalToLogService error:', e); } } /** * Log trace run data with OSS upload for large fields */ logTrace(run: Run, status: RunStatus, name: string, agentName: string): void { try { const { child_runs: childs, ...runData } = run; if (runData.tags?.includes('langsmith:hidden')) { return; } const env = this.getEnv(); FIELDS_TO_OSS.forEach(field => { if (!runData[field]) { return; } const jsonstr = JSON.stringify(runData[field]); if (field === 'outputs') { (runData as any).cost = runData?.outputs?.llmOutput; } delete runData[field]; const key = `agents/${name}/${env}/traces/${run.trace_id}/runs/${run.id}/${field}`; this.backgroundTaskHelper.run(async () => { try { await this.uploadToOss(key, jsonstr); } catch (e) { this.logger.warn( `[TraceLogErr] Failed to upload run data to OSS for run_id=${run.id}, field=${field}, error:`, e, ); } }); (runData as any)[field] = { compress: 'none', key } as IResource; }); const runJSON = JSON.stringify({ ...runData, child_run_ids: childs?.map(child => child.id) }); const logInfo = this.getLogInfoPrefix(run, status, name) + `,run=${runJSON}`; if (process.env.FAAS_ENV) { this.logger.info(logInfo); } else { const logger = getCustomLogger('agentTraceLogger') || this.logger; logger.info(`[${agentName}]${logInfo}`); } if (env === 'local') { this.backgroundTaskHelper.run(async () => { await this.syncLocalToLogService(logInfo, agentName); }); } } catch (e) { this.logger.warn('[TraceLogErr] logTrace error:', e); } } } ================================================ FILE: core/agent-tracing/src/types.ts ================================================ // Claude SDK Message Types export interface ClaudeTextContent { type: 'text'; text: string; } export interface ClaudeToolUseContent { type: 'tool_use'; id: string; name: string; input?: Record; } export interface ClaudeToolResultContent { type: 'tool_result'; content: string | ClaudeToolResultContent[]; tool_use_id: string; is_error?: boolean; } export type ClaudeContentBlock = ClaudeTextContent | ClaudeToolUseContent | ClaudeToolResultContent; export interface ClaudeTokenUsage { input_tokens: number; output_tokens: number; cache_creation_input_tokens?: number; cache_read_input_tokens?: number; server_tool_use?: { web_search_requests?: number; web_fetch_requests?: number; }; service_tier?: string; cache_creation?: { ephemeral_1h_input_tokens?: number; ephemeral_5m_input_tokens?: number; }; } export interface ClaudeMessageContent { id?: string; type?: string; role?: string; content?: ClaudeContentBlock[]; model?: string; usage?: ClaudeTokenUsage; context_management?: any; stop_reason?: string; stop_sequence?: string; container?: any; [key: string]: any; } export interface ClaudeModelUsage { [modelName: string]: { inputTokens: number; outputTokens: number; cacheReadInputTokens?: number; cacheCreationInputTokens?: number; webSearchRequests?: number; costUSD?: number; contextWindow?: number; maxOutputTokens?: number; }; } export interface ClaudeMessage { type: 'system' | 'assistant' | 'user' | 'result'; subtype?: 'init' | 'success' | 'error'; session_id?: string; uuid: string; message?: ClaudeMessageContent; // System/init message fields cwd?: string; tools?: string[]; mcp_servers?: Array<{ name: string; status: string }>; model?: string; permissionMode?: string; slash_commands?: string[]; apiKeySource?: string; claude_code_version?: string; output_style?: string; agents?: string[]; skills?: any[]; plugins?: any[]; // Result message fields is_error?: boolean; duration_ms?: number; duration_api_ms?: number; num_turns?: number; result?: string; total_cost_usd?: number; usage?: ClaudeTokenUsage; modelUsage?: ClaudeModelUsage; permission_denials?: any[]; // User message fields (tool results) parent_tool_use_id?: string | null; tool_use_id?: string; tool_use_result?: string; // Error fields error?: string; [key: string]: any; } // Shared tracing types (from LangGraphTracer) export interface IResource { compress: 'none' | 'gzip'; key: string; } export interface IRunCost { promptTokens?: number; completionTokens?: number; totalTokens?: number; cacheCreationInputTokens?: number; cacheReadInputTokens?: number; totalCost?: number; } const FIELDS_TO_OSS = [ 'inputs', 'outputs', 'attachments', 'serialized', 'events' ] as const; export const RunStatus = { START: 'start', END: 'end', ERROR: 'error', } as const; export type RunStatus = (typeof RunStatus)[keyof typeof RunStatus]; /** All context needed to create a trace for one agent execution. */ export interface CreateTraceOptions { /** Server-side trace ID for linking to the request call chain. Defaults to a random UUID. */ traceId?: string; /** Thread ID (conversation/session identifier), recorded in metadata. */ threadId?: string; /** Additional inputs to merge into root run's inputs (e.g. user messages). */ inputs?: Record; } /** User-facing config passed to tracer.configure() */ export interface TracerConfig { agentName?: string; } /** Apply user-facing TracerConfig to a tracer instance. */ export function applyTracerConfig(tracer: { agentName: string }, config: TracerConfig): void { if (config.agentName !== undefined) { tracer.agentName = config.agentName; } } export { FIELDS_TO_OSS }; ================================================ FILE: core/agent-tracing/test/ClaudeAgentTracer.test.ts ================================================ import assert from 'node:assert/strict'; import type { SDKMessage } from '@anthropic-ai/claude-agent-sdk'; import { ClaudeAgentTracer } from '../src/ClaudeAgentTracer'; import { RunStatus } from '../src/types'; import { createMockLogger, createCapturingTracingService } from './TestUtils'; // ---------- Shared setup ---------- function createTestEnv() { const { tracingService, capturedRuns } = createCapturingTracingService(); const mockLogger = createMockLogger(); const claudeTracer = new ClaudeAgentTracer(); (claudeTracer as any).logger = mockLogger; (claudeTracer as any).tracingService = tracingService; return { claudeTracer, capturedRuns }; } // ---------- Mock data factories ---------- function createMockInit(overrides?: Partial): SDKMessage { return { type: 'system', subtype: 'init', session_id: 'test-session-001', uuid: 'uuid-init', tools: [ 'Bash', 'Read' ], model: 'claude-sonnet-4-5-20250929', cwd: '/test', mcp_servers: [], permissionMode: 'default', apiKeySource: 'api_key', claude_code_version: '1.0.0', output_style: 'text', slash_commands: [], skills: [], plugins: [], ...overrides, } as unknown as SDKMessage; } function createMockAssistantWithTool(overrides?: Partial): SDKMessage { return { type: 'assistant', uuid: 'uuid-assistant-tool', session_id: 'test-session-001', parent_tool_use_id: null, message: { id: 'msg_1', type: 'message', role: 'assistant', content: [ { type: 'text', text: 'Let me run that command for you.' }, { type: 'tool_use', id: 'tu_1', name: 'Bash', input: { command: 'echo hello' } }, ], model: 'claude-sonnet-4-5-20250929', usage: { input_tokens: 100, output_tokens: 50 }, stop_reason: 'tool_use', }, ...overrides, } as unknown as SDKMessage; } function createMockUserToolResult(overrides?: Partial): SDKMessage { return { type: 'user', uuid: 'uuid-user-result', session_id: 'test-session-001', parent_tool_use_id: null, message: { role: 'user', content: [ { type: 'tool_result', tool_use_id: 'tu_1', content: 'hello', is_error: false, }, ], }, ...overrides, } as unknown as SDKMessage; } function createMockAssistantTextOnly(overrides?: Partial): SDKMessage { return { type: 'assistant', uuid: 'uuid-assistant-text', session_id: 'test-session-001', parent_tool_use_id: null, message: { id: 'msg_2', type: 'message', role: 'assistant', content: [{ type: 'text', text: 'The answer is 21.' }], model: 'claude-sonnet-4-5-20250929', usage: { input_tokens: 80, output_tokens: 30 }, stop_reason: 'end_turn', }, ...overrides, } as unknown as SDKMessage; } function createMockResult(overrides?: Partial): SDKMessage { return { type: 'result', subtype: 'success', session_id: 'test-session-001', uuid: 'uuid-result', is_error: false, duration_ms: 1500, duration_api_ms: 1200, num_turns: 1, result: 'hello', stop_reason: null, total_cost_usd: 0.003, usage: { input_tokens: 100, output_tokens: 50, cache_creation_input_tokens: 0, cache_read_input_tokens: 0, }, modelUsage: {}, permission_denials: [], ...overrides, } as unknown as SDKMessage; } // Noise messages that should be filtered out function createMockToolProgress(): SDKMessage { return { type: 'tool_progress', tool_use_id: 'tu_1', tool_name: 'Bash', parent_tool_use_id: null, elapsed_time_seconds: 0.5, uuid: 'uuid-progress', session_id: 'test-session-001', } as unknown as SDKMessage; } function createMockStreamEvent(): SDKMessage { return { type: 'stream_event', event: { type: 'content_block_start', index: 0, content_block: { type: 'text', text: '' } }, parent_tool_use_id: null, uuid: 'uuid-stream', session_id: 'test-session-001', } as unknown as SDKMessage; } // ---------- Tests ---------- describe('test/ClaudeAgentTracer.test.ts', () => { let originalFaasEnv: string | undefined; beforeEach(() => { originalFaasEnv = process.env.FAAS_ENV; process.env.FAAS_ENV = 'dev'; }); afterEach(() => { if (originalFaasEnv === undefined) { delete process.env.FAAS_ENV; } else { process.env.FAAS_ENV = originalFaasEnv; } }); describe('Streaming mode + tool use', () => { it('should trace tool execution with session.processMessage', async () => { const { claudeTracer, capturedRuns } = createTestEnv(); const trace = claudeTracer.createTrace(); // Feed messages one-by-one, including noise messages that should be filtered const messages: SDKMessage[] = [ createMockInit(), createMockStreamEvent(), // noise — should be ignored createMockAssistantWithTool(), createMockToolProgress(), // noise — should be ignored createMockUserToolResult(), createMockResult(), ]; for (const msg of messages) { await trace.processMessage(msg); } // Root run start + end const rootStart = capturedRuns.find(e => !e.run.parent_run_id && e.status === RunStatus.START); assert(rootStart, 'Should have root_run start'); assert.strictEqual(rootStart.run.run_type, 'chain'); const rootEnd = capturedRuns.find(e => !e.run.parent_run_id && e.status === RunStatus.END); assert(rootEnd, 'Should have root_run end'); // LLM child run const llmRuns = capturedRuns.filter(e => !!e.run.parent_run_id && e.run.run_type === 'llm'); assert(llmRuns.length >= 1, `Should have >= 1 LLM run, got ${llmRuns.length}`); // Tool child run start + end const toolRuns = capturedRuns.filter(e => !!e.run.parent_run_id && e.run.run_type === 'tool'); assert(toolRuns.length >= 2, `Should have >= 2 tool run entries (start+end), got ${toolRuns.length}`); const toolStart = toolRuns.find(e => e.status === RunStatus.START); assert(toolStart, 'Should have tool start'); assert.strictEqual(toolStart.run.name, 'Bash'); const toolEnd = toolRuns.find(e => e.status === RunStatus.END); assert(toolEnd, 'Should have tool end'); // All runs share the same trace_id (auto-generated UUID, NOT session_id) const traceIds = new Set(capturedRuns.map(e => e.run.trace_id)); assert.strictEqual(traceIds.size, 1, `All runs should share one trace_id, got ${traceIds.size}`); assert.notStrictEqual([ ...traceIds ][0], 'test-session-001', 'trace_id should NOT equal session_id'); // Root run should carry session_id as thread_id in extra.metadata const rootExtra = rootStart.run.extra as Record; assert.strictEqual(rootExtra?.metadata?.thread_id, 'test-session-001', 'thread_id should match session_id'); // Child runs reference root run as parent const childEntries = capturedRuns.filter(e => !!e.run.parent_run_id); for (const child of childEntries) { assert.strictEqual( child.run.parent_run_id, rootStart.run.id, `Child run ${child.run.name} should reference root as parent`, ); } // Cost data on root end const llmOutput = (rootEnd.run.outputs as any)?.llmOutput; assert(llmOutput, 'Root end should have llmOutput'); assert.strictEqual(llmOutput.promptTokens, 100); assert.strictEqual(llmOutput.completionTokens, 50); assert.strictEqual(llmOutput.totalCost, 0.003); }); }); describe('Separate traceId and sessionId', () => { it('should use provided traceId and record threadId in metadata', async () => { const { claudeTracer, capturedRuns } = createTestEnv(); const trace = claudeTracer.createTrace({ traceId: 'server-trace-abc', threadId: 'my-thread-id', }); const messages: SDKMessage[] = [ createMockInit(), createMockAssistantTextOnly(), createMockResult(), ]; for (const msg of messages) { await trace.processMessage(msg); } // All runs should use the provided traceId const traceIds = new Set(capturedRuns.map(e => e.run.trace_id)); assert.strictEqual(traceIds.size, 1); assert.strictEqual([ ...traceIds ][0], 'server-trace-abc', 'trace_id should be the server-side traceId'); // thread_id in metadata should be the sessionId, not the traceId const rootStart = capturedRuns.find(e => !e.run.parent_run_id && e.status === RunStatus.START); assert(rootStart); const rootExtra = rootStart.run.extra as Record; assert.strictEqual(rootExtra?.metadata?.thread_id, 'my-thread-id', 'thread_id should be the provided threadId'); }); it('should fallback threadId to init message session_id when not provided', async () => { const { claudeTracer, capturedRuns } = createTestEnv(); const trace = claudeTracer.createTrace({ traceId: 'server-trace-xyz' }); await trace.processMessage(createMockInit()); await trace.processMessage(createMockResult()); const rootStart = capturedRuns.find(e => !e.run.parent_run_id && e.status === RunStatus.START); assert(rootStart); const rootExtra = rootStart.run.extra as Record; assert.strictEqual(rootExtra?.metadata?.thread_id, 'test-session-001', 'thread_id should fallback to init session_id'); const traceIds = new Set(capturedRuns.map(e => e.run.trace_id)); assert.strictEqual([ ...traceIds ][0], 'server-trace-xyz'); }); }); describe('Trace inputs in root run', () => { it('should merge inputs into root run inputs when provided via createTrace', async () => { const { claudeTracer, capturedRuns } = createTestEnv(); const trace = claudeTracer.createTrace({ inputs: { messages: [{ role: 'user', content: 'Hello, what is 1+1?' }] }, }); const messages: SDKMessage[] = [ createMockInit(), createMockAssistantTextOnly(), createMockResult(), ]; for (const msg of messages) { await trace.processMessage(msg); } const rootStart = capturedRuns.find(e => !e.run.parent_run_id && e.status === RunStatus.START); assert(rootStart, 'Should have root_run start'); assert.deepStrictEqual( (rootStart.run.inputs as any).messages, [{ role: 'user', content: 'Hello, what is 1+1?' }], ); // Config fields should be in extra, not inputs const rootExtra = rootStart.run.extra as Record; assert.deepStrictEqual(rootExtra.tools, [ 'Bash', 'Read' ]); }); it('should not have extra inputs when not provided', async () => { const { claudeTracer, capturedRuns } = createTestEnv(); const trace = claudeTracer.createTrace(); await trace.processMessage(createMockInit()); await trace.processMessage(createMockResult()); const rootStart = capturedRuns.find(e => !e.run.parent_run_id && e.status === RunStatus.START); assert(rootStart, 'Should have root_run start'); assert.strictEqual((rootStart.run.inputs as any).messages, undefined); }); }); describe('Trace outputs.messages in root run', () => { it('should collect assistant text messages into outputs.messages', async () => { const { claudeTracer, capturedRuns } = createTestEnv(); const trace = claudeTracer.createTrace(); const messages: SDKMessage[] = [ createMockInit(), createMockAssistantWithTool(), createMockUserToolResult(), createMockAssistantTextOnly(), createMockResult(), ]; for (const msg of messages) { await trace.processMessage(msg); } const rootEnd = capturedRuns.find(e => !e.run.parent_run_id && e.status === RunStatus.END); assert(rootEnd, 'Should have root_run end'); const outputMessages = (rootEnd.run.outputs as any)?.messages; assert(Array.isArray(outputMessages), 'outputs.messages should be an array'); assert.strictEqual(outputMessages.length, 2); // First message has text + tool_use assert.strictEqual(outputMessages[0].role, 'assistant'); assert.strictEqual(outputMessages[0].content.length, 2); assert.strictEqual(outputMessages[0].content[0].type, 'text'); assert.strictEqual(outputMessages[0].content[0].text, 'Let me run that command for you.'); assert.strictEqual(outputMessages[0].content[1].type, 'tool_use'); assert.strictEqual(outputMessages[0].content[1].name, 'Bash'); // Second message has text only assert.strictEqual(outputMessages[1].role, 'assistant'); assert.deepStrictEqual(outputMessages[1].content, [{ type: 'text', text: 'The answer is 21.' }]); }); it('should have empty messages array when no assistant text', async () => { const { claudeTracer, capturedRuns } = createTestEnv(); const trace = claudeTracer.createTrace(); await trace.processMessage(createMockInit()); await trace.processMessage(createMockResult()); const rootEnd = capturedRuns.find(e => !e.run.parent_run_id && e.status === RunStatus.END); assert(rootEnd, 'Should have root_run end'); const outputMessages = (rootEnd.run.outputs as any)?.messages; assert(Array.isArray(outputMessages), 'outputs.messages should be an array'); assert.strictEqual(outputMessages.length, 0); }); }); describe('Batch mode + text-only', () => { it('should trace a text-only response via processMessages', async () => { const { claudeTracer, capturedRuns } = createTestEnv(); const messages: SDKMessage[] = [ createMockInit(), createMockAssistantTextOnly(), createMockResult({ usage: { input_tokens: 80, output_tokens: 30, cache_creation_input_tokens: 0, cache_read_input_tokens: 0, }, total_cost_usd: 0.002, }), ]; await claudeTracer.processMessages(messages); assert(capturedRuns.length > 0, 'Should have tracing entries'); // Root run start + end const rootEntries = capturedRuns.filter(e => !e.run.parent_run_id); assert(rootEntries.length >= 2, `Should have root start + end, got ${rootEntries.length}`); const rootEnd = rootEntries.find(e => e.status === RunStatus.END); assert(rootEnd, 'Should have root end'); // LLM child run with text content const llmRuns = capturedRuns.filter(e => !!e.run.parent_run_id && e.run.run_type === 'llm'); assert(llmRuns.length >= 1, `Should have >= 1 LLM run, got ${llmRuns.length}`); // No tool runs const toolRuns = capturedRuns.filter(e => !!e.run.parent_run_id && e.run.run_type === 'tool'); assert.strictEqual(toolRuns.length, 0, 'Should have no tool runs for text-only'); // Cost and token counts const llmOutput = (rootEnd.run.outputs as any)?.llmOutput; assert(llmOutput, 'Should have llmOutput'); assert.strictEqual(llmOutput.promptTokens, 80); assert.strictEqual(llmOutput.completionTokens, 30); assert.strictEqual(llmOutput.totalTokens, 110); assert.strictEqual(llmOutput.totalCost, 0.002); // trace_id consistency const traceIds = new Set(capturedRuns.map(e => e.run.trace_id)); assert.strictEqual(traceIds.size, 1, 'All runs should share one trace_id'); }); }); describe('Error scenario', () => { it('should trace an error result with ERROR status', async () => { const { claudeTracer, capturedRuns } = createTestEnv(); const trace = claudeTracer.createTrace(); const messages: SDKMessage[] = [ createMockInit(), createMockAssistantTextOnly(), { type: 'result', subtype: 'error_during_execution', session_id: 'test-session-001', uuid: 'uuid-result-err', is_error: true, duration_ms: 500, duration_api_ms: 400, num_turns: 1, stop_reason: null, total_cost_usd: 0.001, errors: [ 'Something went wrong', 'Another error' ], usage: { input_tokens: 50, output_tokens: 10, cache_creation_input_tokens: 0, cache_read_input_tokens: 0, }, modelUsage: {}, permission_denials: [], } as unknown as SDKMessage, ]; for (const msg of messages) { await trace.processMessage(msg); } // Root run should end with ERROR status const rootError = capturedRuns.find(e => !e.run.parent_run_id && e.status === RunStatus.ERROR); assert(rootError, 'Should have root_run with error status'); assert(rootError.run, 'Root error run should exist'); }); }); describe('Guard clauses — messages before init', () => { it('should warn and ignore assistant message received before init', async () => { const { claudeTracer, capturedRuns } = createTestEnv(); const trace = claudeTracer.createTrace(); // Send assistant message without a preceding init await trace.processMessage(createMockAssistantWithTool()); // Nothing should have been traced assert.strictEqual(capturedRuns.length, 0, 'No runs should be captured before init'); }); it('should warn and ignore result message received before init', async () => { const { claudeTracer, capturedRuns } = createTestEnv(); const trace = claudeTracer.createTrace(); // Send result message without a preceding init await trace.processMessage(createMockResult()); // Nothing should have been traced assert.strictEqual(capturedRuns.length, 0, 'No runs should be captured before init'); }); }); describe('Pending tool runs cleanup on result', () => { it('should log ERROR for pending tool runs that never received a result', async () => { const { claudeTracer, capturedRuns } = createTestEnv(); const trace = claudeTracer.createTrace(); // Init → assistant calls a tool → result arrives WITHOUT the tool_result const messages: SDKMessage[] = [ createMockInit(), createMockAssistantWithTool(), // creates a pending tool run (tu_1) createMockResult(), // result arrives before tool_result ]; for (const msg of messages) { await trace.processMessage(msg); } // The pending tool run should have been force-closed with ERROR status const toolErrors = capturedRuns.filter(e => e.run.run_type === 'tool' && e.status === RunStatus.ERROR); assert(toolErrors.length >= 1, `Should have at least one tool ERROR entry, got ${toolErrors.length}`); }); }); describe('processMessages edge cases', () => { it('should warn and return early for empty message array', async () => { const { claudeTracer, capturedRuns } = createTestEnv(); await claudeTracer.processMessages([]); assert.strictEqual(capturedRuns.length, 0, 'No runs should be captured for empty input'); }); it('should warn and return early when no init message is present', async () => { const { claudeTracer, capturedRuns } = createTestEnv(); // Only assistant and result, no system/init const messages: SDKMessage[] = [ createMockAssistantTextOnly(), createMockResult() ]; await claudeTracer.processMessages(messages); assert.strictEqual(capturedRuns.length, 0, 'No runs should be captured without an init message'); }); }); describe('Internal error handling', () => { it('should catch errors thrown inside processMessage without propagating', async () => { const { claudeTracer } = createTestEnv(); const trace = claudeTracer.createTrace(); // Force an error by replacing logTrace with a throwing stub (claudeTracer as any).tracingService = { logTrace: () => { throw new Error('unexpected logTrace error'); }, }; // Should NOT throw — error is swallowed by the catch block await assert.doesNotReject(async () => { await trace.processMessage(createMockInit()); }); }); it('should catch errors thrown inside processMessages without propagating', async () => { const { claudeTracer } = createTestEnv(); // Replace createTrace with a throwing stub to trigger the outer catch (claudeTracer as any).createTrace = () => { throw new Error('unexpected createTrace error'); }; // Should NOT throw — error is swallowed by the catch block await assert.doesNotReject(async () => { await claudeTracer.processMessages([ createMockInit() ]); }); }); }); }); ================================================ FILE: core/agent-tracing/test/Configure.test.ts ================================================ import assert from 'node:assert/strict'; import { ClaudeAgentTracer } from '../src/ClaudeAgentTracer'; import { LangGraphTracer } from '../src/LangGraphTracer'; import { createMockLogger, createCapturingTracingService } from './TestUtils'; describe('test/Configure.test.ts', () => { describe('LangGraphTracer.configure()', () => { it('should set agentName and delegate to TracingService', () => { const { tracingService } = createCapturingTracingService(); const tracer = new LangGraphTracer(); (tracer as any).tracingService = tracingService; tracer.configure({ agentName: 'MyAgent', }); assert.strictEqual(tracer.agentName, 'MyAgent'); assert.strictEqual(tracer.name, 'LangGraphTracer', 'name should remain default'); }); it('should not change agentName when not provided', () => { const { tracingService } = createCapturingTracingService(); const tracer = new LangGraphTracer(); (tracer as any).tracingService = tracingService; tracer.agentName = 'existing'; tracer.configure({}); assert.strictEqual(tracer.agentName, 'existing'); }); }); describe('ClaudeAgentTracer.configure()', () => { it('should set agentName and delegate to TracingService', () => { const { tracingService } = createCapturingTracingService(); const mockLogger = createMockLogger(); const tracer = new ClaudeAgentTracer(); (tracer as any).logger = mockLogger; (tracer as any).tracingService = tracingService; tracer.configure({ agentName: 'MyClaude', }); assert.strictEqual(tracer.agentName, 'MyClaude'); assert.strictEqual(tracer.name, 'ClaudeAgentTracer', 'name should remain default'); }); it('should not change agentName when not provided', () => { const { tracingService } = createCapturingTracingService(); const mockLogger = createMockLogger(); const tracer = new ClaudeAgentTracer(); (tracer as any).logger = mockLogger; (tracer as any).tracingService = tracingService; tracer.agentName = 'existing'; tracer.configure({}); assert.strictEqual(tracer.agentName, 'existing'); }); }); }); ================================================ FILE: core/agent-tracing/test/LangGraphTracer.test.ts ================================================ import assert from 'node:assert/strict'; import type { Run } from '@langchain/core/tracers/base'; import { FakeLLM } from '@langchain/core/utils/testing'; import { StateGraph, Annotation, START, END } from '@langchain/langgraph'; import { LangGraphTracer } from '../src/LangGraphTracer'; import { RunStatus } from '../src/types'; import { type CapturedEntry, createCapturingTracingService, createMockRun } from './TestUtils'; const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); /** Shared state schema for test graphs */ const GraphState = Annotation.Root({ query: Annotation, result: Annotation, }); describe('test/LangGraphTracer.test.ts', () => { let tracer: LangGraphTracer; let capturedRuns: CapturedEntry[]; let originalFaasEnv: string | undefined; beforeEach(() => { originalFaasEnv = process.env.FAAS_ENV; process.env.FAAS_ENV = 'dev'; const capturing = createCapturingTracingService(); capturedRuns = capturing.capturedRuns; tracer = new LangGraphTracer(); (tracer as any).tracingService = capturing.tracingService; }); afterEach(() => { if (originalFaasEnv === undefined) { delete process.env.FAAS_ENV; } else { process.env.FAAS_ENV = originalFaasEnv; } }); describe('Single-node StateGraph triggers chain lifecycle hooks', () => { it('should trigger onChainStart and onChainEnd via graph.invoke', async () => { const graph = new StateGraph(GraphState) .addNode('process', (state: typeof GraphState.State) => { return { result: `processed: ${state.query}` }; }) .addEdge(START, 'process') .addEdge('process', END) .compile(); await graph.invoke({ query: 'hello', result: '' }, { callbacks: [ tracer ] }); await sleep(500); const startEntries = capturedRuns.filter(e => e.status === RunStatus.START); const endEntries = capturedRuns.filter(e => e.status === RunStatus.END); assert(startEntries.length >= 1, `Should have at least one start entry, got ${startEntries.length}`); assert(endEntries.length >= 1, `Should have at least one end entry, got ${endEntries.length}`); // Verify a chain run is present with run_type=chain const chainStart = startEntries.find(e => e.run.run_type === 'chain'); assert(chainStart, 'Should have a chain start entry with run_type=chain'); }); it('should produce Run with valid id, trace_id, and run_type fields', async () => { const graph = new StateGraph(GraphState) .addNode('echo', (state: typeof GraphState.State) => { return { result: state.query }; }) .addEdge(START, 'echo') .addEdge('echo', END) .compile(); await graph.invoke({ query: 'test', result: '' }, { callbacks: [ tracer ] }); await sleep(500); assert(capturedRuns.length > 0, 'Should have captured runs'); for (const entry of capturedRuns) { assert(entry.run.id, 'Run should have an id'); assert(entry.run.trace_id, 'Run should have a trace_id'); assert(entry.run.run_type, 'Run should have a run_type'); } }); }); describe('Multi-node linear StateGraph triggers parent-child chain hooks', () => { it('should generate root and child chain runs with parent_run_id relationship', async () => { const graph = new StateGraph(GraphState) .addNode('preprocess', (state: typeof GraphState.State) => { return { query: state.query.toUpperCase() }; }) .addNode('respond', (state: typeof GraphState.State) => { return { result: `answer to ${state.query}` }; }) .addEdge(START, 'preprocess') .addEdge('preprocess', 'respond') .addEdge('respond', END) .compile(); await graph.invoke({ query: 'question', result: '' }, { callbacks: [ tracer ] }); await sleep(500); const chainEntries = capturedRuns.filter(e => e.run.run_type === 'chain'); assert( chainEntries.length >= 2, `Should have at least 2 chain runs (root + child nodes), got ${chainEntries.length}`, ); // The graph root run should have no parent_run_id const rootRun = chainEntries.find(e => !e.run.parent_run_id); assert(rootRun, 'Should have a root chain run (no parent_run_id)'); // Child node runs should have parent_run_id const childRuns = chainEntries.filter(e => e.run.parent_run_id); assert(childRuns.length >= 1, 'Should have at least one child chain run with parent_run_id'); // Verify root vs child distinction const rootEntries = capturedRuns.filter(e => !e.run.parent_run_id); const childEntries = capturedRuns.filter(e => !!e.run.parent_run_id); assert(rootEntries.length >= 1, 'Should have root run entries'); assert(childEntries.length >= 1, 'Should have child run entries'); }); it('should share the same trace_id across all runs in a graph invocation', async () => { const graph = new StateGraph(GraphState) .addNode('step1', (state: typeof GraphState.State) => { return { query: `[step1] ${state.query}` }; }) .addNode('step2', (state: typeof GraphState.State) => { return { result: `[step2] ${state.query}` }; }) .addEdge(START, 'step1') .addEdge('step1', 'step2') .addEdge('step2', END) .compile(); await graph.invoke({ query: 'hello', result: '' }, { callbacks: [ tracer ] }); await sleep(500); const traceIds = new Set(capturedRuns.map(e => e.run.trace_id).filter(Boolean)); assert.strictEqual( traceIds.size, 1, `All runs should share the same trace_id, got ${traceIds.size} distinct trace_ids`, ); }); }); describe('StateGraph with LLM node triggers both chain and LLM hooks', () => { it('should trace both chain runs (graph) and LLM runs (FakeLLM inside node)', async () => { const llm = new FakeLLM({ response: 'llm answer' }); const graph = new StateGraph(GraphState) .addNode('ask_llm', async (state: typeof GraphState.State) => { const response = await llm.invoke(state.query); return { result: response }; }) .addEdge(START, 'ask_llm') .addEdge('ask_llm', END) .compile(); await graph.invoke({ query: 'what is LangGraph?', result: '' }, { callbacks: [ tracer ] }); await sleep(500); // Should have chain runs from the graph itself const chainEntries = capturedRuns.filter(e => e.run.run_type === 'chain'); assert(chainEntries.length >= 1, `Should have at least one chain run, got ${chainEntries.length}`); // Should have LLM runs from the FakeLLM invocation inside the node const llmEntries = capturedRuns.filter(e => e.run.run_type === 'llm'); assert(llmEntries.length >= 2, `Should have at least 2 LLM entries (start + end), got ${llmEntries.length}`); }); }); describe('StateGraph node error triggers onChainError', () => { it('should trigger error hook and include error status in captured runs', async () => { const graph = new StateGraph(GraphState) .addNode('fail_node', () => { throw new Error('Node execution failed'); }) .addEdge(START, 'fail_node') .addEdge('fail_node', END) .compile(); try { await graph.invoke({ query: 'trigger error', result: '' }, { callbacks: [ tracer ] }); } catch { // Expected error } await sleep(500); // Should have a start entry const startEntries = capturedRuns.filter(e => e.status === RunStatus.START); assert(startEntries.length >= 1, 'Should have at least one start entry before the error'); // Should have an error entry const errorEntries = capturedRuns.filter(e => e.status === RunStatus.ERROR); assert(errorEntries.length >= 1, `Should have at least one error entry, got ${errorEntries.length}`); // Verify the error run has the error field set assert(errorEntries[0].run, 'Error run should exist'); assert(errorEntries[0].run.error, 'Error run should have error field set'); }); }); describe('Direct hook invocation (unit coverage)', () => { it('should log tool hooks: onToolStart, onToolEnd, onToolError', () => { const run = createMockRun({ run_type: 'tool', name: 'BashTool' }); tracer.onToolStart(run); tracer.onToolEnd(run); tracer.onToolError({ ...run, error: 'tool failed' } as Run); const toolEntries = capturedRuns.filter(e => e.run.run_type === 'tool'); assert.strictEqual(toolEntries.length, 3); assert.strictEqual(toolEntries[0].status, RunStatus.START); assert.strictEqual(toolEntries[1].status, RunStatus.END); assert.strictEqual(toolEntries[2].status, RunStatus.ERROR); }); it('should log LLM hooks: onLLMStart, onLLMEnd, onLLMError', () => { const run = createMockRun({ run_type: 'llm', name: 'claude-3' }); tracer.onLLMStart(run); tracer.onLLMEnd(run); tracer.onLLMError({ ...run, error: 'llm error' } as Run); const llmEntries = capturedRuns.filter(e => e.run.run_type === 'llm'); assert.strictEqual(llmEntries.length, 3); assert.strictEqual(llmEntries[0].status, RunStatus.START); assert.strictEqual(llmEntries[1].status, RunStatus.END); assert.strictEqual(llmEntries[2].status, RunStatus.ERROR); }); it('should log retriever hooks: onRetrieverStart, onRetrieverEnd, onRetrieverError', () => { const run = createMockRun({ run_type: 'retriever', name: 'VectorRetriever' }); tracer.onRetrieverStart(run); tracer.onRetrieverEnd(run); tracer.onRetrieverError({ ...run, error: 'retriever error' } as Run); const retrieverEntries = capturedRuns.filter(e => e.run.run_type === 'retriever'); assert.strictEqual(retrieverEntries.length, 3); assert.strictEqual(retrieverEntries[0].status, RunStatus.START); assert.strictEqual(retrieverEntries[1].status, RunStatus.END); assert.strictEqual(retrieverEntries[2].status, RunStatus.ERROR); }); it('should log agent hooks: onAgentAction, onAgentEnd', () => { const run = createMockRun({ run_type: 'chain', name: 'AgentExecutor' }); tracer.onAgentAction(run); tracer.onAgentEnd(run); assert.strictEqual(capturedRuns[0].status, RunStatus.START); assert.strictEqual(capturedRuns[1].status, RunStatus.END); }); }); describe('Run data completeness via graph.invoke', () => { it('should produce runs with all required fields populated', async () => { const graph = new StateGraph(GraphState) .addNode('check', (state: typeof GraphState.State) => { return { result: `checked: ${state.query}` }; }) .addEdge(START, 'check') .addEdge('check', END) .compile(); await graph.invoke({ query: 'check fields', result: '' }, { callbacks: [ tracer ] }); await sleep(500); assert(capturedRuns.length > 0, 'Should have captured runs'); for (const entry of capturedRuns) { assert(typeof entry.run.id === 'string' && entry.run.id.length > 0, 'Run must have non-empty id'); assert( typeof entry.run.trace_id === 'string' && entry.run.trace_id.length > 0, 'Run must have non-empty trace_id', ); assert(typeof entry.run.run_type === 'string', 'Run must have run_type'); assert(typeof entry.run.name === 'string', 'Run must have name'); } }); it('should produce end runs with outputs present', async () => { const graph = new StateGraph(GraphState) .addNode('output_node', (state: typeof GraphState.State) => { return { result: `output for ${state.query}` }; }) .addEdge(START, 'output_node') .addEdge('output_node', END) .compile(); await graph.invoke({ query: 'output test', result: '' }, { callbacks: [ tracer ] }); await sleep(500); const endEntries = capturedRuns.filter(e => e.status === RunStatus.END); assert(endEntries.length >= 1, 'Should have end entries'); for (const entry of endEntries) { if (entry.run.outputs) { assert(typeof entry.run.outputs === 'object', 'End run outputs should be an object'); } } }); }); }); ================================================ FILE: core/agent-tracing/test/TestUtils.ts ================================================ import type { Logger } from '@eggjs/tegg-types'; import type { Run } from '@langchain/core/tracers/base'; import { TracingService } from '../src/TracingService'; export interface CapturedEntry { run: Run; status: string; name: string; agentName: string; } export function createMockRun(overrides?: Partial): Run { return { id: 'run-001', name: 'TestRun', run_type: 'chain', inputs: {}, outputs: {}, start_time: Date.now(), end_time: Date.now() + 100, execution_order: 1, child_execution_order: 1, child_runs: [], events: [], trace_id: 'trace-001', parent_run_id: undefined, tags: [], extra: {}, error: undefined, ...overrides, } as Run; } export function createMockLogger(logs?: string[]): Logger { return { info: (msg: string) => { logs?.push(msg); }, warn: (msg: string) => { logs?.push(msg); }, error: (msg: string) => { logs?.push(msg); }, } as unknown as Logger; } /** * Create a mock TracingService that captures Run objects directly. * Use capturedRuns to assert on traced runs without parsing log strings. */ export function createCapturingTracingService(): { tracingService: TracingService; capturedRuns: CapturedEntry[]; } { const capturedRuns: CapturedEntry[] = []; const tracingService = { logTrace: (run: Run, status: string, name: string, agentName: string) => { capturedRuns.push({ run, status, name, agentName }); }, } as unknown as TracingService; return { tracingService, capturedRuns }; } ================================================ FILE: core/agent-tracing/test/TracingService.test.ts ================================================ import assert from 'node:assert/strict'; import { TracingService } from '../src/TracingService'; import { RunStatus } from '../src/types'; import { createMockRun } from './TestUtils'; // ---------- Helpers ---------- function makeTracingService({ withOss = true, withLogService = true, }: { withOss?: boolean; withLogService?: boolean; } = {}) { const infoLogs: string[] = []; const warnLogs: string[] = []; const ossPuts: Array<{ key: string; content: string }> = []; const logServiceSends: string[] = []; const service = new TracingService(); (service as any).logger = { info: (msg: string) => infoLogs.push(msg), warn: (...args: unknown[]) => warnLogs.push(args.join(' ')), error: (msg: string) => warnLogs.push(msg), }; (service as any).backgroundTaskHelper = { run: async (fn: () => Promise) => fn(), }; if (withOss) { (service as any).ossClient = { put: async (key: string, content: Buffer) => { ossPuts.push({ key, content: content.toString() }); }, }; } if (withLogService) { (service as any).logServiceClient = { send: async (log: string) => { logServiceSends.push(log); }, }; } return { service, infoLogs, warnLogs, ossPuts, logServiceSends }; } // ---------- Tests ---------- describe('test/TracingService.test.ts', () => { let originalFaasEnv: string | undefined; let originalServerEnv: string | undefined; beforeEach(() => { originalFaasEnv = process.env.FAAS_ENV; originalServerEnv = process.env.SERVER_ENV; }); afterEach(() => { if (originalFaasEnv === undefined) { delete process.env.FAAS_ENV; } else { process.env.FAAS_ENV = originalFaasEnv; } if (originalServerEnv === undefined) { delete process.env.SERVER_ENV; } else { process.env.SERVER_ENV = originalServerEnv; } }); describe('getEnv()', () => { it('should return FAAS_ENV when set', () => { process.env.FAAS_ENV = 'prod'; delete process.env.SERVER_ENV; const { service } = makeTracingService(); assert.strictEqual(service.getEnv(), 'prod'); }); it('should fall back to SERVER_ENV when FAAS_ENV is not set', () => { delete process.env.FAAS_ENV; process.env.SERVER_ENV = 'gray'; const { service } = makeTracingService(); assert.strictEqual(service.getEnv(), 'gray'); }); it('should normalize prepub to pre', () => { delete process.env.FAAS_ENV; process.env.SERVER_ENV = 'prepub'; const { service } = makeTracingService(); assert.strictEqual(service.getEnv(), 'pre'); }); it('should default to local when neither env var is set', () => { delete process.env.FAAS_ENV; delete process.env.SERVER_ENV; const { service } = makeTracingService(); assert.strictEqual(service.getEnv(), 'local'); }); }); describe('isOnlineEnv()', () => { it('should return true for prod', () => { process.env.FAAS_ENV = 'prod'; const { service } = makeTracingService(); assert.strictEqual(service.isOnlineEnv(), true); }); it('should return true for pre', () => { process.env.FAAS_ENV = 'pre'; const { service } = makeTracingService(); assert.strictEqual(service.isOnlineEnv(), true); }); it('should return true for gray', () => { process.env.FAAS_ENV = 'gray'; const { service } = makeTracingService(); assert.strictEqual(service.isOnlineEnv(), true); }); it('should return false for local', () => { delete process.env.FAAS_ENV; delete process.env.SERVER_ENV; const { service } = makeTracingService(); assert.strictEqual(service.isOnlineEnv(), false); }); it('should return false for dev', () => { process.env.FAAS_ENV = 'dev'; const { service } = makeTracingService(); assert.strictEqual(service.isOnlineEnv(), false); }); }); describe('getLogInfoPrefix()', () => { it('should format prefix for root run with FAAS_ENV set', () => { process.env.FAAS_ENV = 'prod'; const { service } = makeTracingService(); const run = createMockRun({ trace_id: 'trace-xyz', id: 'run-123', parent_run_id: undefined }); const prefix = service.getLogInfoPrefix(run, RunStatus.START, 'MyAgent'); assert(prefix.includes('[agent_run][MyAgent]')); assert(prefix.includes('traceId=trace-xyz')); assert(prefix.includes('threadId=unknown')); assert(prefix.includes('type=root_run')); assert(prefix.includes('status=start')); assert(prefix.includes('run_id=run-123')); assert(prefix.includes('parent_run_id=')); }); it('should include threadId from run.extra.metadata when available', () => { process.env.FAAS_ENV = 'dev'; const { service } = makeTracingService(); const run = createMockRun({ extra: { metadata: { thread_id: 'thread-abc' } } }); const prefix = service.getLogInfoPrefix(run, RunStatus.START, 'MyAgent'); assert(prefix.includes('threadId=thread-abc')); }); it('should mark child run when parent_run_id is set', () => { process.env.FAAS_ENV = 'dev'; const { service } = makeTracingService(); const run = createMockRun({ parent_run_id: 'parent-001' }); const prefix = service.getLogInfoPrefix(run, RunStatus.END, 'MyAgent'); assert(prefix.includes('type=child_run')); assert(prefix.includes('parent_run_id=parent-001')); }); it('should include env segment when SERVER_ENV is set and FAAS_ENV is not', () => { delete process.env.FAAS_ENV; process.env.SERVER_ENV = 'pre'; const { service } = makeTracingService(); const run = createMockRun(); const prefix = service.getLogInfoPrefix(run, RunStatus.END, 'MyAgent'); assert(prefix.includes('env=pre')); }); }); describe('uploadToOss()', () => { it('should upload content to OSS client', async () => { process.env.FAAS_ENV = 'dev'; const { service, ossPuts } = makeTracingService({ withOss: true }); await service.uploadToOss('my/key', 'hello content'); assert.strictEqual(ossPuts.length, 1); assert.strictEqual(ossPuts[0].key, 'my/key'); assert.strictEqual(ossPuts[0].content, 'hello content'); }); it('should warn and skip when no OSS client is configured', async () => { const { service, warnLogs } = makeTracingService({ withOss: false }); await service.uploadToOss('my/key', 'content'); assert(warnLogs.some(log => log.includes('OSS client not configured'))); }); }); describe('syncLocalToLogService()', () => { it('should send log to log service with agentName prefix', async () => { const { service, logServiceSends } = makeTracingService({ withLogService: true }); await service.syncLocalToLogService('trace log line', 'MyAgent'); assert.strictEqual(logServiceSends.length, 1); assert(logServiceSends[0].includes('[MyAgent]')); assert(logServiceSends[0].includes('trace log line')); }); it('should skip silently when no log service client configured', async () => { const { service, logServiceSends } = makeTracingService({ withLogService: false }); await service.syncLocalToLogService('trace log line', 'MyAgent'); assert.strictEqual(logServiceSends.length, 0); }); it('should warn when agentName is empty', async () => { const { service, warnLogs } = makeTracingService({ withLogService: true }); await service.syncLocalToLogService('log', ''); assert(warnLogs.some(log => log.includes('agentName is empty'))); }); it('should handle log service errors gracefully', async () => { const { service, warnLogs } = makeTracingService({ withLogService: false }); (service as any).logServiceClient = { send: async () => { throw new Error('network error'); }, }; await service.syncLocalToLogService('log', 'MyAgent'); assert(warnLogs.some(log => log.includes('syncLocalToLogService error'))); }); }); describe('logTrace()', () => { it('should log trace via logger.info when FAAS_ENV is set', () => { process.env.FAAS_ENV = 'dev'; const { service, infoLogs } = makeTracingService(); const run = createMockRun({ outputs: undefined }); service.logTrace(run, RunStatus.END, 'LangGraphTracer', 'MyAgent'); assert(infoLogs.some(log => log.includes('[agent_run]'))); }); it('should skip runs tagged with langsmith:hidden', () => { process.env.FAAS_ENV = 'dev'; const { service, infoLogs } = makeTracingService(); const run = createMockRun({ tags: [ 'langsmith:hidden' ] }); service.logTrace(run, RunStatus.END, 'LangGraphTracer', 'MyAgent'); assert.strictEqual(infoLogs.length, 0); }); it('should upload outputs field to OSS and replace with IResource', async () => { process.env.FAAS_ENV = 'dev'; const { service, ossPuts, infoLogs } = makeTracingService({ withOss: true }); const run = createMockRun({ outputs: { result: 'data', llmOutput: { promptTokens: 10 } } }); service.logTrace(run, RunStatus.END, 'LangGraphTracer', 'MyAgent'); // backgroundTaskHelper runs synchronously in mock, so OSS put should be done assert(ossPuts.length >= 1, 'Should have uploaded to OSS'); // The logged run should have outputs replaced with IResource const logLine = infoLogs.find(log => log.includes('[agent_run]')); assert(logLine, 'Should have a log line'); const runJson = logLine!.match(/,run=({.*})$/)?.[1]; assert(runJson, 'Should have run JSON in log'); const parsed = JSON.parse(runJson); assert(parsed.outputs?.key, 'outputs should be replaced with IResource'); assert.strictEqual(parsed.cost?.promptTokens, 10, 'cost should be extracted from llmOutput'); }); it('should sync to log service when env is local', async () => { delete process.env.FAAS_ENV; delete process.env.SERVER_ENV; const { service, logServiceSends } = makeTracingService({ withLogService: true }); const run = createMockRun({ outputs: undefined }); service.logTrace(run, RunStatus.END, 'LangGraphTracer', 'MyAgent'); assert(logServiceSends.length >= 1, 'Should have synced to log service in local env'); }); it('should include child run ids in logged json', () => { process.env.FAAS_ENV = 'dev'; const { service, infoLogs } = makeTracingService(); const childRun = createMockRun({ id: 'child-001' }); const run = createMockRun({ child_runs: [ childRun ], outputs: undefined }); service.logTrace(run, RunStatus.END, 'LangGraphTracer', 'MyAgent'); const logLine = infoLogs.find(log => log.includes('[agent_run]')); const runJson = logLine?.match(/,run=({.*})$/)?.[1]; const parsed = JSON.parse(runJson!); assert.deepStrictEqual(parsed.child_run_ids, [ 'child-001' ]); }); it('should warn when OSS upload fails inside backgroundTask', async () => { process.env.FAAS_ENV = 'dev'; const { service, warnLogs } = makeTracingService({ withOss: false }); // Make the OSS client throw on put (service as any).ossClient = { put: async () => { throw new Error('oss upload failed'); }, }; // Track background tasks so we can await them const pendingTasks: Array> = []; (service as any).backgroundTaskHelper = { run: (fn: () => Promise) => { pendingTasks.push(fn()); }, }; const run = createMockRun({ outputs: { result: 'data' } }); service.logTrace(run, RunStatus.END, 'LangGraphTracer', 'MyAgent'); // Wait for all background tasks (the catch block inside fn() calls logger.warn) await Promise.allSettled(pendingTasks); assert( warnLogs.some(log => log.includes('Failed to upload run data to OSS')), 'Should warn about OSS upload failure', ); }); it('should catch and warn when logTrace itself throws', () => { process.env.FAAS_ENV = 'dev'; const { service, warnLogs } = makeTracingService(); // Make backgroundTaskHelper.run throw synchronously to trigger the outer catch (service as any).backgroundTaskHelper = { run: () => { throw new Error('backgroundTask error'); }, }; const run = createMockRun({ outputs: { result: 'data' } }); // Should NOT throw — the outer catch block in logTrace swallows the error assert.doesNotThrow(() => { service.logTrace(run, RunStatus.END, 'LangGraphTracer', 'MyAgent'); }); assert( warnLogs.some(log => log.includes('logTrace error')), 'Should warn about logTrace error', ); }); }); }); ================================================ FILE: core/agent-tracing/tsconfig.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "dist", "baseUrl": "./" }, "exclude": [ "dist", "node_modules", "test" ] } ================================================ FILE: core/agent-tracing/tsconfig.pub.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "dist", "baseUrl": "./" }, "exclude": [ "dist", "node_modules", "test" ] } ================================================ FILE: core/ajv-decorator/CHANGELOG.md ================================================ # Change Log All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. ## [3.78.15](https://github.com/eggjs/tegg/compare/v3.78.14...v3.78.15) (2026-04-23) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.78.14](https://github.com/eggjs/tegg/compare/v3.78.13...v3.78.14) (2026-04-21) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.78.13](https://github.com/eggjs/tegg/compare/v3.78.12...v3.78.13) (2026-04-21) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.78.12](https://github.com/eggjs/tegg/compare/v3.78.11...v3.78.12) (2026-04-18) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.78.11](https://github.com/eggjs/tegg/compare/v3.78.10...v3.78.11) (2026-04-18) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.78.10](https://github.com/eggjs/tegg/compare/v3.78.9...v3.78.10) (2026-04-18) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.78.9](https://github.com/eggjs/tegg/compare/v3.78.8...v3.78.9) (2026-04-18) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.78.8](https://github.com/eggjs/tegg/compare/v3.78.7...v3.78.8) (2026-04-18) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.78.7](https://github.com/eggjs/tegg/compare/v3.78.6...v3.78.7) (2026-04-18) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.78.6](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.6) (2026-04-18) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.78.5](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.5) (2026-04-18) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.78.4](https://github.com/eggjs/tegg/compare/v3.78.3...v3.78.4) (2026-04-18) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.78.3](https://github.com/eggjs/tegg/compare/v3.78.2...v3.78.3) (2026-04-15) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.78.2](https://github.com/eggjs/tegg/compare/v3.78.1...v3.78.2) (2026-04-09) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.78.1](https://github.com/eggjs/tegg/compare/v3.78.0...v3.78.1) (2026-04-09) **Note:** Version bump only for package @eggjs/ajv-decorator # [3.78.0](https://github.com/eggjs/tegg/compare/v3.77.2...v3.78.0) (2026-04-09) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.77.2](https://github.com/eggjs/tegg/compare/v3.77.1...v3.77.2) (2026-04-07) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.77.1](https://github.com/eggjs/tegg/compare/v3.77.0...v3.77.1) (2026-04-01) **Note:** Version bump only for package @eggjs/ajv-decorator # [3.77.0](https://github.com/eggjs/tegg/compare/v3.76.1...v3.77.0) (2026-04-01) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.76.1](https://github.com/eggjs/tegg/compare/v3.76.0...v3.76.1) (2026-04-01) **Note:** Version bump only for package @eggjs/ajv-decorator # [3.76.0](https://github.com/eggjs/tegg/compare/v3.75.1...v3.76.0) (2026-04-01) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.75.1](https://github.com/eggjs/tegg/compare/v3.75.0...v3.75.1) (2026-04-01) **Note:** Version bump only for package @eggjs/ajv-decorator # [3.75.0](https://github.com/eggjs/tegg/compare/v3.74.0...v3.75.0) (2026-03-30) **Note:** Version bump only for package @eggjs/ajv-decorator # [3.74.0](https://github.com/eggjs/tegg/compare/v3.73.0...v3.74.0) (2026-03-30) **Note:** Version bump only for package @eggjs/ajv-decorator # [3.73.0](https://github.com/eggjs/tegg/compare/v3.72.0...v3.73.0) (2026-03-25) **Note:** Version bump only for package @eggjs/ajv-decorator # [3.72.0](https://github.com/eggjs/tegg/compare/v3.71.2...v3.72.0) (2026-02-04) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.71.2](https://github.com/eggjs/tegg/compare/v3.71.1...v3.71.2) (2026-01-30) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.71.1](https://github.com/eggjs/tegg/compare/v3.71.0...v3.71.1) (2026-01-28) **Note:** Version bump only for package @eggjs/ajv-decorator # [3.71.0](https://github.com/eggjs/tegg/compare/v3.70.1...v3.71.0) (2026-01-26) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.70.1](https://github.com/eggjs/tegg/compare/v3.70.0...v3.70.1) (2026-01-14) **Note:** Version bump only for package @eggjs/ajv-decorator # [3.70.0](https://github.com/eggjs/tegg/compare/v3.69.0...v3.70.0) (2026-01-07) **Note:** Version bump only for package @eggjs/ajv-decorator # [3.69.0](https://github.com/eggjs/tegg/compare/v3.68.0...v3.69.0) (2026-01-06) **Note:** Version bump only for package @eggjs/ajv-decorator # [3.68.0](https://github.com/eggjs/tegg/compare/v3.67.2...v3.68.0) (2025-12-29) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.67.2](https://github.com/eggjs/tegg/compare/v3.67.1...v3.67.2) (2025-12-24) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.67.1](https://github.com/eggjs/tegg/compare/v3.67.0...v3.67.1) (2025-12-23) **Note:** Version bump only for package @eggjs/ajv-decorator # [3.67.0](https://github.com/eggjs/tegg/compare/v3.66.0...v3.67.0) (2025-12-18) **Note:** Version bump only for package @eggjs/ajv-decorator # [3.66.0](https://github.com/eggjs/tegg/compare/v3.65.3...v3.66.0) (2025-12-16) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.65.3](https://github.com/eggjs/tegg/compare/v3.65.2...v3.65.3) (2025-12-16) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.65.2](https://github.com/eggjs/tegg/compare/v3.65.1...v3.65.2) (2025-12-16) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.65.1](https://github.com/eggjs/tegg/compare/v3.65.0...v3.65.1) (2025-12-16) **Note:** Version bump only for package @eggjs/ajv-decorator # [3.65.0](https://github.com/eggjs/tegg/compare/v3.64.5...v3.65.0) (2025-12-10) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.64.5](https://github.com/eggjs/tegg/compare/v3.64.4...v3.64.5) (2025-12-08) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.64.4](https://github.com/eggjs/tegg/compare/v3.64.3...v3.64.4) (2025-12-06) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.64.3](https://github.com/eggjs/tegg/compare/v3.64.2...v3.64.3) (2025-12-06) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.64.2](https://github.com/eggjs/tegg/compare/v3.64.1...v3.64.2) (2025-11-27) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.64.1](https://github.com/eggjs/tegg/compare/v3.64.0...v3.64.1) (2025-11-27) **Note:** Version bump only for package @eggjs/ajv-decorator # [3.64.0](https://github.com/eggjs/tegg/compare/v3.63.2...v3.64.0) (2025-11-27) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.63.2](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.2) (2025-11-18) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.63.1](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.1) (2025-11-18) **Note:** Version bump only for package @eggjs/ajv-decorator # [3.63.0](https://github.com/eggjs/tegg/compare/v3.62.3...v3.63.0) (2025-11-18) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.62.3](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.3) (2025-11-05) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.62.2](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.2) (2025-11-05) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.62.1](https://github.com/eggjs/tegg/compare/v3.62.0...v3.62.1) (2025-11-03) **Note:** Version bump only for package @eggjs/ajv-decorator # [3.62.0](https://github.com/eggjs/tegg/compare/v3.61.0...v3.62.0) (2025-09-24) **Note:** Version bump only for package @eggjs/ajv-decorator # [3.61.0](https://github.com/eggjs/tegg/compare/v3.60.3...v3.61.0) (2025-08-15) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.60.3](https://github.com/eggjs/tegg/compare/v3.60.2...v3.60.3) (2025-08-06) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.60.2](https://github.com/eggjs/tegg/compare/v3.60.1...v3.60.2) (2025-08-06) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.60.1](https://github.com/eggjs/tegg/compare/v3.60.0...v3.60.1) (2025-07-28) **Note:** Version bump only for package @eggjs/ajv-decorator # [3.60.0](https://github.com/eggjs/tegg/compare/v3.59.1...v3.60.0) (2025-07-28) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.59.1](https://github.com/eggjs/tegg/compare/v3.59.0...v3.59.1) (2025-07-16) **Note:** Version bump only for package @eggjs/ajv-decorator # [3.59.0](https://github.com/eggjs/tegg/compare/v3.58.0...v3.59.0) (2025-07-07) **Note:** Version bump only for package @eggjs/ajv-decorator # [3.58.0](https://github.com/eggjs/tegg/compare/v3.57.14...v3.58.0) (2025-07-01) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.57.14](https://github.com/eggjs/tegg/compare/v3.57.13...v3.57.14) (2025-06-18) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.57.13](https://github.com/eggjs/tegg/compare/v3.57.12...v3.57.13) (2025-06-18) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.57.12](https://github.com/eggjs/tegg/compare/v3.57.11...v3.57.12) (2025-06-16) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.57.11](https://github.com/eggjs/tegg/compare/v3.57.10...v3.57.11) (2025-06-16) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.57.10](https://github.com/eggjs/tegg/compare/v3.57.9...v3.57.10) (2025-06-12) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.57.9](https://github.com/eggjs/tegg/compare/v3.57.8...v3.57.9) (2025-05-29) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.57.8](https://github.com/eggjs/tegg/compare/v3.57.7...v3.57.8) (2025-05-29) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.57.7](https://github.com/eggjs/tegg/compare/v3.57.6...v3.57.7) (2025-05-28) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.57.6](https://github.com/eggjs/tegg/compare/v3.57.5...v3.57.6) (2025-05-27) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.57.5](https://github.com/eggjs/tegg/compare/v3.57.4...v3.57.5) (2025-05-15) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.57.4](https://github.com/eggjs/tegg/compare/v3.57.3...v3.57.4) (2025-05-14) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.57.3](https://github.com/eggjs/tegg/compare/v3.57.2...v3.57.3) (2025-05-13) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.57.2](https://github.com/eggjs/tegg/compare/v3.57.1...v3.57.2) (2025-05-13) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.57.1](https://github.com/eggjs/tegg/compare/v3.57.0...v3.57.1) (2025-05-13) **Note:** Version bump only for package @eggjs/ajv-decorator # [3.57.0](https://github.com/eggjs/tegg/compare/v3.56.3...v3.57.0) (2025-05-09) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.56.3](https://github.com/eggjs/tegg/compare/v3.56.2...v3.56.3) (2025-05-07) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.56.2](https://github.com/eggjs/tegg/compare/v3.56.1...v3.56.2) (2025-05-01) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.56.1](https://github.com/eggjs/tegg/compare/v3.56.0...v3.56.1) (2025-04-29) **Note:** Version bump only for package @eggjs/ajv-decorator # [3.56.0](https://github.com/eggjs/tegg/compare/v3.55.0...v3.56.0) (2025-04-29) **Note:** Version bump only for package @eggjs/ajv-decorator # [3.55.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.55.0) (2025-04-29) **Note:** Version bump only for package @eggjs/ajv-decorator # [3.54.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.54.0) (2025-04-29) **Note:** Version bump only for package @eggjs/ajv-decorator # [3.53.0](https://github.com/eggjs/tegg/compare/v3.52.1...v3.53.0) (2025-04-21) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.52.1](https://github.com/eggjs/tegg/compare/v3.52.0...v3.52.1) (2025-03-14) **Note:** Version bump only for package @eggjs/ajv-decorator # [3.52.0](https://github.com/eggjs/tegg/compare/v3.51.2...v3.52.0) (2024-12-30) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.51.2](https://github.com/eggjs/tegg/compare/v3.51.1...v3.51.2) (2024-12-09) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.51.1](https://github.com/eggjs/tegg/compare/v3.51.0...v3.51.1) (2024-11-06) **Note:** Version bump only for package @eggjs/ajv-decorator # [3.51.0](https://github.com/eggjs/tegg/compare/v3.50.1...v3.51.0) (2024-10-30) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.50.1](https://github.com/eggjs/tegg/compare/v3.50.0...v3.50.1) (2024-10-23) **Note:** Version bump only for package @eggjs/ajv-decorator # [3.50.0](https://github.com/eggjs/tegg/compare/v3.49.0...v3.50.0) (2024-10-22) **Note:** Version bump only for package @eggjs/ajv-decorator # [3.49.0](https://github.com/eggjs/tegg/compare/v3.48.1...v3.49.0) (2024-10-21) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.48.1](https://github.com/eggjs/tegg/compare/v3.48.0...v3.48.1) (2024-10-14) **Note:** Version bump only for package @eggjs/ajv-decorator # [3.48.0](https://github.com/eggjs/tegg/compare/v3.47.2...v3.48.0) (2024-10-10) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.47.2](https://github.com/eggjs/tegg/compare/v3.47.1...v3.47.2) (2024-10-10) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.47.1](https://github.com/eggjs/tegg/compare/v3.47.0...v3.47.1) (2024-10-10) **Note:** Version bump only for package @eggjs/ajv-decorator # [3.47.0](https://github.com/eggjs/tegg/compare/v3.46.4...v3.47.0) (2024-10-10) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.46.4](https://github.com/eggjs/tegg/compare/v3.46.3...v3.46.4) (2024-10-09) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.46.3](https://github.com/eggjs/tegg/compare/v3.46.2...v3.46.3) (2024-10-08) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.46.2](https://github.com/eggjs/tegg/compare/v3.46.1...v3.46.2) (2024-10-07) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.46.1](https://github.com/eggjs/tegg/compare/v3.46.0...v3.46.1) (2024-09-30) **Note:** Version bump only for package @eggjs/ajv-decorator # [3.46.0](https://github.com/eggjs/tegg/compare/v3.45.0...v3.46.0) (2024-09-29) **Note:** Version bump only for package @eggjs/ajv-decorator # [3.45.0](https://github.com/eggjs/tegg/compare/v3.44.1...v3.45.0) (2024-09-29) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.44.1](https://github.com/eggjs/tegg/compare/v3.44.0...v3.44.1) (2024-09-29) **Note:** Version bump only for package @eggjs/ajv-decorator # [3.44.0](https://github.com/eggjs/tegg/compare/v3.43.2...v3.44.0) (2024-09-29) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.43.2](https://github.com/eggjs/tegg/compare/v3.43.1...v3.43.2) (2024-09-14) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.43.1](https://github.com/eggjs/tegg/compare/v3.43.0...v3.43.1) (2024-09-14) **Note:** Version bump only for package @eggjs/ajv-decorator # [3.43.0](https://github.com/eggjs/tegg/compare/v3.42.0...v3.43.0) (2024-09-13) **Note:** Version bump only for package @eggjs/ajv-decorator # [3.42.0](https://github.com/eggjs/tegg/compare/v3.41.0...v3.42.0) (2024-09-10) **Note:** Version bump only for package @eggjs/ajv-decorator # [3.41.0](https://github.com/eggjs/tegg/compare/v3.40.1...v3.41.0) (2024-08-26) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.40.1](https://github.com/eggjs/tegg/compare/v3.40.0...v3.40.1) (2024-08-23) **Note:** Version bump only for package @eggjs/ajv-decorator # [3.40.0](https://github.com/eggjs/tegg/compare/v3.39.5...v3.40.0) (2024-08-22) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.39.5](https://github.com/eggjs/tegg/compare/v3.39.4...v3.39.5) (2024-08-09) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.39.4](https://github.com/eggjs/tegg/compare/v3.39.3...v3.39.4) (2024-07-09) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.39.3](https://github.com/eggjs/tegg/compare/v3.39.2...v3.39.3) (2024-04-28) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.39.2](https://github.com/eggjs/tegg/compare/v3.39.1...v3.39.2) (2024-04-28) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.39.1](https://github.com/eggjs/tegg/compare/v3.39.0...v3.39.1) (2024-04-28) **Note:** Version bump only for package @eggjs/ajv-decorator # [3.39.0](https://github.com/eggjs/tegg/compare/v3.38.0...v3.39.0) (2024-04-19) **Note:** Version bump only for package @eggjs/ajv-decorator # [3.38.0](https://github.com/eggjs/tegg/compare/v3.37.3...v3.38.0) (2024-04-18) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.37.3](https://github.com/eggjs/tegg/compare/v3.37.2...v3.37.3) (2024-04-17) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.37.2](https://github.com/eggjs/tegg/compare/v3.37.1...v3.37.2) (2024-04-17) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.37.1](https://github.com/eggjs/tegg/compare/v3.37.0...v3.37.1) (2024-04-16) **Note:** Version bump only for package @eggjs/ajv-decorator # [3.37.0](https://github.com/eggjs/tegg/compare/v3.36.3...v3.37.0) (2024-04-16) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.36.3](https://github.com/eggjs/tegg/compare/v3.36.2...v3.36.3) (2024-04-10) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.36.2](https://github.com/eggjs/tegg/compare/v3.36.1...v3.36.2) (2024-04-08) **Note:** Version bump only for package @eggjs/ajv-decorator ## [3.36.1](https://github.com/eggjs/tegg/compare/v3.36.0...v3.36.1) (2024-04-07) **Note:** Version bump only for package @eggjs/ajv-decorator # [3.36.0](https://github.com/eggjs/tegg/compare/v3.35.1...v3.36.0) (2024-04-02) ### Features * impl ajv + typebox Validator ([#201](https://github.com/eggjs/tegg/issues/201)) ([9fd585d](https://github.com/eggjs/tegg/commit/9fd585de9b613466c96b73494a08a494db34ea57)) ================================================ FILE: core/ajv-decorator/README.md ================================================ # `@eggjs/ajv-decorator` ## Usage Please read [@eggjs/tegg-ajv-plugin](../../plugin/ajv-plugin) ================================================ FILE: core/ajv-decorator/index.ts ================================================ export * from '@sinclair/typebox'; export * from './src/enum/TransformEnum'; export * from './src/error/AjvInvalidParamError'; export * from './src/type/Ajv'; ================================================ FILE: core/ajv-decorator/package.json ================================================ { "name": "@eggjs/ajv-decorator", "version": "3.78.15", "description": "tegg ajv decorator", "keywords": [ "egg", "typescript", "decorator", "tegg", "ajv" ], "main": "dist/index.js", "files": [ "dist/**/*.js", "dist/**/*.d.ts" ], "typings": "dist/index.d.ts", "scripts": { "test": "cross-env NODE_ENV=test NODE_OPTIONS='--no-deprecation' mocha", "clean": "tsc -b --clean", "tsc": "ut run clean && tsc -p ./tsconfig.json", "tsc:pub": "ut run clean && tsc -p ./tsconfig.pub.json", "prepublishOnly": "ut tsc:pub" }, "license": "MIT", "homepage": "https://github.com/eggjs/tegg", "bugs": { "url": "https://github.com/eggjs/tegg/issues" }, "repository": { "type": "git", "url": "git@github.com:eggjs/tegg.git", "directory": "core/ajv-decorator" }, "engines": { "node": ">=16.0.0" }, "dependencies": { "@sinclair/typebox": "^0.32.20", "ajv": "^8.12.0" }, "publishConfig": { "access": "public" }, "devDependencies": { "@types/mocha": "^10.0.1", "@types/node": "^20.2.4", "cross-env": "^7.0.3", "mocha": "^10.2.0", "ts-node": "^10.9.1", "typescript": "^5.0.4" }, "gitHead": "240a3e9f40fda65ebb7589727d197db5ce17916c" } ================================================ FILE: core/ajv-decorator/src/enum/TransformEnum.ts ================================================ /** * This keyword allows a string to be modified during validation. * This keyword applies only to strings. If the data is not a string, the transform keyword is ignored. * @see https://github.com/ajv-validator/ajv-keywords?tab=readme-ov-file#transform */ export enum TransformEnum { /** remove whitespace from start and end */ trim = 'trim', /** remove whitespace from start */ trimStart = 'trimStart', /** * @alias trimStart */ trimLeft = 'trimLeft', /** remove whitespace from end */ trimEnd = 'trimEnd', /** * @alias trimEnd */ trimRight = 'trimRight', /** convert to lower case */ toLowerCase = 'toLowerCase', /** convert to upper case */ toUpperCase = 'toUpperCase', /** * change string case to be equal to one of `enum` values in the schema * * **NOTE**: requires that all allowed values are unique when case insensitive * ```ts * const schema = { * type: "array", * items: { * type: "string", * transform: ["trim", Transform.toEnumCase], * enum: ["pH"], * }, * }; * * const data = ["ph", " Ph", "PH", "pH "]; * ajv.validate(schema, data); * console.log(data) // ['pH','pH','pH','pH']; * ``` */ toEnumCase = 'toEnumCase', } ================================================ FILE: core/ajv-decorator/src/error/AjvInvalidParamError.ts ================================================ import { type ErrorObject } from 'ajv/dist/2019'; export interface AjvInvalidParamErrorOptions { errorData: unknown; currentSchema: string; errors: ErrorObject[]; } export class AjvInvalidParamError extends Error { errorData: unknown; currentSchema: string; errors: ErrorObject[]; constructor(message: string, options: AjvInvalidParamErrorOptions) { super(message); this.name = this.constructor.name; this.errorData = options.errorData; this.currentSchema = options.currentSchema; this.errors = options.errors; } } ================================================ FILE: core/ajv-decorator/src/type/Ajv.ts ================================================ import type { Schema } from 'ajv/dist/2019'; export interface Ajv { validate(schema: Schema, data: unknown): void; } ================================================ FILE: core/ajv-decorator/test/TransformEnum.test.ts ================================================ import { strict as assert } from 'node:assert'; import { TransformEnum } from '..'; describe('core/ajv-decorator/test/TransformEnum.test.ts', () => { it('should get TransformEnum', () => { assert.equal(TransformEnum.trim, 'trim'); }); }); ================================================ FILE: core/ajv-decorator/tsconfig.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "dist", "baseUrl": "./" }, "exclude": [ "dist", "node_modules", "test" ] } ================================================ FILE: core/ajv-decorator/tsconfig.pub.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "dist", "baseUrl": "./" }, "exclude": [ "dist", "node_modules", "test" ] } ================================================ FILE: core/aop-decorator/CHANGELOG.md ================================================ # Change Log All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. ## [3.78.15](https://github.com/eggjs/tegg/compare/v3.78.14...v3.78.15) (2026-04-23) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.78.14](https://github.com/eggjs/tegg/compare/v3.78.13...v3.78.14) (2026-04-21) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.78.13](https://github.com/eggjs/tegg/compare/v3.78.12...v3.78.13) (2026-04-21) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.78.12](https://github.com/eggjs/tegg/compare/v3.78.11...v3.78.12) (2026-04-18) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.78.11](https://github.com/eggjs/tegg/compare/v3.78.10...v3.78.11) (2026-04-18) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.78.10](https://github.com/eggjs/tegg/compare/v3.78.9...v3.78.10) (2026-04-18) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.78.9](https://github.com/eggjs/tegg/compare/v3.78.8...v3.78.9) (2026-04-18) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.78.8](https://github.com/eggjs/tegg/compare/v3.78.7...v3.78.8) (2026-04-18) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.78.7](https://github.com/eggjs/tegg/compare/v3.78.6...v3.78.7) (2026-04-18) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.78.6](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.6) (2026-04-18) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.78.5](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.5) (2026-04-18) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.78.4](https://github.com/eggjs/tegg/compare/v3.78.3...v3.78.4) (2026-04-18) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.78.3](https://github.com/eggjs/tegg/compare/v3.78.2...v3.78.3) (2026-04-15) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.78.2](https://github.com/eggjs/tegg/compare/v3.78.1...v3.78.2) (2026-04-09) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.78.1](https://github.com/eggjs/tegg/compare/v3.78.0...v3.78.1) (2026-04-09) **Note:** Version bump only for package @eggjs/aop-decorator # [3.78.0](https://github.com/eggjs/tegg/compare/v3.77.2...v3.78.0) (2026-04-09) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.77.2](https://github.com/eggjs/tegg/compare/v3.77.1...v3.77.2) (2026-04-07) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.77.1](https://github.com/eggjs/tegg/compare/v3.77.0...v3.77.1) (2026-04-01) **Note:** Version bump only for package @eggjs/aop-decorator # [3.77.0](https://github.com/eggjs/tegg/compare/v3.76.1...v3.77.0) (2026-04-01) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.76.1](https://github.com/eggjs/tegg/compare/v3.76.0...v3.76.1) (2026-04-01) **Note:** Version bump only for package @eggjs/aop-decorator # [3.76.0](https://github.com/eggjs/tegg/compare/v3.75.1...v3.76.0) (2026-04-01) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.75.1](https://github.com/eggjs/tegg/compare/v3.75.0...v3.75.1) (2026-04-01) **Note:** Version bump only for package @eggjs/aop-decorator # [3.75.0](https://github.com/eggjs/tegg/compare/v3.74.0...v3.75.0) (2026-03-30) **Note:** Version bump only for package @eggjs/aop-decorator # [3.74.0](https://github.com/eggjs/tegg/compare/v3.73.0...v3.74.0) (2026-03-30) **Note:** Version bump only for package @eggjs/aop-decorator # [3.73.0](https://github.com/eggjs/tegg/compare/v3.72.0...v3.73.0) (2026-03-25) **Note:** Version bump only for package @eggjs/aop-decorator # [3.72.0](https://github.com/eggjs/tegg/compare/v3.71.2...v3.72.0) (2026-02-04) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.71.2](https://github.com/eggjs/tegg/compare/v3.71.1...v3.71.2) (2026-01-30) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.71.1](https://github.com/eggjs/tegg/compare/v3.71.0...v3.71.1) (2026-01-28) **Note:** Version bump only for package @eggjs/aop-decorator # [3.71.0](https://github.com/eggjs/tegg/compare/v3.70.1...v3.71.0) (2026-01-26) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.70.1](https://github.com/eggjs/tegg/compare/v3.70.0...v3.70.1) (2026-01-14) **Note:** Version bump only for package @eggjs/aop-decorator # [3.70.0](https://github.com/eggjs/tegg/compare/v3.69.0...v3.70.0) (2026-01-07) **Note:** Version bump only for package @eggjs/aop-decorator # [3.69.0](https://github.com/eggjs/tegg/compare/v3.68.0...v3.69.0) (2026-01-06) **Note:** Version bump only for package @eggjs/aop-decorator # [3.68.0](https://github.com/eggjs/tegg/compare/v3.67.2...v3.68.0) (2025-12-29) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.67.2](https://github.com/eggjs/tegg/compare/v3.67.1...v3.67.2) (2025-12-24) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.67.1](https://github.com/eggjs/tegg/compare/v3.67.0...v3.67.1) (2025-12-23) **Note:** Version bump only for package @eggjs/aop-decorator # [3.67.0](https://github.com/eggjs/tegg/compare/v3.66.0...v3.67.0) (2025-12-18) **Note:** Version bump only for package @eggjs/aop-decorator # [3.66.0](https://github.com/eggjs/tegg/compare/v3.65.3...v3.66.0) (2025-12-16) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.65.3](https://github.com/eggjs/tegg/compare/v3.65.2...v3.65.3) (2025-12-16) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.65.2](https://github.com/eggjs/tegg/compare/v3.65.1...v3.65.2) (2025-12-16) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.65.1](https://github.com/eggjs/tegg/compare/v3.65.0...v3.65.1) (2025-12-16) **Note:** Version bump only for package @eggjs/aop-decorator # [3.65.0](https://github.com/eggjs/tegg/compare/v3.64.5...v3.65.0) (2025-12-10) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.64.5](https://github.com/eggjs/tegg/compare/v3.64.4...v3.64.5) (2025-12-08) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.64.4](https://github.com/eggjs/tegg/compare/v3.64.3...v3.64.4) (2025-12-06) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.64.3](https://github.com/eggjs/tegg/compare/v3.64.2...v3.64.3) (2025-12-06) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.64.2](https://github.com/eggjs/tegg/compare/v3.64.1...v3.64.2) (2025-11-27) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.64.1](https://github.com/eggjs/tegg/compare/v3.64.0...v3.64.1) (2025-11-27) **Note:** Version bump only for package @eggjs/aop-decorator # [3.64.0](https://github.com/eggjs/tegg/compare/v3.63.2...v3.64.0) (2025-11-27) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.63.2](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.2) (2025-11-18) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.63.1](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.1) (2025-11-18) **Note:** Version bump only for package @eggjs/aop-decorator # [3.63.0](https://github.com/eggjs/tegg/compare/v3.62.3...v3.63.0) (2025-11-18) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.62.3](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.3) (2025-11-05) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.62.2](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.2) (2025-11-05) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.62.1](https://github.com/eggjs/tegg/compare/v3.62.0...v3.62.1) (2025-11-03) **Note:** Version bump only for package @eggjs/aop-decorator # [3.62.0](https://github.com/eggjs/tegg/compare/v3.61.0...v3.62.0) (2025-09-24) **Note:** Version bump only for package @eggjs/aop-decorator # [3.61.0](https://github.com/eggjs/tegg/compare/v3.60.3...v3.61.0) (2025-08-15) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.60.3](https://github.com/eggjs/tegg/compare/v3.60.2...v3.60.3) (2025-08-06) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.60.2](https://github.com/eggjs/tegg/compare/v3.60.1...v3.60.2) (2025-08-06) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.60.1](https://github.com/eggjs/tegg/compare/v3.60.0...v3.60.1) (2025-07-28) **Note:** Version bump only for package @eggjs/aop-decorator # [3.60.0](https://github.com/eggjs/tegg/compare/v3.59.1...v3.60.0) (2025-07-28) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.59.1](https://github.com/eggjs/tegg/compare/v3.59.0...v3.59.1) (2025-07-16) **Note:** Version bump only for package @eggjs/aop-decorator # [3.59.0](https://github.com/eggjs/tegg/compare/v3.58.0...v3.59.0) (2025-07-07) **Note:** Version bump only for package @eggjs/aop-decorator # [3.58.0](https://github.com/eggjs/tegg/compare/v3.57.14...v3.58.0) (2025-07-01) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.57.14](https://github.com/eggjs/tegg/compare/v3.57.13...v3.57.14) (2025-06-18) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.57.13](https://github.com/eggjs/tegg/compare/v3.57.12...v3.57.13) (2025-06-18) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.57.12](https://github.com/eggjs/tegg/compare/v3.57.11...v3.57.12) (2025-06-16) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.57.11](https://github.com/eggjs/tegg/compare/v3.57.10...v3.57.11) (2025-06-16) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.57.10](https://github.com/eggjs/tegg/compare/v3.57.9...v3.57.10) (2025-06-12) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.57.9](https://github.com/eggjs/tegg/compare/v3.57.8...v3.57.9) (2025-05-29) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.57.8](https://github.com/eggjs/tegg/compare/v3.57.7...v3.57.8) (2025-05-29) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.57.7](https://github.com/eggjs/tegg/compare/v3.57.6...v3.57.7) (2025-05-28) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.57.6](https://github.com/eggjs/tegg/compare/v3.57.5...v3.57.6) (2025-05-27) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.57.5](https://github.com/eggjs/tegg/compare/v3.57.4...v3.57.5) (2025-05-15) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.57.4](https://github.com/eggjs/tegg/compare/v3.57.3...v3.57.4) (2025-05-14) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.57.3](https://github.com/eggjs/tegg/compare/v3.57.2...v3.57.3) (2025-05-13) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.57.2](https://github.com/eggjs/tegg/compare/v3.57.1...v3.57.2) (2025-05-13) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.57.1](https://github.com/eggjs/tegg/compare/v3.57.0...v3.57.1) (2025-05-13) **Note:** Version bump only for package @eggjs/aop-decorator # [3.57.0](https://github.com/eggjs/tegg/compare/v3.56.3...v3.57.0) (2025-05-09) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.56.3](https://github.com/eggjs/tegg/compare/v3.56.2...v3.56.3) (2025-05-07) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.56.2](https://github.com/eggjs/tegg/compare/v3.56.1...v3.56.2) (2025-05-01) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.56.1](https://github.com/eggjs/tegg/compare/v3.56.0...v3.56.1) (2025-04-29) **Note:** Version bump only for package @eggjs/aop-decorator # [3.56.0](https://github.com/eggjs/tegg/compare/v3.55.0...v3.56.0) (2025-04-29) **Note:** Version bump only for package @eggjs/aop-decorator # [3.55.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.55.0) (2025-04-29) **Note:** Version bump only for package @eggjs/aop-decorator # [3.54.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.54.0) (2025-04-29) **Note:** Version bump only for package @eggjs/aop-decorator # [3.53.0](https://github.com/eggjs/tegg/compare/v3.52.1...v3.53.0) (2025-04-21) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.52.1](https://github.com/eggjs/tegg/compare/v3.52.0...v3.52.1) (2025-03-14) **Note:** Version bump only for package @eggjs/aop-decorator # [3.52.0](https://github.com/eggjs/tegg/compare/v3.51.2...v3.52.0) (2024-12-30) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.51.2](https://github.com/eggjs/tegg/compare/v3.51.1...v3.51.2) (2024-12-09) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.51.1](https://github.com/eggjs/tegg/compare/v3.51.0...v3.51.1) (2024-11-06) **Note:** Version bump only for package @eggjs/aop-decorator # [3.51.0](https://github.com/eggjs/tegg/compare/v3.50.1...v3.51.0) (2024-10-30) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.50.1](https://github.com/eggjs/tegg/compare/v3.50.0...v3.50.1) (2024-10-23) **Note:** Version bump only for package @eggjs/aop-decorator # [3.50.0](https://github.com/eggjs/tegg/compare/v3.49.0...v3.50.0) (2024-10-22) **Note:** Version bump only for package @eggjs/aop-decorator # [3.49.0](https://github.com/eggjs/tegg/compare/v3.48.1...v3.49.0) (2024-10-21) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.48.1](https://github.com/eggjs/tegg/compare/v3.48.0...v3.48.1) (2024-10-14) **Note:** Version bump only for package @eggjs/aop-decorator # [3.48.0](https://github.com/eggjs/tegg/compare/v3.47.2...v3.48.0) (2024-10-10) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.47.2](https://github.com/eggjs/tegg/compare/v3.47.1...v3.47.2) (2024-10-10) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.47.1](https://github.com/eggjs/tegg/compare/v3.47.0...v3.47.1) (2024-10-10) **Note:** Version bump only for package @eggjs/aop-decorator # [3.47.0](https://github.com/eggjs/tegg/compare/v3.46.4...v3.47.0) (2024-10-10) ### Features * impl GlobalGraph build hook ([#246](https://github.com/eggjs/tegg/issues/246)) ([48fce45](https://github.com/eggjs/tegg/commit/48fce4512e99259ec26a9b032bfcc9f4046ad235)) ## [3.46.4](https://github.com/eggjs/tegg/compare/v3.46.3...v3.46.4) (2024-10-09) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.46.3](https://github.com/eggjs/tegg/compare/v3.46.2...v3.46.3) (2024-10-08) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.46.2](https://github.com/eggjs/tegg/compare/v3.46.1...v3.46.2) (2024-10-07) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.46.1](https://github.com/eggjs/tegg/compare/v3.46.0...v3.46.1) (2024-09-30) **Note:** Version bump only for package @eggjs/aop-decorator # [3.46.0](https://github.com/eggjs/tegg/compare/v3.45.0...v3.46.0) (2024-09-29) **Note:** Version bump only for package @eggjs/aop-decorator # [3.45.0](https://github.com/eggjs/tegg/compare/v3.44.1...v3.45.0) (2024-09-29) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.44.1](https://github.com/eggjs/tegg/compare/v3.44.0...v3.44.1) (2024-09-29) **Note:** Version bump only for package @eggjs/aop-decorator # [3.44.0](https://github.com/eggjs/tegg/compare/v3.43.2...v3.44.0) (2024-09-29) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.43.2](https://github.com/eggjs/tegg/compare/v3.43.1...v3.43.2) (2024-09-14) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.43.1](https://github.com/eggjs/tegg/compare/v3.43.0...v3.43.1) (2024-09-14) **Note:** Version bump only for package @eggjs/aop-decorator # [3.43.0](https://github.com/eggjs/tegg/compare/v3.42.0...v3.43.0) (2024-09-13) **Note:** Version bump only for package @eggjs/aop-decorator # [3.42.0](https://github.com/eggjs/tegg/compare/v3.41.0...v3.42.0) (2024-09-10) **Note:** Version bump only for package @eggjs/aop-decorator # [3.41.0](https://github.com/eggjs/tegg/compare/v3.40.1...v3.41.0) (2024-08-26) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.40.1](https://github.com/eggjs/tegg/compare/v3.40.0...v3.40.1) (2024-08-23) **Note:** Version bump only for package @eggjs/aop-decorator # [3.40.0](https://github.com/eggjs/tegg/compare/v3.39.5...v3.40.0) (2024-08-22) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.39.5](https://github.com/eggjs/tegg/compare/v3.39.4...v3.39.5) (2024-08-09) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.39.4](https://github.com/eggjs/tegg/compare/v3.39.3...v3.39.4) (2024-07-09) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.39.3](https://github.com/eggjs/tegg/compare/v3.39.2...v3.39.3) (2024-04-28) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.39.2](https://github.com/eggjs/tegg/compare/v3.39.1...v3.39.2) (2024-04-28) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.39.1](https://github.com/eggjs/tegg/compare/v3.39.0...v3.39.1) (2024-04-28) **Note:** Version bump only for package @eggjs/aop-decorator # [3.39.0](https://github.com/eggjs/tegg/compare/v3.38.0...v3.39.0) (2024-04-19) **Note:** Version bump only for package @eggjs/aop-decorator # [3.38.0](https://github.com/eggjs/tegg/compare/v3.37.3...v3.38.0) (2024-04-18) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.37.3](https://github.com/eggjs/tegg/compare/v3.37.2...v3.37.3) (2024-04-17) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.37.2](https://github.com/eggjs/tegg/compare/v3.37.1...v3.37.2) (2024-04-17) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.37.1](https://github.com/eggjs/tegg/compare/v3.37.0...v3.37.1) (2024-04-16) **Note:** Version bump only for package @eggjs/aop-decorator # [3.37.0](https://github.com/eggjs/tegg/compare/v3.36.3...v3.37.0) (2024-04-16) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.36.3](https://github.com/eggjs/tegg/compare/v3.36.2...v3.36.3) (2024-04-10) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.36.2](https://github.com/eggjs/tegg/compare/v3.36.1...v3.36.2) (2024-04-08) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.36.1](https://github.com/eggjs/tegg/compare/v3.36.0...v3.36.1) (2024-04-07) **Note:** Version bump only for package @eggjs/aop-decorator # [3.36.0](https://github.com/eggjs/tegg/compare/v3.35.1...v3.36.0) (2024-04-02) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.35.1](https://github.com/eggjs/tegg/compare/v3.35.0...v3.35.1) (2024-03-26) **Note:** Version bump only for package @eggjs/aop-decorator # [3.35.0](https://github.com/eggjs/tegg/compare/v3.34.0...v3.35.0) (2024-03-26) **Note:** Version bump only for package @eggjs/aop-decorator # [3.34.0](https://github.com/eggjs/tegg/compare/v3.33.1...v3.34.0) (2024-03-22) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.33.1](https://github.com/eggjs/tegg/compare/v3.33.0...v3.33.1) (2024-03-22) **Note:** Version bump only for package @eggjs/aop-decorator # [3.33.0](https://github.com/eggjs/tegg/compare/v3.32.0...v3.33.0) (2024-03-22) **Note:** Version bump only for package @eggjs/aop-decorator # [3.32.0](https://github.com/eggjs/tegg/compare/v3.31.0...v3.32.0) (2024-02-19) **Note:** Version bump only for package @eggjs/aop-decorator # [3.31.0](https://github.com/eggjs/tegg/compare/v3.30.1...v3.31.0) (2024-01-31) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.30.1](https://github.com/eggjs/tegg/compare/v3.30.0...v3.30.1) (2024-01-25) **Note:** Version bump only for package @eggjs/aop-decorator # [3.30.0](https://github.com/eggjs/tegg/compare/v3.29.0...v3.30.0) (2024-01-17) **Note:** Version bump only for package @eggjs/aop-decorator # [3.29.0](https://github.com/eggjs/tegg/compare/v3.28.2...v3.29.0) (2023-12-26) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.28.2](https://github.com/eggjs/tegg/compare/v3.28.1...v3.28.2) (2023-12-12) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.28.1](https://github.com/eggjs/tegg/compare/v3.28.0...v3.28.1) (2023-12-11) **Note:** Version bump only for package @eggjs/aop-decorator # [3.28.0](https://github.com/eggjs/tegg/compare/v3.27.0...v3.28.0) (2023-12-10) **Note:** Version bump only for package @eggjs/aop-decorator # [3.27.0](https://github.com/eggjs/tegg/compare/v3.26.0...v3.27.0) (2023-11-23) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.25.2](https://github.com/eggjs/tegg/compare/v3.25.1...v3.25.2) (2023-11-06) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.25.1](https://github.com/eggjs/tegg/compare/v3.25.0...v3.25.1) (2023-11-03) **Note:** Version bump only for package @eggjs/aop-decorator # [3.25.0](https://github.com/eggjs/tegg/compare/v3.24.0...v3.25.0) (2023-11-03) **Note:** Version bump only for package @eggjs/aop-decorator # [3.24.0](https://github.com/eggjs/tegg/compare/v3.23.0...v3.24.0) (2023-10-26) **Note:** Version bump only for package @eggjs/aop-decorator # [3.23.0](https://github.com/eggjs/tegg/compare/v3.22.0...v3.23.0) (2023-09-20) **Note:** Version bump only for package @eggjs/aop-decorator # [3.20.0](https://github.com/eggjs/tegg/compare/v3.19.0...v3.20.0) (2023-09-07) **Note:** Version bump only for package @eggjs/aop-decorator # [3.19.0](https://github.com/eggjs/tegg/compare/v3.18.1...v3.19.0) (2023-08-31) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.18.1](https://github.com/eggjs/tegg/compare/v3.18.0...v3.18.1) (2023-08-29) **Note:** Version bump only for package @eggjs/aop-decorator # [3.18.0](https://github.com/eggjs/tegg/compare/v3.17.0...v3.18.0) (2023-08-29) **Note:** Version bump only for package @eggjs/aop-decorator # [3.17.0](https://github.com/eggjs/tegg/compare/v3.16.0...v3.17.0) (2023-08-24) **Note:** Version bump only for package @eggjs/aop-decorator # [3.16.0](https://github.com/eggjs/tegg/compare/v3.15.0...v3.16.0) (2023-08-24) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.14.3](https://github.com/eggjs/tegg/compare/v3.14.2...v3.14.3) (2023-08-14) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.14.2](https://github.com/eggjs/tegg/compare/v3.14.1...v3.14.2) (2023-08-14) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.14.1](https://github.com/eggjs/tegg/compare/v3.14.0...v3.14.1) (2023-08-11) **Note:** Version bump only for package @eggjs/aop-decorator # [3.14.0](https://github.com/eggjs/tegg/compare/v3.13.0...v3.14.0) (2023-07-31) **Note:** Version bump only for package @eggjs/aop-decorator # [3.13.0](https://github.com/eggjs/tegg/compare/v3.12.0...v3.13.0) (2023-07-25) **Note:** Version bump only for package @eggjs/aop-decorator # [3.12.0](https://github.com/eggjs/tegg/compare/v3.11.1...v3.12.0) (2023-07-13) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.11.1](https://github.com/eggjs/tegg/compare/v3.11.0...v3.11.1) (2023-06-29) **Note:** Version bump only for package @eggjs/aop-decorator # [3.11.0](https://github.com/eggjs/tegg/compare/v3.10.0...v3.11.0) (2023-06-29) **Note:** Version bump only for package @eggjs/aop-decorator # [3.10.0](https://github.com/eggjs/tegg/compare/v3.9.0...v3.10.0) (2023-06-28) **Note:** Version bump only for package @eggjs/aop-decorator # [3.9.0](https://github.com/eggjs/tegg/compare/v3.8.0...v3.9.0) (2023-06-20) ### Features * implement advice params ([76ec8ad](https://github.com/eggjs/tegg/commit/76ec8ad7b7170a637e59d74d49c1f00d8a201321)) # [3.8.0](https://github.com/eggjs/tegg/compare/v3.7.0...v3.8.0) (2023-05-30) ### Features * impl EggObjectLifecycle hook in decorator ([#119](https://github.com/eggjs/tegg/issues/119)) ([cced8a2](https://github.com/eggjs/tegg/commit/cced8a2e009c33d5040fa21d00409fddef471b0e)) # [3.7.0](https://github.com/eggjs/tegg/compare/v3.6.3...v3.7.0) (2023-04-03) **Note:** Version bump only for package @eggjs/aop-decorator # [3.5.0](https://github.com/eggjs/tegg/compare/v3.4.1...v3.5.0) (2023-02-10) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.2.3](https://github.com/eggjs/tegg/compare/v3.2.2...v3.2.3) (2023-01-16) **Note:** Version bump only for package @eggjs/aop-decorator ## [3.2.2](https://github.com/eggjs/tegg/compare/v3.2.1...v3.2.2) (2023-01-06) **Note:** Version bump only for package @eggjs/aop-decorator # [3.1.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.1.0) (2022-12-27) ### Bug Fixes * fix file path for advice decorator ([#64](https://github.com/eggjs/tegg/issues/64)) ([d6aa091](https://github.com/eggjs/tegg/commit/d6aa091851b5d1ca63e7e56e081df4d15ab3284e)) # [3.0.0](https://github.com/eggjs/tegg/compare/v3.0.0-alpha.0...v3.0.0) (2022-12-26) **Note:** Version bump only for package @eggjs/aop-decorator # [3.0.0-alpha.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.0.0-alpha.0) (2022-12-22) ### Bug Fixes * fix file path for advice decorator ([#64](https://github.com/eggjs/tegg/issues/64)) ([d6aa091](https://github.com/eggjs/tegg/commit/d6aa091851b5d1ca63e7e56e081df4d15ab3284e)) # [1.3.0](https://github.com/eggjs/tegg/compare/v1.2.0...v1.3.0) (2022-07-01) **Note:** Version bump only for package @eggjs/aop-decorator # [0.2.0](https://github.com/eggjs/tegg/compare/v0.1.19...v0.2.0) (2022-01-20) ### Features * impl aop ([c53df00](https://github.com/eggjs/tegg/commit/c53df001d1455a0a105689694775d880541d9d2f)) ================================================ FILE: core/aop-decorator/README.md ================================================ # `@eggjs/aop-decorator` # Usage Please read [@eggjs/tegg-aop-plugin](../../plugin/aop/README.md) ================================================ FILE: core/aop-decorator/index.ts ================================================ export * from '@eggjs/tegg-types/aop'; export * from './src/decorator/Advice'; export * from './src/decorator/Pointcut'; export * from './src/decorator/Crosscut'; export * from './src/model/Aspect'; export * from './src/model/PointcutInfo'; export * from './src/util/AdviceInfoUtil'; export * from './src/util/CrosscutInfoUtil'; export * from './src/util/PointcutAdviceInfoUtil'; export * from './src/util/AspectInfoUtil'; export * from './src/AspectMetaBuilder'; export * from './src/CrosscutAdviceFactory'; ================================================ FILE: core/aop-decorator/package.json ================================================ { "name": "@eggjs/aop-decorator", "version": "3.78.15", "description": "tegg aop decorator", "keywords": [ "tegg", "aop", "typescript", "egg" ], "author": "killagu ", "homepage": "https://github.com/eggjs/tegg", "repository": { "type": "git", "url": "git@github.com:eggjs/tegg.git", "directory": "core/aop-decorator" }, "dependencies": { "@eggjs/core-decorator": "^3.78.15", "@eggjs/tegg-common-util": "^3.78.15", "@eggjs/tegg-metadata": "^3.78.15", "@eggjs/tegg-types": "^3.78.15" }, "scripts": { "test": "cross-env NODE_ENV=test NODE_OPTIONS='--no-deprecation' mocha", "clean": "tsc -b --clean", "tsc": "ut run clean && tsc -p ./tsconfig.json", "tsc:pub": "ut run clean && tsc -p ./tsconfig.pub.json", "prepublishOnly": "ut tsc:pub" }, "publishConfig": { "access": "public" }, "engines": { "node": ">=14.0.0" }, "license": "MIT", "main": "dist/index.js", "files": [ "dist/**/*.js", "dist/**/*.d.ts" ], "bugs": { "url": "https://github.com/eggjs/tegg/issues" }, "devDependencies": { "@types/mocha": "^10.0.1", "@types/node": "^20.2.4", "cross-env": "^7.0.3", "mocha": "^10.2.0", "ts-node": "^10.9.1", "typescript": "^5.0.4" }, "gitHead": "240a3e9f40fda65ebb7589727d197db5ce17916c" } ================================================ FILE: core/aop-decorator/src/AspectMetaBuilder.ts ================================================ import type { EggProtoImplClass } from '@eggjs/tegg-types'; import { CrosscutAdviceFactory } from './CrosscutAdviceFactory'; import { Aspect, AspectBuilder } from './model/Aspect'; import { PointcutAdviceInfoUtil } from './util/PointcutAdviceInfoUtil'; export class AspectMetaBuilder { private readonly clazz: EggProtoImplClass; private readonly crosscutAdviceFactory: CrosscutAdviceFactory; constructor(clazz: EggProtoImplClass, options: { crosscutAdviceFactory: CrosscutAdviceFactory; }) { this.clazz = clazz; this.crosscutAdviceFactory = options.crosscutAdviceFactory; } build(): Array { const aspectList: Array = []; const methods = AspectMetaBuilder.getAllMethods(this.clazz); for (const method of methods) { const aspect = this.doBuildMethodAspect(method); if (aspect) { aspectList.push(aspect); } } return aspectList; } static getAllMethods(clazz): PropertyKey[] { const methodSet = new Set(); function getMethods(obj) { if (obj) { const propDescs = Object.getOwnPropertyDescriptors(obj); for (const [ name, desc ] of Object.entries(propDescs)) { if (desc.value instanceof Function) { methodSet.add(name); } } getMethods(Object.getPrototypeOf(obj)); } } getMethods(clazz.prototype); return Array.from(methodSet); } private doBuildMethodAspect(method: PropertyKey): Aspect | undefined { const crosscutAdviceList = this.crosscutAdviceFactory.getAdvice(this.clazz, method); // decorator execute in reverse order const pointcutAdviceList = PointcutAdviceInfoUtil.getPointcutAdviceInfoList(this.clazz, method); if (!crosscutAdviceList.length && !pointcutAdviceList.length) return; const aspectBuilder = new AspectBuilder(this.clazz, method); for (const advice of crosscutAdviceList) { aspectBuilder.addAdvice(advice); } for (const advice of pointcutAdviceList) { aspectBuilder.addAdvice(advice); } return aspectBuilder.build(); } } ================================================ FILE: core/aop-decorator/src/CrosscutAdviceFactory.ts ================================================ import assert from 'node:assert'; import type { EggProtoImplClass, IAdvice, AdviceInfo } from '@eggjs/tegg-types'; import { CrosscutInfoUtil } from './util/CrosscutInfoUtil'; export class CrosscutAdviceFactory { private readonly crosscutAdviceClazzList: Array> = []; registerCrossAdviceClazz(clazz: EggProtoImplClass) { assert(CrosscutInfoUtil.isCrosscutAdvice(clazz), `clazz ${clazz.name} is not crosscut advice`); this.crosscutAdviceClazzList.push(clazz); } getAdvice(clazz: EggProtoImplClass, method: PropertyKey): Array { const result: Array = []; for (const crosscutAdviceClazz of this.crosscutAdviceClazzList) { const crosscutInfoList = CrosscutInfoUtil.getCrosscutInfoList(crosscutAdviceClazz); for (const crosscutInfo of crosscutInfoList) { if (crosscutInfo.pointcutInfo.match(clazz, method)) { result.push(crosscutInfo.adviceInfo); } } } return result; } } ================================================ FILE: core/aop-decorator/src/decorator/Advice.ts ================================================ import { Prototype, PrototypeUtil } from '@eggjs/core-decorator'; import { StackUtil } from '@eggjs/tegg-common-util'; import { AccessLevel, ObjectInitType } from '@eggjs/tegg-types'; import type { EggProtoImplClass, IAdvice, PrototypeParams } from '@eggjs/tegg-types'; import { AdviceInfoUtil } from '../util/AdviceInfoUtil'; const defaultAdviceParam = { accessLevel: AccessLevel.PUBLIC, initType: ObjectInitType.CONTEXT, }; export function Advice(param?: PrototypeParams) { return function(constructor: EggProtoImplClass) { AdviceInfoUtil.setIsAdvice(true, constructor); const func = Prototype({ ...defaultAdviceParam, ...param, }); func(constructor); PrototypeUtil.setFilePath(constructor, StackUtil.getCalleeFromStack(false, 5)); }; } ================================================ FILE: core/aop-decorator/src/decorator/Crosscut.ts ================================================ import { PointcutType } from '@eggjs/tegg-types'; import type { CrosscutInfo, EggProtoImplClass, IAdvice, CrosscutParam, CrosscutOptions } from '@eggjs/tegg-types'; import { CrosscutInfoUtil } from '../util/CrosscutInfoUtil'; import { ClassPointInfo, CustomPointInfo, NamePointInfo } from '../model/PointcutInfo'; const defaultCrossOptions = { order: 100, }; export function Crosscut(param: CrosscutParam, options?: CrosscutOptions) { return function(constructor: EggProtoImplClass) { let crosscutInfo: CrosscutInfo; if (param.type === PointcutType.CLASS) { crosscutInfo = { pointcutInfo: new ClassPointInfo(param.clazz, param.methodName), adviceInfo: { clazz: constructor, order: options?.order ?? defaultCrossOptions.order, adviceParams: options?.adviceParams, }, }; } else if (param.type === PointcutType.NAME) { crosscutInfo = { pointcutInfo: new NamePointInfo(param.className, param.methodName), adviceInfo: { clazz: constructor, order: options?.order ?? defaultCrossOptions.order, adviceParams: options?.adviceParams, }, }; } else { crosscutInfo = { pointcutInfo: new CustomPointInfo(param.callback), adviceInfo: { clazz: constructor, order: options?.order ?? defaultCrossOptions.order, adviceParams: options?.adviceParams, }, }; } CrosscutInfoUtil.setIsCrosscutAdvice(true, constructor); CrosscutInfoUtil.addCrosscutInfo(crosscutInfo, constructor); }; } ================================================ FILE: core/aop-decorator/src/decorator/Pointcut.ts ================================================ import type { EggProtoImplClass, IAdvice, PointcutOptions } from '@eggjs/tegg-types'; import { PointcutAdviceInfoUtil } from '../util/PointcutAdviceInfoUtil'; import assert from 'assert'; import { AdviceInfoUtil } from '../util/AdviceInfoUtil'; const defaultPointcutOptions = { order: 1000, }; export function Pointcut(adviceClazz: EggProtoImplClass>, options?: PointcutOptions) { return function(target: any, propertyKey: PropertyKey) { assert(AdviceInfoUtil.isAdvice(adviceClazz), `class ${adviceClazz} has no @Advice decorator`); const targetClazz = target.constructor as EggProtoImplClass; const methodName = propertyKey as string; PointcutAdviceInfoUtil.addPointcutAdviceInfo({ clazz: adviceClazz, order: options?.order ?? defaultPointcutOptions.order, adviceParams: options?.adviceParams, }, targetClazz, methodName); }; } ================================================ FILE: core/aop-decorator/src/model/Aspect.ts ================================================ import type { AdviceInfo, AspectAdvice, EggProtoImplClass, IAdvice } from '@eggjs/tegg-types'; export class Aspect { readonly clazz: EggProtoImplClass; readonly method: PropertyKey; readonly adviceList: readonly AspectAdvice[]; constructor(clazz: EggProtoImplClass, method: PropertyKey, adviceList: readonly AspectAdvice[]) { this.clazz = clazz; this.method = method; this.adviceList = adviceList; } } export class AspectBuilder { readonly clazz: EggProtoImplClass; readonly method: PropertyKey; private readonly adviceList: Array; constructor(clazz: EggProtoImplClass, method: PropertyKey) { this.clazz = clazz; this.method = method; this.adviceList = []; } addAdvice(adviceInfo: AdviceInfo) { this.adviceList.push(adviceInfo); } build(): Aspect { this.adviceList.sort((a, b) => a.order - b.order); const aspectAdviceList = this.adviceList.map((t, i) => { return { clazz: t.clazz, name: this.adviceName(t.clazz, i), adviceParams: t.adviceParams, }; }); return new Aspect(this.clazz, this.method, aspectAdviceList); } private adviceName(advice: EggProtoImplClass, index: number) { return `${this.clazz.name}#${String(this.method)}#${advice.name}#${index}`; } } ================================================ FILE: core/aop-decorator/src/model/PointcutInfo.ts ================================================ import { PointcutType } from '@eggjs/tegg-types'; import type { CustomPointcutCallback, EggProtoImplClass, PointcutInfo } from '@eggjs/tegg-types'; export class ClassPointInfo implements PointcutInfo { readonly type = PointcutType.CLASS; readonly clazz: EggProtoImplClass; readonly method: PropertyKey; constructor(clazz: EggProtoImplClass, method: PropertyKey) { this.clazz = clazz; this.method = method; } match(clazz: EggProtoImplClass, method: PropertyKey): boolean { return ( // self class this.clazz === clazz || // inherit case clazz.prototype instanceof this.clazz ) && this.method === method; } } export class NamePointInfo implements PointcutInfo { readonly type = PointcutType.NAME; readonly className: RegExp; readonly methodName: RegExp; constructor(className: RegExp, methodName: RegExp) { this.className = className; this.methodName = methodName; } match(clazz: EggProtoImplClass, method: PropertyKey): boolean { return this.className.test(clazz.name) && this.methodName.test(String(method)); } } export class CustomPointInfo implements PointcutInfo { readonly type = PointcutType.CUSTOM; readonly cb: CustomPointcutCallback; constructor(cb: CustomPointcutCallback) { this.cb = cb; } match(clazz: EggProtoImplClass, method: PropertyKey): boolean { return this.cb(clazz, method); } } ================================================ FILE: core/aop-decorator/src/util/AdviceInfoUtil.ts ================================================ import { MetadataUtil } from '@eggjs/core-decorator'; import type { EggProtoImplClass, IAdvice } from '@eggjs/tegg-types'; export const IS_ADVICE = Symbol.for('EggPrototype#isAdvice'); export class AdviceInfoUtil { static setIsAdvice(isAdvice: boolean, clazz: EggProtoImplClass) { MetadataUtil.defineMetaData(IS_ADVICE, isAdvice, clazz); } static isAdvice(clazz: EggProtoImplClass): boolean { return !!MetadataUtil.getMetaData(IS_ADVICE, clazz); } } ================================================ FILE: core/aop-decorator/src/util/AspectInfoUtil.ts ================================================ import { MetadataUtil } from '@eggjs/core-decorator'; import { ASPECT_LIST } from '@eggjs/tegg-types'; import type { EggProtoImplClass, IAdvice } from '@eggjs/tegg-types'; import { Aspect } from '../model/Aspect'; export class AspectInfoUtil { static setAspectList(aspectList: Array, clazz: EggProtoImplClass) { MetadataUtil.defineMetaData(ASPECT_LIST, aspectList, clazz); } static getAspectList(clazz: EggProtoImplClass): Array { return MetadataUtil.getMetaData(ASPECT_LIST, clazz) || []; } } ================================================ FILE: core/aop-decorator/src/util/CrosscutInfoUtil.ts ================================================ import { MetadataUtil } from '@eggjs/core-decorator'; import { IS_CROSSCUT_ADVICE, CROSSCUT_INFO_LIST } from '@eggjs/tegg-types'; import type { CrosscutInfo, EggProtoImplClass, IAdvice } from '@eggjs/tegg-types'; export class CrosscutInfoUtil { static setIsCrosscutAdvice(isCrosscutAdvice: boolean, clazz: EggProtoImplClass) { MetadataUtil.defineMetaData(IS_CROSSCUT_ADVICE, isCrosscutAdvice, clazz); } static isCrosscutAdvice(clazz: EggProtoImplClass): boolean { return !!MetadataUtil.getMetaData(IS_CROSSCUT_ADVICE, clazz); } static addCrosscutInfo(crosscutInfo: CrosscutInfo, clazz: EggProtoImplClass) { const crosscutInfoList = MetadataUtil.initOwnArrayMetaData(CROSSCUT_INFO_LIST, clazz, []); crosscutInfoList.push(crosscutInfo); } static getCrosscutInfoList(clazz: EggProtoImplClass): Array { return MetadataUtil.getArrayMetaData(CROSSCUT_INFO_LIST, clazz) || []; } } ================================================ FILE: core/aop-decorator/src/util/PointcutAdviceInfoUtil.ts ================================================ import { MetadataUtil } from '@eggjs/core-decorator'; import { POINTCUT_ADVICE_INFO_LIAR } from '@eggjs/tegg-types'; import type { AdviceInfo, EggProtoImplClass } from '@eggjs/tegg-types'; interface PointcutAdviceInfo { method: PropertyKey; adviceInfo: AdviceInfo; } export class PointcutAdviceInfoUtil { static addPointcutAdviceInfo(adviceInfo: AdviceInfo, clazz: EggProtoImplClass, method: PropertyKey) { const pointcutAdviceInfoList = MetadataUtil.initOwnArrayMetaData(POINTCUT_ADVICE_INFO_LIAR, clazz, []); // FIXME: parent/child should has correct order pointcutAdviceInfoList.unshift({ method, adviceInfo, }); } static getPointcutAdviceInfoList(clazz: EggProtoImplClass, method: PropertyKey): Array { const pointcutAdviceInfoList: Array | undefined = MetadataUtil.getMetaData(POINTCUT_ADVICE_INFO_LIAR, clazz) || []; return pointcutAdviceInfoList.filter(t => t.method === method).map(t => t.adviceInfo); } } ================================================ FILE: core/aop-decorator/test/AspectMetaBuilder.test.ts ================================================ import assert from 'node:assert'; import { PrototypeUtil } from '@eggjs/core-decorator'; import { CrosscutAdviceFactory } from '../src/CrosscutAdviceFactory'; import { CrosscutClassAdviceExample, CrosscutCustomAdviceExample, CrosscutExample, CrosscutNameAdviceExample, } from './fixtures/CrosscutExample'; import { GetterExample, PointcutAdviceAfterReturnExample, PointcutAdviceBeforeCallExample, PointcutExample, } from './fixtures/PointcutExample'; import { AspectMetaBuilder } from '../src/AspectMetaBuilder'; import { ChildExample, CrosscutNoOverwriteParentExample, CrosscutOverwriteChildExample, CrosscutOverwriteParentExample, ParentExample, PointcutAdviceNoOverwriteParentExample, PointcutAdviceOverwriteChildExample, PointcutAdviceOverwriteParentExample, } from './fixtures/InheritExample'; describe('test/AspectMetaBuild.test.ts', () => { const crosscutAdviceFactory = new CrosscutAdviceFactory(); crosscutAdviceFactory.registerCrossAdviceClazz(CrosscutClassAdviceExample); crosscutAdviceFactory.registerCrossAdviceClazz(CrosscutNameAdviceExample); crosscutAdviceFactory.registerCrossAdviceClazz(CrosscutCustomAdviceExample); crosscutAdviceFactory.registerCrossAdviceClazz(CrosscutOverwriteParentExample); crosscutAdviceFactory.registerCrossAdviceClazz(CrosscutOverwriteChildExample); crosscutAdviceFactory.registerCrossAdviceClazz(CrosscutNoOverwriteParentExample); describe('Pointcut', () => { it('should work', () => { const builder = new AspectMetaBuilder(PointcutExample, { crosscutAdviceFactory, }); const aspects = builder.build(); assert(aspects.length === 1); const aspect = aspects[0]; assert(aspect.clazz === PointcutExample); assert(aspect.method === 'hello'); const advices = aspect.adviceList; assert.deepStrictEqual(advices, [ { name: 'PointcutExample#hello#PointcutAdviceBeforeCallExample#0', clazz: PointcutAdviceBeforeCallExample, adviceParams: undefined }, { name: 'PointcutExample#hello#PointcutAdviceAfterReturnExample#1', clazz: PointcutAdviceAfterReturnExample, adviceParams: undefined }, ]); }); }); describe('Crosscut', () => { it('should work', () => { const builder = new AspectMetaBuilder(CrosscutExample, { crosscutAdviceFactory, }); const aspects = builder.build(); assert(aspects.length === 1); const aspect = aspects[0]; assert(aspect.clazz === CrosscutExample); assert(aspect.method === 'hello'); const advices = aspect.adviceList; assert.deepStrictEqual(advices, [ { name: 'CrosscutExample#hello#CrosscutClassAdviceExample#0', clazz: CrosscutClassAdviceExample, adviceParams: undefined }, { name: 'CrosscutExample#hello#CrosscutNameAdviceExample#1', clazz: CrosscutNameAdviceExample, adviceParams: undefined }, { name: 'CrosscutExample#hello#CrosscutCustomAdviceExample#2', clazz: CrosscutCustomAdviceExample, adviceParams: undefined }, ]); }); }); describe('inherit', () => { it('child should work', () => { const builder = new AspectMetaBuilder(ChildExample, { crosscutAdviceFactory, }); const aspects = builder.build(); assert(aspects.length === 2); const overwriteAspect = aspects.find(t => t.method === 'overwriteMethod'); assert(overwriteAspect); assert(overwriteAspect.clazz === ChildExample); const overwriteAdvices = overwriteAspect.adviceList; assert.deepStrictEqual(overwriteAdvices, [ { name: 'ChildExample#overwriteMethod#CrosscutOverwriteParentExample#0', clazz: CrosscutOverwriteParentExample, adviceParams: undefined }, { name: 'ChildExample#overwriteMethod#CrosscutOverwriteChildExample#1', clazz: CrosscutOverwriteChildExample, adviceParams: undefined }, // FIXME: parent/child should has correct order { name: 'ChildExample#overwriteMethod#PointcutAdviceOverwriteChildExample#2', clazz: PointcutAdviceOverwriteChildExample, adviceParams: undefined }, { name: 'ChildExample#overwriteMethod#PointcutAdviceOverwriteParentExample#3', clazz: PointcutAdviceOverwriteParentExample, adviceParams: undefined }, ]); const noOverwriteAspect = aspects.find(t => t.method === 'noOverwriteMethod'); assert(noOverwriteAspect); assert(noOverwriteAspect.clazz === ChildExample); const noOverwriteAdvices = noOverwriteAspect.adviceList; assert.deepStrictEqual(noOverwriteAdvices, [ { name: 'ChildExample#noOverwriteMethod#CrosscutNoOverwriteParentExample#0', clazz: CrosscutNoOverwriteParentExample, adviceParams: undefined }, { name: 'ChildExample#noOverwriteMethod#PointcutAdviceNoOverwriteParentExample#1', clazz: PointcutAdviceNoOverwriteParentExample, adviceParams: undefined }, ]); }); it('parent should work', () => { const builder = new AspectMetaBuilder(ParentExample, { crosscutAdviceFactory, }); const aspects = builder.build(); assert(aspects.length === 2); const overwriteAspect = aspects.find(t => t.method === 'overwriteMethod'); assert(overwriteAspect); assert(overwriteAspect.clazz === ParentExample); const overwriteAdvices = overwriteAspect.adviceList; assert.deepStrictEqual(overwriteAdvices, [ { name: 'ParentExample#overwriteMethod#CrosscutOverwriteParentExample#0', clazz: CrosscutOverwriteParentExample, adviceParams: undefined }, { name: 'ParentExample#overwriteMethod#PointcutAdviceOverwriteParentExample#1', clazz: PointcutAdviceOverwriteParentExample, adviceParams: undefined }, ]); const noOverwriteAspect = aspects.find(t => t.method === 'noOverwriteMethod'); assert(noOverwriteAspect); assert(noOverwriteAspect.clazz === ParentExample); const noOverwriteAdvices = noOverwriteAspect.adviceList; assert.deepStrictEqual(noOverwriteAdvices, [ { name: 'ParentExample#noOverwriteMethod#CrosscutNoOverwriteParentExample#0', clazz: CrosscutNoOverwriteParentExample, adviceParams: undefined }, { name: 'ParentExample#noOverwriteMethod#PointcutAdviceNoOverwriteParentExample#1', clazz: PointcutAdviceNoOverwriteParentExample, adviceParams: undefined }, ]); }); }); it('should not access getter', () => { const builder = new AspectMetaBuilder(GetterExample, { crosscutAdviceFactory, }); const aspects = builder.build(); assert(aspects.length === 1); }); it('should has right file path', () => { const filePath = PrototypeUtil.getFilePath(CrosscutClassAdviceExample); assert(filePath === require.resolve('./fixtures/CrosscutExample')); }); }); ================================================ FILE: core/aop-decorator/test/fixtures/CrosscutExample.ts ================================================ import { ContextProto } from '@eggjs/core-decorator'; import { PointcutType } from '@eggjs/tegg-types'; import type { EggProtoImplClass, IAdvice } from '@eggjs/tegg-types'; import { Advice, Crosscut } from '../..'; @ContextProto() export class CrosscutExample { constructor() { } hello() { console.log('hello'); } } @Crosscut({ type: PointcutType.CLASS, clazz: CrosscutExample, methodName: 'hello', }) @Advice() export class CrosscutClassAdviceExample implements IAdvice{ } @Crosscut({ type: PointcutType.NAME, className: /crosscut.*/i, methodName: /hello/, }) @Advice() export class CrosscutNameAdviceExample implements IAdvice{ } @Crosscut({ type: PointcutType.CUSTOM, callback: (clazz: EggProtoImplClass, method: PropertyKey) => { return clazz === CrosscutExample && method === 'hello'; } }) @Advice() export class CrosscutCustomAdviceExample implements IAdvice{ } ================================================ FILE: core/aop-decorator/test/fixtures/InheritExample.ts ================================================ import { ContextProto } from '@eggjs/core-decorator'; import { PointcutType } from '@eggjs/tegg-types'; import type { AdviceContext, IAdvice } from '@eggjs/tegg-types'; import { Advice } from '../../src/decorator/Advice'; import { Pointcut } from '../../src/decorator/Pointcut'; import { Crosscut } from '../../src/decorator/Crosscut'; @Advice() export class PointcutAdviceNoOverwriteParentExample implements IAdvice { async beforeCall(ctx: AdviceContext): Promise { console.log('ctx: ', ctx); } } @Advice() export class PointcutAdviceOverwriteParentExample implements IAdvice { async beforeCall(ctx: AdviceContext): Promise { console.log('ctx: ', ctx); } } @Advice() export class PointcutAdviceOverwriteChildExample implements IAdvice { async beforeCall(ctx: AdviceContext): Promise { console.log('ctx: ', ctx); } } @ContextProto() export class ParentExample { @Pointcut(PointcutAdviceOverwriteParentExample) overwriteMethod() { } @Pointcut(PointcutAdviceNoOverwriteParentExample) noOverwriteMethod() { } } @ContextProto() export class ChildExample extends ParentExample { @Pointcut(PointcutAdviceOverwriteChildExample) overwriteMethod() { } } @Advice() @Crosscut({ type: PointcutType.CLASS, clazz: ParentExample, methodName: 'noOverwriteMethod', }) export class CrosscutNoOverwriteParentExample implements IAdvice { async beforeCall(ctx: AdviceContext): Promise { console.log('ctx: ', ctx); } } @Advice() @Crosscut({ type: PointcutType.CLASS, clazz: ParentExample, methodName: 'overwriteMethod', }) export class CrosscutOverwriteParentExample implements IAdvice { async beforeCall(ctx: AdviceContext): Promise { console.log('ctx: ', ctx); } } @Advice() @Crosscut({ type: PointcutType.CLASS, clazz: ChildExample, methodName: 'overwriteMethod', }) export class CrosscutOverwriteChildExample implements IAdvice { async beforeCall(ctx: AdviceContext): Promise { console.log('ctx: ', ctx); } } ================================================ FILE: core/aop-decorator/test/fixtures/PointcutExample.ts ================================================ import { ContextProto } from '@eggjs/core-decorator'; import type { AdviceContext, IAdvice } from '@eggjs/tegg-types'; import { Advice, Pointcut } from '../..'; @Advice() export class PointcutAdviceBeforeCallExample implements IAdvice { async beforeCall(ctx: AdviceContext): Promise { console.log('ctx: ', ctx); } } @Advice() export class PointcutAdviceAfterReturnExample implements IAdvice { async afterReturn(ctx: AdviceContext): Promise { console.log('ctx: ', ctx); } } @ContextProto() export class GetterExample { get badGetter() { throw new Error('never access getter'); } @Pointcut(PointcutAdviceBeforeCallExample) foo() { } } @ContextProto() export class PointcutExample { @Pointcut(PointcutAdviceBeforeCallExample) @Pointcut(PointcutAdviceAfterReturnExample) hello() { console.log('hello'); } } ================================================ FILE: core/aop-decorator/tsconfig.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "dist", "baseUrl": "./" }, "exclude": [ "dist", "node_modules", "test" ] } ================================================ FILE: core/aop-decorator/tsconfig.pub.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "dist", "baseUrl": "./" }, "exclude": [ "dist", "node_modules", "test" ] } ================================================ FILE: core/aop-runtime/CHANGELOG.md ================================================ # Change Log All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. ## [3.78.15](https://github.com/eggjs/tegg/compare/v3.78.14...v3.78.15) (2026-04-23) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.78.14](https://github.com/eggjs/tegg/compare/v3.78.13...v3.78.14) (2026-04-21) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.78.13](https://github.com/eggjs/tegg/compare/v3.78.12...v3.78.13) (2026-04-21) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.78.12](https://github.com/eggjs/tegg/compare/v3.78.11...v3.78.12) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.78.11](https://github.com/eggjs/tegg/compare/v3.78.10...v3.78.11) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.78.10](https://github.com/eggjs/tegg/compare/v3.78.9...v3.78.10) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.78.9](https://github.com/eggjs/tegg/compare/v3.78.8...v3.78.9) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.78.8](https://github.com/eggjs/tegg/compare/v3.78.7...v3.78.8) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.78.7](https://github.com/eggjs/tegg/compare/v3.78.6...v3.78.7) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.78.6](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.6) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.78.5](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.5) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.78.4](https://github.com/eggjs/tegg/compare/v3.78.3...v3.78.4) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.78.3](https://github.com/eggjs/tegg/compare/v3.78.2...v3.78.3) (2026-04-15) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.78.2](https://github.com/eggjs/tegg/compare/v3.78.1...v3.78.2) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.78.1](https://github.com/eggjs/tegg/compare/v3.78.0...v3.78.1) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-aop-runtime # [3.78.0](https://github.com/eggjs/tegg/compare/v3.77.2...v3.78.0) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.77.2](https://github.com/eggjs/tegg/compare/v3.77.1...v3.77.2) (2026-04-07) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.77.1](https://github.com/eggjs/tegg/compare/v3.77.0...v3.77.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-aop-runtime # [3.77.0](https://github.com/eggjs/tegg/compare/v3.76.1...v3.77.0) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.76.1](https://github.com/eggjs/tegg/compare/v3.76.0...v3.76.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-aop-runtime # [3.76.0](https://github.com/eggjs/tegg/compare/v3.75.1...v3.76.0) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.75.1](https://github.com/eggjs/tegg/compare/v3.75.0...v3.75.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-aop-runtime # [3.75.0](https://github.com/eggjs/tegg/compare/v3.74.0...v3.75.0) (2026-03-30) **Note:** Version bump only for package @eggjs/tegg-aop-runtime # [3.74.0](https://github.com/eggjs/tegg/compare/v3.73.0...v3.74.0) (2026-03-30) **Note:** Version bump only for package @eggjs/tegg-aop-runtime # [3.73.0](https://github.com/eggjs/tegg/compare/v3.72.0...v3.73.0) (2026-03-25) **Note:** Version bump only for package @eggjs/tegg-aop-runtime # [3.72.0](https://github.com/eggjs/tegg/compare/v3.71.2...v3.72.0) (2026-02-04) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.71.2](https://github.com/eggjs/tegg/compare/v3.71.1...v3.71.2) (2026-01-30) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.71.1](https://github.com/eggjs/tegg/compare/v3.71.0...v3.71.1) (2026-01-28) **Note:** Version bump only for package @eggjs/tegg-aop-runtime # [3.71.0](https://github.com/eggjs/tegg/compare/v3.70.1...v3.71.0) (2026-01-26) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.70.1](https://github.com/eggjs/tegg/compare/v3.70.0...v3.70.1) (2026-01-14) **Note:** Version bump only for package @eggjs/tegg-aop-runtime # [3.70.0](https://github.com/eggjs/tegg/compare/v3.69.0...v3.70.0) (2026-01-07) **Note:** Version bump only for package @eggjs/tegg-aop-runtime # [3.69.0](https://github.com/eggjs/tegg/compare/v3.68.0...v3.69.0) (2026-01-06) **Note:** Version bump only for package @eggjs/tegg-aop-runtime # [3.68.0](https://github.com/eggjs/tegg/compare/v3.67.2...v3.68.0) (2025-12-29) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.67.2](https://github.com/eggjs/tegg/compare/v3.67.1...v3.67.2) (2025-12-24) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.67.1](https://github.com/eggjs/tegg/compare/v3.67.0...v3.67.1) (2025-12-23) **Note:** Version bump only for package @eggjs/tegg-aop-runtime # [3.67.0](https://github.com/eggjs/tegg/compare/v3.66.0...v3.67.0) (2025-12-18) **Note:** Version bump only for package @eggjs/tegg-aop-runtime # [3.66.0](https://github.com/eggjs/tegg/compare/v3.65.3...v3.66.0) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.65.3](https://github.com/eggjs/tegg/compare/v3.65.2...v3.65.3) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.65.2](https://github.com/eggjs/tegg/compare/v3.65.1...v3.65.2) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.65.1](https://github.com/eggjs/tegg/compare/v3.65.0...v3.65.1) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-aop-runtime # [3.65.0](https://github.com/eggjs/tegg/compare/v3.64.5...v3.65.0) (2025-12-10) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.64.5](https://github.com/eggjs/tegg/compare/v3.64.4...v3.64.5) (2025-12-08) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.64.4](https://github.com/eggjs/tegg/compare/v3.64.3...v3.64.4) (2025-12-06) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.64.3](https://github.com/eggjs/tegg/compare/v3.64.2...v3.64.3) (2025-12-06) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.64.2](https://github.com/eggjs/tegg/compare/v3.64.1...v3.64.2) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.64.1](https://github.com/eggjs/tegg/compare/v3.64.0...v3.64.1) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-aop-runtime # [3.64.0](https://github.com/eggjs/tegg/compare/v3.63.2...v3.64.0) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.63.2](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.2) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.63.1](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.1) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg-aop-runtime # [3.63.0](https://github.com/eggjs/tegg/compare/v3.62.3...v3.63.0) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.62.3](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.3) (2025-11-05) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.62.2](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.2) (2025-11-05) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.62.1](https://github.com/eggjs/tegg/compare/v3.62.0...v3.62.1) (2025-11-03) **Note:** Version bump only for package @eggjs/tegg-aop-runtime # [3.62.0](https://github.com/eggjs/tegg/compare/v3.61.0...v3.62.0) (2025-09-24) **Note:** Version bump only for package @eggjs/tegg-aop-runtime # [3.61.0](https://github.com/eggjs/tegg/compare/v3.60.3...v3.61.0) (2025-08-15) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.60.3](https://github.com/eggjs/tegg/compare/v3.60.2...v3.60.3) (2025-08-06) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.60.2](https://github.com/eggjs/tegg/compare/v3.60.1...v3.60.2) (2025-08-06) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.60.1](https://github.com/eggjs/tegg/compare/v3.60.0...v3.60.1) (2025-07-28) **Note:** Version bump only for package @eggjs/tegg-aop-runtime # [3.60.0](https://github.com/eggjs/tegg/compare/v3.59.1...v3.60.0) (2025-07-28) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.59.1](https://github.com/eggjs/tegg/compare/v3.59.0...v3.59.1) (2025-07-16) **Note:** Version bump only for package @eggjs/tegg-aop-runtime # [3.59.0](https://github.com/eggjs/tegg/compare/v3.58.0...v3.59.0) (2025-07-07) **Note:** Version bump only for package @eggjs/tegg-aop-runtime # [3.58.0](https://github.com/eggjs/tegg/compare/v3.57.14...v3.58.0) (2025-07-01) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.57.14](https://github.com/eggjs/tegg/compare/v3.57.13...v3.57.14) (2025-06-18) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.57.13](https://github.com/eggjs/tegg/compare/v3.57.12...v3.57.13) (2025-06-18) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.57.12](https://github.com/eggjs/tegg/compare/v3.57.11...v3.57.12) (2025-06-16) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.57.11](https://github.com/eggjs/tegg/compare/v3.57.10...v3.57.11) (2025-06-16) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.57.10](https://github.com/eggjs/tegg/compare/v3.57.9...v3.57.10) (2025-06-12) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.57.9](https://github.com/eggjs/tegg/compare/v3.57.8...v3.57.9) (2025-05-29) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.57.8](https://github.com/eggjs/tegg/compare/v3.57.7...v3.57.8) (2025-05-29) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.57.7](https://github.com/eggjs/tegg/compare/v3.57.6...v3.57.7) (2025-05-28) ### Bug Fixes * the loading order issue in multi-module mode ([#324](https://github.com/eggjs/tegg/issues/324)) ([c9610bd](https://github.com/eggjs/tegg/commit/c9610bd53dee7bebd069bc6766e869cb2b2f9fc9)) ## [3.57.6](https://github.com/eggjs/tegg/compare/v3.57.5...v3.57.6) (2025-05-27) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.57.5](https://github.com/eggjs/tegg/compare/v3.57.4...v3.57.5) (2025-05-15) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.57.4](https://github.com/eggjs/tegg/compare/v3.57.3...v3.57.4) (2025-05-14) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.57.3](https://github.com/eggjs/tegg/compare/v3.57.2...v3.57.3) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.57.2](https://github.com/eggjs/tegg/compare/v3.57.1...v3.57.2) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.57.1](https://github.com/eggjs/tegg/compare/v3.57.0...v3.57.1) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-aop-runtime # [3.57.0](https://github.com/eggjs/tegg/compare/v3.56.3...v3.57.0) (2025-05-09) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.56.3](https://github.com/eggjs/tegg/compare/v3.56.2...v3.56.3) (2025-05-07) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.56.2](https://github.com/eggjs/tegg/compare/v3.56.1...v3.56.2) (2025-05-01) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.56.1](https://github.com/eggjs/tegg/compare/v3.56.0...v3.56.1) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-aop-runtime # [3.56.0](https://github.com/eggjs/tegg/compare/v3.55.0...v3.56.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-aop-runtime # [3.55.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.55.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-aop-runtime # [3.54.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.54.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-aop-runtime # [3.53.0](https://github.com/eggjs/tegg/compare/v3.52.1...v3.53.0) (2025-04-21) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.52.1](https://github.com/eggjs/tegg/compare/v3.52.0...v3.52.1) (2025-03-14) **Note:** Version bump only for package @eggjs/tegg-aop-runtime # [3.52.0](https://github.com/eggjs/tegg/compare/v3.51.2...v3.52.0) (2024-12-30) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.51.2](https://github.com/eggjs/tegg/compare/v3.51.1...v3.51.2) (2024-12-09) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.51.1](https://github.com/eggjs/tegg/compare/v3.51.0...v3.51.1) (2024-11-06) **Note:** Version bump only for package @eggjs/tegg-aop-runtime # [3.51.0](https://github.com/eggjs/tegg/compare/v3.50.1...v3.51.0) (2024-10-30) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.50.1](https://github.com/eggjs/tegg/compare/v3.50.0...v3.50.1) (2024-10-23) **Note:** Version bump only for package @eggjs/tegg-aop-runtime # [3.50.0](https://github.com/eggjs/tegg/compare/v3.49.0...v3.50.0) (2024-10-22) **Note:** Version bump only for package @eggjs/tegg-aop-runtime # [3.49.0](https://github.com/eggjs/tegg/compare/v3.48.1...v3.49.0) (2024-10-21) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.48.1](https://github.com/eggjs/tegg/compare/v3.48.0...v3.48.1) (2024-10-14) ### Bug Fixes * fix merge qualifier ([#250](https://github.com/eggjs/tegg/issues/250)) ([d5a8a93](https://github.com/eggjs/tegg/commit/d5a8a93abad570f69881f9fa42f39d7b5cd436be)) # [3.48.0](https://github.com/eggjs/tegg/compare/v3.47.2...v3.48.0) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.47.2](https://github.com/eggjs/tegg/compare/v3.47.1...v3.47.2) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.47.1](https://github.com/eggjs/tegg/compare/v3.47.0...v3.47.1) (2024-10-10) ### Bug Fixes * fix aop in constructor inject type ([#247](https://github.com/eggjs/tegg/issues/247)) ([d169bb2](https://github.com/eggjs/tegg/commit/d169bb2fbbc86335315619866b4134a25296f552)) # [3.47.0](https://github.com/eggjs/tegg/compare/v3.46.4...v3.47.0) (2024-10-10) ### Features * impl GlobalGraph build hook ([#246](https://github.com/eggjs/tegg/issues/246)) ([48fce45](https://github.com/eggjs/tegg/commit/48fce4512e99259ec26a9b032bfcc9f4046ad235)) ## [3.46.4](https://github.com/eggjs/tegg/compare/v3.46.3...v3.46.4) (2024-10-09) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.46.3](https://github.com/eggjs/tegg/compare/v3.46.2...v3.46.3) (2024-10-08) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.46.2](https://github.com/eggjs/tegg/compare/v3.46.1...v3.46.2) (2024-10-07) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.46.1](https://github.com/eggjs/tegg/compare/v3.46.0...v3.46.1) (2024-09-30) **Note:** Version bump only for package @eggjs/tegg-aop-runtime # [3.46.0](https://github.com/eggjs/tegg/compare/v3.45.0...v3.46.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-aop-runtime # [3.45.0](https://github.com/eggjs/tegg/compare/v3.44.1...v3.45.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.44.1](https://github.com/eggjs/tegg/compare/v3.44.0...v3.44.1) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-aop-runtime # [3.44.0](https://github.com/eggjs/tegg/compare/v3.43.2...v3.44.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.43.2](https://github.com/eggjs/tegg/compare/v3.43.1...v3.43.2) (2024-09-14) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.43.1](https://github.com/eggjs/tegg/compare/v3.43.0...v3.43.1) (2024-09-14) **Note:** Version bump only for package @eggjs/tegg-aop-runtime # [3.43.0](https://github.com/eggjs/tegg/compare/v3.42.0...v3.43.0) (2024-09-13) **Note:** Version bump only for package @eggjs/tegg-aop-runtime # [3.42.0](https://github.com/eggjs/tegg/compare/v3.41.0...v3.42.0) (2024-09-10) **Note:** Version bump only for package @eggjs/tegg-aop-runtime # [3.41.0](https://github.com/eggjs/tegg/compare/v3.40.1...v3.41.0) (2024-08-26) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.40.1](https://github.com/eggjs/tegg/compare/v3.40.0...v3.40.1) (2024-08-23) **Note:** Version bump only for package @eggjs/tegg-aop-runtime # [3.40.0](https://github.com/eggjs/tegg/compare/v3.39.5...v3.40.0) (2024-08-22) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.39.5](https://github.com/eggjs/tegg/compare/v3.39.4...v3.39.5) (2024-08-09) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.39.4](https://github.com/eggjs/tegg/compare/v3.39.3...v3.39.4) (2024-07-09) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.39.3](https://github.com/eggjs/tegg/compare/v3.39.2...v3.39.3) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.39.2](https://github.com/eggjs/tegg/compare/v3.39.1...v3.39.2) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.39.1](https://github.com/eggjs/tegg/compare/v3.39.0...v3.39.1) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-aop-runtime # [3.39.0](https://github.com/eggjs/tegg/compare/v3.38.0...v3.39.0) (2024-04-19) **Note:** Version bump only for package @eggjs/tegg-aop-runtime # [3.38.0](https://github.com/eggjs/tegg/compare/v3.37.3...v3.38.0) (2024-04-18) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.37.3](https://github.com/eggjs/tegg/compare/v3.37.2...v3.37.3) (2024-04-17) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.37.2](https://github.com/eggjs/tegg/compare/v3.37.1...v3.37.2) (2024-04-17) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.37.1](https://github.com/eggjs/tegg/compare/v3.37.0...v3.37.1) (2024-04-16) **Note:** Version bump only for package @eggjs/tegg-aop-runtime # [3.37.0](https://github.com/eggjs/tegg/compare/v3.36.3...v3.37.0) (2024-04-16) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.36.3](https://github.com/eggjs/tegg/compare/v3.36.2...v3.36.3) (2024-04-10) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.36.2](https://github.com/eggjs/tegg/compare/v3.36.1...v3.36.2) (2024-04-08) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.36.1](https://github.com/eggjs/tegg/compare/v3.36.0...v3.36.1) (2024-04-07) **Note:** Version bump only for package @eggjs/tegg-aop-runtime # [3.36.0](https://github.com/eggjs/tegg/compare/v3.35.1...v3.36.0) (2024-04-02) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.35.1](https://github.com/eggjs/tegg/compare/v3.35.0...v3.35.1) (2024-03-26) **Note:** Version bump only for package @eggjs/tegg-aop-runtime # [3.35.0](https://github.com/eggjs/tegg/compare/v3.34.0...v3.35.0) (2024-03-26) **Note:** Version bump only for package @eggjs/tegg-aop-runtime # [3.34.0](https://github.com/eggjs/tegg/compare/v3.33.1...v3.34.0) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.33.1](https://github.com/eggjs/tegg/compare/v3.33.0...v3.33.1) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-aop-runtime # [3.33.0](https://github.com/eggjs/tegg/compare/v3.32.0...v3.33.0) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-aop-runtime # [3.32.0](https://github.com/eggjs/tegg/compare/v3.31.0...v3.32.0) (2024-02-19) **Note:** Version bump only for package @eggjs/tegg-aop-runtime # [3.31.0](https://github.com/eggjs/tegg/compare/v3.30.1...v3.31.0) (2024-01-31) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.30.1](https://github.com/eggjs/tegg/compare/v3.30.0...v3.30.1) (2024-01-25) ### Bug Fixes * fix modify ctx.args in aop beforeCall not work ([#187](https://github.com/eggjs/tegg/issues/187)) ([7656424](https://github.com/eggjs/tegg/commit/765642414387c8a9940525cd3c519fcb5fd694a0)) # [3.30.0](https://github.com/eggjs/tegg/compare/v3.29.0...v3.30.0) (2024-01-17) **Note:** Version bump only for package @eggjs/tegg-aop-runtime # [3.29.0](https://github.com/eggjs/tegg/compare/v3.28.2...v3.29.0) (2023-12-26) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.28.2](https://github.com/eggjs/tegg/compare/v3.28.1...v3.28.2) (2023-12-12) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.28.1](https://github.com/eggjs/tegg/compare/v3.28.0...v3.28.1) (2023-12-11) **Note:** Version bump only for package @eggjs/tegg-aop-runtime # [3.28.0](https://github.com/eggjs/tegg/compare/v3.27.0...v3.28.0) (2023-12-10) **Note:** Version bump only for package @eggjs/tegg-aop-runtime # [3.27.0](https://github.com/eggjs/tegg/compare/v3.26.0...v3.27.0) (2023-11-23) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.25.2](https://github.com/eggjs/tegg/compare/v3.25.1...v3.25.2) (2023-11-06) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.25.1](https://github.com/eggjs/tegg/compare/v3.25.0...v3.25.1) (2023-11-03) **Note:** Version bump only for package @eggjs/tegg-aop-runtime # [3.25.0](https://github.com/eggjs/tegg/compare/v3.24.0...v3.25.0) (2023-11-03) **Note:** Version bump only for package @eggjs/tegg-aop-runtime # [3.24.0](https://github.com/eggjs/tegg/compare/v3.23.0...v3.24.0) (2023-10-26) **Note:** Version bump only for package @eggjs/tegg-aop-runtime # [3.23.0](https://github.com/eggjs/tegg/compare/v3.22.0...v3.23.0) (2023-09-20) **Note:** Version bump only for package @eggjs/tegg-aop-runtime # [3.20.0](https://github.com/eggjs/tegg/compare/v3.19.0...v3.20.0) (2023-09-07) **Note:** Version bump only for package @eggjs/tegg-aop-runtime # [3.19.0](https://github.com/eggjs/tegg/compare/v3.18.1...v3.19.0) (2023-08-31) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.18.1](https://github.com/eggjs/tegg/compare/v3.18.0...v3.18.1) (2023-08-29) **Note:** Version bump only for package @eggjs/tegg-aop-runtime # [3.18.0](https://github.com/eggjs/tegg/compare/v3.17.0...v3.18.0) (2023-08-29) **Note:** Version bump only for package @eggjs/tegg-aop-runtime # [3.17.0](https://github.com/eggjs/tegg/compare/v3.16.0...v3.17.0) (2023-08-24) **Note:** Version bump only for package @eggjs/tegg-aop-runtime # [3.16.0](https://github.com/eggjs/tegg/compare/v3.15.0...v3.16.0) (2023-08-24) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.14.3](https://github.com/eggjs/tegg/compare/v3.14.2...v3.14.3) (2023-08-14) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.14.2](https://github.com/eggjs/tegg/compare/v3.14.1...v3.14.2) (2023-08-14) ### Bug Fixes * init all context advice if root proto miss ([#139](https://github.com/eggjs/tegg/issues/139)) ([0602ea8](https://github.com/eggjs/tegg/commit/0602ea81578bf717ee4b4c490ace8c1c133478c5)) ## [3.14.1](https://github.com/eggjs/tegg/compare/v3.14.0...v3.14.1) (2023-08-11) **Note:** Version bump only for package @eggjs/tegg-aop-runtime # [3.14.0](https://github.com/eggjs/tegg/compare/v3.13.0...v3.14.0) (2023-07-31) **Note:** Version bump only for package @eggjs/tegg-aop-runtime # [3.13.0](https://github.com/eggjs/tegg/compare/v3.12.0...v3.13.0) (2023-07-25) **Note:** Version bump only for package @eggjs/tegg-aop-runtime # [3.12.0](https://github.com/eggjs/tegg/compare/v3.11.1...v3.12.0) (2023-07-13) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.11.1](https://github.com/eggjs/tegg/compare/v3.11.0...v3.11.1) (2023-06-29) **Note:** Version bump only for package @eggjs/tegg-aop-runtime # [3.11.0](https://github.com/eggjs/tegg/compare/v3.10.0...v3.11.0) (2023-06-29) **Note:** Version bump only for package @eggjs/tegg-aop-runtime # [3.10.0](https://github.com/eggjs/tegg/compare/v3.9.0...v3.10.0) (2023-06-28) **Note:** Version bump only for package @eggjs/tegg-aop-runtime # [3.9.0](https://github.com/eggjs/tegg/compare/v3.8.0...v3.9.0) (2023-06-20) ### Features * implement advice params ([76ec8ad](https://github.com/eggjs/tegg/commit/76ec8ad7b7170a637e59d74d49c1f00d8a201321)) # [3.8.0](https://github.com/eggjs/tegg/compare/v3.7.0...v3.8.0) (2023-05-30) ### Features * impl EggObjectLifecycle hook in decorator ([#119](https://github.com/eggjs/tegg/issues/119)) ([cced8a2](https://github.com/eggjs/tegg/commit/cced8a2e009c33d5040fa21d00409fddef471b0e)) # [3.7.0](https://github.com/eggjs/tegg/compare/v3.6.3...v3.7.0) (2023-04-03) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.6.3](https://github.com/eggjs/tegg/compare/v3.6.2...v3.6.3) (2023-03-02) **Note:** Version bump only for package @eggjs/tegg-aop-runtime # [3.5.0](https://github.com/eggjs/tegg/compare/v3.4.1...v3.5.0) (2023-02-10) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.3.1](https://github.com/eggjs/tegg/compare/v3.3.0...v3.3.1) (2023-01-28) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.2.3](https://github.com/eggjs/tegg/compare/v3.2.2...v3.2.3) (2023-01-16) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.2.2](https://github.com/eggjs/tegg/compare/v3.2.1...v3.2.2) (2023-01-06) **Note:** Version bump only for package @eggjs/tegg-aop-runtime ## [3.2.1](https://github.com/eggjs/tegg/compare/v3.2.0...v3.2.1) (2022-12-28) **Note:** Version bump only for package @eggjs/tegg-aop-runtime # [3.1.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.1.0) (2022-12-27) ### Bug Fixes * fix events type from any to keyof Events ([#54](https://github.com/eggjs/tegg/issues/54)) ([a2551b2](https://github.com/eggjs/tegg/commit/a2551b2d9f9eabf9ed5c87f83489615eefa3e6d1)) * fix mock prototype in aop not work ([#66](https://github.com/eggjs/tegg/issues/66)) ([16640eb](https://github.com/eggjs/tegg/commit/16640eb751405532b2a1241b17624ce3ac2d1c7a)) ### Features * **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) # [3.0.0](https://github.com/eggjs/tegg/compare/v3.0.0-alpha.0...v3.0.0) (2022-12-26) **Note:** Version bump only for package @eggjs/tegg-aop-runtime # [3.0.0-alpha.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.0.0-alpha.0) (2022-12-22) ### Bug Fixes * fix events type from any to keyof Events ([#54](https://github.com/eggjs/tegg/issues/54)) ([a2551b2](https://github.com/eggjs/tegg/commit/a2551b2d9f9eabf9ed5c87f83489615eefa3e6d1)) * fix mock prototype in aop not work ([#66](https://github.com/eggjs/tegg/issues/66)) ([16640eb](https://github.com/eggjs/tegg/commit/16640eb751405532b2a1241b17624ce3ac2d1c7a)) ### Features * **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) ## [1.4.1](https://github.com/eggjs/tegg/compare/@eggjs/tegg-aop-runtime@1.4.0...@eggjs/tegg-aop-runtime@1.4.1) (2022-09-04) ### Bug Fixes * fix events type from any to keyof Events ([#54](https://github.com/eggjs/tegg/issues/54)) ([a2551b2](https://github.com/eggjs/tegg/commit/a2551b2d9f9eabf9ed5c87f83489615eefa3e6d1)) # 1.4.0 (2022-08-24) # 1.3.0 (2022-07-01) # 1.2.0 (2022-06-29) ## 1.1.1 (2022-06-21) # 1.1.0 (2022-06-15) ## 1.0.5 (2022-04-24) ### Bug Fixes * should throw with no aop proto ([#34](https://github.com/eggjs/tegg/issues/34)) ([5c0b98a](https://github.com/eggjs/tegg/commit/5c0b98a89924f5bad062018e32fbd7993169126c)) ## 1.0.3 (2022-02-08) ### Bug Fixes * rm console ([7455ce6](https://github.com/eggjs/tegg/commit/7455ce6fcc04fe9463a1ecf6e03e049ce2faf6f0)) ## 1.0.2 (2022-02-08) ## 1.0.1 (2022-02-08) # 1.0.0 (2022-02-08) # 0.2.0 (2022-01-20) ### Features * impl aop ([c53df00](https://github.com/eggjs/tegg/commit/c53df001d1455a0a105689694775d880541d9d2f)) # [1.3.0](https://github.com/eggjs/tegg/compare/v1.2.0...v1.3.0) (2022-07-01) **Note:** Version bump only for package @eggjs/tegg-aop-runtime # [0.2.0](https://github.com/eggjs/tegg/compare/v0.1.19...v0.2.0) (2022-01-20) ### Features * impl aop ([c53df00](https://github.com/eggjs/tegg/commit/c53df001d1455a0a105689694775d880541d9d2f)) ================================================ FILE: core/aop-runtime/README.md ================================================ # `aop-runtime` ## Usage This is an internal tegg library, you probably shouldn't use it directly. ================================================ FILE: core/aop-runtime/index.ts ================================================ export * from './src/EggPrototypeCrossCutHook'; export * from './src/EggObjectAopHook'; export * from './src/LoadUnitAopHook'; export * from './src/CrossCutGraphHook'; export * from './src/PointCutGraphHook'; ================================================ FILE: core/aop-runtime/package.json ================================================ { "name": "@eggjs/tegg-aop-runtime", "version": "3.78.15", "description": "tegg aop", "main": "dist/index.js", "eggModule": { "name": "teggAopRuntime" }, "files": [ "dist/**/*.js", "dist/**/*.d.ts" ], "typings": "dist/index.d.ts", "keywords": [ "egg", "typescript", "runtime", "tegg", "aop" ], "scripts": { "test": "cross-env NODE_ENV=test NODE_OPTIONS='--no-deprecation' mocha", "clean": "tsc -b --clean && rm -rf dist", "tsc": "ut run clean && tsc -p ./tsconfig.json", "tsc:pub": "ut run clean && tsc -p ./tsconfig.pub.json", "prepublishOnly": "ut tsc:pub" }, "author": "killagu ", "license": "MIT", "homepage": "https://github.com/eggjs/tegg", "bugs": { "url": "https://github.com/eggjs/tegg/issues" }, "repository": { "type": "git", "url": "git@github.com:eggjs/tegg.git", "directory": "core/aop-runtime" }, "engines": { "node": ">=10.0.0" }, "publishConfig": { "access": "public" }, "dependencies": { "@eggjs/aop-decorator": "^3.78.15", "@eggjs/core-decorator": "^3.78.15", "@eggjs/tegg-common-util": "^3.78.15", "@eggjs/tegg-lifecycle": "^3.78.15", "@eggjs/tegg-metadata": "^3.78.15", "@eggjs/tegg-runtime": "^3.78.15", "@eggjs/tegg-types": "^3.78.15", "koa-compose": "^4.1.0" }, "devDependencies": { "@eggjs/module-test-util": "^3.78.15", "@eggjs/tegg-loader": "^3.78.15", "@types/mocha": "^10.0.1", "@types/node": "^20.2.4", "cross-env": "^7.0.3", "mm": "^3.2.1", "mocha": "^10.2.0", "ts-node": "^10.9.1", "typescript": "^5.0.4" }, "gitHead": "240a3e9f40fda65ebb7589727d197db5ce17916c" } ================================================ FILE: core/aop-runtime/src/AspectExecutor.ts ================================================ import type { AdviceContext, AspectAdvice, IAdvice } from '@eggjs/tegg-types'; import compose from 'koa-compose'; import type { Middleware } from 'koa-compose'; interface InternalAdviceContext { that: T; method: PropertyKey; args: any[]; } export class AspectExecutor { obj: Object; method: PropertyKey; aspectAdviceList: readonly AspectAdvice[]; constructor(obj: object, method: PropertyKey, aspectAdviceList: readonly AspectAdvice[]) { this.obj = obj; this.method = method; this.aspectAdviceList = aspectAdviceList; } async execute(...args: any[]) { const ctx: InternalAdviceContext = { that: this.obj, method: this.method, args, }; await this.beforeCall(ctx); try { const result = await this.doExecute(ctx); await this.afterReturn(ctx, result); return result; } catch (e) { await this.afterThrow(ctx, e); throw e; } finally { await this.afterFinally(ctx); } } async beforeCall(ctx: InternalAdviceContext) { for (const aspectAdvice of this.aspectAdviceList) { const advice: IAdvice = ctx.that[aspectAdvice.name]; if (advice.beforeCall) { /** * 这里...写法使传入的参数变成了一个新的对象 * 因此beforeCall里面如果修改了ctx.args * 最新的args是不会在方法里生效的 * 先保证args可以生效 * 不改动其余地方 */ const params = { ...ctx, adviceParams: aspectAdvice.adviceParams }; await advice.beforeCall(params); ctx.args = params.args; } } } async afterReturn(ctx: InternalAdviceContext, result: any) { for (const aspectAdvice of this.aspectAdviceList) { const advice: IAdvice = ctx.that[aspectAdvice.name]; if (advice.afterReturn) { await advice.afterReturn({ ...ctx, adviceParams: aspectAdvice.adviceParams }, result); } } } async afterThrow(ctx: InternalAdviceContext, error: Error) { for (const aspectAdvice of this.aspectAdviceList) { const advice: IAdvice = ctx.that[aspectAdvice.name]; if (advice.afterThrow) { await advice.afterThrow({ ...ctx, adviceParams: aspectAdvice.adviceParams }, error); } } } async afterFinally(ctx: InternalAdviceContext) { for (const aspectAdvice of this.aspectAdviceList) { const advice: IAdvice = ctx.that[aspectAdvice.name]; if (advice.afterFinally) { await advice.afterFinally({ ...ctx, adviceParams: aspectAdvice.adviceParams }); } } } async doExecute(ctx: InternalAdviceContext) { const lastCall = () => { const originMethod = Object.getPrototypeOf(this.obj)[this.method]; return Reflect.apply(originMethod, ctx.that, ctx.args); }; const functions: Array> = []; for (const aspectAdvice of this.aspectAdviceList) { const advice: IAdvice = ctx.that[aspectAdvice.name]; const fn = advice.around; if (fn) { functions.push(async (ctx: InternalAdviceContext, next: () => Promise) => { const fnCtx: AdviceContext = { ...ctx, adviceParams: aspectAdvice.adviceParams, }; return await fn.call(advice, fnCtx, next); }); } } functions.push(lastCall); return compose(functions)(ctx); } } ================================================ FILE: core/aop-runtime/src/CrossCutGraphHook.ts ================================================ import { AspectMetaBuilder, CrosscutInfo, CrosscutInfoUtil } from '@eggjs/aop-decorator'; import { GraphNode } from '@eggjs/tegg-common-util'; import { ClassProtoDescriptor, GlobalGraph, ProtoDependencyMeta, ProtoNode, } from '@eggjs/tegg-metadata'; export function crossCutGraphHook(globalGraph: GlobalGraph) { for (const moduleNode of globalGraph.moduleGraph.nodes.values()) { for (const crossCutProtoNode of moduleNode.val.protos) { const protoNodes = findCrossCuttedClazz(globalGraph, crossCutProtoNode); if (!protoNodes) continue; for (const crossCuttedProtoNode of protoNodes) { const crossCuttedModuleNode = globalGraph.findModuleNode(crossCuttedProtoNode.val.proto.instanceModuleName); if (!crossCuttedModuleNode) continue; globalGraph.addInject( crossCuttedModuleNode, crossCuttedProtoNode, crossCutProtoNode, crossCutProtoNode.val.proto.name); } } } } function findCrossCuttedClazz(globalGraph: GlobalGraph, protoNode: GraphNode) { const proto = protoNode.val.proto; if (!ClassProtoDescriptor.isClassProtoDescriptor(proto)) { return; } if (!CrosscutInfoUtil.isCrosscutAdvice(proto.clazz)) { return; } const crosscutInfoList = CrosscutInfoUtil.getCrosscutInfoList(proto.clazz); const result: GraphNode[] = []; for (const crosscutInfo of crosscutInfoList) { for (const protoNode of globalGraph.protoGraph.nodes.values()) { if (checkClazzMatchCrossCut(protoNode, crosscutInfo)) { result.push(protoNode); } } } return result; } function checkClazzMatchCrossCut(protoNode: GraphNode, crosscutInfo: CrosscutInfo) { const proto = protoNode.val.proto; if (!ClassProtoDescriptor.isClassProtoDescriptor(proto)) { return; } const allMethods = AspectMetaBuilder.getAllMethods(proto.clazz); for (const method of allMethods) { if (crosscutInfo.pointcutInfo.match(proto.clazz, method)) { return true; } } return false; } ================================================ FILE: core/aop-runtime/src/EggObjectAopHook.ts ================================================ import { ASPECT_LIST, InjectType } from '@eggjs/tegg-types'; import type { EggObject, EggObjectLifeCycleContext, LifecycleHook } from '@eggjs/tegg-types'; import { Aspect } from '@eggjs/aop-decorator'; import { AspectExecutor } from './AspectExecutor'; import { PrototypeUtil } from '@eggjs/core-decorator'; import assert from 'node:assert'; import { EggContainerFactory } from '@eggjs/tegg-runtime'; export class EggObjectAopHook implements LifecycleHook { private hijackMethods(obj: any, aspectList: Array) { for (const aspect of aspectList) { const newExecutor = new AspectExecutor(obj, aspect.method, aspect.adviceList); obj[aspect.method] = newExecutor.execute.bind(newExecutor); } } // constructor inject only paas obj to constructor // should manually define obj to property private injectAdvice(eggObject: EggObject, obj: any, aspectList: Array) { if (eggObject.proto.getMetaData(PrototypeUtil.INJECT_TYPE) !== InjectType.CONSTRUCTOR) { return; } for (const aspect of aspectList) { for (const advice of aspect.adviceList) { const injectObject = eggObject.proto.injectObjects.find(t => t.objName === advice.name); assert(injectObject, `not found inject advice ${advice.name}`); const adviceObj = EggContainerFactory.getEggObject(injectObject!.proto, advice.name); Object.defineProperty(obj, advice.name, { value: adviceObj.obj, enumerable: false, }); } } } async postCreate(_: EggObjectLifeCycleContext, eggObject: EggObject): Promise { const aspectList: Array | undefined = eggObject.proto.getMetaData(ASPECT_LIST); if (!aspectList || !aspectList.length) return; const propertyDesc = eggObject.constructor && Reflect.getOwnPropertyDescriptor(eggObject.constructor.prototype, 'obj')!; // process the lazy getter if (propertyDesc?.get) { let obj; const self = this; Object.defineProperty(eggObject, 'obj', { ...propertyDesc, get(): any { if (!obj) { obj = Reflect.apply(propertyDesc.get!, eggObject, []); self.hijackMethods(obj, aspectList); self.injectAdvice(eggObject, obj, aspectList); } return obj; }, }); } else { this.hijackMethods(eggObject.obj, aspectList); this.injectAdvice(eggObject, eggObject.obj, aspectList); } } } ================================================ FILE: core/aop-runtime/src/EggPrototypeCrossCutHook.ts ================================================ import type { EggPrototype, EggPrototypeLifecycleContext, LifecycleHook } from '@eggjs/tegg-types'; import { CrosscutAdviceFactory, CrosscutInfoUtil } from '@eggjs/aop-decorator'; export class EggPrototypeCrossCutHook implements LifecycleHook { private readonly crosscutAdviceFactory: CrosscutAdviceFactory; constructor(crosscutAdviceFactory: CrosscutAdviceFactory) { this.crosscutAdviceFactory = crosscutAdviceFactory; } async preCreate(ctx: EggPrototypeLifecycleContext): Promise { if (CrosscutInfoUtil.isCrosscutAdvice(ctx.clazz)) { this.crosscutAdviceFactory.registerCrossAdviceClazz(ctx.clazz); } } } ================================================ FILE: core/aop-runtime/src/LoadUnitAopHook.ts ================================================ import { AspectInfoUtil, AspectMetaBuilder, CrosscutAdviceFactory } from '@eggjs/aop-decorator'; import { PrototypeUtil } from '@eggjs/core-decorator'; import { TeggError } from '@eggjs/tegg-metadata'; import type { EggPrototype, EggPrototypeWithClazz, LifecycleHook, LoadUnit, LoadUnitLifecycleContext, } from '@eggjs/tegg-types'; export class LoadUnitAopHook implements LifecycleHook { private readonly crosscutAdviceFactory: CrosscutAdviceFactory; constructor(crosscutAdviceFactory: CrosscutAdviceFactory) { this.crosscutAdviceFactory = crosscutAdviceFactory; } async postCreate(_: LoadUnitLifecycleContext, loadUnit: LoadUnit): Promise { for (const proto of loadUnit.iterateEggPrototype()) { const protoWithClazz = proto as EggPrototypeWithClazz; const clazz = protoWithClazz.clazz; if (!clazz) continue; const builder = new AspectMetaBuilder(clazz, { crosscutAdviceFactory: this.crosscutAdviceFactory, }); const aspectList = builder.build(); AspectInfoUtil.setAspectList(aspectList, clazz); for (const aspect of aspectList) { for (const advice of aspect.adviceList) { const adviceProto = PrototypeUtil.getClazzProto(advice.clazz); if (!adviceProto) { throw TeggError.create(`Aop Advice(${advice.clazz.name}) not found in loadUnits`, 'advice_not_found'); } proto.injectObjects.push({ refName: advice.name, objName: advice.name, qualifiers: [], proto: adviceProto as EggPrototype, }); } } } } } ================================================ FILE: core/aop-runtime/src/PointCutGraphHook.ts ================================================ import { AspectMetaBuilder, PointcutAdviceInfoUtil } from '@eggjs/aop-decorator'; import { PrototypeUtil, QualifierUtil } from '@eggjs/core-decorator'; import { GraphNode } from '@eggjs/tegg-common-util'; import { ClassProtoDescriptor, GlobalGraph, ProtoDependencyMeta, ProtoNode, } from '@eggjs/tegg-metadata'; import assert from 'node:assert'; export function pointCutGraphHook(globalGraph: GlobalGraph) { for (const moduleNode of globalGraph.moduleGraph.nodes.values()) { for (const pointCuttedProtoNode of moduleNode.val.protos) { const pointCutAdviceProtoList = findPointCutAdvice(globalGraph, pointCuttedProtoNode); if (!pointCutAdviceProtoList) continue; for (const pointCutAdviceProto of pointCutAdviceProtoList) { globalGraph.addInject( moduleNode, pointCuttedProtoNode, pointCutAdviceProto, pointCutAdviceProto.val.proto.name); } } } } function findPointCutAdvice(globalGraph: GlobalGraph, protoNode: GraphNode) { const proto = protoNode.val.proto; if (!ClassProtoDescriptor.isClassProtoDescriptor(proto)) { return; } const result: Set> = new Set(); const allMethods = AspectMetaBuilder.getAllMethods(proto.clazz); for (const method of allMethods) { const adviceInfoList = PointcutAdviceInfoUtil.getPointcutAdviceInfoList(proto.clazz, method); for (const { clazz } of adviceInfoList) { const property = PrototypeUtil.getProperty(clazz); assert(property, 'not found property'); const injectProto = globalGraph.findDependencyProtoNode(protoNode.val.proto, { objName: property.name, refName: property.name, qualifiers: QualifierUtil.mergeQualifiers( property?.qualifiers ?? [], QualifierUtil.getProtoQualifiers(clazz), ), }); if (injectProto) { result.add(injectProto); } } } return Array.from(result); } ================================================ FILE: core/aop-runtime/test/aop-runtime.test.ts ================================================ import assert from 'node:assert'; import path from 'node:path'; import mm from 'mm'; import { EggObjectLifecycleUtil, LoadUnitInstanceFactory } from '@eggjs/tegg-runtime'; import { EggPrototypeLifecycleUtil, LoadUnitFactory, LoadUnitLifecycleUtil } from '@eggjs/tegg-metadata'; import type { LoadUnitInstance } from '@eggjs/tegg-types'; import { CrosscutAdviceFactory } from '@eggjs/aop-decorator'; import { CoreTestHelper, EggTestContext } from '../../test-util'; import { Hello } from './fixtures/modules/hello_succeed/Hello'; import { crosscutAdviceParams } from './fixtures/modules/hello_cross_cut/HelloCrossCut'; import { pointcutAdviceParams } from './fixtures/modules/hello_point_cut/HelloPointCut'; import { EggObjectAopHook } from '../src/EggObjectAopHook'; import { LoadUnitAopHook } from '../src/LoadUnitAopHook'; import { EggPrototypeCrossCutHook } from '../src/EggPrototypeCrossCutHook'; import { crossCutGraphHook } from '../src/CrossCutGraphHook'; import { pointCutGraphHook } from '../src/PointCutGraphHook'; import { CallTrace } from './fixtures/modules/hello_cross_cut/CallTrace'; import { HelloConstructorInject } from './fixtures/modules/constructor_inject_aop/Hello'; describe('test/aop-runtime.test.ts', () => { describe('succeed call', () => { let modules: Array; let crosscutAdviceFactory: CrosscutAdviceFactory; let eggObjectAopHook: EggObjectAopHook; let loadUnitAopHook: LoadUnitAopHook; let eggPrototypeCrossCutHook: EggPrototypeCrossCutHook; beforeEach(async () => { crosscutAdviceFactory = new CrosscutAdviceFactory(); eggObjectAopHook = new EggObjectAopHook(); loadUnitAopHook = new LoadUnitAopHook(crosscutAdviceFactory); eggPrototypeCrossCutHook = new EggPrototypeCrossCutHook(crosscutAdviceFactory); EggPrototypeLifecycleUtil.registerLifecycle(eggPrototypeCrossCutHook); LoadUnitLifecycleUtil.registerLifecycle(loadUnitAopHook); EggObjectLifecycleUtil.registerLifecycle(eggObjectAopHook); modules = await CoreTestHelper.prepareModules([ path.join(__dirname, '..'), path.join(__dirname, 'fixtures/modules/hello_succeed'), path.join(__dirname, 'fixtures/modules/hello_point_cut'), path.join(__dirname, 'fixtures/modules/hello_cross_cut'), ], [ crossCutGraphHook, pointCutGraphHook, ]); }); afterEach(async () => { for (const module of modules) { await LoadUnitFactory.destroyLoadUnit(module.loadUnit); await LoadUnitInstanceFactory.destroyLoadUnitInstance(module); } EggPrototypeLifecycleUtil.deleteLifecycle(eggPrototypeCrossCutHook); LoadUnitLifecycleUtil.deleteLifecycle(loadUnitAopHook); EggObjectLifecycleUtil.deleteLifecycle(eggObjectAopHook); }); it('should work', async () => { await EggTestContext.mockContext(async () => { const hello = await CoreTestHelper.getObject(Hello); const callTrace = await CoreTestHelper.getObject(CallTrace); const msg = await hello.hello('aop'); const traceMsg = callTrace.msgs; assert.deepStrictEqual(msg, `withCrossAroundResult(withPointAroundResult(hello withPointAroundParam(withCrosscutAroundParam(aop))${JSON.stringify(pointcutAdviceParams)})${JSON.stringify(crosscutAdviceParams)})`); assert.deepStrictEqual(traceMsg, [ { className: 'CrosscutAdvice', methodName: 'beforeCall', id: 233, name: 'aop', adviceParams: crosscutAdviceParams, }, { className: 'PointcutAdvice', methodName: 'beforeCall', id: 233, name: 'aop', adviceParams: pointcutAdviceParams, }, { className: 'CrosscutAdvice', methodName: 'afterReturn', id: 233, name: 'withPointAroundParam(withCrosscutAroundParam(aop))', result: `withCrossAroundResult(withPointAroundResult(hello withPointAroundParam(withCrosscutAroundParam(aop))${JSON.stringify(pointcutAdviceParams)})${JSON.stringify(crosscutAdviceParams)})`, adviceParams: crosscutAdviceParams, }, { className: 'PointcutAdvice', methodName: 'afterReturn', id: 233, name: 'withPointAroundParam(withCrosscutAroundParam(aop))', result: `withCrossAroundResult(withPointAroundResult(hello withPointAroundParam(withCrosscutAroundParam(aop))${JSON.stringify(pointcutAdviceParams)})${JSON.stringify(crosscutAdviceParams)})`, adviceParams: pointcutAdviceParams, }, { className: 'CrosscutAdvice', methodName: 'afterFinally', id: 233, name: 'withPointAroundParam(withCrosscutAroundParam(aop))', adviceParams: crosscutAdviceParams, }, { className: 'PointcutAdvice', methodName: 'afterFinally', id: 233, name: 'withPointAroundParam(withCrosscutAroundParam(aop))', adviceParams: pointcutAdviceParams, }, ]); await assert.rejects(async () => { await hello.helloWithException('foo'); }, new Error('ops, exception for withPointAroundParam(foo)')); assert.deepStrictEqual(callTrace.msgs[callTrace.msgs.length - 2], { className: 'PointcutAdvice', methodName: 'afterThrow', id: 233, name: 'withPointAroundParam(foo)', result: 'ops, exception for withPointAroundParam(foo)', adviceParams: pointcutAdviceParams, }); }); }); it('mock should work', async () => { await EggTestContext.mockContext(async () => { const hello = await CoreTestHelper.getObject(Hello); let helloMocked = false; mm(Hello.prototype, 'hello', async () => { helloMocked = true; }); await hello.hello('aop'); assert(helloMocked); }); }); }); describe('should failed', () => { let crosscutAdviceFactory: CrosscutAdviceFactory; let eggObjectAopHook: EggObjectAopHook; let loadUnitAopHook: LoadUnitAopHook; let eggPrototypeCrossCutHook: EggPrototypeCrossCutHook; beforeEach(async () => { crosscutAdviceFactory = new CrosscutAdviceFactory(); eggObjectAopHook = new EggObjectAopHook(); loadUnitAopHook = new LoadUnitAopHook(crosscutAdviceFactory); eggPrototypeCrossCutHook = new EggPrototypeCrossCutHook(crosscutAdviceFactory); EggPrototypeLifecycleUtil.registerLifecycle(eggPrototypeCrossCutHook); LoadUnitLifecycleUtil.registerLifecycle(loadUnitAopHook); EggObjectLifecycleUtil.registerLifecycle(eggObjectAopHook); }); it('should throw', async () => { await assert.rejects(async () => { await CoreTestHelper.prepareModules([ path.join(__dirname, '..'), path.join(__dirname, 'fixtures/modules/should_throw'), ]); }, /Aop Advice\(PointcutAdvice\) not found in loadUnits/); }); }); describe('aop constructor should work', () => { let modules: Array; let crosscutAdviceFactory: CrosscutAdviceFactory; let eggObjectAopHook: EggObjectAopHook; let loadUnitAopHook: LoadUnitAopHook; let eggPrototypeCrossCutHook: EggPrototypeCrossCutHook; beforeEach(async () => { crosscutAdviceFactory = new CrosscutAdviceFactory(); eggObjectAopHook = new EggObjectAopHook(); loadUnitAopHook = new LoadUnitAopHook(crosscutAdviceFactory); eggPrototypeCrossCutHook = new EggPrototypeCrossCutHook(crosscutAdviceFactory); EggPrototypeLifecycleUtil.registerLifecycle(eggPrototypeCrossCutHook); LoadUnitLifecycleUtil.registerLifecycle(loadUnitAopHook); EggObjectLifecycleUtil.registerLifecycle(eggObjectAopHook); modules = await CoreTestHelper.prepareModules([ path.join(__dirname, '..'), path.join(__dirname, 'fixtures/modules/constructor_inject_aop'), path.join(__dirname, 'fixtures/modules/hello_point_cut'), path.join(__dirname, 'fixtures/modules/hello_cross_cut'), ], [ crossCutGraphHook, pointCutGraphHook, ]); }); afterEach(async () => { for (const module of modules) { await LoadUnitFactory.destroyLoadUnit(module.loadUnit); await LoadUnitInstanceFactory.destroyLoadUnitInstance(module); } EggPrototypeLifecycleUtil.deleteLifecycle(eggPrototypeCrossCutHook); LoadUnitLifecycleUtil.deleteLifecycle(loadUnitAopHook); EggObjectLifecycleUtil.deleteLifecycle(eggObjectAopHook); }); it('should work', async () => { await EggTestContext.mockContext(async () => { const hello = await CoreTestHelper.getObject(HelloConstructorInject); const callTrace = await CoreTestHelper.getObject(CallTrace); const msg = await hello.hello('aop'); const traceMsg = callTrace.msgs; console.log('msg: ', msg, traceMsg); assert.deepStrictEqual(msg, `withPointAroundResult(hello withPointAroundParam(aop)${JSON.stringify(pointcutAdviceParams)})`); assert.deepStrictEqual(traceMsg, [ { className: 'PointcutAdvice', methodName: 'beforeCall', id: 233, name: 'aop', adviceParams: pointcutAdviceParams, }, { className: 'PointcutAdvice', methodName: 'afterReturn', id: 233, name: 'withPointAroundParam(aop)', result: `withPointAroundResult(hello withPointAroundParam(aop)${JSON.stringify(pointcutAdviceParams)})`, adviceParams: pointcutAdviceParams, }, { className: 'PointcutAdvice', methodName: 'afterFinally', id: 233, name: 'withPointAroundParam(aop)', adviceParams: pointcutAdviceParams, }, ]); await assert.rejects(async () => { await hello.helloWithException('foo'); }, new Error('ops, exception for withPointAroundParam(foo)')); assert.deepStrictEqual(callTrace.msgs[callTrace.msgs.length - 2], { className: 'PointcutAdvice', methodName: 'afterThrow', id: 233, name: 'withPointAroundParam(foo)', result: 'ops, exception for withPointAroundParam(foo)', adviceParams: pointcutAdviceParams, }); }); }); it('mock should work', async () => { await EggTestContext.mockContext(async () => { const hello = await CoreTestHelper.getObject(HelloConstructorInject); let helloMocked = false; mm(HelloConstructorInject.prototype, 'hello', async () => { helloMocked = true; }); await hello.hello('aop'); assert(helloMocked); }); }); }); describe('mutil aop', () => { it('should work', async () => { const modules = await CoreTestHelper.prepareModules([ path.join(__dirname, '..'), path.join(__dirname, 'fixtures/mutli/a'), path.join(__dirname, 'fixtures/mutli/b'), path.join(__dirname, 'fixtures/mutli/c'), path.join(__dirname, 'fixtures/mutli/cross'), ], [ crossCutGraphHook, pointCutGraphHook, ]); assert.deepStrictEqual(modules.map(module => module.id), [ 'LOAD_UNIT:teggAopRuntime:INSTANCE', 'LOAD_UNIT:helloPointCut:INSTANCE', 'LOAD_UNIT:aopModuleA:INSTANCE', 'LOAD_UNIT:aopModuleB:INSTANCE', 'LOAD_UNIT:aopModuleC:INSTANCE', ]); }); }); }); ================================================ FILE: core/aop-runtime/test/fixtures/modules/constructor_inject_aop/Hello.ts ================================================ import { ContextProto, Inject, SingletonProto } from '@eggjs/core-decorator'; import { Pointcut } from '@eggjs/aop-decorator'; import { PointcutAdvice, pointcutAdviceParams } from '../hello_point_cut/HelloPointCut'; @SingletonProto() export class Foo { } @ContextProto() export class HelloConstructorInject { id = 233; constructor(@Inject() readonly foo: Foo) { } @Pointcut(PointcutAdvice, { adviceParams: pointcutAdviceParams }) async hello(name: string) { return `hello ${name}`; } @Pointcut(PointcutAdvice, { adviceParams: pointcutAdviceParams }) async helloWithException(name: string) { throw new Error(`ops, exception for ${name}`); } } ================================================ FILE: core/aop-runtime/test/fixtures/modules/constructor_inject_aop/package.json ================================================ { "name": "aop-module", "eggModule": { "name": "aopModule" } } ================================================ FILE: core/aop-runtime/test/fixtures/modules/hello_cross_cut/CallTrace.ts ================================================ import { AccessLevel, SingletonProto } from "@eggjs/core-decorator"; export interface CallTraceMsg { className: string; methodName: string; id: number; name: string; result?: string; adviceParams?: any; } @SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) export class CallTrace { msgs: Array = []; addMsg(msg: CallTraceMsg) { this.msgs.push(msg); } } ================================================ FILE: core/aop-runtime/test/fixtures/modules/hello_cross_cut/HelloCrossCut.ts ================================================ import assert from 'node:assert'; import { AccessLevel, Inject } from '@eggjs/core-decorator'; import { Advice, Crosscut } from '@eggjs/aop-decorator'; import { AdviceContext, IAdvice, PointcutType } from '@eggjs/tegg-types'; import { Hello } from '../hello_succeed/Hello'; import { CallTrace } from './CallTrace'; export const crosscutAdviceParams = { cross: Math.random().toString(), cut: Math.random().toString(), }; @Crosscut({ type: PointcutType.CLASS, clazz: Hello, methodName: 'hello', }, { adviceParams: crosscutAdviceParams }) @Advice({ accessLevel: AccessLevel.PUBLIC, }) export class CrosscutAdvice implements IAdvice { @Inject() callTrace: CallTrace; async beforeCall(ctx: AdviceContext): Promise { assert.ok(ctx.adviceParams); assert.deepStrictEqual(ctx.adviceParams, crosscutAdviceParams); this.callTrace.addMsg({ className: CrosscutAdvice.name, methodName: 'beforeCall', id: ctx.that.id, name: ctx.args[0], adviceParams: ctx.adviceParams, }); } async afterReturn(ctx: AdviceContext, result: any): Promise { assert.ok(ctx.adviceParams); assert.deepStrictEqual(ctx.adviceParams, crosscutAdviceParams); this.callTrace.addMsg({ className: CrosscutAdvice.name, methodName: 'afterReturn', id: ctx.that.id, name: ctx.args[0], result, adviceParams: ctx.adviceParams, }); } async afterFinally(ctx: AdviceContext): Promise { assert.ok(ctx.adviceParams); assert.deepStrictEqual(ctx.adviceParams, crosscutAdviceParams); this.callTrace.addMsg({ className: CrosscutAdvice.name, methodName: 'afterFinally', id: ctx.that.id, name: ctx.args[0], adviceParams: ctx.adviceParams, }); } async around(ctx: AdviceContext, next: () => Promise): Promise { assert.ok(ctx.adviceParams); assert.deepStrictEqual(ctx.adviceParams, crosscutAdviceParams); ctx.args[0] = `withCrosscutAroundParam(${ctx.args[0]})`; const result = await next(); return `withCrossAroundResult(${result}${JSON.stringify(ctx.adviceParams)})`; } } ================================================ FILE: core/aop-runtime/test/fixtures/modules/hello_cross_cut/package.json ================================================ { "name": "hello-cross-cut", "eggModule": { "name": "helloCrossCut" } } ================================================ FILE: core/aop-runtime/test/fixtures/modules/hello_point_cut/HelloPointCut.ts ================================================ import assert from 'node:assert'; import { AccessLevel, Inject } from '@eggjs/core-decorator'; import { Advice } from '@eggjs/aop-decorator'; import { AdviceContext, IAdvice } from '@eggjs/tegg-types'; import { Hello } from '../hello_succeed/Hello'; import { CallTrace } from '../hello_cross_cut/CallTrace'; export const pointcutAdviceParams = { point: Math.random().toString(), cut: Math.random().toString(), }; // 测试aop修改ctx的args的值 const TEST_CTX_ARGS_VALUE = 123; @Advice({ accessLevel: AccessLevel.PUBLIC, }) export class PointcutAdvice implements IAdvice { @Inject() callTrace: CallTrace; async beforeCall(ctx: AdviceContext): Promise { assert.ok(ctx.adviceParams); assert.deepStrictEqual(ctx.adviceParams, pointcutAdviceParams); this.callTrace.addMsg({ className: PointcutAdvice.name, methodName: 'beforeCall', id: ctx.that.id, name: ctx.args[0], adviceParams: ctx.adviceParams, }); ctx.args = [...ctx.args, TEST_CTX_ARGS_VALUE]; } async afterReturn(ctx: AdviceContext, result: any): Promise { assert.ok(ctx.adviceParams); assert.deepStrictEqual(ctx.adviceParams, pointcutAdviceParams); assert.deepStrictEqual(ctx.args[ctx.args.length - 1], TEST_CTX_ARGS_VALUE); this.callTrace.addMsg({ className: PointcutAdvice.name, methodName: 'afterReturn', id: ctx.that.id, name: ctx.args[0], result, adviceParams: ctx.adviceParams, }); } async afterThrow(ctx: AdviceContext, error: Error): Promise { assert.ok(ctx.adviceParams); assert.deepStrictEqual(ctx.adviceParams, pointcutAdviceParams); this.callTrace.addMsg({ className: PointcutAdvice.name, methodName: 'afterThrow', id: ctx.that.id, name: ctx.args[0], result: error.message, adviceParams: ctx.adviceParams, }); } async afterFinally(ctx: AdviceContext): Promise { assert.ok(ctx.adviceParams); assert.deepStrictEqual(ctx.adviceParams, pointcutAdviceParams); this.callTrace.addMsg({ className: PointcutAdvice.name, methodName: 'afterFinally', id: ctx.that.id, name: ctx.args[0], adviceParams: ctx.adviceParams, }); } async around(ctx: AdviceContext, next: () => Promise): Promise { assert.ok(ctx.adviceParams); assert.deepStrictEqual(ctx.adviceParams, pointcutAdviceParams); ctx.args[0] = `withPointAroundParam(${ctx.args[0]})`; const result = await next(); return `withPointAroundResult(${result}${JSON.stringify(pointcutAdviceParams)})`; } } ================================================ FILE: core/aop-runtime/test/fixtures/modules/hello_point_cut/package.json ================================================ { "name": "hello-point-cut", "eggModule": { "name": "helloPointCut" } } ================================================ FILE: core/aop-runtime/test/fixtures/modules/hello_succeed/Hello.ts ================================================ import { ContextProto } from '@eggjs/core-decorator'; import { Pointcut } from '@eggjs/aop-decorator'; import { PointcutAdvice, pointcutAdviceParams } from '../hello_point_cut/HelloPointCut'; @ContextProto() export class Hello { id = 233; @Pointcut(PointcutAdvice, { adviceParams: pointcutAdviceParams }) async hello(name: string) { return `hello ${name}`; } @Pointcut(PointcutAdvice, { adviceParams: pointcutAdviceParams }) async helloWithException(name: string) { throw new Error(`ops, exception for ${name}`); } } ================================================ FILE: core/aop-runtime/test/fixtures/modules/hello_succeed/package.json ================================================ { "name": "aop-module", "eggModule": { "name": "aopModule" } } ================================================ FILE: core/aop-runtime/test/fixtures/modules/should_throw/Hello.ts ================================================ import { ContextProto } from '@eggjs/core-decorator'; import { Advice, Pointcut } from '@eggjs/aop-decorator'; import type { AdviceContext, IAdvice } from '@eggjs/tegg-types'; @Advice() class PointcutAdvice implements IAdvice { async beforeCall(ctx: AdviceContext): Promise { console.info(ctx); } } @ContextProto() export class Hello { id = 233; @Pointcut(PointcutAdvice) async hello(name: string) { return `hello ${name}`; } } ================================================ FILE: core/aop-runtime/test/fixtures/modules/should_throw/package.json ================================================ { "name": "throw-aop-module", "eggModule": { "name": "throwAopModule" } } ================================================ FILE: core/aop-runtime/test/fixtures/mutli/a/A.ts ================================================ import { ContextProto } from '@eggjs/core-decorator'; import { Base } from '../c/Base'; @ContextProto() export class A extends Base { id = 233; async hello(name: string) { return `hello A ${name}`; } } ================================================ FILE: core/aop-runtime/test/fixtures/mutli/a/package.json ================================================ { "name": "aop-module-a", "eggModule": { "name": "aopModuleA" } } ================================================ FILE: core/aop-runtime/test/fixtures/mutli/b/B.ts ================================================ import { ContextProto } from '@eggjs/core-decorator'; import { Base } from '../c/Base'; @ContextProto() export class B extends Base { id = 233; async hello(name: string) { return `hello B ${name}`; } } ================================================ FILE: core/aop-runtime/test/fixtures/mutli/b/package.json ================================================ { "name": "aop-module-b", "eggModule": { "name": "aopModuleB" } } ================================================ FILE: core/aop-runtime/test/fixtures/mutli/c/Base.ts ================================================ import { ContextProto } from '@eggjs/core-decorator'; @ContextProto() export class Base { id = 233; async hello(name: string) { return `hello base ${name}`; } } ================================================ FILE: core/aop-runtime/test/fixtures/mutli/c/package.json ================================================ { "name": "aop-module-c", "eggModule": { "name": "aopModuleC" } } ================================================ FILE: core/aop-runtime/test/fixtures/mutli/cross/Cross.ts ================================================ import { AccessLevel } from '@eggjs/core-decorator'; import { Advice, Crosscut } from '@eggjs/aop-decorator'; import { AdviceContext, IAdvice, PointcutType } from '@eggjs/tegg-types'; import { Base } from '../c/Base'; export const pointcutAdviceParams = { point: Math.random().toString(), cut: Math.random().toString(), }; @Crosscut({ type: PointcutType.CLASS, clazz: Base, methodName: 'hello', }) @Advice({ accessLevel: AccessLevel.PUBLIC, }) export class BaseAdvice implements IAdvice { async beforeCall(_ctx: AdviceContext): Promise { } async afterReturn(_ctx: AdviceContext, _result: any): Promise { } async afterThrow(_ctx: AdviceContext, _error: Error): Promise { } async afterFinally(_ctx: AdviceContext): Promise { } async around(_ctx: AdviceContext, _next: () => Promise): Promise { } } ================================================ FILE: core/aop-runtime/test/fixtures/mutli/cross/package.json ================================================ { "name": "hello-point-cut", "eggModule": { "name": "helloPointCut" } } ================================================ FILE: core/aop-runtime/tsconfig.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "dist", "baseUrl": "./" }, "exclude": [ "dist", "node_modules", "test" ] } ================================================ FILE: core/aop-runtime/tsconfig.pub.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "dist", "baseUrl": "./" }, "exclude": [ "dist", "node_modules", "test" ] } ================================================ FILE: core/background-task/CHANGELOG.md ================================================ # Change Log All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. ## [3.78.15](https://github.com/eggjs/tegg/compare/v3.78.14...v3.78.15) (2026-04-23) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.78.14](https://github.com/eggjs/tegg/compare/v3.78.13...v3.78.14) (2026-04-21) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.78.13](https://github.com/eggjs/tegg/compare/v3.78.12...v3.78.13) (2026-04-21) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.78.12](https://github.com/eggjs/tegg/compare/v3.78.11...v3.78.12) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.78.11](https://github.com/eggjs/tegg/compare/v3.78.10...v3.78.11) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.78.10](https://github.com/eggjs/tegg/compare/v3.78.9...v3.78.10) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.78.9](https://github.com/eggjs/tegg/compare/v3.78.8...v3.78.9) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.78.8](https://github.com/eggjs/tegg/compare/v3.78.7...v3.78.8) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.78.7](https://github.com/eggjs/tegg/compare/v3.78.6...v3.78.7) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.78.6](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.6) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.78.5](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.5) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.78.4](https://github.com/eggjs/tegg/compare/v3.78.3...v3.78.4) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.78.3](https://github.com/eggjs/tegg/compare/v3.78.2...v3.78.3) (2026-04-15) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.78.2](https://github.com/eggjs/tegg/compare/v3.78.1...v3.78.2) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.78.1](https://github.com/eggjs/tegg/compare/v3.78.0...v3.78.1) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-background-task # [3.78.0](https://github.com/eggjs/tegg/compare/v3.77.2...v3.78.0) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.77.2](https://github.com/eggjs/tegg/compare/v3.77.1...v3.77.2) (2026-04-07) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.77.1](https://github.com/eggjs/tegg/compare/v3.77.0...v3.77.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-background-task # [3.77.0](https://github.com/eggjs/tegg/compare/v3.76.1...v3.77.0) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.76.1](https://github.com/eggjs/tegg/compare/v3.76.0...v3.76.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-background-task # [3.76.0](https://github.com/eggjs/tegg/compare/v3.75.1...v3.76.0) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.75.1](https://github.com/eggjs/tegg/compare/v3.75.0...v3.75.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-background-task # [3.75.0](https://github.com/eggjs/tegg/compare/v3.74.0...v3.75.0) (2026-03-30) **Note:** Version bump only for package @eggjs/tegg-background-task # [3.74.0](https://github.com/eggjs/tegg/compare/v3.73.0...v3.74.0) (2026-03-30) **Note:** Version bump only for package @eggjs/tegg-background-task # [3.73.0](https://github.com/eggjs/tegg/compare/v3.72.0...v3.73.0) (2026-03-25) **Note:** Version bump only for package @eggjs/tegg-background-task # [3.72.0](https://github.com/eggjs/tegg/compare/v3.71.2...v3.72.0) (2026-02-04) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.71.2](https://github.com/eggjs/tegg/compare/v3.71.1...v3.71.2) (2026-01-30) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.71.1](https://github.com/eggjs/tegg/compare/v3.71.0...v3.71.1) (2026-01-28) **Note:** Version bump only for package @eggjs/tegg-background-task # [3.71.0](https://github.com/eggjs/tegg/compare/v3.70.1...v3.71.0) (2026-01-26) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.70.1](https://github.com/eggjs/tegg/compare/v3.70.0...v3.70.1) (2026-01-14) **Note:** Version bump only for package @eggjs/tegg-background-task # [3.70.0](https://github.com/eggjs/tegg/compare/v3.69.0...v3.70.0) (2026-01-07) **Note:** Version bump only for package @eggjs/tegg-background-task # [3.69.0](https://github.com/eggjs/tegg/compare/v3.68.0...v3.69.0) (2026-01-06) **Note:** Version bump only for package @eggjs/tegg-background-task # [3.68.0](https://github.com/eggjs/tegg/compare/v3.67.2...v3.68.0) (2025-12-29) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.67.2](https://github.com/eggjs/tegg/compare/v3.67.1...v3.67.2) (2025-12-24) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.67.1](https://github.com/eggjs/tegg/compare/v3.67.0...v3.67.1) (2025-12-23) **Note:** Version bump only for package @eggjs/tegg-background-task # [3.67.0](https://github.com/eggjs/tegg/compare/v3.66.0...v3.67.0) (2025-12-18) **Note:** Version bump only for package @eggjs/tegg-background-task # [3.66.0](https://github.com/eggjs/tegg/compare/v3.65.3...v3.66.0) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.65.3](https://github.com/eggjs/tegg/compare/v3.65.2...v3.65.3) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.65.2](https://github.com/eggjs/tegg/compare/v3.65.1...v3.65.2) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.65.1](https://github.com/eggjs/tegg/compare/v3.65.0...v3.65.1) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-background-task # [3.65.0](https://github.com/eggjs/tegg/compare/v3.64.5...v3.65.0) (2025-12-10) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.64.5](https://github.com/eggjs/tegg/compare/v3.64.4...v3.64.5) (2025-12-08) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.64.4](https://github.com/eggjs/tegg/compare/v3.64.3...v3.64.4) (2025-12-06) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.64.3](https://github.com/eggjs/tegg/compare/v3.64.2...v3.64.3) (2025-12-06) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.64.2](https://github.com/eggjs/tegg/compare/v3.64.1...v3.64.2) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.64.1](https://github.com/eggjs/tegg/compare/v3.64.0...v3.64.1) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-background-task # [3.64.0](https://github.com/eggjs/tegg/compare/v3.63.2...v3.64.0) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.63.2](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.2) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.63.1](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.1) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg-background-task # [3.63.0](https://github.com/eggjs/tegg/compare/v3.62.3...v3.63.0) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.62.3](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.3) (2025-11-05) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.62.2](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.2) (2025-11-05) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.62.1](https://github.com/eggjs/tegg/compare/v3.62.0...v3.62.1) (2025-11-03) **Note:** Version bump only for package @eggjs/tegg-background-task # [3.62.0](https://github.com/eggjs/tegg/compare/v3.61.0...v3.62.0) (2025-09-24) **Note:** Version bump only for package @eggjs/tegg-background-task # [3.61.0](https://github.com/eggjs/tegg/compare/v3.60.3...v3.61.0) (2025-08-15) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.60.3](https://github.com/eggjs/tegg/compare/v3.60.2...v3.60.3) (2025-08-06) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.60.2](https://github.com/eggjs/tegg/compare/v3.60.1...v3.60.2) (2025-08-06) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.60.1](https://github.com/eggjs/tegg/compare/v3.60.0...v3.60.1) (2025-07-28) **Note:** Version bump only for package @eggjs/tegg-background-task # [3.60.0](https://github.com/eggjs/tegg/compare/v3.59.1...v3.60.0) (2025-07-28) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.59.1](https://github.com/eggjs/tegg/compare/v3.59.0...v3.59.1) (2025-07-16) **Note:** Version bump only for package @eggjs/tegg-background-task # [3.59.0](https://github.com/eggjs/tegg/compare/v3.58.0...v3.59.0) (2025-07-07) **Note:** Version bump only for package @eggjs/tegg-background-task # [3.58.0](https://github.com/eggjs/tegg/compare/v3.57.14...v3.58.0) (2025-07-01) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.57.14](https://github.com/eggjs/tegg/compare/v3.57.13...v3.57.14) (2025-06-18) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.57.13](https://github.com/eggjs/tegg/compare/v3.57.12...v3.57.13) (2025-06-18) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.57.12](https://github.com/eggjs/tegg/compare/v3.57.11...v3.57.12) (2025-06-16) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.57.11](https://github.com/eggjs/tegg/compare/v3.57.10...v3.57.11) (2025-06-16) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.57.10](https://github.com/eggjs/tegg/compare/v3.57.9...v3.57.10) (2025-06-12) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.57.9](https://github.com/eggjs/tegg/compare/v3.57.8...v3.57.9) (2025-05-29) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.57.8](https://github.com/eggjs/tegg/compare/v3.57.7...v3.57.8) (2025-05-29) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.57.7](https://github.com/eggjs/tegg/compare/v3.57.6...v3.57.7) (2025-05-28) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.57.6](https://github.com/eggjs/tegg/compare/v3.57.5...v3.57.6) (2025-05-27) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.57.5](https://github.com/eggjs/tegg/compare/v3.57.4...v3.57.5) (2025-05-15) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.57.4](https://github.com/eggjs/tegg/compare/v3.57.3...v3.57.4) (2025-05-14) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.57.3](https://github.com/eggjs/tegg/compare/v3.57.2...v3.57.3) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.57.2](https://github.com/eggjs/tegg/compare/v3.57.1...v3.57.2) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.57.1](https://github.com/eggjs/tegg/compare/v3.57.0...v3.57.1) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-background-task # [3.57.0](https://github.com/eggjs/tegg/compare/v3.56.3...v3.57.0) (2025-05-09) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.56.3](https://github.com/eggjs/tegg/compare/v3.56.2...v3.56.3) (2025-05-07) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.56.2](https://github.com/eggjs/tegg/compare/v3.56.1...v3.56.2) (2025-05-01) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.56.1](https://github.com/eggjs/tegg/compare/v3.56.0...v3.56.1) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-background-task # [3.56.0](https://github.com/eggjs/tegg/compare/v3.55.0...v3.56.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-background-task # [3.55.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.55.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-background-task # [3.54.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.54.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-background-task # [3.53.0](https://github.com/eggjs/tegg/compare/v3.52.1...v3.53.0) (2025-04-21) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.52.1](https://github.com/eggjs/tegg/compare/v3.52.0...v3.52.1) (2025-03-14) **Note:** Version bump only for package @eggjs/tegg-background-task # [3.52.0](https://github.com/eggjs/tegg/compare/v3.51.2...v3.52.0) (2024-12-30) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.51.2](https://github.com/eggjs/tegg/compare/v3.51.1...v3.51.2) (2024-12-09) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.51.1](https://github.com/eggjs/tegg/compare/v3.51.0...v3.51.1) (2024-11-06) **Note:** Version bump only for package @eggjs/tegg-background-task # [3.51.0](https://github.com/eggjs/tegg/compare/v3.50.1...v3.51.0) (2024-10-30) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.50.1](https://github.com/eggjs/tegg/compare/v3.50.0...v3.50.1) (2024-10-23) **Note:** Version bump only for package @eggjs/tegg-background-task # [3.50.0](https://github.com/eggjs/tegg/compare/v3.49.0...v3.50.0) (2024-10-22) **Note:** Version bump only for package @eggjs/tegg-background-task # [3.49.0](https://github.com/eggjs/tegg/compare/v3.48.1...v3.49.0) (2024-10-21) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.48.1](https://github.com/eggjs/tegg/compare/v3.48.0...v3.48.1) (2024-10-14) **Note:** Version bump only for package @eggjs/tegg-background-task # [3.48.0](https://github.com/eggjs/tegg/compare/v3.47.2...v3.48.0) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.47.2](https://github.com/eggjs/tegg/compare/v3.47.1...v3.47.2) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.47.1](https://github.com/eggjs/tegg/compare/v3.47.0...v3.47.1) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-background-task # [3.47.0](https://github.com/eggjs/tegg/compare/v3.46.4...v3.47.0) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.46.4](https://github.com/eggjs/tegg/compare/v3.46.3...v3.46.4) (2024-10-09) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.46.3](https://github.com/eggjs/tegg/compare/v3.46.2...v3.46.3) (2024-10-08) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.46.2](https://github.com/eggjs/tegg/compare/v3.46.1...v3.46.2) (2024-10-07) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.46.1](https://github.com/eggjs/tegg/compare/v3.46.0...v3.46.1) (2024-09-30) **Note:** Version bump only for package @eggjs/tegg-background-task # [3.46.0](https://github.com/eggjs/tegg/compare/v3.45.0...v3.46.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-background-task # [3.45.0](https://github.com/eggjs/tegg/compare/v3.44.1...v3.45.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.44.1](https://github.com/eggjs/tegg/compare/v3.44.0...v3.44.1) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-background-task # [3.44.0](https://github.com/eggjs/tegg/compare/v3.43.2...v3.44.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.43.2](https://github.com/eggjs/tegg/compare/v3.43.1...v3.43.2) (2024-09-14) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.43.1](https://github.com/eggjs/tegg/compare/v3.43.0...v3.43.1) (2024-09-14) **Note:** Version bump only for package @eggjs/tegg-background-task # [3.43.0](https://github.com/eggjs/tegg/compare/v3.42.0...v3.43.0) (2024-09-13) **Note:** Version bump only for package @eggjs/tegg-background-task # [3.42.0](https://github.com/eggjs/tegg/compare/v3.41.0...v3.42.0) (2024-09-10) **Note:** Version bump only for package @eggjs/tegg-background-task # [3.41.0](https://github.com/eggjs/tegg/compare/v3.40.1...v3.41.0) (2024-08-26) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.40.1](https://github.com/eggjs/tegg/compare/v3.40.0...v3.40.1) (2024-08-23) **Note:** Version bump only for package @eggjs/tegg-background-task # [3.40.0](https://github.com/eggjs/tegg/compare/v3.39.5...v3.40.0) (2024-08-22) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.39.5](https://github.com/eggjs/tegg/compare/v3.39.4...v3.39.5) (2024-08-09) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.39.4](https://github.com/eggjs/tegg/compare/v3.39.3...v3.39.4) (2024-07-09) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.39.3](https://github.com/eggjs/tegg/compare/v3.39.2...v3.39.3) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.39.2](https://github.com/eggjs/tegg/compare/v3.39.1...v3.39.2) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.39.1](https://github.com/eggjs/tegg/compare/v3.39.0...v3.39.1) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-background-task # [3.39.0](https://github.com/eggjs/tegg/compare/v3.38.0...v3.39.0) (2024-04-19) **Note:** Version bump only for package @eggjs/tegg-background-task # [3.38.0](https://github.com/eggjs/tegg/compare/v3.37.3...v3.38.0) (2024-04-18) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.37.3](https://github.com/eggjs/tegg/compare/v3.37.2...v3.37.3) (2024-04-17) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.37.2](https://github.com/eggjs/tegg/compare/v3.37.1...v3.37.2) (2024-04-17) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.37.1](https://github.com/eggjs/tegg/compare/v3.37.0...v3.37.1) (2024-04-16) **Note:** Version bump only for package @eggjs/tegg-background-task # [3.37.0](https://github.com/eggjs/tegg/compare/v3.36.3...v3.37.0) (2024-04-16) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.36.3](https://github.com/eggjs/tegg/compare/v3.36.2...v3.36.3) (2024-04-10) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.36.2](https://github.com/eggjs/tegg/compare/v3.36.1...v3.36.2) (2024-04-08) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.36.1](https://github.com/eggjs/tegg/compare/v3.36.0...v3.36.1) (2024-04-07) **Note:** Version bump only for package @eggjs/tegg-background-task # [3.36.0](https://github.com/eggjs/tegg/compare/v3.35.1...v3.36.0) (2024-04-02) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.35.1](https://github.com/eggjs/tegg/compare/v3.35.0...v3.35.1) (2024-03-26) **Note:** Version bump only for package @eggjs/tegg-background-task # [3.35.0](https://github.com/eggjs/tegg/compare/v3.34.0...v3.35.0) (2024-03-26) **Note:** Version bump only for package @eggjs/tegg-background-task # [3.34.0](https://github.com/eggjs/tegg/compare/v3.33.1...v3.34.0) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.33.1](https://github.com/eggjs/tegg/compare/v3.33.0...v3.33.1) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-background-task # [3.33.0](https://github.com/eggjs/tegg/compare/v3.32.0...v3.33.0) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-background-task # [3.32.0](https://github.com/eggjs/tegg/compare/v3.31.0...v3.32.0) (2024-02-19) **Note:** Version bump only for package @eggjs/tegg-background-task # [3.31.0](https://github.com/eggjs/tegg/compare/v3.30.1...v3.31.0) (2024-01-31) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.30.1](https://github.com/eggjs/tegg/compare/v3.30.0...v3.30.1) (2024-01-25) **Note:** Version bump only for package @eggjs/tegg-background-task # [3.30.0](https://github.com/eggjs/tegg/compare/v3.29.0...v3.30.0) (2024-01-17) **Note:** Version bump only for package @eggjs/tegg-background-task # [3.29.0](https://github.com/eggjs/tegg/compare/v3.28.2...v3.29.0) (2023-12-26) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.28.2](https://github.com/eggjs/tegg/compare/v3.28.1...v3.28.2) (2023-12-12) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.28.1](https://github.com/eggjs/tegg/compare/v3.28.0...v3.28.1) (2023-12-11) **Note:** Version bump only for package @eggjs/tegg-background-task # [3.28.0](https://github.com/eggjs/tegg/compare/v3.27.0...v3.28.0) (2023-12-10) **Note:** Version bump only for package @eggjs/tegg-background-task # [3.27.0](https://github.com/eggjs/tegg/compare/v3.26.0...v3.27.0) (2023-11-23) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.25.2](https://github.com/eggjs/tegg/compare/v3.25.1...v3.25.2) (2023-11-06) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.25.1](https://github.com/eggjs/tegg/compare/v3.25.0...v3.25.1) (2023-11-03) **Note:** Version bump only for package @eggjs/tegg-background-task # [3.25.0](https://github.com/eggjs/tegg/compare/v3.24.0...v3.25.0) (2023-11-03) **Note:** Version bump only for package @eggjs/tegg-background-task # [3.24.0](https://github.com/eggjs/tegg/compare/v3.23.0...v3.24.0) (2023-10-26) **Note:** Version bump only for package @eggjs/tegg-background-task # [3.23.0](https://github.com/eggjs/tegg/compare/v3.22.0...v3.23.0) (2023-09-20) **Note:** Version bump only for package @eggjs/tegg-background-task # [3.20.0](https://github.com/eggjs/tegg/compare/v3.19.0...v3.20.0) (2023-09-07) **Note:** Version bump only for package @eggjs/tegg-background-task # [3.19.0](https://github.com/eggjs/tegg/compare/v3.18.1...v3.19.0) (2023-08-31) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.18.1](https://github.com/eggjs/tegg/compare/v3.18.0...v3.18.1) (2023-08-29) **Note:** Version bump only for package @eggjs/tegg-background-task # [3.18.0](https://github.com/eggjs/tegg/compare/v3.17.0...v3.18.0) (2023-08-29) **Note:** Version bump only for package @eggjs/tegg-background-task # [3.17.0](https://github.com/eggjs/tegg/compare/v3.16.0...v3.17.0) (2023-08-24) **Note:** Version bump only for package @eggjs/tegg-background-task # [3.16.0](https://github.com/eggjs/tegg/compare/v3.15.0...v3.16.0) (2023-08-24) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.14.3](https://github.com/eggjs/tegg/compare/v3.14.2...v3.14.3) (2023-08-14) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.14.2](https://github.com/eggjs/tegg/compare/v3.14.1...v3.14.2) (2023-08-14) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.14.1](https://github.com/eggjs/tegg/compare/v3.14.0...v3.14.1) (2023-08-11) **Note:** Version bump only for package @eggjs/tegg-background-task # [3.14.0](https://github.com/eggjs/tegg/compare/v3.13.0...v3.14.0) (2023-07-31) **Note:** Version bump only for package @eggjs/tegg-background-task # [3.13.0](https://github.com/eggjs/tegg/compare/v3.12.0...v3.13.0) (2023-07-25) **Note:** Version bump only for package @eggjs/tegg-background-task # [3.12.0](https://github.com/eggjs/tegg/compare/v3.11.1...v3.12.0) (2023-07-13) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.11.1](https://github.com/eggjs/tegg/compare/v3.11.0...v3.11.1) (2023-06-29) **Note:** Version bump only for package @eggjs/tegg-background-task # [3.11.0](https://github.com/eggjs/tegg/compare/v3.10.0...v3.11.0) (2023-06-29) **Note:** Version bump only for package @eggjs/tegg-background-task # [3.10.0](https://github.com/eggjs/tegg/compare/v3.9.0...v3.10.0) (2023-06-28) **Note:** Version bump only for package @eggjs/tegg-background-task # [3.8.0](https://github.com/eggjs/tegg/compare/v3.7.0...v3.8.0) (2023-05-30) ### Features * impl EggObjectLifecycle hook in decorator ([#119](https://github.com/eggjs/tegg/issues/119)) ([cced8a2](https://github.com/eggjs/tegg/commit/cced8a2e009c33d5040fa21d00409fddef471b0e)) # [3.7.0](https://github.com/eggjs/tegg/compare/v3.6.3...v3.7.0) (2023-04-03) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.6.3](https://github.com/eggjs/tegg/compare/v3.6.2...v3.6.3) (2023-03-02) **Note:** Version bump only for package @eggjs/tegg-background-task # [3.6.0](https://github.com/eggjs/tegg/compare/v3.5.2...v3.6.0) (2023-02-13) ### Features * add backgroundTask.timeout config ([#101](https://github.com/eggjs/tegg/issues/101)) ([0b1eee0](https://github.com/eggjs/tegg/commit/0b1eee00d6feb9c6d4509023dffe85c0ada749c2)) # [3.5.0](https://github.com/eggjs/tegg/compare/v3.4.1...v3.5.0) (2023-02-10) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.4.1](https://github.com/eggjs/tegg/compare/v3.4.0...v3.4.1) (2023-02-02) ### Bug Fixes * BackgroundTaskHelper should support recursively call ([#90](https://github.com/eggjs/tegg/issues/90)) ([368ac03](https://github.com/eggjs/tegg/commit/368ac0343d0d4e96b3768e7fd169b721551d0e4b)) ## [3.3.1](https://github.com/eggjs/tegg/compare/v3.3.0...v3.3.1) (2023-01-28) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.2.3](https://github.com/eggjs/tegg/compare/v3.2.2...v3.2.3) (2023-01-16) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.2.2](https://github.com/eggjs/tegg/compare/v3.2.1...v3.2.2) (2023-01-06) **Note:** Version bump only for package @eggjs/tegg-background-task ## [3.2.1](https://github.com/eggjs/tegg/compare/v3.2.0...v3.2.1) (2022-12-28) **Note:** Version bump only for package @eggjs/tegg-background-task # [3.1.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.1.0) (2022-12-27) ### Bug Fixes * inject context proto to singleton proto ([#72](https://github.com/eggjs/tegg/issues/72)) ([fcc0b2b](https://github.com/eggjs/tegg/commit/fcc0b2b48fc9bce580c1f2bcfcc38039ae909951)) ### Features * **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) # [3.0.0](https://github.com/eggjs/tegg/compare/v3.0.0-alpha.0...v3.0.0) (2022-12-26) **Note:** Version bump only for package @eggjs/tegg-background-task # [3.0.0-alpha.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.0.0-alpha.0) (2022-12-22) ### Features * **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) # [1.3.0](https://github.com/eggjs/tegg/compare/v1.2.0...v1.3.0) (2022-07-01) **Note:** Version bump only for package @eggjs/tegg-background-task # [0.2.0](https://github.com/eggjs/tegg/compare/v0.1.19...v0.2.0) (2022-01-20) **Note:** Version bump only for package @eggjs/tegg-background-task ================================================ FILE: core/background-task/README.md ================================================ # `@eggjs/tegg-background-task` ## install ```sh npm i --save @eggjs/tegg-background-task ``` ## Usage ```ts import { BackgroundTaskHelper } from '@eggjs/tegg-background-task'; @ContextProto() export default class BackgroundService { @Inject() private readonly backgroundTaskHelper:BackgroundTaskHelper async backgroundAdd() { this.backgroundTaskHelper.run(async () => { // do the background task }); } } ``` ## Background tegg release the request context after request is done. So use the `process.nextTick`, `setTimeout`, `setInterval` in request is not safe. Please use the `backgroundTaskHelper`, the release process will wait all the background tasks are done. ## Timeout The release process will wait tasks done, but not forever. The default timeout is 5s, if task will cost more than 5s, two ways to resolve - use the `SingletonProto` to do the work, `SingletonProto` never release - set longer timeout to `backgroundTaskHelper.timeout` ================================================ FILE: core/background-task/index.ts ================================================ export * from './src/BackgroundTaskHelper'; ================================================ FILE: core/background-task/package.json ================================================ { "name": "@eggjs/tegg-background-task", "description": "background util for tegg", "version": "3.78.15", "keywords": [ "egg", "typescript", "background", "async", "tegg" ], "main": "dist/index.js", "files": [ "dist/**/*.js", "dist/**/*.d.ts" ], "typings": "dist/index.d.ts", "scripts": { "test": "cross-env NODE_ENV=test NODE_OPTIONS='--no-deprecation' mocha", "clean": "tsc -b --clean", "tsc": "ut run clean && tsc -p ./tsconfig.json", "tsc:pub": "ut run clean && tsc -p ./tsconfig.pub.json", "prepublishOnly": "ut tsc:pub" }, "homepage": "https://github.com/eggjs/tegg", "bugs": { "url": "https://github.com/eggjs/tegg/issues" }, "repository": { "type": "git", "url": "git@github.com:eggjs/tegg.git", "directory": "core/background-task" }, "engines": { "node": ">=14.0.0" }, "author": "killagu ", "license": "MIT", "dependencies": { "@eggjs/core-decorator": "^3.78.15", "@eggjs/tegg-lifecycle": "^3.78.15", "@eggjs/tegg-runtime": "^3.78.15", "@eggjs/tegg-types": "^3.78.15" }, "devDependencies": { "@eggjs/tegg-common-util": "^3.78.15", "@types/mocha": "^10.0.1", "@types/node": "^20.2.4", "cross-env": "^7.0.3", "egg": "^3.9.1", "mocha": "^10.2.0", "ts-node": "^10.9.1", "typescript": "^5.0.4" }, "publishConfig": { "access": "public" }, "gitHead": "240a3e9f40fda65ebb7589727d197db5ce17916c" } ================================================ FILE: core/background-task/src/BackgroundTaskHelper.ts ================================================ import assert from 'node:assert'; import type { EggLogger, EggAppConfig } from 'egg'; import { ContextProto, Inject } from '@eggjs/core-decorator'; import { AccessLevel } from '@eggjs/tegg-types'; import type { EggObjectLifecycle } from '@eggjs/tegg-types'; import { ContextHandler, EggContextLifecycleUtil } from '@eggjs/tegg-runtime'; @ContextProto({ accessLevel: AccessLevel.PUBLIC, }) export class BackgroundTaskHelper implements EggObjectLifecycle { @Inject() logger: EggLogger; // default timeout for async task timeout = 5000; @Inject() config: EggAppConfig; private backgroundTasks: Array> = []; async init() { const ctx = ContextHandler.getContext(); assert(ctx, 'background task helper must be init in context'); EggContextLifecycleUtil.registerObjectLifecycle(ctx, { preDestroy: async () => { await this.doPreDestroy(); }, }); if (this.config.backgroundTask?.timeout) { this.timeout = this.config.backgroundTask.timeout; } } run(fn: () => Promise) { const backgroundTask = new Promise(resolve => { try { fn() // fn is resolve, resolve the task .then(resolve) .catch(e => { e.message = '[BackgroundTaskHelper] background throw error:' + e.message; this.logger.error(e); // fn is rejected, resolve the task resolve(); }); } catch (e) { e.message = '[BackgroundTaskHelper] create background throw error:' + e.message; this.logger.error(e); // create task failed, resolve the task resolve(); } }); this.backgroundTasks.push(backgroundTask); } async doPreDestroy(): Promise { // quick quit if (!this.backgroundTasks.length) return; const backgroundTasks = this.backgroundTasks.slice(); if (this.timeout <= 0 || this.timeout === Infinity) { await Promise.all(backgroundTasks); } else { const { promise: timeout, resolve } = this.sleep(); await Promise.race([ // not block the pre destroy process too long timeout, // ensure all background task are done before destroy the context Promise.all(backgroundTasks), ]); // always resolve the sleep promise resolve(); } if (this.backgroundTasks.length !== backgroundTasks.length) { this.backgroundTasks = this.backgroundTasks.slice(backgroundTasks.length); return this.doPreDestroy(); } } private sleep() { let timer; let promiseResolve; const now = Date.now(); const p = new Promise(r => { promiseResolve = r; timer = setTimeout(() => { this.logger.error(`[BackgroundTaskHelper] task is timeout actual is ${Date.now() - now} expect is ${this.timeout}`); r(); }, this.timeout); }); function resolve() { // clear timeout and resolve the promise clearTimeout(timer); promiseResolve(); } return { promise: p, resolve, }; } } ================================================ FILE: core/background-task/test/BackgroundTaskHelper.test.ts ================================================ import assert from 'node:assert'; import { TimerUtil } from '@eggjs/tegg-common-util'; import { BackgroundTaskHelper } from '../src/BackgroundTaskHelper'; describe('test/BackgroundTaskHelper.test.ts', () => { let helper: BackgroundTaskHelper; beforeEach(() => { helper = new BackgroundTaskHelper(); helper.logger = console as any; }); describe('fn is ok', () => { it('should done', async () => { let run = false; helper.run(async () => { await TimerUtil.sleep(10); run = true; }); await helper.doPreDestroy(); assert(run); }); }); describe('fn is timeout', () => { it('should done', async () => { let run = false; helper.timeout = 10; helper.run(async () => { await TimerUtil.sleep(100); run = true; }); await helper.doPreDestroy(); assert(run === false); }); }); describe('fn reject', () => { it('should done', async () => { helper.run(async () => { await TimerUtil.sleep(10); throw new Error('mock error'); }); await helper.doPreDestroy(); }); }); describe('fn throw error', () => { it('should done', async () => { helper.run(() => { throw new Error('mock error'); }); await helper.doPreDestroy(); }); }); describe('recursive fn', () => { it('should done', async () => { let runDone = 0; helper.run(async () => { await TimerUtil.sleep(10); runDone++; helper.run(async () => { await TimerUtil.sleep(10); runDone++; }); }); await helper.doPreDestroy(); assert(runDone === 2); }); }); }); ================================================ FILE: core/background-task/tsconfig.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "dist", "baseUrl": "./" }, "exclude": [ "dist", "node_modules", "test" ] } ================================================ FILE: core/background-task/tsconfig.pub.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "dist", "baseUrl": "./" }, "exclude": [ "dist", "node_modules", "test" ] } ================================================ FILE: core/common-util/CHANGELOG.md ================================================ # Change Log All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. ## [3.78.15](https://github.com/eggjs/tegg/compare/v3.78.14...v3.78.15) (2026-04-23) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.78.14](https://github.com/eggjs/tegg/compare/v3.78.13...v3.78.14) (2026-04-21) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.78.13](https://github.com/eggjs/tegg/compare/v3.78.12...v3.78.13) (2026-04-21) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.78.12](https://github.com/eggjs/tegg/compare/v3.78.11...v3.78.12) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.78.11](https://github.com/eggjs/tegg/compare/v3.78.10...v3.78.11) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.78.10](https://github.com/eggjs/tegg/compare/v3.78.9...v3.78.10) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.78.9](https://github.com/eggjs/tegg/compare/v3.78.8...v3.78.9) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.78.8](https://github.com/eggjs/tegg/compare/v3.78.7...v3.78.8) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.78.7](https://github.com/eggjs/tegg/compare/v3.78.6...v3.78.7) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.78.6](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.6) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.78.5](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.5) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.78.4](https://github.com/eggjs/tegg/compare/v3.78.3...v3.78.4) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.78.3](https://github.com/eggjs/tegg/compare/v3.78.2...v3.78.3) (2026-04-15) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.78.2](https://github.com/eggjs/tegg/compare/v3.78.1...v3.78.2) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.78.1](https://github.com/eggjs/tegg/compare/v3.78.0...v3.78.1) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-common-util # [3.78.0](https://github.com/eggjs/tegg/compare/v3.77.2...v3.78.0) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.77.2](https://github.com/eggjs/tegg/compare/v3.77.1...v3.77.2) (2026-04-07) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.77.1](https://github.com/eggjs/tegg/compare/v3.77.0...v3.77.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-common-util # [3.77.0](https://github.com/eggjs/tegg/compare/v3.76.1...v3.77.0) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.76.1](https://github.com/eggjs/tegg/compare/v3.76.0...v3.76.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-common-util # [3.76.0](https://github.com/eggjs/tegg/compare/v3.75.1...v3.76.0) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.75.1](https://github.com/eggjs/tegg/compare/v3.75.0...v3.75.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-common-util # [3.75.0](https://github.com/eggjs/tegg/compare/v3.74.0...v3.75.0) (2026-03-30) **Note:** Version bump only for package @eggjs/tegg-common-util # [3.74.0](https://github.com/eggjs/tegg/compare/v3.73.0...v3.74.0) (2026-03-30) **Note:** Version bump only for package @eggjs/tegg-common-util # [3.73.0](https://github.com/eggjs/tegg/compare/v3.72.0...v3.73.0) (2026-03-25) **Note:** Version bump only for package @eggjs/tegg-common-util # [3.72.0](https://github.com/eggjs/tegg/compare/v3.71.2...v3.72.0) (2026-02-04) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.71.2](https://github.com/eggjs/tegg/compare/v3.71.1...v3.71.2) (2026-01-30) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.71.1](https://github.com/eggjs/tegg/compare/v3.71.0...v3.71.1) (2026-01-28) **Note:** Version bump only for package @eggjs/tegg-common-util # [3.71.0](https://github.com/eggjs/tegg/compare/v3.70.1...v3.71.0) (2026-01-26) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.70.1](https://github.com/eggjs/tegg/compare/v3.70.0...v3.70.1) (2026-01-14) **Note:** Version bump only for package @eggjs/tegg-common-util # [3.70.0](https://github.com/eggjs/tegg/compare/v3.69.0...v3.70.0) (2026-01-07) **Note:** Version bump only for package @eggjs/tegg-common-util # [3.69.0](https://github.com/eggjs/tegg/compare/v3.68.0...v3.69.0) (2026-01-06) **Note:** Version bump only for package @eggjs/tegg-common-util # [3.68.0](https://github.com/eggjs/tegg/compare/v3.67.2...v3.68.0) (2025-12-29) ### Features * impl iterator of moduleConfigs ([#386](https://github.com/eggjs/tegg/issues/386)) ([ee1f1a2](https://github.com/eggjs/tegg/commit/ee1f1a27a5a7de09bc6ee2c35376211541be409b)) ## [3.67.2](https://github.com/eggjs/tegg/compare/v3.67.1...v3.67.2) (2025-12-24) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.67.1](https://github.com/eggjs/tegg/compare/v3.67.0...v3.67.1) (2025-12-23) **Note:** Version bump only for package @eggjs/tegg-common-util # [3.67.0](https://github.com/eggjs/tegg/compare/v3.66.0...v3.67.0) (2025-12-18) **Note:** Version bump only for package @eggjs/tegg-common-util # [3.66.0](https://github.com/eggjs/tegg/compare/v3.65.3...v3.66.0) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.65.3](https://github.com/eggjs/tegg/compare/v3.65.2...v3.65.3) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.65.2](https://github.com/eggjs/tegg/compare/v3.65.1...v3.65.2) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.65.1](https://github.com/eggjs/tegg/compare/v3.65.0...v3.65.1) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-common-util # [3.65.0](https://github.com/eggjs/tegg/compare/v3.64.5...v3.65.0) (2025-12-10) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.64.5](https://github.com/eggjs/tegg/compare/v3.64.4...v3.64.5) (2025-12-08) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.64.4](https://github.com/eggjs/tegg/compare/v3.64.3...v3.64.4) (2025-12-06) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.64.3](https://github.com/eggjs/tegg/compare/v3.64.2...v3.64.3) (2025-12-06) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.64.2](https://github.com/eggjs/tegg/compare/v3.64.1...v3.64.2) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.64.1](https://github.com/eggjs/tegg/compare/v3.64.0...v3.64.1) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-common-util # [3.64.0](https://github.com/eggjs/tegg/compare/v3.63.2...v3.64.0) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.63.2](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.2) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.63.1](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.1) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg-common-util # [3.63.0](https://github.com/eggjs/tegg/compare/v3.62.3...v3.63.0) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.62.3](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.3) (2025-11-05) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.62.2](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.2) (2025-11-05) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.62.1](https://github.com/eggjs/tegg/compare/v3.62.0...v3.62.1) (2025-11-03) **Note:** Version bump only for package @eggjs/tegg-common-util # [3.62.0](https://github.com/eggjs/tegg/compare/v3.61.0...v3.62.0) (2025-09-24) ### Bug Fixes * deduplicate modules reference ([#343](https://github.com/eggjs/tegg/issues/343)) ([aa5daf7](https://github.com/eggjs/tegg/commit/aa5daf7e8db49c8b273ba2102c127fd14a2de044)) # [3.61.0](https://github.com/eggjs/tegg/compare/v3.60.3...v3.61.0) (2025-08-15) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.60.3](https://github.com/eggjs/tegg/compare/v3.60.2...v3.60.3) (2025-08-06) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.60.2](https://github.com/eggjs/tegg/compare/v3.60.1...v3.60.2) (2025-08-06) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.60.1](https://github.com/eggjs/tegg/compare/v3.60.0...v3.60.1) (2025-07-28) **Note:** Version bump only for package @eggjs/tegg-common-util # [3.60.0](https://github.com/eggjs/tegg/compare/v3.59.1...v3.60.0) (2025-07-28) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.59.1](https://github.com/eggjs/tegg/compare/v3.59.0...v3.59.1) (2025-07-16) **Note:** Version bump only for package @eggjs/tegg-common-util # [3.59.0](https://github.com/eggjs/tegg/compare/v3.58.0...v3.59.0) (2025-07-07) **Note:** Version bump only for package @eggjs/tegg-common-util # [3.58.0](https://github.com/eggjs/tegg/compare/v3.57.14...v3.58.0) (2025-07-01) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.57.14](https://github.com/eggjs/tegg/compare/v3.57.13...v3.57.14) (2025-06-18) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.57.13](https://github.com/eggjs/tegg/compare/v3.57.12...v3.57.13) (2025-06-18) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.57.12](https://github.com/eggjs/tegg/compare/v3.57.11...v3.57.12) (2025-06-16) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.57.11](https://github.com/eggjs/tegg/compare/v3.57.10...v3.57.11) (2025-06-16) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.57.10](https://github.com/eggjs/tegg/compare/v3.57.9...v3.57.10) (2025-06-12) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.57.9](https://github.com/eggjs/tegg/compare/v3.57.8...v3.57.9) (2025-05-29) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.57.8](https://github.com/eggjs/tegg/compare/v3.57.7...v3.57.8) (2025-05-29) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.57.7](https://github.com/eggjs/tegg/compare/v3.57.6...v3.57.7) (2025-05-28) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.57.6](https://github.com/eggjs/tegg/compare/v3.57.5...v3.57.6) (2025-05-27) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.57.5](https://github.com/eggjs/tegg/compare/v3.57.4...v3.57.5) (2025-05-15) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.57.4](https://github.com/eggjs/tegg/compare/v3.57.3...v3.57.4) (2025-05-14) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.57.3](https://github.com/eggjs/tegg/compare/v3.57.2...v3.57.3) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.57.2](https://github.com/eggjs/tegg/compare/v3.57.1...v3.57.2) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.57.1](https://github.com/eggjs/tegg/compare/v3.57.0...v3.57.1) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-common-util # [3.57.0](https://github.com/eggjs/tegg/compare/v3.56.3...v3.57.0) (2025-05-09) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.56.3](https://github.com/eggjs/tegg/compare/v3.56.2...v3.56.3) (2025-05-07) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.56.2](https://github.com/eggjs/tegg/compare/v3.56.1...v3.56.2) (2025-05-01) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.56.1](https://github.com/eggjs/tegg/compare/v3.56.0...v3.56.1) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-common-util # [3.56.0](https://github.com/eggjs/tegg/compare/v3.55.0...v3.56.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-common-util # [3.55.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.55.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-common-util # [3.54.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.54.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-common-util # [3.53.0](https://github.com/eggjs/tegg/compare/v3.52.1...v3.53.0) (2025-04-21) ### Bug Fixes * stream end ([#302](https://github.com/eggjs/tegg/issues/302)) ([7f1f4b3](https://github.com/eggjs/tegg/commit/7f1f4b396294af5609c9454f6882d213dc237512)) ### Features * add timeout metadata for http controller ([#301](https://github.com/eggjs/tegg/issues/301)) ([68980c2](https://github.com/eggjs/tegg/commit/68980c23de81dbc9bd86c1d8df7b3952f52aa5ce)) ## [3.52.1](https://github.com/eggjs/tegg/compare/v3.52.0...v3.52.1) (2025-03-14) **Note:** Version bump only for package @eggjs/tegg-common-util # [3.52.0](https://github.com/eggjs/tegg/compare/v3.51.2...v3.52.0) (2024-12-30) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.51.2](https://github.com/eggjs/tegg/compare/v3.51.1...v3.51.2) (2024-12-09) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.51.1](https://github.com/eggjs/tegg/compare/v3.51.0...v3.51.1) (2024-11-06) **Note:** Version bump only for package @eggjs/tegg-common-util # [3.51.0](https://github.com/eggjs/tegg/compare/v3.50.1...v3.51.0) (2024-10-30) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.50.1](https://github.com/eggjs/tegg/compare/v3.50.0...v3.50.1) (2024-10-23) **Note:** Version bump only for package @eggjs/tegg-common-util # [3.50.0](https://github.com/eggjs/tegg/compare/v3.49.0...v3.50.0) (2024-10-22) **Note:** Version bump only for package @eggjs/tegg-common-util # [3.49.0](https://github.com/eggjs/tegg/compare/v3.48.1...v3.49.0) (2024-10-21) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.48.1](https://github.com/eggjs/tegg/compare/v3.48.0...v3.48.1) (2024-10-14) **Note:** Version bump only for package @eggjs/tegg-common-util # [3.48.0](https://github.com/eggjs/tegg/compare/v3.47.2...v3.48.0) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.47.2](https://github.com/eggjs/tegg/compare/v3.47.1...v3.47.2) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.47.1](https://github.com/eggjs/tegg/compare/v3.47.0...v3.47.1) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-common-util # [3.47.0](https://github.com/eggjs/tegg/compare/v3.46.4...v3.47.0) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.46.4](https://github.com/eggjs/tegg/compare/v3.46.3...v3.46.4) (2024-10-09) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.46.3](https://github.com/eggjs/tegg/compare/v3.46.2...v3.46.3) (2024-10-08) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.46.2](https://github.com/eggjs/tegg/compare/v3.46.1...v3.46.2) (2024-10-07) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.46.1](https://github.com/eggjs/tegg/compare/v3.46.0...v3.46.1) (2024-09-30) **Note:** Version bump only for package @eggjs/tegg-common-util # [3.46.0](https://github.com/eggjs/tegg/compare/v3.45.0...v3.46.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-common-util # [3.45.0](https://github.com/eggjs/tegg/compare/v3.44.1...v3.45.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.44.1](https://github.com/eggjs/tegg/compare/v3.44.0...v3.44.1) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-common-util # [3.44.0](https://github.com/eggjs/tegg/compare/v3.43.2...v3.44.0) (2024-09-29) ### Features * support inject in constructor ([#237](https://github.com/eggjs/tegg/issues/237)) ([e68b1ed](https://github.com/eggjs/tegg/commit/e68b1ed6a90432f1cb35a6f562914b7b04cb5114)) ## [3.43.2](https://github.com/eggjs/tegg/compare/v3.43.1...v3.43.2) (2024-09-14) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.43.1](https://github.com/eggjs/tegg/compare/v3.43.0...v3.43.1) (2024-09-14) **Note:** Version bump only for package @eggjs/tegg-common-util # [3.43.0](https://github.com/eggjs/tegg/compare/v3.42.0...v3.43.0) (2024-09-13) **Note:** Version bump only for package @eggjs/tegg-common-util # [3.42.0](https://github.com/eggjs/tegg/compare/v3.41.0...v3.42.0) (2024-09-10) **Note:** Version bump only for package @eggjs/tegg-common-util # [3.41.0](https://github.com/eggjs/tegg/compare/v3.40.1...v3.41.0) (2024-08-26) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.40.1](https://github.com/eggjs/tegg/compare/v3.40.0...v3.40.1) (2024-08-23) **Note:** Version bump only for package @eggjs/tegg-common-util # [3.40.0](https://github.com/eggjs/tegg/compare/v3.39.5...v3.40.0) (2024-08-22) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.39.5](https://github.com/eggjs/tegg/compare/v3.39.4...v3.39.5) (2024-08-09) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.39.4](https://github.com/eggjs/tegg/compare/v3.39.3...v3.39.4) (2024-07-09) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.39.3](https://github.com/eggjs/tegg/compare/v3.39.2...v3.39.3) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.39.2](https://github.com/eggjs/tegg/compare/v3.39.1...v3.39.2) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.39.1](https://github.com/eggjs/tegg/compare/v3.39.0...v3.39.1) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-common-util # [3.39.0](https://github.com/eggjs/tegg/compare/v3.38.0...v3.39.0) (2024-04-19) ### Features * use app.loader.getTypeFiles to generate module config file names ([#213](https://github.com/eggjs/tegg/issues/213)) ([e0656a4](https://github.com/eggjs/tegg/commit/e0656a4d59beef103a5627461d9b9c87996928e3)) # [3.38.0](https://github.com/eggjs/tegg/compare/v3.37.3...v3.38.0) (2024-04-18) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.37.3](https://github.com/eggjs/tegg/compare/v3.37.2...v3.37.3) (2024-04-17) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.37.2](https://github.com/eggjs/tegg/compare/v3.37.1...v3.37.2) (2024-04-17) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.37.1](https://github.com/eggjs/tegg/compare/v3.37.0...v3.37.1) (2024-04-16) **Note:** Version bump only for package @eggjs/tegg-common-util # [3.37.0](https://github.com/eggjs/tegg/compare/v3.36.3...v3.37.0) (2024-04-16) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.36.3](https://github.com/eggjs/tegg/compare/v3.36.2...v3.36.3) (2024-04-10) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.36.2](https://github.com/eggjs/tegg/compare/v3.36.1...v3.36.2) (2024-04-08) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.36.1](https://github.com/eggjs/tegg/compare/v3.36.0...v3.36.1) (2024-04-07) **Note:** Version bump only for package @eggjs/tegg-common-util # [3.36.0](https://github.com/eggjs/tegg/compare/v3.35.1...v3.36.0) (2024-04-02) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.35.1](https://github.com/eggjs/tegg/compare/v3.35.0...v3.35.1) (2024-03-26) **Note:** Version bump only for package @eggjs/tegg-common-util # [3.35.0](https://github.com/eggjs/tegg/compare/v3.34.0...v3.35.0) (2024-03-26) **Note:** Version bump only for package @eggjs/tegg-common-util # [3.34.0](https://github.com/eggjs/tegg/compare/v3.33.1...v3.34.0) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.33.1](https://github.com/eggjs/tegg/compare/v3.33.0...v3.33.1) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-common-util # [3.33.0](https://github.com/eggjs/tegg/compare/v3.32.0...v3.33.0) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-common-util # [3.32.0](https://github.com/eggjs/tegg/compare/v3.31.0...v3.32.0) (2024-02-19) **Note:** Version bump only for package @eggjs/tegg-common-util # [3.31.0](https://github.com/eggjs/tegg/compare/v3.30.1...v3.31.0) (2024-01-31) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.30.1](https://github.com/eggjs/tegg/compare/v3.30.0...v3.30.1) (2024-01-25) **Note:** Version bump only for package @eggjs/tegg-common-util # [3.30.0](https://github.com/eggjs/tegg/compare/v3.29.0...v3.30.0) (2024-01-17) ### Bug Fixes * config for env is not merged when default config is empty ([#178](https://github.com/eggjs/tegg/issues/178)) ([9c1de22](https://github.com/eggjs/tegg/commit/9c1de223e9c9befb0a803ac5a1bd843f74aa9493)) ### Features * scan framework dependencies as optional module ([#184](https://github.com/eggjs/tegg/issues/184)) ([a4908c6](https://github.com/eggjs/tegg/commit/a4908c6c640000c7068def57d32052cca15adf47)) # [3.29.0](https://github.com/eggjs/tegg/compare/v3.28.2...v3.29.0) (2023-12-26) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.28.2](https://github.com/eggjs/tegg/compare/v3.28.1...v3.28.2) (2023-12-12) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.28.1](https://github.com/eggjs/tegg/compare/v3.28.0...v3.28.1) (2023-12-11) **Note:** Version bump only for package @eggjs/tegg-common-util # [3.28.0](https://github.com/eggjs/tegg/compare/v3.27.0...v3.28.0) (2023-12-10) **Note:** Version bump only for package @eggjs/tegg-common-util # [3.27.0](https://github.com/eggjs/tegg/compare/v3.26.0...v3.27.0) (2023-11-23) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.25.2](https://github.com/eggjs/tegg/compare/v3.25.1...v3.25.2) (2023-11-06) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.25.1](https://github.com/eggjs/tegg/compare/v3.25.0...v3.25.1) (2023-11-03) **Note:** Version bump only for package @eggjs/tegg-common-util # [3.25.0](https://github.com/eggjs/tegg/compare/v3.24.0...v3.25.0) (2023-11-03) ### Features * tegg plugin support ModuleConfig ([#162](https://github.com/eggjs/tegg/issues/162)) ([58bd9fa](https://github.com/eggjs/tegg/commit/58bd9fafdd0d56aabdde5f7c33f17c45568bada8)) # [3.24.0](https://github.com/eggjs/tegg/compare/v3.23.0...v3.24.0) (2023-10-26) **Note:** Version bump only for package @eggjs/tegg-common-util # [3.23.0](https://github.com/eggjs/tegg/compare/v3.22.0...v3.23.0) (2023-09-20) **Note:** Version bump only for package @eggjs/tegg-common-util # [3.20.0](https://github.com/eggjs/tegg/compare/v3.19.0...v3.20.0) (2023-09-07) ### Features * support load module config with env ([#151](https://github.com/eggjs/tegg/issues/151)) ([c087226](https://github.com/eggjs/tegg/commit/c087226bd7764242fadce5622fccd9e9fee56322)) # [3.19.0](https://github.com/eggjs/tegg/compare/v3.18.1...v3.19.0) (2023-08-31) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.18.1](https://github.com/eggjs/tegg/compare/v3.18.0...v3.18.1) (2023-08-29) **Note:** Version bump only for package @eggjs/tegg-common-util # [3.18.0](https://github.com/eggjs/tegg/compare/v3.17.0...v3.18.0) (2023-08-29) **Note:** Version bump only for package @eggjs/tegg-common-util # [3.17.0](https://github.com/eggjs/tegg/compare/v3.16.0...v3.17.0) (2023-08-24) **Note:** Version bump only for package @eggjs/tegg-common-util # [3.16.0](https://github.com/eggjs/tegg/compare/v3.15.0...v3.16.0) (2023-08-24) ### Features * implement RuntimeConfig ([#144](https://github.com/eggjs/tegg/issues/144)) ([0862655](https://github.com/eggjs/tegg/commit/0862655846f6765349d406ee697c036cec2a37bd)) ## [3.14.3](https://github.com/eggjs/tegg/compare/v3.14.2...v3.14.3) (2023-08-14) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.14.2](https://github.com/eggjs/tegg/compare/v3.14.1...v3.14.2) (2023-08-14) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.14.1](https://github.com/eggjs/tegg/compare/v3.14.0...v3.14.1) (2023-08-11) **Note:** Version bump only for package @eggjs/tegg-common-util # [3.14.0](https://github.com/eggjs/tegg/compare/v3.13.0...v3.14.0) (2023-07-31) **Note:** Version bump only for package @eggjs/tegg-common-util # [3.13.0](https://github.com/eggjs/tegg/compare/v3.12.0...v3.13.0) (2023-07-25) **Note:** Version bump only for package @eggjs/tegg-common-util # [3.12.0](https://github.com/eggjs/tegg/compare/v3.11.1...v3.12.0) (2023-07-13) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.11.1](https://github.com/eggjs/tegg/compare/v3.11.0...v3.11.1) (2023-06-29) **Note:** Version bump only for package @eggjs/tegg-common-util # [3.11.0](https://github.com/eggjs/tegg/compare/v3.10.0...v3.11.0) (2023-06-29) **Note:** Version bump only for package @eggjs/tegg-common-util # [3.10.0](https://github.com/eggjs/tegg/compare/v3.9.0...v3.10.0) (2023-06-28) ### Bug Fixes * use posix join for package path ([#127](https://github.com/eggjs/tegg/issues/127)) ([53672f4](https://github.com/eggjs/tegg/commit/53672f404edb72c7330e125f72dd356cde0607ad)) # [3.8.0](https://github.com/eggjs/tegg/compare/v3.7.0...v3.8.0) (2023-05-30) ### Features * impl EggObjectLifecycle hook in decorator ([#119](https://github.com/eggjs/tegg/issues/119)) ([cced8a2](https://github.com/eggjs/tegg/commit/cced8a2e009c33d5040fa21d00409fddef471b0e)) # [3.7.0](https://github.com/eggjs/tegg/compare/v3.6.3...v3.7.0) (2023-04-03) ### Features * The exposed module reads the options. ([#112](https://github.com/eggjs/tegg/issues/112)) ([a52b44b](https://github.com/eggjs/tegg/commit/a52b44b753463bfdef6fbbc39f920be8eccf1567)) ## [3.2.3](https://github.com/eggjs/tegg/compare/v3.2.2...v3.2.3) (2023-01-16) **Note:** Version bump only for package @eggjs/tegg-common-util ## [3.2.2](https://github.com/eggjs/tegg/compare/v3.2.1...v3.2.2) (2023-01-06) **Note:** Version bump only for package @eggjs/tegg-common-util # [0.2.0](https://github.com/eggjs/tegg/compare/v0.1.19...v0.2.0) (2022-01-20) **Note:** Version bump only for package @eggjs/tegg-common-util ================================================ FILE: core/common-util/README.md ================================================ # @eggjs/tegg-common-util Common utility functions for tegg framework. ## ModuleConfigUtil.deduplicateModules A utility method for deduplicating module references to avoid adding duplicate modules. ### Features - **Path-based deduplication**: Removes modules with duplicate paths - **Name-based deduplication**: Removes modules with duplicate names (throws error if found) - **Priority handling**: Prioritizes non-optional modules over optional ones - **Error handling**: Throws error for duplicate module names with different paths - **Simple API**: No complex options, straightforward behavior ### API ```typescript public static deduplicateModules( moduleReferences: readonly ModuleReference[] ): readonly ModuleReference[] ``` **Note**: This method does not accept options parameters. The behavior is fixed and optimized for common use cases. ### Behavior 1. **Path Deduplication**: If multiple modules have the same path, only one is kept 2. **Optional Priority**: When paths are the same, non-optional modules are prioritized over optional ones 3. **Name Validation**: If modules have the same name but different paths, an error is thrown 4. **First Occurrence**: When both modules have the same optional status, the first occurrence is kept ### Usage Examples #### Basic Usage ```typescript import { ModuleConfigUtil } from '@eggjs/tegg-common-util'; const modules = [ { name: 'module1', path: '/path/to/module1' }, { name: 'module2', path: '/path/to/module2' }, { name: 'module1', path: '/different/path/to/module1' }, // Will throw error ]; const result = ModuleConfigUtil.deduplicateModules(modules); // Throws Error: Duplicate module name "module1" found ``` #### With Optional Modules ```typescript const modules = [ { name: 'module1', path: '/path/to/module1', optional: true }, { name: 'module1', path: '/path/to/module1' }, // Non-optional version ]; const result = ModuleConfigUtil.deduplicateModules(modules); // Result: 1 module, non-optional version kept ``` #### Path Deduplication ```typescript const modules = [ { name: 'module1', path: '/path/to/module1' }, { name: 'module2', path: '/path/to/module1' }, // Same path, different name ]; const result = ModuleConfigUtil.deduplicateModules(modules); // Result: 1 module, first occurrence kept ``` ### Use Cases #### 1. Plugin/Config Module Scanner ```typescript // In ModuleScanner.loadModuleReferences() return ModuleConfigUtil.deduplicateModules(allModuleReferences); // Automatically handles deduplication with optimal defaults ``` #### 2. Standalone Runner ```typescript // In Runner.getModuleReferences() return ModuleConfigUtil.deduplicateModules(allModuleReferences); // Simple and reliable deduplication ``` #### 3. Error Handling ```typescript try { const result = ModuleConfigUtil.deduplicateModules(modules); } catch (error) { if (error.message.includes('Duplicate module name')) { // Handle duplicate module name error console.error('Configuration error:', error.message); } throw error; } ``` ### Benefits 1. **Simplicity**: No complex configuration needed, works out of the box 2. **Reliability**: Consistent behavior across different use cases 3. **Performance**: Optimized for common scenarios 4. **Error Prevention**: Catches configuration errors early 5. **Maintainability**: Simple API reduces complexity ### Migration from Options-based API #### Before (Options-based - Not Available) ```typescript // This API never existed in the actual implementation const result = ModuleConfigUtil.deduplicateModules(modules, { prioritizeNonOptional: true, allowNameDuplicates: false, logPrefix: '[tegg/config]', logger: customLogger, }); ``` #### After (Current Implementation) ```typescript // Current implementation - simple and direct const result = ModuleConfigUtil.deduplicateModules(modules); // Automatically handles all deduplication logic ``` ### Important Notes - **No Options**: The method signature is fixed and does not accept configuration options - **Error on Name Duplicates**: Duplicate names with different paths will cause an error - **Automatic Priority**: Non-optional modules are automatically prioritized over optional ones - **Path-based Deduplication**: Same path modules are deduplicated with smart priority handling ================================================ FILE: core/common-util/index.ts ================================================ export * from '@eggjs/tegg-types/common'; export * from './src/MapUtil'; export * from './src/NameUtil'; export * from './src/Graph'; export * from './src/ObjectUtils'; export * from './src/FSUtil'; export * from './src/StackUtil'; export * from './src/ProxyUtil'; export * from './src/ModuleConfig'; export * from './src/ModuleConfigs'; export * from './src/TimerUtil'; export * from './src/StreamUtil'; ================================================ FILE: core/common-util/package.json ================================================ { "name": "@eggjs/tegg-common-util", "description": "common util for tegg", "version": "3.78.15", "keywords": [ "egg", "typescript", "collection", "tegg" ], "main": "dist/index.js", "files": [ "dist/**/*.js", "dist/**/*.d.ts" ], "typings": "dist/index.d.ts", "scripts": { "test": "cross-env NODE_ENV=test NODE_OPTIONS='--no-deprecation' mocha", "clean": "tsc -b --clean", "tsc": "ut run clean && tsc -p ./tsconfig.json", "tsc:pub": "ut run clean && tsc -p ./tsconfig.pub.json", "prepublishOnly": "ut tsc:pub" }, "author": "killagu ", "license": "MIT", "homepage": "https://github.com/eggjs/tegg", "bugs": { "url": "https://github.com/eggjs/tegg/issues" }, "repository": { "type": "git", "url": "git@github.com:eggjs/tegg.git", "directory": "core/common-util" }, "engines": { "node": ">=14.0.0" }, "dependencies": { "@eggjs/tegg-types": "^3.78.15", "extend2": "^1.0.0", "globby": "^11.1.0", "js-yaml": "^3.14.0" }, "publishConfig": { "access": "public" }, "devDependencies": { "@types/mocha": "^10.0.1", "@types/node": "^20.2.4", "cross-env": "^7.0.3", "mocha": "^10.2.0", "ts-node": "^10.9.1", "typescript": "^5.0.4" }, "gitHead": "240a3e9f40fda65ebb7589727d197db5ce17916c" } ================================================ FILE: core/common-util/src/FSUtil.ts ================================================ import { promises as fs } from 'fs'; export class FSUtil { static async fileExists(filePath: string): Promise { try { await fs.access(filePath); } catch (_) { return false; } return true; } } ================================================ FILE: core/common-util/src/Graph.ts ================================================ import type { GraphNodeObj } from '@eggjs/tegg-types'; const inspect = Symbol.for('nodejs.util.inspect.custom'); export interface EdgeMeta { equal(meta: EdgeMeta): boolean; toString(): string; } export class GraphNode { val: T; toNodeMap: Map, meta?: M}> = new Map(); fromNodeMap: Map, meta?: M}> = new Map(); constructor(val: T) { this.val = val; } get id() { return this.val.id; } addToVertex(node: GraphNode, meta?: M) { if (this.toNodeMap.has(node.id)) { return false; } this.toNodeMap.set(node.id, { node, meta }); return true; } addFromVertex(node: GraphNode, meta?: M) { if (this.fromNodeMap.has(node.id)) { return false; } this.fromNodeMap.set(node.id, { node, meta }); return true; } [inspect]() { return this.toJSON(); } toJSON() { return { val: this.val, toNodes: Array.from(this.toNodeMap.values()), fromNodes: Array.from(this.fromNodeMap.values()), }; } toString() { return this.val.toString(); } } export class GraphPath { nodeIdMap: Map = new Map(); nodes: Array<{ node: GraphNode, meta?: M }> = []; pushVertex(node: GraphNode, meta?: M): boolean { const val = this.nodeIdMap.get(node.id) || 0; this.nodeIdMap.set(node.id, val + 1); this.nodes.push({ node, meta }); return val === 0; } popVertex() { const nodeHandler = this.nodes.pop(); if (nodeHandler) { const val = this.nodeIdMap.get(nodeHandler.node.id)!; this.nodeIdMap.set(nodeHandler.node.id, val - 1); } } toString() { const res = this.nodes.reduce((p, c) => { let msg = ''; if (c.meta) { msg += ` ${c.meta.toString()} -> `; } else if (p.length) { msg += ' -> '; } msg += c.node.val.toString(); p.push(msg); return p; }, new Array()); return res.join(''); } [inspect]() { return this.toString(); } } export class Graph { nodes: Map> = new Map(); addVertex(node: GraphNode): boolean { if (this.nodes.has(node.id)) { return false; } this.nodes.set(node.id, node); return true; } addEdge(from: GraphNode, to: GraphNode, meta?: M): boolean { to.addFromVertex(from, meta); return from.addToVertex(to, meta); } findToNode(id: string, meta: M): GraphNode | undefined { const node = this.nodes.get(id); if (!node) return undefined; for (const { node: toNode, meta: edgeMeta } of node.toNodeMap.values()) { if (edgeMeta && meta.equal(edgeMeta)) { return toNode; } } return undefined; } appendVertexToPath(node: GraphNode, accessPath: GraphPath, meta?: M): boolean { if (!accessPath.pushVertex(node, meta)) { return false; } for (const toNode of node.toNodeMap.values()) { if (!this.appendVertexToPath(toNode.node, accessPath, toNode.meta)) { return false; } } accessPath.popVertex(); return true; } loopPath(): GraphPath | undefined { const accessPath = new GraphPath(); const nodes = Array.from(this.nodes.values()); for (const node of nodes) { if (!this.appendVertexToPath(node, accessPath)) { return accessPath; } } return; } accessNode(node: GraphNode, nodes: Array>, accessed: boolean[], res: Array>) { const index = nodes.indexOf(node); if (accessed[index]) { return; } if (!node.toNodeMap.size) { accessed[nodes.indexOf(node)] = true; res.push(node); return; } for (const toNode of node.toNodeMap.values()) { this.accessNode(toNode.node, nodes, accessed, res); } accessed[nodes.indexOf(node)] = true; res.push(node); } // sort by direct // priority: // 1. vertex can not be access // 2. reverse by access direct // // notice: // 1. sort result is not stable // 2. graph with loop can not be sort sort(): Array> { const res: Array> = []; const nodes = Array.from(this.nodes.values()); const accessed: boolean[] = []; for (let i = 0; i < nodes.length; ++i) { accessed.push(false); } for (let i = 0; i < nodes.length; ++i) { const node = nodes[i]; this.accessNode(node, nodes, accessed, res); } return res; } } ================================================ FILE: core/common-util/src/MapUtil.ts ================================================ export class MapUtil { static getOrStore(map: Map, key: K, value: V): V { if (!map.has(key)) { map.set(key, value); } return map.get(key)!; } } ================================================ FILE: core/common-util/src/ModuleConfig.ts ================================================ import assert from 'node:assert'; import fs, { promises as fsPromise } from 'node:fs'; import path from 'node:path'; import extend from 'extend2'; import globby from 'globby'; import yaml from 'js-yaml'; import type { InlineModuleReferenceConfig, ModuleConfig, ModuleReference, ModuleReferenceConfig, NpmModuleReferenceConfig, ReadModuleReferenceOptions, } from '@eggjs/tegg-types'; import { FSUtil } from './FSUtil'; export class ModuleReferenceConfigHelp { static isInlineModuleReference(moduleReference: ModuleReferenceConfig): moduleReference is InlineModuleReferenceConfig { return !!(moduleReference as InlineModuleReferenceConfig).path; } static isNpmModuleReference(moduleReference: ModuleReferenceConfig): moduleReference is NpmModuleReferenceConfig { return !!(moduleReference as NpmModuleReferenceConfig).package; } } const DEFAULT_READ_MODULE_REF_OPTS = { deep: 10, }; export class ModuleConfigUtil { static configNames: string[] | undefined; public static setConfigNames(configNames: string[] | undefined) { ModuleConfigUtil.configNames = configNames; } public static readModuleReference(baseDir: string, options?: ReadModuleReferenceOptions): readonly ModuleReference[] { // 1. module.json exits use module.json as module reference // 1. module.json not exits scan baseDir get package.json to find modules const configDir = path.join(baseDir, 'config'); const moduleJsonPath = path.join(configDir, 'module.json'); if (fs.existsSync(moduleJsonPath)) { return this.readModuleReferenceFromModuleJson(configDir, moduleJsonPath, options?.cwd || baseDir); } return this.readModuleReferenceFromScan(baseDir, options); } private static readModuleReferenceFromModuleJson(configDir: string, moduleJsonPath: string, cwd?: string): readonly ModuleReference[] { const moduleJsonContent = fs.readFileSync(moduleJsonPath, 'utf8'); const moduleJson: ModuleReferenceConfig[] = JSON.parse(moduleJsonContent); const moduleReferenceList: ModuleReference[] = []; for (const moduleReferenceConfig of moduleJson) { let moduleReference: ModuleReference; if (ModuleReferenceConfigHelp.isNpmModuleReference(moduleReferenceConfig)) { const options = cwd ? { paths: [ cwd ] } : {}; // path.posix for windows keep path as foo/package.json const pkgJson = path.posix.join(moduleReferenceConfig.package, 'package.json'); const file = require.resolve(pkgJson, options); const modulePath = path.dirname(file); moduleReference = { path: modulePath, name: ModuleConfigUtil.readModuleNameSync(modulePath), }; } else if (ModuleReferenceConfigHelp.isInlineModuleReference(moduleReferenceConfig)) { const modulePath = path.join(configDir, moduleReferenceConfig.path); moduleReference = { path: modulePath, name: ModuleConfigUtil.readModuleNameSync(modulePath), }; } else { throw new Error('unknown type of module reference config: ' + JSON.stringify(moduleReferenceConfig)); } moduleReferenceList.push(moduleReference); } return moduleReferenceList; } private static readModuleReferenceFromScan(baseDir: string, options?: ReadModuleReferenceOptions): readonly ModuleReference[] { const ref: ModuleReference[] = []; const realOptions: ReadModuleReferenceOptions = Object.assign({}, DEFAULT_READ_MODULE_REF_OPTS, options); const packagePaths = globby.sync([ '**/package.json', // not load node_modules '!**/node_modules', // not load files in .xxx/ '!**/+(.*)/**', // not load coverage '!**/coverage', ...(realOptions.extraFilePattern || []), ], { cwd: baseDir, deep: realOptions.deep, }); const moduleDirSet = new Set(); for (const packagePath of packagePaths) { const absolutePkgPath = path.join(baseDir, packagePath); let realPkgPath; try { realPkgPath = fs.realpathSync(absolutePkgPath); } catch (_) { continue; } const moduleDir = path.dirname(realPkgPath); // skip the symbolic link if (moduleDirSet.has(moduleDir)) { continue; } moduleDirSet.add(moduleDir); let name: string; try { name = this.readModuleNameSync(moduleDir); } catch (_) { continue; } ref.push({ path: moduleDir, name, }); } const moduleReferences = this.readModuleFromNodeModules(baseDir); for (const moduleReference of moduleReferences) { const moduleBasePath = path.basename(moduleReference.path); moduleDirSet.forEach(modulePath => { if (path.basename(modulePath) === moduleBasePath) { throw new Error('duplicate import of module reference: ' + moduleBasePath); } }); ref.push({ path: moduleReference.path, name: moduleReference.name, }); } return ref; } public static readModuleFromNodeModules(baseDir: string) { const ref: ModuleReference[] = []; let pkgContent: string; try { pkgContent = fs.readFileSync(path.join(baseDir, 'package.json'), 'utf8'); } catch (_) { return []; } const pkg = JSON.parse(pkgContent); for (const dependencyKey of Object.keys(pkg.dependencies || {})) { let packageJsonPath: string; try { // https://nodejs.org/api/packages.html#package-entry-points // ignore cases where the package entry is exports but package.json is not exported packageJsonPath = require.resolve(`${dependencyKey}/package.json`, { paths: [ baseDir ] }); } catch (_) { continue; } const absolutePkgPath = path.dirname(packageJsonPath); const realPkgPath = fs.realpathSync(absolutePkgPath); try { const name = this.readModuleNameSync(realPkgPath); ref.push({ path: realPkgPath, name, }); } catch (_) { continue; } } return ref; } public static resolveModuleDir(moduleDir: string, baseDir?: string): string { if (path.isAbsolute(moduleDir)) { return moduleDir; } assert(baseDir, 'baseDir is required'); return path.join(baseDir, 'config', moduleDir); } private static getModuleName(pkg: any) { assert(pkg.eggModule && pkg.eggModule.name, 'eggModule.name not found in package.json'); return pkg.eggModule.name; } public static async readModuleName(baseDir: string, moduleDir: string): Promise { moduleDir = ModuleConfigUtil.resolveModuleDir(moduleDir, baseDir); const pkgContent = await fsPromise.readFile(path.join(moduleDir, 'package.json'), 'utf8'); const pkg = JSON.parse(pkgContent); return ModuleConfigUtil.getModuleName(pkg); } public static readModuleNameSync(moduleDir: string, baseDir?: string): string { moduleDir = ModuleConfigUtil.resolveModuleDir(moduleDir, baseDir); const pkgContent = fs.readFileSync(path.join(moduleDir, 'package.json'), 'utf8'); const pkg = JSON.parse(pkgContent); return ModuleConfigUtil.getModuleName(pkg); } public static async loadModuleConfig(moduleDir: string, baseDir?: string, env?: string): Promise { const modulePath = ModuleConfigUtil.resolveModuleDir(moduleDir, baseDir); let configNames: string[]; if (env) { configNames = [ 'module', `module.${env}` ]; } else { // assert(ModuleConfigUtil.configNames, 'should setConfigNames before load module config'); configNames = ModuleConfigUtil.configNames || [ 'module' ]; } const target: ModuleConfig = {}; for (const configName of configNames) { let config = await ModuleConfigUtil.#loadOne(modulePath, configName); // both module.yml and module.default.yml are ok for default config if (configName === 'module.default' && !config) { config = await ModuleConfigUtil.#loadOne(modulePath, 'module'); } if (config) { extend(true, target, config); } } return target; } static async #loadOne(moduleDir: string, configName: string): Promise { const yamlConfigPath = path.join(moduleDir, `${configName}.yml`); let config = await ModuleConfigUtil.#loadYaml(yamlConfigPath); if (!config) { const jsonConfigPath = path.join(moduleDir, `${configName}.json`); config = await ModuleConfigUtil.#loadJson(jsonConfigPath); } return config; } static async #loadJson(moduleJsonPath: string): Promise { const moduleJsonPathExists = await FSUtil.fileExists(moduleJsonPath); if (!moduleJsonPathExists) { return; } const moduleJsonContent = await fsPromise.readFile(moduleJsonPath, 'utf8'); const moduleJson = JSON.parse(moduleJsonContent); return moduleJson.config; } static async #loadYaml(moduleYamlPath: string): Promise { const moduleYamlPathExists = await FSUtil.fileExists(moduleYamlPath); if (!moduleYamlPathExists) { return; } const moduleYamlContent = await fsPromise.readFile(moduleYamlPath, 'utf8'); return yaml.safeLoad(moduleYamlContent) as ModuleConfigUtil; } public static loadModuleConfigSync(moduleDir: string, baseDir?: string, env?: string): ModuleConfig { const modulePath = ModuleConfigUtil.resolveModuleDir(moduleDir, baseDir); let configNames: string[]; if (env) { configNames = [ 'module', `module.${env}` ]; } else { // assert(ModuleConfigUtil.configNames, 'should setConfigNames before load module config'); configNames = ModuleConfigUtil.configNames || [ 'module' ]; } const target: ModuleConfig = {}; for (const configName of configNames) { let config = ModuleConfigUtil.#loadOneSync(modulePath, configName); // both module.yml and module.default.yml are ok for default config if (configName === 'module.default' && !config) { config = ModuleConfigUtil.#loadOneSync(modulePath, 'module'); } if (config) { extend(true, target, config); } } return target; } static #loadOneSync(moduleDir: string, configName: string): ModuleConfig | undefined { const yamlConfigPath = path.join(moduleDir, `${configName}.yml`); let config = ModuleConfigUtil.#loadYamlSync(yamlConfigPath); if (!config) { const jsonConfigPath = path.join(moduleDir, `${configName}.json`); config = ModuleConfigUtil.#loadJsonSync(jsonConfigPath); } return config; } static #loadJsonSync(moduleJsonPath: string): ModuleConfig | undefined { const moduleJsonPathExists = fs.existsSync(moduleJsonPath); if (!moduleJsonPathExists) { return; } const moduleJsonContent = fs.readFileSync(moduleJsonPath, 'utf8'); const moduleJson = JSON.parse(moduleJsonContent); return moduleJson.config; } static #loadYamlSync(moduleYamlPath: string): ModuleConfig | undefined { const moduleYamlPathExists = fs.existsSync(moduleYamlPath); if (!moduleYamlPathExists) { return; } const moduleYamlContent = fs.readFileSync(moduleYamlPath, 'utf8'); return yaml.safeLoad(moduleYamlContent) as ModuleConfig; } /** * 去重模块引用,避免重复添加相同的模块 * @param moduleReferences 模块引用数组 * @return 去重后的模块引用数组 */ public static deduplicateModules( moduleReferences: readonly ModuleReference[], ): readonly ModuleReference[] { const moduleMap = new Map(); const nameMap = new Map(); for (const moduleRef of moduleReferences) { const key = moduleRef.path; const existingRef = moduleMap.get(key); // 如果路径相同,优先保留非 optional 模块 if (existingRef) { // 优先保留非 optional 模块 // 将 undefined 视为 false(非可选) const existingOptional = existingRef.optional ?? false; const currentOptional = moduleRef.optional ?? false; if (!existingOptional && currentOptional) { // 保留现有的非 optional 模块,跳过当前的 optional 模块 continue; } else if (existingOptional && !currentOptional) { // 用非 optional 模块替换现有的 optional 模块 // 在替换之前,先检查新模块的名称是否与已有的其他模块名称冲突 if (existingRef.name !== moduleRef.name) { const existingByName = nameMap.get(moduleRef.name); if (existingByName) { // 如果名称重复但路径不同,直接报错 throw new Error(`Duplicate module name "${moduleRef.name}" found: existing at ${existingByName.path}, duplicate at ${moduleRef.path}`); } } // 确保新模块的 optional 属性为 false const newModuleRef = { ...moduleRef, optional: false, }; moduleMap.set(key, newModuleRef); // 同时更新 nameMap if (nameMap.get(existingRef.name) === existingRef) { nameMap.delete(existingRef.name); } // 如果名称不同,需要更新 nameMap if (existingRef.name !== newModuleRef.name) { nameMap.delete(existingRef.name); } nameMap.set(newModuleRef.name, newModuleRef); continue; } // 如果都是 optional 或都是非 optional,保留第一个 continue; } // 检查模块名称是否重复 const existingByName = nameMap.get(moduleRef.name); if (existingByName) { // 如果名称重复但路径不同,直接报错 throw new Error(`Duplicate module name "${moduleRef.name}" found: existing at ${existingByName.path}, duplicate at ${moduleRef.path}`); } // 添加新模块 moduleMap.set(key, moduleRef); nameMap.set(moduleRef.name, moduleRef); } return Array.from(moduleMap.values()); } } ================================================ FILE: core/common-util/src/ModuleConfigs.ts ================================================ import type { ModuleConfig, ModuleConfigHolder } from '@eggjs/tegg-types'; export class ModuleConfigs { constructor(readonly inner: Record) { } get(moduleName: string): ModuleConfig | undefined { return this.inner[moduleName]?.config; } * [Symbol.iterator](): Iterator<[string, ModuleConfigHolder]> { yield* Object.entries(this.inner); } } ================================================ FILE: core/common-util/src/NameUtil.ts ================================================ export class NameUtil { static getClassName(constructor: Function) { return constructor.name[0].toLowerCase() + constructor.name.substring(1); } } ================================================ FILE: core/common-util/src/ObjectUtils.ts ================================================ import { EggProtoImplClass } from '@eggjs/tegg-types'; export class ObjectUtils { static getProperties(obj: object): string[] { const properties: string[] = []; do { for (const property of Object.getOwnPropertyNames(obj)) { properties.push(property); } } while ((obj = Object.getPrototypeOf(obj)) && obj !== Object.prototype); return properties; } static getFunctionArgNameList(func: Function): string[] { if (func.length === 0) { return []; } let sourcecode = func.toString(); sourcecode = sourcecode // Remove /* ... */ .replace(/\/\*[\s\S]*?\*\//g, '') // Remove // .replace(/\/\/(.)*/g, '') // Remove { ... } .replace(/{[\s\S]*}/, '') // Remove => .replace(/=>/g, '') .trim(); let argsString = sourcecode.substring(sourcecode.indexOf('(') + 1, sourcecode.length - 1); // Remove =(...,...) argsString = argsString.replace(/=\([\s\S]*\)/g, ''); const args = argsString.split(','); const argNames = args.map(arg => { // Remove default value return arg.replace(/=[\s\S]*/g, '').trim(); }).filter(arg => arg.length); return argNames; } static getConstructorArgNameList(clazz: EggProtoImplClass): string[] { if (clazz.length === 0) { return []; } const classString = clazz.toString(); const constructorMatch = classString.match(/constructor\s*\(([^)]+)\)/); if (!constructorMatch) { return []; } const params = constructorMatch[1].split(',').map(param => param.trim()); return params.map(param => param.match(/(\w+)\s*(?=\s*(?:=|\/\/|\s*$))/)) .filter(Boolean) .map(match => match![0].trim()); } } ================================================ FILE: core/common-util/src/ProxyUtil.ts ================================================ export class ProxyUtil { static safeProxy(obj: T, getter: (obj: T, p: PropertyKey) => any) { return new Proxy(obj, { get(target: T, p: PropertyKey): any { if (Object.prototype[p]) { return target[p]; } return getter(target, p); }, }); } } ================================================ FILE: core/common-util/src/StackUtil.ts ================================================ /** * Capture call site stack from v8. * https://github.com/v8/v8/wiki/Stack-Trace-API */ function prepareObjectStackTrace(_, stack) { return stack; } export class StackUtil { // from egg-core/utils // https://github.com/eggjs/egg-core/blob/master/lib/utils/index.js#L51 static getCalleeFromStack(withLine: boolean, stackIndex?: number) { stackIndex = stackIndex === undefined ? 2 : stackIndex; const limit = Error.stackTraceLimit; const prep = Error.prepareStackTrace; Error.prepareStackTrace = prepareObjectStackTrace; Error.stackTraceLimit = 10; // capture the stack const obj: { stack: NodeJS.CallSite[] } = { stack: [], }; Error.captureStackTrace(obj); const callSite = obj.stack[stackIndex]; let fileName; /* istanbul ignore else */ if (callSite) { // egg-mock will create a proxy // https://github.com/eggjs/egg-mock/blob/master/lib/app.js#L174 fileName = callSite.getFileName(); } Error.prepareStackTrace = prep; Error.stackTraceLimit = limit; /* istanbul ignore if */ if (!callSite || !fileName) return ''; if (!withLine) return fileName; return `${fileName}:${callSite.getLineNumber()}:${callSite.getColumnNumber()}`; } } ================================================ FILE: core/common-util/src/StreamUtil.ts ================================================ import { Stream } from 'node:stream'; export class StreamUtil { static isStream(obj: any): boolean { return obj instanceof Stream; } } ================================================ FILE: core/common-util/src/TimerUtil.ts ================================================ class TimeoutError extends Error { constructor(message: string) { super(message); this.name = 'TimeoutError'; } } export class TimerUtil { static TimeoutError = TimeoutError; static async sleep(ms: number) { await new Promise(resolve => setTimeout(resolve, ms)); } static async timeout(fn: () => Promise, ms?: number): Promise { if (!ms) { return await fn(); } let timer: NodeJS.Timeout; const promise = new Promise((resolve, reject) => { timer = setTimeout(() => reject(new TimeoutError('timeout')), ms); fn().then(resolve).catch(reject); }); return await promise.finally(() => { if (timer) { clearTimeout(timer); } }); } } ================================================ FILE: core/common-util/test/MapUtil.test.ts ================================================ import assert from 'node:assert'; import { MapUtil } from '..'; describe('test/MapUtil.test.ts', () => { it('should set value if key not exists', () => { const map = new Map(); const key = 'test_key'; const val = 'test_val'; const getVal = MapUtil.getOrStore(map, key, val); assert(getVal === val); assert(map.has(key)); }); it('should not set value if key exits', () => { const map = new Map(); const key = 'test_key'; const val = 'test_val'; map.set(key, val); const initVal = 'test_val2333'; const getVal = MapUtil.getOrStore(map, key, initVal); assert(initVal !== getVal); assert(getVal === val); }); }); ================================================ FILE: core/common-util/test/ModuleConfig.test.ts ================================================ import { strict as assert } from 'node:assert'; import path from 'node:path'; import { ModuleConfigUtil } from '../src/ModuleConfig'; import { ModuleConfigs } from '../src/ModuleConfigs'; import type { ModuleReference } from '@eggjs/tegg-types'; describe('test/ModuleConfig.test.ts', () => { describe('load yaml config', () => { afterEach(() => { ModuleConfigUtil.setConfigNames(undefined); }); it('should work', () => { const config = ModuleConfigUtil.loadModuleConfigSync(path.join(__dirname, './fixtures/modules/foo-yaml')); assert.deepStrictEqual(config, { mysql: { host: '127.0.0.1' } }); }); it('should load env', () => { const config = ModuleConfigUtil.loadModuleConfigSync(path.join(__dirname, './fixtures/modules/dev-module-config'), undefined, 'dev'); assert.deepStrictEqual(config, { mysql: { host: '127.0.0.1', port: 11306 } }); }); it('should load with configNames', async () => { ModuleConfigUtil.setConfigNames([ 'module.default', 'module.dev' ]); const config = await ModuleConfigUtil.loadModuleConfig(path.join(__dirname, './fixtures/modules/dev-module-config')); const configSync = ModuleConfigUtil.loadModuleConfigSync(path.join(__dirname, './fixtures/modules/dev-module-config')); assert.deepStrictEqual(config, { mysql: { host: '127.0.0.1', port: 11306 } }); assert.deepStrictEqual(configSync, { mysql: { host: '127.0.0.1', port: 11306 } }); }); // it('should throw error without initialization', async () => { // await assert.rejects(async () => { // await ModuleConfigUtil.loadModuleConfig(path.join(__dirname, './fixtures/modules/dev-module-config')); // }, /should setConfigNames before load module config/); // // assert.throws(() => { // ModuleConfigUtil.loadModuleConfigSync(path.join(__dirname, './fixtures/modules/dev-module-config')); // }, /should setConfigNames before load module config/); // }); }); describe('load module reference', () => { describe('module.json not exits', () => { it('should work', () => { const fixturesPath = path.join(__dirname, './fixtures/apps/app-with-no-module-json'); const ref = ModuleConfigUtil.readModuleReference(fixturesPath); assert.deepStrictEqual(ref, [ { path: path.join(fixturesPath, 'app/module-a'), name: 'moduleA' }, { path: path.join(fixturesPath, 'app/module-b'), name: 'moduleB' }, { path: path.join(fixturesPath, 'app/module-b/test/fixtures/module-e'), name: 'moduleE' }, { path: path.join(fixturesPath, 'node_modules/module-c'), name: 'moduleC' }, ]); }); it('duplicated module', () => { const fixturesPath = path.join(__dirname, './fixtures/apps/app-with-no-module-json-duplicated'); assert.throws(() => { ModuleConfigUtil.readModuleReference(fixturesPath); }, /duplicate import of module reference/, 'did not throw with expected message'); }); describe('has symlink', () => { it('should work', () => { const fixturesPath = path.join(__dirname, './fixtures/apps/app-with-symlink'); const ref = ModuleConfigUtil.readModuleReference(fixturesPath); assert.deepStrictEqual(ref, [ { path: path.join(fixturesPath, 'app/module-a'), name: 'moduleA' }, ]); }); }); }); describe('module.json exits', () => { it('should work', () => { const fixturesPath = path.join(__dirname, './fixtures/apps/app-with-module-json'); const ref = ModuleConfigUtil.readModuleReference(fixturesPath); assert.deepStrictEqual(ref, [ { path: path.join(fixturesPath, 'app/module-a'), name: 'moduleA' }, { path: path.join(fixturesPath, 'app/module-b'), name: 'moduleB' }, ]); }); }); describe('module.json has pkg', () => { it('should work', () => { const fixturesPath = path.join(__dirname, './fixtures/apps/app-with-module-pkg-json'); const ref = ModuleConfigUtil.readModuleReference(fixturesPath, { cwd: fixturesPath, }); assert.deepStrictEqual(ref, [ { path: path.join(fixturesPath, 'node_modules/module-a'), name: 'moduleA' }, ]); }); }); describe('filter module', () => { it('should work', () => { const fixturesPath = path.join(__dirname, './fixtures/apps/app-with-modules'); const readModuleOptions = { cwd: fixturesPath, extraFilePattern: [ '!**/dist' ], }; const ref = ModuleConfigUtil.readModuleReference(fixturesPath, readModuleOptions); assert.deepStrictEqual(ref, [ { path: path.join(fixturesPath, 'app/module-a'), name: 'moduleA' }, ]); }); }); }); describe('read package dependencies', () => { it('should success if package.json not exist', async () => { const dir = path.resolve(__dirname, './fixtures/monorepo/foo'); const ret = ModuleConfigUtil.readModuleFromNodeModules(dir); assert.deepStrictEqual(ret, []); }); it('should success whether dependencies entry has exported package.json', async () => { const dir = path.resolve(__dirname, './fixtures/monorepo/packages/d'); const ret = ModuleConfigUtil.readModuleFromNodeModules(dir); assert.deepStrictEqual(ret, [{ path: path.resolve(__dirname, './fixtures/monorepo/packages/d/node_modules/e'), name: 'e', }]); }); it('should read dependencies from self node_modules', async () => { const dir = path.resolve(__dirname, './fixtures/monorepo/packages/a'); const ret = ModuleConfigUtil.readModuleFromNodeModules(dir); assert.deepStrictEqual(ret, [{ path: path.resolve(__dirname, './fixtures/monorepo/packages/a/node_modules/c'), name: 'c', }]); }); it('should read dependencies from parent node_modules', async () => { const dir = path.resolve(__dirname, './fixtures/monorepo/packages/b'); const ret = ModuleConfigUtil.readModuleFromNodeModules(dir); assert.deepStrictEqual(ret, [{ path: path.resolve(__dirname, './fixtures/monorepo/packages/a'), name: 'a', }]); }); }); it('should iterate over all module configs', () => { const mockInner = { module1: { name: 'module1', reference: { path: '/path/to/module1', name: 'module1' }, config: { foo: 'bar' }, }, module2: { name: 'module2', reference: { path: '/path/to/module2', name: 'module2' }, config: { baz: 'qux' }, }, }; const moduleConfigs = new ModuleConfigs(mockInner); const result: Array<[string, any]> = []; for (const [ name, holder ] of moduleConfigs) { result.push([ name, holder ]); } assert.strictEqual(result.length, 2); assert.strictEqual(result[0][0], 'module1'); assert.deepStrictEqual(result[0][1], mockInner.module1); assert.strictEqual(result[1][0], 'module2'); assert.deepStrictEqual(result[1][1], mockInner.module2); }); it('should work with empty configs', () => { const moduleConfigs = new ModuleConfigs({}); const result: Array<[string, any]> = []; for (const [ name, holder ] of moduleConfigs) { result.push([ name, holder ]); } assert.strictEqual(result.length, 0); }); }); describe('ModuleConfigUtil.deduplicateModules', () => { describe('basic deduplication', () => { it('should remove duplicate modules by path', () => { const mockModules: ModuleReference[] = [ { name: 'module1', path: '/path/to/module1' }, { name: 'module2', path: '/path/to/module2' }, { name: 'module1-duplicate', path: '/path/to/module1' }, // 路径重复 ]; const result = ModuleConfigUtil.deduplicateModules(mockModules); assert.strictEqual(result.length, 2); assert.ok(result.find(m => m.name === 'module1')); assert.ok(result.find(m => m.name === 'module2')); assert.strictEqual(result.find(m => m.name === 'module1-duplicate'), undefined); }); it('should remove duplicate modules by name', () => { const mockModules: ModuleReference[] = [ { name: 'module1', path: '/path/to/module1' }, { name: 'module2', path: '/path/to/module2' }, { name: 'module1', path: '/different/path/to/module1' }, // 名称重复但路径不同 ]; // 名称重复会抛出错误,因为实际实现不允许名称重复 assert.throws(() => { ModuleConfigUtil.deduplicateModules(mockModules); }, /Duplicate module name "module1"/); }); it('should handle empty input', () => { const result = ModuleConfigUtil.deduplicateModules([]); assert.strictEqual(result.length, 0); }); it('should handle single module', () => { const mockModules: ModuleReference[] = [ { name: 'module1', path: '/path/to/module1' }, ]; const result = ModuleConfigUtil.deduplicateModules(mockModules); assert.strictEqual(result.length, 1); assert.strictEqual(result[0].name, 'module1'); }); }); describe('optional module handling', () => { it('should prioritize non-optional modules over optional ones', () => { const mockModules: ModuleReference[] = [ { name: 'module1', path: '/path/to/module1', optional: true }, { name: 'module2', path: '/path/to/module2' }, { name: 'module1', path: '/path/to/module1' }, // 非 optional 版本 ]; const result = ModuleConfigUtil.deduplicateModules(mockModules); assert.strictEqual(result.length, 2); const module1 = result.find(m => m.name === 'module1'); assert.ok(module1); assert.strictEqual(module1!.optional, false); // 应该保留非 optional 版本 }); it('should keep optional module when no non-optional version exists', () => { const mockModules: ModuleReference[] = [ { name: 'module1', path: '/path/to/module1', optional: true }, { name: 'module2', path: '/path/to/module2' }, ]; const result = ModuleConfigUtil.deduplicateModules(mockModules); assert.strictEqual(result.length, 2); const module1 = result.find(m => m.name === 'module1'); assert.ok(module1); assert.strictEqual(module1!.optional, true); }); }); describe('name conflict handling', () => { it('should throw error for duplicate names with different paths', () => { const mockModules: ModuleReference[] = [ { name: 'module1', path: '/path/to/module1' }, { name: 'module1', path: '/different/path/to/module1' }, // 名称重复但路径不同 ]; assert.throws(() => { ModuleConfigUtil.deduplicateModules(mockModules); }, /Duplicate module name "module1"/); }); }); describe('complex scenarios', () => { it('should handle mixed scenarios with optional and non-optional modules', () => { const mockModules: ModuleReference[] = [ { name: 'module1', path: '/path/to/module1', optional: true }, { name: 'module2', path: '/path/to/module2' }, { name: 'module1', path: '/path/to/module1' }, // 非 optional 版本,应该替换 optional 版本 { name: 'module3', path: '/path/to/module3' }, ]; const result = ModuleConfigUtil.deduplicateModules(mockModules); assert.strictEqual(result.length, 3); const module1 = result.find(m => m.name === 'module1'); const module2 = result.find(m => m.name === 'module2'); const module3 = result.find(m => m.name === 'module3'); assert.ok(module1); assert.strictEqual(module1!.optional, false); // 应该保留非 optional 版本 assert.ok(module2); assert.ok(module3); }); it('should demonstrate mm usage for mocking', () => { const mockModules: ModuleReference[] = [ { name: 'module1', path: '/path/to/module1' }, { name: 'module1', path: '/different/path/to/module1' }, // 名称重复,应该触发错误 ]; assert.throws(() => { ModuleConfigUtil.deduplicateModules(mockModules); }, /Duplicate module name "module1"/); }); }); describe('complex scenarios', () => { it('should correctly update nameMap when replacing optional modules', () => { // 这个测试验证当非可选模块替换可选模块且名称不同时, // nameMap 能正确更新,不会在后续处理中产生错误的重复名称警告 const mockModules: ModuleReference[] = [ { name: 'oldName', path: '/path/to/module1', optional: true }, { name: 'newName', path: '/path/to/module1' }, // 非 optional 版本,名称不同 ]; const result = ModuleConfigUtil.deduplicateModules(mockModules); // 验证结果 assert.strictEqual(result.length, 1); const module1 = result.find(m => m.path === '/path/to/module1'); assert.ok(module1); assert.strictEqual(module1!.name, 'newName'); assert.strictEqual(module1!.optional, false); }); it('should handle name conflicts when replacing optional modules', () => { // 这个测试验证当非可选模块替换可选模块且名称相同时, // 能够正确处理名称冲突 const mockModules: ModuleReference[] = [ { name: 'module1', path: '/path/to/module1', optional: true }, { name: 'module1', path: '/path/to/module1' }, // 非 optional 版本,名称相同 ]; const result = ModuleConfigUtil.deduplicateModules(mockModules); // 验证结果 assert.strictEqual(result.length, 1); const module1 = result.find(m => m.path === '/path/to/module1'); assert.ok(module1); assert.strictEqual(module1!.name, 'module1'); assert.strictEqual(module1!.optional, false); }); it('should throw when replacing optional introduces a name collision with another path', () => { const mockModules: ModuleReference[] = [ { name: 'other', path: '/path/to/other' }, { name: 'oldName', path: '/path/to/module1', optional: true }, // 非 optional 版本替换同一路径,但名称与已有模块冲突 { name: 'other', path: '/path/to/module1' }, ]; assert.throws(() => { ModuleConfigUtil.deduplicateModules(mockModules); }, /Duplicate module name "other"/); }); }); }); ================================================ FILE: core/common-util/test/NameUtil.test.ts ================================================ import assert from 'node:assert'; import { NameUtil } from '..'; describe('test/NameUtil.test.ts', () => { it('should work', () => { class Hello {} const name = NameUtil.getClassName(Hello); assert(name === 'hello'); }); }); ================================================ FILE: core/common-util/test/ObjectUtil.test.ts ================================================ import assert from 'node:assert'; import { ObjectUtils } from '..'; export function InitTypeQualifier() { return function(_target: any, _propertyKey?: PropertyKey, _parameterIndex?: number) { console.log(_target, _propertyKey, _parameterIndex); // ... }; } export function ModuleQualifier(_foo: string) { return function(_target: any, _propertyKey?: PropertyKey, _parameterIndex?: number) { console.log(_target, _propertyKey, _parameterIndex, _foo); // ... }; } export function Inject(_arg?: any) { return function(_target: any, _propertyKey?: PropertyKey, _parameterIndex?: number) { console.log(_target, _propertyKey, _parameterIndex, _arg); // ... }; } describe('test/ObjectUtil.test.ts', () => { it('should work', () => { function mockFunction(/* test */ctx: object, foo: string, bar = '233') { // test console.log(ctx, foo, bar); } const argNames = ObjectUtils.getFunctionArgNameList(mockFunction); assert.deepStrictEqual(argNames, [ 'ctx', 'foo', 'bar' ]); }); it('getConstructorArgNameList should work', () => { class ConstructorObject { constructor( @InitTypeQualifier() @ModuleQualifier('foo') @Inject({ name: 'fooCache' }) readonly xCache: any, // fpp... /* test */ @Inject() readonly cache: unknown, readonly v233 = 666, ) { } } const argNames = ObjectUtils.getConstructorArgNameList(ConstructorObject); assert.deepStrictEqual(argNames, [ 'xCache', 'cache', 'v233', ]); }); }); ================================================ FILE: core/common-util/test/ProtoGraph.test.ts ================================================ import assert from 'node:assert'; import type { GraphNodeObj } from '@eggjs/tegg-types'; import { GraphNode, Graph } from '../src/Graph'; describe('test/LoadUnit/Graph.test.ts', () => { class GraphNodeVal implements GraphNodeObj { id: string; constructor(id: string) { this.id = id; } toString() { return `id:${this.id}`; } } describe('hasLoop', () => { it('if has loop, should return path', () => { const graph = new Graph(); const node1 = new GraphNode(new GraphNodeVal('1')); const node2 = new GraphNode(new GraphNodeVal('2')); const node3 = new GraphNode(new GraphNodeVal('3')); const node4 = new GraphNode(new GraphNodeVal('4')); const node5 = new GraphNode(new GraphNodeVal('5')); graph.addVertex(node2); graph.addVertex(node3); graph.addVertex(node1); graph.addVertex(node4); graph.addVertex(node5); graph.addEdge(node1, node5); graph.addEdge(node1, node2); graph.addEdge(node2, node3); graph.addEdge(node3, node1); const loopPath = graph.loopPath(); assert(loopPath!.toString() === 'id:2 -> id:3 -> id:1 -> id:2'); }); it('if do not has loop, should return undefined', () => { const graph = new Graph(); const node1 = new GraphNode({ id: '1' }); const node2 = new GraphNode({ id: '2' }); const node3 = new GraphNode({ id: '3' }); const node4 = new GraphNode({ id: '4' }); const node5 = new GraphNode({ id: '5' }); graph.addVertex(node2); graph.addVertex(node3); graph.addVertex(node1); graph.addVertex(node4); graph.addVertex(node5); graph.addEdge(node1, node5); graph.addEdge(node1, node2); graph.addEdge(node2, node3); graph.addEdge(node3, node4); const loopPath = graph.loopPath(); assert(!loopPath); }); }); describe('sort', () => { it('can not access vertex should at first', () => { const graph = new Graph(); const node1 = new GraphNode({ id: '1' }); const node2 = new GraphNode({ id: '2' }); const node3 = new GraphNode({ id: '3' }); graph.addVertex(node1); graph.addVertex(node2); graph.addVertex(node3); graph.addEdge(node2, node3); const sortRes = graph.sort(); assert(sortRes[0] === node1); }); it('should have reverse order with access direct', () => { const graph = new Graph(); const node1 = new GraphNode({ id: '1' }); const node2 = new GraphNode({ id: '2' }); const node3 = new GraphNode({ id: '3' }); const node4 = new GraphNode({ id: '4' }); const node5 = new GraphNode({ id: '5' }); graph.addVertex(node1); graph.addVertex(node2); graph.addVertex(node3); graph.addVertex(node4); graph.addVertex(node5); graph.addEdge(node1, node2); graph.addEdge(node2, node5); graph.addEdge(node3, node4); graph.addEdge(node4, node5); const sortRes = graph.sort(); assert.deepStrictEqual(sortRes, [ node5, node2, node1, node4, node3, ]); }); }); }); ================================================ FILE: core/common-util/test/TimerUtil.test.ts ================================================ import { strict as assert } from 'node:assert'; import { TimerUtil } from '..'; describe('test/TimerUtil.test.ts', () => { it('should sleep work', async () => { const start = Date.now(); await TimerUtil.sleep(3); const use = Date.now() - start; assert(use > 1, `use time ${use}ms`); }); describe('timeout', () => { const fixture = Symbol.for('timeout#res'); const delay = (ms: number) => () => new Promise(resolve => setTimeout(() => resolve(fixture), ms)); it('should work without ms', async () => { const res = await TimerUtil.timeout(delay(50)); assert.equal(res, fixture); }); it('should work with ms', async () => { const res = await TimerUtil.timeout(delay(50), 100); assert.equal(res, fixture); }); it('should timeout', async () => { await assert.rejects(TimerUtil.timeout(delay(100), 50), (e: any) => { return e instanceof TimerUtil.TimeoutError && e.message === 'timeout'; }); }); it('should throw error', async () => { const e = new Error('test'); await assert.rejects(TimerUtil.timeout(async () => { throw e; }, 100), (err: any) => err === e); }); }); }); ================================================ FILE: core/common-util/test/fixtures/apps/app-with-module-json/app/module-a/package.json ================================================ { "name": "module-a", "eggModule": { "name": "moduleA" } } ================================================ FILE: core/common-util/test/fixtures/apps/app-with-module-json/app/module-b/package.json ================================================ { "name": "module-b", "eggModule": { "name": "moduleB" } } ================================================ FILE: core/common-util/test/fixtures/apps/app-with-module-json/config/module.json ================================================ [ { "path": "../app/module-a" }, { "path": "../app/module-b" } ] ================================================ FILE: core/common-util/test/fixtures/apps/app-with-module-json/package.json ================================================ { "name": "foo" } ================================================ FILE: core/common-util/test/fixtures/apps/app-with-module-pkg-json/config/module.json ================================================ [ { "package": "module-a" } ] ================================================ FILE: core/common-util/test/fixtures/apps/app-with-module-pkg-json/node_modules/module-a/index.js ================================================ 'use strict'; module.exports = {}; ================================================ FILE: core/common-util/test/fixtures/apps/app-with-module-pkg-json/node_modules/module-a/package.json ================================================ { "name": "module-a", "main": "index.js", "eggModule": { "name": "moduleA" } } ================================================ FILE: core/common-util/test/fixtures/apps/app-with-module-pkg-json/package.json ================================================ { "name": "foo" } ================================================ FILE: core/common-util/test/fixtures/apps/app-with-modules/app/module-a/package.json ================================================ { "name": "module-a", "eggModule": { "name": "moduleA" } } ================================================ FILE: core/common-util/test/fixtures/apps/app-with-modules/package.json ================================================ { "name": "foo" } ================================================ FILE: core/common-util/test/fixtures/apps/app-with-no-module-json/.sff/.other/module-d/package.json ================================================ { "name": "module-d", "eggModule": { "name": "moduleD" } } ================================================ FILE: core/common-util/test/fixtures/apps/app-with-no-module-json/.sff/module-c/package.json ================================================ { "name": "module-c", "eggModule": { "name": "moduleC" } } ================================================ FILE: core/common-util/test/fixtures/apps/app-with-no-module-json/app/module-a/package.json ================================================ { "name": "module-a", "eggModule": { "name": "moduleA" } } ================================================ FILE: core/common-util/test/fixtures/apps/app-with-no-module-json/app/module-b/package.json ================================================ { "name": "module-b", "eggModule": { "name": "moduleB" } } ================================================ FILE: core/common-util/test/fixtures/apps/app-with-no-module-json/app/module-b/test/fixtures/module-e/package.json ================================================ { "name": "module-e", "eggModule": { "name": "moduleE" } } ================================================ FILE: core/common-util/test/fixtures/apps/app-with-no-module-json/node_modules/dep/index.js ================================================ 'use strict'; module.exports = {}; ================================================ FILE: core/common-util/test/fixtures/apps/app-with-no-module-json/node_modules/dep/package.json ================================================ { "name": "dep", "main": "index.js" } ================================================ FILE: core/common-util/test/fixtures/apps/app-with-no-module-json/node_modules/module-c/index.js ================================================ 'use strict'; module.exports = {}; ================================================ FILE: core/common-util/test/fixtures/apps/app-with-no-module-json/node_modules/module-c/package.json ================================================ { "name": "module-c", "main": "index.js", "eggModule": { "name": "moduleC" } } ================================================ FILE: core/common-util/test/fixtures/apps/app-with-no-module-json/package.json ================================================ { "name": "foo", "dependencies": { "module-c": "^1.0.0", "dep": "^1.0.0" } } ================================================ FILE: core/common-util/test/fixtures/apps/app-with-no-module-json-duplicated/.sff/.other/module-d/package.json ================================================ { "name": "module-d", "eggModule": { "name": "moduleD" } } ================================================ FILE: core/common-util/test/fixtures/apps/app-with-no-module-json-duplicated/.sff/module-c/package.json ================================================ { "name": "module-c", "eggModule": { "name": "moduleC" } } ================================================ FILE: core/common-util/test/fixtures/apps/app-with-no-module-json-duplicated/app/module-a/package.json ================================================ { "name": "module-a", "eggModule": { "name": "moduleA" } } ================================================ FILE: core/common-util/test/fixtures/apps/app-with-no-module-json-duplicated/app/module-b/package.json ================================================ { "name": "module-b", "eggModule": { "name": "moduleB" } } ================================================ FILE: core/common-util/test/fixtures/apps/app-with-no-module-json-duplicated/app/module-b/test/fixtures/module-e/package.json ================================================ { "name": "module-e", "eggModule": { "name": "moduleE" } } ================================================ FILE: core/common-util/test/fixtures/apps/app-with-no-module-json-duplicated/node_modules/module-b/index.js ================================================ 'use strict'; module.exports = {}; ================================================ FILE: core/common-util/test/fixtures/apps/app-with-no-module-json-duplicated/node_modules/module-b/package.json ================================================ { "name": "module-b", "main": "index.js", "eggModule": { "name": "moduleB" } } ================================================ FILE: core/common-util/test/fixtures/apps/app-with-no-module-json-duplicated/package.json ================================================ { "name": "foo", "dependencies": { "module-b": "^1.0.0" } } ================================================ FILE: core/common-util/test/fixtures/apps/app-with-symlink/app/module-a/package.json ================================================ { "name": "module-a", "eggModule": { "name": "moduleA" } } ================================================ FILE: core/common-util/test/fixtures/apps/app-with-symlink/package.json ================================================ { "name": "app-with-symlink" } ================================================ FILE: core/common-util/test/fixtures/modules/dev-module-config/module.dev.yml ================================================ mysql: port: 11306 ================================================ FILE: core/common-util/test/fixtures/modules/dev-module-config/module.yml ================================================ mysql: host: 127.0.0.1 ================================================ FILE: core/common-util/test/fixtures/modules/foo-yaml/module.yml ================================================ mysql: host: 127.0.0.1 ================================================ FILE: core/common-util/test/fixtures/monorepo/foo/.gitkeep ================================================ ================================================ FILE: core/common-util/test/fixtures/monorepo/packages/a/node_modules/c/package.json ================================================ { "eggModule": { "name": "c" }, "dependencies": {}, "name": "c", "author": "" } ================================================ FILE: core/common-util/test/fixtures/monorepo/packages/a/package.json ================================================ { "eggModule": { "name": "a" }, "dependencies": { "c": "*" }, "name": "b", "author": "" } ================================================ FILE: core/common-util/test/fixtures/monorepo/packages/b/package.json ================================================ { "eggModule": { "name": "b" }, "dependencies": { "a": "*" }, "name": "b", "author": "" } ================================================ FILE: core/common-util/test/fixtures/monorepo/packages/d/node_modules/e/package.json ================================================ { "eggModule": { "name": "e" }, "name": "e", "exports": { "./package.json": "./package.json" }, "author": "" } ================================================ FILE: core/common-util/test/fixtures/monorepo/packages/d/node_modules/f/package.json ================================================ { "eggModule": { "name": "f" }, "name": "f", "exports": { "./index.js": "./index.js" }, "author": "" } ================================================ FILE: core/common-util/test/fixtures/monorepo/packages/d/package.json ================================================ { "eggModule": { "name": "d" }, "dependencies": { "e": "*", "f": "*" }, "name": "d", "author": "" } ================================================ FILE: core/common-util/tsconfig.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "dist", "baseUrl": "./" }, "exclude": [ "dist", "node_modules", "test" ] } ================================================ FILE: core/common-util/tsconfig.pub.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "dist", "baseUrl": "./" }, "exclude": [ "dist", "node_modules", "test" ] } ================================================ FILE: core/controller-decorator/CHANGELOG.md ================================================ # Change Log All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. ## [3.78.15](https://github.com/eggjs/tegg/compare/v3.78.14...v3.78.15) (2026-04-23) ### Bug Fixes * **agent-runtime:** hold cancelRun until executor session is committed ([#441](https://github.com/eggjs/tegg/issues/441)) ([4e02a28](https://github.com/eggjs/tegg/commit/4e02a28bdfe9b924c1190482fd3d85f8cad1fcfa)) ## [3.78.14](https://github.com/eggjs/tegg/compare/v3.78.13...v3.78.14) (2026-04-21) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.78.13](https://github.com/eggjs/tegg/compare/v3.78.12...v3.78.13) (2026-04-21) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.78.12](https://github.com/eggjs/tegg/compare/v3.78.11...v3.78.12) (2026-04-18) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.78.11](https://github.com/eggjs/tegg/compare/v3.78.10...v3.78.11) (2026-04-18) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.78.10](https://github.com/eggjs/tegg/compare/v3.78.9...v3.78.10) (2026-04-18) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.78.9](https://github.com/eggjs/tegg/compare/v3.78.8...v3.78.9) (2026-04-18) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.78.8](https://github.com/eggjs/tegg/compare/v3.78.7...v3.78.8) (2026-04-18) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.78.7](https://github.com/eggjs/tegg/compare/v3.78.6...v3.78.7) (2026-04-18) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.78.6](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.6) (2026-04-18) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.78.5](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.5) (2026-04-18) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.78.4](https://github.com/eggjs/tegg/compare/v3.78.3...v3.78.4) (2026-04-18) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.78.3](https://github.com/eggjs/tegg/compare/v3.78.2...v3.78.3) (2026-04-15) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.78.2](https://github.com/eggjs/tegg/compare/v3.78.1...v3.78.2) (2026-04-09) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.78.1](https://github.com/eggjs/tegg/compare/v3.78.0...v3.78.1) (2026-04-09) **Note:** Version bump only for package @eggjs/controller-decorator # [3.78.0](https://github.com/eggjs/tegg/compare/v3.77.2...v3.78.0) (2026-04-09) ### Features * **agent-runtime:** rewrite streamRun with StreamEvent format and reconnection ([#432](https://github.com/eggjs/tegg/issues/432)) ([d03dac2](https://github.com/eggjs/tegg/commit/d03dac2ddd78641acb47e19275488ad9fbfcda2a)) ## [3.77.2](https://github.com/eggjs/tegg/compare/v3.77.1...v3.77.2) (2026-04-07) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.77.1](https://github.com/eggjs/tegg/compare/v3.77.0...v3.77.1) (2026-04-01) **Note:** Version bump only for package @eggjs/controller-decorator # [3.77.0](https://github.com/eggjs/tegg/compare/v3.76.1...v3.77.0) (2026-04-01) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.76.1](https://github.com/eggjs/tegg/compare/v3.76.0...v3.76.1) (2026-04-01) **Note:** Version bump only for package @eggjs/controller-decorator # [3.76.0](https://github.com/eggjs/tegg/compare/v3.75.1...v3.76.0) (2026-04-01) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.75.1](https://github.com/eggjs/tegg/compare/v3.75.0...v3.75.1) (2026-04-01) **Note:** Version bump only for package @eggjs/controller-decorator # [3.75.0](https://github.com/eggjs/tegg/compare/v3.74.0...v3.75.0) (2026-03-30) **Note:** Version bump only for package @eggjs/controller-decorator # [3.74.0](https://github.com/eggjs/tegg/compare/v3.73.0...v3.74.0) (2026-03-30) **Note:** Version bump only for package @eggjs/controller-decorator # [3.73.0](https://github.com/eggjs/tegg/compare/v3.72.0...v3.73.0) (2026-03-25) ### Features * add agent-runtime package with @AgentController decorator ([#411](https://github.com/eggjs/tegg/issues/411)) ([d4d0006](https://github.com/eggjs/tegg/commit/d4d00061e90230f82c0958bcf5268f8a511395db)) # [3.72.0](https://github.com/eggjs/tegg/compare/v3.71.2...v3.72.0) (2026-02-04) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.71.2](https://github.com/eggjs/tegg/compare/v3.71.1...v3.71.2) (2026-01-30) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.71.1](https://github.com/eggjs/tegg/compare/v3.71.0...v3.71.1) (2026-01-28) **Note:** Version bump only for package @eggjs/controller-decorator # [3.71.0](https://github.com/eggjs/tegg/compare/v3.70.1...v3.71.0) (2026-01-26) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.70.1](https://github.com/eggjs/tegg/compare/v3.70.0...v3.70.1) (2026-01-14) **Note:** Version bump only for package @eggjs/controller-decorator # [3.70.0](https://github.com/eggjs/tegg/compare/v3.69.0...v3.70.0) (2026-01-07) **Note:** Version bump only for package @eggjs/controller-decorator # [3.69.0](https://github.com/eggjs/tegg/compare/v3.68.0...v3.69.0) (2026-01-06) **Note:** Version bump only for package @eggjs/controller-decorator # [3.68.0](https://github.com/eggjs/tegg/compare/v3.67.2...v3.68.0) (2025-12-29) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.67.2](https://github.com/eggjs/tegg/compare/v3.67.1...v3.67.2) (2025-12-24) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.67.1](https://github.com/eggjs/tegg/compare/v3.67.0...v3.67.1) (2025-12-23) ### Bug Fixes * zod v4 ([#381](https://github.com/eggjs/tegg/issues/381)) ([43614c8](https://github.com/eggjs/tegg/commit/43614c8734084a98b1a25c6e907c9c12ff41cb8f)) # [3.67.0](https://github.com/eggjs/tegg/compare/v3.66.0...v3.67.0) (2025-12-18) **Note:** Version bump only for package @eggjs/controller-decorator # [3.66.0](https://github.com/eggjs/tegg/compare/v3.65.3...v3.66.0) (2025-12-16) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.65.3](https://github.com/eggjs/tegg/compare/v3.65.2...v3.65.3) (2025-12-16) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.65.2](https://github.com/eggjs/tegg/compare/v3.65.1...v3.65.2) (2025-12-16) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.65.1](https://github.com/eggjs/tegg/compare/v3.65.0...v3.65.1) (2025-12-16) **Note:** Version bump only for package @eggjs/controller-decorator # [3.65.0](https://github.com/eggjs/tegg/compare/v3.64.5...v3.65.0) (2025-12-10) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.64.5](https://github.com/eggjs/tegg/compare/v3.64.4...v3.64.5) (2025-12-08) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.64.4](https://github.com/eggjs/tegg/compare/v3.64.3...v3.64.4) (2025-12-06) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.64.3](https://github.com/eggjs/tegg/compare/v3.64.2...v3.64.3) (2025-12-06) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.64.2](https://github.com/eggjs/tegg/compare/v3.64.1...v3.64.2) (2025-11-27) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.64.1](https://github.com/eggjs/tegg/compare/v3.64.0...v3.64.1) (2025-11-27) **Note:** Version bump only for package @eggjs/controller-decorator # [3.64.0](https://github.com/eggjs/tegg/compare/v3.63.2...v3.64.0) (2025-11-27) ### Bug Fixes * mcp zod type and langchain test version ([#369](https://github.com/eggjs/tegg/issues/369)) ([8178168](https://github.com/eggjs/tegg/commit/81781685c392346d21c56b649bfe8bb7a99bc9fb)) ## [3.63.2](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.2) (2025-11-18) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.63.1](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.1) (2025-11-18) **Note:** Version bump only for package @eggjs/controller-decorator # [3.63.0](https://github.com/eggjs/tegg/compare/v3.62.3...v3.63.0) (2025-11-18) ### Features * add MiddlewareGraphHook to handle controller middleware depende… ([#361](https://github.com/eggjs/tegg/issues/361)) ([7ab3eae](https://github.com/eggjs/tegg/commit/7ab3eae1af20e14101e1df63628a426cb5f6d3db)) ## [3.62.3](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.3) (2025-11-05) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.62.2](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.2) (2025-11-05) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.62.1](https://github.com/eggjs/tegg/compare/v3.62.0...v3.62.1) (2025-11-03) **Note:** Version bump only for package @eggjs/controller-decorator # [3.62.0](https://github.com/eggjs/tegg/compare/v3.61.0...v3.62.0) (2025-09-24) **Note:** Version bump only for package @eggjs/controller-decorator # [3.61.0](https://github.com/eggjs/tegg/compare/v3.60.3...v3.61.0) (2025-08-15) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.60.3](https://github.com/eggjs/tegg/compare/v3.60.2...v3.60.3) (2025-08-06) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.60.2](https://github.com/eggjs/tegg/compare/v3.60.1...v3.60.2) (2025-08-06) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.60.1](https://github.com/eggjs/tegg/compare/v3.60.0...v3.60.1) (2025-07-28) **Note:** Version bump only for package @eggjs/controller-decorator # [3.60.0](https://github.com/eggjs/tegg/compare/v3.59.1...v3.60.0) (2025-07-28) ### Features * add multiple mcp server ([#337](https://github.com/eggjs/tegg/issues/337)) ([5b5e233](https://github.com/eggjs/tegg/commit/5b5e233510111b63bbcba14da1703becccebbd2f)) ## [3.59.1](https://github.com/eggjs/tegg/compare/v3.59.0...v3.59.1) (2025-07-16) **Note:** Version bump only for package @eggjs/controller-decorator # [3.59.0](https://github.com/eggjs/tegg/compare/v3.58.0...v3.59.0) (2025-07-07) **Note:** Version bump only for package @eggjs/controller-decorator # [3.58.0](https://github.com/eggjs/tegg/compare/v3.57.14...v3.58.0) (2025-07-01) ### Features * add timeout ([#334](https://github.com/eggjs/tegg/issues/334)) ([6d5d94b](https://github.com/eggjs/tegg/commit/6d5d94b6f319388a94b4adf4d427b95d2b851c17)) ## [3.57.14](https://github.com/eggjs/tegg/compare/v3.57.13...v3.57.14) (2025-06-18) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.57.13](https://github.com/eggjs/tegg/compare/v3.57.12...v3.57.13) (2025-06-18) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.57.12](https://github.com/eggjs/tegg/compare/v3.57.11...v3.57.12) (2025-06-16) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.57.11](https://github.com/eggjs/tegg/compare/v3.57.10...v3.57.11) (2025-06-16) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.57.10](https://github.com/eggjs/tegg/compare/v3.57.9...v3.57.10) (2025-06-12) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.57.9](https://github.com/eggjs/tegg/compare/v3.57.8...v3.57.9) (2025-05-29) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.57.8](https://github.com/eggjs/tegg/compare/v3.57.7...v3.57.8) (2025-05-29) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.57.7](https://github.com/eggjs/tegg/compare/v3.57.6...v3.57.7) (2025-05-28) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.57.6](https://github.com/eggjs/tegg/compare/v3.57.5...v3.57.6) (2025-05-27) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.57.5](https://github.com/eggjs/tegg/compare/v3.57.4...v3.57.5) (2025-05-15) ### Bug Fixes * zod phantom dependency ([#322](https://github.com/eggjs/tegg/issues/322)) ([e92372e](https://github.com/eggjs/tegg/commit/e92372eb884d0f5d8227d340a3d7db01b51267cf)) ## [3.57.4](https://github.com/eggjs/tegg/compare/v3.57.3...v3.57.4) (2025-05-14) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.57.3](https://github.com/eggjs/tegg/compare/v3.57.2...v3.57.3) (2025-05-13) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.57.2](https://github.com/eggjs/tegg/compare/v3.57.1...v3.57.2) (2025-05-13) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.57.1](https://github.com/eggjs/tegg/compare/v3.57.0...v3.57.1) (2025-05-13) **Note:** Version bump only for package @eggjs/controller-decorator # [3.57.0](https://github.com/eggjs/tegg/compare/v3.56.3...v3.57.0) (2025-05-09) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.56.3](https://github.com/eggjs/tegg/compare/v3.56.2...v3.56.3) (2025-05-07) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.56.2](https://github.com/eggjs/tegg/compare/v3.56.1...v3.56.2) (2025-05-01) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.56.1](https://github.com/eggjs/tegg/compare/v3.56.0...v3.56.1) (2025-04-29) **Note:** Version bump only for package @eggjs/controller-decorator # [3.56.0](https://github.com/eggjs/tegg/compare/v3.55.0...v3.56.0) (2025-04-29) **Note:** Version bump only for package @eggjs/controller-decorator # [3.55.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.55.0) (2025-04-29) ### Features * add mcp ([#307](https://github.com/eggjs/tegg/issues/307)) ([a9a57b4](https://github.com/eggjs/tegg/commit/a9a57b4d7102dd552e09d33c3f82fc15a245790a)) # [3.54.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.54.0) (2025-04-29) ### Features * add mcp ([#307](https://github.com/eggjs/tegg/issues/307)) ([a9a57b4](https://github.com/eggjs/tegg/commit/a9a57b4d7102dd552e09d33c3f82fc15a245790a)) # [3.53.0](https://github.com/eggjs/tegg/compare/v3.52.1...v3.53.0) (2025-04-21) ### Bug Fixes * stream end ([#302](https://github.com/eggjs/tegg/issues/302)) ([7f1f4b3](https://github.com/eggjs/tegg/commit/7f1f4b396294af5609c9454f6882d213dc237512)) ### Features * add timeout metadata for http controller ([#301](https://github.com/eggjs/tegg/issues/301)) ([68980c2](https://github.com/eggjs/tegg/commit/68980c23de81dbc9bd86c1d8df7b3952f52aa5ce)) ## [3.52.1](https://github.com/eggjs/tegg/compare/v3.52.0...v3.52.1) (2025-03-14) **Note:** Version bump only for package @eggjs/controller-decorator # [3.52.0](https://github.com/eggjs/tegg/compare/v3.51.2...v3.52.0) (2024-12-30) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.51.2](https://github.com/eggjs/tegg/compare/v3.51.1...v3.51.2) (2024-12-09) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.51.1](https://github.com/eggjs/tegg/compare/v3.51.0...v3.51.1) (2024-11-06) **Note:** Version bump only for package @eggjs/controller-decorator # [3.51.0](https://github.com/eggjs/tegg/compare/v3.50.1...v3.51.0) (2024-10-30) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.50.1](https://github.com/eggjs/tegg/compare/v3.50.0...v3.50.1) (2024-10-23) **Note:** Version bump only for package @eggjs/controller-decorator # [3.50.0](https://github.com/eggjs/tegg/compare/v3.49.0...v3.50.0) (2024-10-22) **Note:** Version bump only for package @eggjs/controller-decorator # [3.49.0](https://github.com/eggjs/tegg/compare/v3.48.1...v3.49.0) (2024-10-21) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.48.1](https://github.com/eggjs/tegg/compare/v3.48.0...v3.48.1) (2024-10-14) **Note:** Version bump only for package @eggjs/controller-decorator # [3.48.0](https://github.com/eggjs/tegg/compare/v3.47.2...v3.48.0) (2024-10-10) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.47.2](https://github.com/eggjs/tegg/compare/v3.47.1...v3.47.2) (2024-10-10) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.47.1](https://github.com/eggjs/tegg/compare/v3.47.0...v3.47.1) (2024-10-10) **Note:** Version bump only for package @eggjs/controller-decorator # [3.47.0](https://github.com/eggjs/tegg/compare/v3.46.4...v3.47.0) (2024-10-10) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.46.4](https://github.com/eggjs/tegg/compare/v3.46.3...v3.46.4) (2024-10-09) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.46.3](https://github.com/eggjs/tegg/compare/v3.46.2...v3.46.3) (2024-10-08) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.46.2](https://github.com/eggjs/tegg/compare/v3.46.1...v3.46.2) (2024-10-07) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.46.1](https://github.com/eggjs/tegg/compare/v3.46.0...v3.46.1) (2024-09-30) **Note:** Version bump only for package @eggjs/controller-decorator # [3.46.0](https://github.com/eggjs/tegg/compare/v3.45.0...v3.46.0) (2024-09-29) **Note:** Version bump only for package @eggjs/controller-decorator # [3.45.0](https://github.com/eggjs/tegg/compare/v3.44.1...v3.45.0) (2024-09-29) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.44.1](https://github.com/eggjs/tegg/compare/v3.44.0...v3.44.1) (2024-09-29) **Note:** Version bump only for package @eggjs/controller-decorator # [3.44.0](https://github.com/eggjs/tegg/compare/v3.43.2...v3.44.0) (2024-09-29) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.43.2](https://github.com/eggjs/tegg/compare/v3.43.1...v3.43.2) (2024-09-14) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.43.1](https://github.com/eggjs/tegg/compare/v3.43.0...v3.43.1) (2024-09-14) **Note:** Version bump only for package @eggjs/controller-decorator # [3.43.0](https://github.com/eggjs/tegg/compare/v3.42.0...v3.43.0) (2024-09-13) ### Features * add http cookies ([#235](https://github.com/eggjs/tegg/issues/235)) ([f46efa5](https://github.com/eggjs/tegg/commit/f46efa54b03bad41504bf76f6ed2baa8c48858ce)) # [3.42.0](https://github.com/eggjs/tegg/compare/v3.41.0...v3.42.0) (2024-09-10) **Note:** Version bump only for package @eggjs/controller-decorator # [3.41.0](https://github.com/eggjs/tegg/compare/v3.40.1...v3.41.0) (2024-08-26) ### Features * export controller info util for get aop middleware ([#233](https://github.com/eggjs/tegg/issues/233)) ([1d3cca8](https://github.com/eggjs/tegg/commit/1d3cca8fad859ae54fb10c1700dda261e93055b3)) ## [3.40.1](https://github.com/eggjs/tegg/compare/v3.40.0...v3.40.1) (2024-08-23) **Note:** Version bump only for package @eggjs/controller-decorator # [3.40.0](https://github.com/eggjs/tegg/compare/v3.39.5...v3.40.0) (2024-08-22) ### Features * @Middleware support Advice ([#231](https://github.com/eggjs/tegg/issues/231)) ([613a89d](https://github.com/eggjs/tegg/commit/613a89da7ea6dd70d50e34aa9f4152358a622625)) ## [3.39.5](https://github.com/eggjs/tegg/compare/v3.39.4...v3.39.5) (2024-08-09) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.39.4](https://github.com/eggjs/tegg/compare/v3.39.3...v3.39.4) (2024-07-09) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.39.3](https://github.com/eggjs/tegg/compare/v3.39.2...v3.39.3) (2024-04-28) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.39.2](https://github.com/eggjs/tegg/compare/v3.39.1...v3.39.2) (2024-04-28) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.39.1](https://github.com/eggjs/tegg/compare/v3.39.0...v3.39.1) (2024-04-28) **Note:** Version bump only for package @eggjs/controller-decorator # [3.39.0](https://github.com/eggjs/tegg/compare/v3.38.0...v3.39.0) (2024-04-19) **Note:** Version bump only for package @eggjs/controller-decorator # [3.38.0](https://github.com/eggjs/tegg/compare/v3.37.3...v3.38.0) (2024-04-18) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.37.3](https://github.com/eggjs/tegg/compare/v3.37.2...v3.37.3) (2024-04-17) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.37.2](https://github.com/eggjs/tegg/compare/v3.37.1...v3.37.2) (2024-04-17) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.37.1](https://github.com/eggjs/tegg/compare/v3.37.0...v3.37.1) (2024-04-16) **Note:** Version bump only for package @eggjs/controller-decorator # [3.37.0](https://github.com/eggjs/tegg/compare/v3.36.3...v3.37.0) (2024-04-16) ### Features * add HTTPHeaders decorator ([#208](https://github.com/eggjs/tegg/issues/208)) ([4678c45](https://github.com/eggjs/tegg/commit/4678c450d8b3c632bbdbe2b49b9c02e99f16733c)) ## [3.36.3](https://github.com/eggjs/tegg/compare/v3.36.2...v3.36.3) (2024-04-10) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.36.2](https://github.com/eggjs/tegg/compare/v3.36.1...v3.36.2) (2024-04-08) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.36.1](https://github.com/eggjs/tegg/compare/v3.36.0...v3.36.1) (2024-04-07) **Note:** Version bump only for package @eggjs/controller-decorator # [3.36.0](https://github.com/eggjs/tegg/compare/v3.35.1...v3.36.0) (2024-04-02) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.35.1](https://github.com/eggjs/tegg/compare/v3.35.0...v3.35.1) (2024-03-26) **Note:** Version bump only for package @eggjs/controller-decorator # [3.35.0](https://github.com/eggjs/tegg/compare/v3.34.0...v3.35.0) (2024-03-26) **Note:** Version bump only for package @eggjs/controller-decorator # [3.34.0](https://github.com/eggjs/tegg/compare/v3.33.1...v3.34.0) (2024-03-22) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.33.1](https://github.com/eggjs/tegg/compare/v3.33.0...v3.33.1) (2024-03-22) **Note:** Version bump only for package @eggjs/controller-decorator # [3.33.0](https://github.com/eggjs/tegg/compare/v3.32.0...v3.33.0) (2024-03-22) **Note:** Version bump only for package @eggjs/controller-decorator # [3.32.0](https://github.com/eggjs/tegg/compare/v3.31.0...v3.32.0) (2024-02-19) **Note:** Version bump only for package @eggjs/controller-decorator # [3.31.0](https://github.com/eggjs/tegg/compare/v3.30.1...v3.31.0) (2024-01-31) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.30.1](https://github.com/eggjs/tegg/compare/v3.30.0...v3.30.1) (2024-01-25) **Note:** Version bump only for package @eggjs/controller-decorator # [3.30.0](https://github.com/eggjs/tegg/compare/v3.29.0...v3.30.0) (2024-01-17) **Note:** Version bump only for package @eggjs/controller-decorator # [3.29.0](https://github.com/eggjs/tegg/compare/v3.28.2...v3.29.0) (2023-12-26) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.28.2](https://github.com/eggjs/tegg/compare/v3.28.1...v3.28.2) (2023-12-12) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.28.1](https://github.com/eggjs/tegg/compare/v3.28.0...v3.28.1) (2023-12-11) **Note:** Version bump only for package @eggjs/controller-decorator # [3.28.0](https://github.com/eggjs/tegg/compare/v3.27.0...v3.28.0) (2023-12-10) **Note:** Version bump only for package @eggjs/controller-decorator # [3.27.0](https://github.com/eggjs/tegg/compare/v3.26.0...v3.27.0) (2023-11-23) **Note:** Version bump only for package @eggjs/controller-decorator # [3.26.0](https://github.com/eggjs/tegg/compare/v3.25.2...v3.26.0) (2023-11-17) ### Features * Add ControllerType = SCHEDULE ([#166](https://github.com/eggjs/tegg/issues/166)) ([2c22e7d](https://github.com/eggjs/tegg/commit/2c22e7d4943659848ddbae7b552febef38b57a3d)) ## [3.25.2](https://github.com/eggjs/tegg/compare/v3.25.1...v3.25.2) (2023-11-06) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.25.1](https://github.com/eggjs/tegg/compare/v3.25.0...v3.25.1) (2023-11-03) **Note:** Version bump only for package @eggjs/controller-decorator # [3.25.0](https://github.com/eggjs/tegg/compare/v3.24.0...v3.25.0) (2023-11-03) **Note:** Version bump only for package @eggjs/controller-decorator # [3.24.0](https://github.com/eggjs/tegg/compare/v3.23.0...v3.24.0) (2023-10-26) ### Features * support Request decorators for HTTPController ([#159](https://github.com/eggjs/tegg/issues/159)) ([945e1eb](https://github.com/eggjs/tegg/commit/945e1eb18237f40879acdd2e43cd53dd2e8272a9)) # [3.23.0](https://github.com/eggjs/tegg/compare/v3.22.0...v3.23.0) (2023-09-20) ### Bug Fixes * typo `acL` to `acl` ([#156](https://github.com/eggjs/tegg/issues/156)) ([a775d34](https://github.com/eggjs/tegg/commit/a775d34d38c481c5f9e90504224553d31ad728d3)) # [3.20.0](https://github.com/eggjs/tegg/compare/v3.19.0...v3.20.0) (2023-09-07) **Note:** Version bump only for package @eggjs/controller-decorator # [3.19.0](https://github.com/eggjs/tegg/compare/v3.18.1...v3.19.0) (2023-08-31) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.18.1](https://github.com/eggjs/tegg/compare/v3.18.0...v3.18.1) (2023-08-29) **Note:** Version bump only for package @eggjs/controller-decorator # [3.18.0](https://github.com/eggjs/tegg/compare/v3.17.0...v3.18.0) (2023-08-29) **Note:** Version bump only for package @eggjs/controller-decorator # [3.17.0](https://github.com/eggjs/tegg/compare/v3.16.0...v3.17.0) (2023-08-24) **Note:** Version bump only for package @eggjs/controller-decorator # [3.16.0](https://github.com/eggjs/tegg/compare/v3.15.0...v3.16.0) (2023-08-24) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.14.3](https://github.com/eggjs/tegg/compare/v3.14.2...v3.14.3) (2023-08-14) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.14.2](https://github.com/eggjs/tegg/compare/v3.14.1...v3.14.2) (2023-08-14) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.14.1](https://github.com/eggjs/tegg/compare/v3.14.0...v3.14.1) (2023-08-11) **Note:** Version bump only for package @eggjs/controller-decorator # [3.14.0](https://github.com/eggjs/tegg/compare/v3.13.0...v3.14.0) (2023-07-31) **Note:** Version bump only for package @eggjs/controller-decorator # [3.13.0](https://github.com/eggjs/tegg/compare/v3.12.0...v3.13.0) (2023-07-25) **Note:** Version bump only for package @eggjs/controller-decorator # [3.12.0](https://github.com/eggjs/tegg/compare/v3.11.1...v3.12.0) (2023-07-13) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.11.1](https://github.com/eggjs/tegg/compare/v3.11.0...v3.11.1) (2023-06-29) **Note:** Version bump only for package @eggjs/controller-decorator # [3.11.0](https://github.com/eggjs/tegg/compare/v3.10.0...v3.11.0) (2023-06-29) **Note:** Version bump only for package @eggjs/controller-decorator # [3.10.0](https://github.com/eggjs/tegg/compare/v3.9.0...v3.10.0) (2023-06-28) **Note:** Version bump only for package @eggjs/controller-decorator # [3.8.0](https://github.com/eggjs/tegg/compare/v3.7.0...v3.8.0) (2023-05-30) ### Features * impl EggObjectLifecycle hook in decorator ([#119](https://github.com/eggjs/tegg/issues/119)) ([cced8a2](https://github.com/eggjs/tegg/commit/cced8a2e009c33d5040fa21d00409fddef471b0e)) # [3.7.0](https://github.com/eggjs/tegg/compare/v3.6.3...v3.7.0) (2023-04-03) **Note:** Version bump only for package @eggjs/controller-decorator # [3.5.0](https://github.com/eggjs/tegg/compare/v3.4.1...v3.5.0) (2023-02-10) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.2.3](https://github.com/eggjs/tegg/compare/v3.2.2...v3.2.3) (2023-01-16) **Note:** Version bump only for package @eggjs/controller-decorator ## [3.2.2](https://github.com/eggjs/tegg/compare/v3.2.1...v3.2.2) (2023-01-06) **Note:** Version bump only for package @eggjs/controller-decorator # [3.1.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.1.0) (2022-12-27) ### Features * **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) * impl Host decorator ([#48](https://github.com/eggjs/tegg/issues/48)) ([65dc7a8](https://github.com/eggjs/tegg/commit/65dc7a899ba72dd0851c35046562766d7f2b71b6)) * middleware decorator allow multi middleware function ([#46](https://github.com/eggjs/tegg/issues/46)) ([a4b55f7](https://github.com/eggjs/tegg/commit/a4b55f7065c3d78e2c98c4b05f01871f666542ef)) * multi host decorator ([#68](https://github.com/eggjs/tegg/issues/68)) ([f6679de](https://github.com/eggjs/tegg/commit/f6679de1495024ecb9182168843300aa91288508)) # [3.0.0](https://github.com/eggjs/tegg/compare/v3.0.0-alpha.0...v3.0.0) (2022-12-26) **Note:** Version bump only for package @eggjs/controller-decorator # [3.0.0-alpha.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.0.0-alpha.0) (2022-12-22) ### Features * **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) * impl Host decorator ([#48](https://github.com/eggjs/tegg/issues/48)) ([65dc7a8](https://github.com/eggjs/tegg/commit/65dc7a899ba72dd0851c35046562766d7f2b71b6)) * middleware decorator allow multi middleware function ([#46](https://github.com/eggjs/tegg/issues/46)) ([a4b55f7](https://github.com/eggjs/tegg/commit/a4b55f7065c3d78e2c98c4b05f01871f666542ef)) * multi host decorator ([#68](https://github.com/eggjs/tegg/issues/68)) ([f6679de](https://github.com/eggjs/tegg/commit/f6679de1495024ecb9182168843300aa91288508)) # [1.5.0](https://github.com/eggjs/tegg/compare/@eggjs/controller-decorator@1.4.0...@eggjs/controller-decorator@1.5.0) (2022-08-16) ### Features * impl Host decorator ([#48](https://github.com/eggjs/tegg/issues/48)) ([65dc7a8](https://github.com/eggjs/tegg/commit/65dc7a899ba72dd0851c35046562766d7f2b71b6)) # 1.4.0 (2022-07-28) ### Features * middleware decorator allow multi middleware function ([#46](https://github.com/eggjs/tegg/issues/46)) ([a4b55f7](https://github.com/eggjs/tegg/commit/a4b55f7065c3d78e2c98c4b05f01871f666542ef)) # 1.3.0 (2022-07-01) ## 1.1.1 (2022-06-21) # 1.1.0 (2022-06-15) ## 1.0.1 (2022-02-08) # 1.0.0 (2022-02-08) # 0.2.0 (2022-01-20) ### Features * impl aop ([c53df00](https://github.com/eggjs/tegg/commit/c53df001d1455a0a105689694775d880541d9d2f)) ## 0.1.19 (2022-01-05) ## 0.1.18 (2021-12-31) ## 0.1.16 (2021-12-15) ## 0.1.13 (2021-11-14) ### Features * impl standalone tegg ([af9f682](https://github.com/eggjs/tegg/commit/af9f6826ef882ef7206e80ee25433a2b19012995)) ## 0.1.12 (2021-11-01) ### Bug Fixes * fix params in controller path ([c652cf2](https://github.com/eggjs/tegg/commit/c652cf211f9a422b2a53a6dc983488c774d973d2)) ## 0.1.10 (2021-10-26) ## 0.1.6 (2021-09-09) ## 0.1.5 (2021-09-08) ## 0.1.2 (2021-09-01) ### Bug Fixes * fix url path join in windows ([9c9176a](https://github.com/eggjs/tegg/commit/9c9176ae1f094016ffa80ace7aa611136bfb9046)) ### Features * impl dynamic inject ([bdafd5a](https://github.com/eggjs/tegg/commit/bdafd5a445b815515fc9e872fcfefc67a53ea562)) # 0.1.0 (2021-07-22) ### Bug Fixes * set publishConfig.access to public ([527f1fa](https://github.com/eggjs/tegg/commit/527f1fa8e3bcaf45ff5b3a63d90473d4a6a2e2b0)) # [1.3.0](https://github.com/eggjs/tegg/compare/v1.2.0...v1.3.0) (2022-07-01) **Note:** Version bump only for package @eggjs/controller-decorator # [0.2.0](https://github.com/eggjs/tegg/compare/v0.1.19...v0.2.0) (2022-01-20) ### Features * impl aop ([c53df00](https://github.com/eggjs/tegg/commit/c53df001d1455a0a105689694775d880541d9d2f)) ================================================ FILE: core/controller-decorator/README.json ================================================ # @eggjs/controller-decorator # Usage Please read [@eggjs/tegg-controller-plugin](../../plugin/controller/README.md) ================================================ FILE: core/controller-decorator/index.ts ================================================ import './src/impl/http/HTTPControllerMetaBuilder'; import './src/impl/mcp/MCPControllerMetaBuilder'; export * from '@eggjs/tegg-types/controller-decorator'; export * from './src/model'; export * from './src/decorator/Context'; export * from './src/decorator/Middleware'; export * from './src/decorator/Acl'; export * from './src/decorator/http/HTTPController'; export * from './src/decorator/http/HTTPMethod'; export * from './src/decorator/http/HTTPParam'; export * from './src/decorator/http/Host'; export * from './src/decorator/mcp/MCPController'; export * from './src/decorator/mcp/MCPPrompt'; export * from './src/decorator/mcp/MCPResource'; export * from './src/decorator/mcp/MCPTool'; export * from './src/decorator/mcp/Extra'; export * from './src/builder/ControllerMetaBuilderFactory'; export * from './src/util/ControllerMetadataUtil'; export * from './src/util/MCPInfoUtil'; export * from './src/util/HTTPPriorityUtil'; export { default as ControllerInfoUtil } from './src/util/ControllerInfoUtil'; export { default as MethodInfoUtil } from './src/util/MethodInfoUtil'; export * from './src/decorator/agent'; export { AgentInfoUtil } from './src/util/AgentInfoUtil'; ================================================ FILE: core/controller-decorator/package.json ================================================ { "name": "@eggjs/controller-decorator", "version": "3.78.15", "description": "tegg controller decorator", "keywords": [ "egg", "typescript", "decorator", "controller", "tegg" ], "main": "dist/index.js", "files": [ "dist/**/*.js", "dist/**/*.d.ts" ], "typings": "dist/index.d.ts", "scripts": { "test": "cross-env NODE_ENV=test NODE_OPTIONS='--no-deprecation' mocha", "clean": "tsc -b --clean", "tsc": "ut run clean && tsc -p ./tsconfig.json", "tsc:pub": "ut run clean && tsc -p ./tsconfig.pub.json", "prepublishOnly": "ut tsc:pub" }, "author": "killagu ", "license": "MIT", "homepage": "https://github.com/eggjs/tegg", "bugs": { "url": "https://github.com/eggjs/tegg/issues" }, "repository": { "type": "git", "url": "git@github.com:eggjs/tegg.git", "directory": "core/controller-decorator" }, "engines": { "node": ">=14.0.0" }, "dependencies": { "@eggjs/aop-decorator": "^3.78.15", "@eggjs/cookies": "^3.0.1", "@eggjs/core-decorator": "^3.78.15", "@eggjs/tegg-common-util": "^3.78.15", "@eggjs/tegg-metadata": "^3.78.15", "@eggjs/tegg-types": "^3.78.15", "@modelcontextprotocol/sdk": "^1.23.0", "is-type-of": "^1.2.1", "path-to-regexp": "^1.8.0", "reflect-metadata": "^0.1.13", "undici": "^5.26.5", "zod": "^4.0.0" }, "devDependencies": { "@types/mocha": "^10.0.1", "@types/node": "^20.2.4", "cross-env": "^7.0.3", "egg": "^3.9.1", "mocha": "^10.2.0", "ts-node": "^10.9.1", "typescript": "^5.0.4" }, "publishConfig": { "access": "public" }, "gitHead": "240a3e9f40fda65ebb7589727d197db5ce17916c" } ================================================ FILE: core/controller-decorator/src/builder/ControllerMetaBuilderFactory.ts ================================================ import type { ControllerMetaBuilder, ControllerMetaBuilderCreator, ControllerMetadata, ControllerTypeLike, EggProtoImplClass, } from '@eggjs/tegg-types'; import ControllerInfoUtil from '../util/ControllerInfoUtil'; import MethodInfoUtil from '../util/MethodInfoUtil'; import { Pointcut } from '@eggjs/aop-decorator'; export class ControllerMetaBuilderFactory { private static builderCreatorMap: Map = new Map(); static registerControllerMetaBuilder(controllerType: ControllerTypeLike, controllerBuilderCreator: ControllerMetaBuilderCreator) { this.builderCreatorMap.set(controllerType, controllerBuilderCreator); } static createControllerMetaBuilder(clazz: EggProtoImplClass, controllerType?: ControllerTypeLike): ControllerMetaBuilder | undefined { if (!controllerType) { controllerType = ControllerInfoUtil.getControllerType(clazz); } if (!controllerType) { return; } const creator = this.builderCreatorMap.get(controllerType); if (!creator) { throw new Error(`not found controller meta builder for type ${controllerType}`); } return creator(clazz); } static build(clazz: EggProtoImplClass, controllerType?: ControllerTypeLike): ControllerMetadata | undefined { const builder = ControllerMetaBuilderFactory.createControllerMetaBuilder(clazz, controllerType); if (!builder) return; const metadata = builder.build(); if (!metadata) return; const controllerAopMws = ControllerInfoUtil.getControllerAopMiddlewares(clazz); for (const { name } of metadata.methods) { const methodAopMws = MethodInfoUtil.getMethodAopMiddlewares(clazz, name); if (MethodInfoUtil.shouldRegisterAopMiddlewarePointCut(clazz, name)) { for (const mw of [ ...methodAopMws, ...controllerAopMws ].reverse()) { Pointcut(mw)(clazz.prototype, name); } MethodInfoUtil.registerAopMiddlewarePointcut(clazz, name); } } return metadata; } } ================================================ FILE: core/controller-decorator/src/decorator/Acl.ts ================================================ import assert from 'node:assert'; import type { EggProtoImplClass } from '@eggjs/tegg-types'; import ControllerInfoUtil from '../util/ControllerInfoUtil'; import MethodInfoUtil from '../util/MethodInfoUtil'; export function Acl(code?: string) { function classAcl(constructor: EggProtoImplClass) { ControllerInfoUtil.setControllerAcl(code, constructor); } function methodAcl(target: any, propertyKey: PropertyKey) { assert(typeof propertyKey === 'string', `[controller/${target.name}] expect method name be typeof string, but now is ${String(propertyKey)}`); const controllerClazz = target.constructor as EggProtoImplClass; const methodName = propertyKey as string; MethodInfoUtil.setMethodAcl(code, controllerClazz, methodName); } return function(target: any, propertyKey?: PropertyKey) { if (propertyKey === undefined) { classAcl(target); } else { methodAcl(target, propertyKey); } }; } ================================================ FILE: core/controller-decorator/src/decorator/Context.ts ================================================ import assert from 'node:assert'; import type { EggProtoImplClass } from '@eggjs/tegg-types'; import MethodInfoUtil from '../util/MethodInfoUtil'; export function Context() { return function(target: any, propertyKey: PropertyKey, parameterIndex: number) { assert(typeof propertyKey === 'string', `[controller/${target.name}] expect method name be typeof string, but now is ${String(propertyKey)}`); const methodName = propertyKey as string; const controllerClazz = target.constructor as EggProtoImplClass; MethodInfoUtil.setMethodContextIndexInArgs(parameterIndex, controllerClazz, methodName); }; } ================================================ FILE: core/controller-decorator/src/decorator/Middleware.ts ================================================ import assert from 'node:assert'; import type { IAdvice, EggProtoImplClass, MiddlewareFunc } from '@eggjs/tegg-types'; import is from 'is-type-of'; import ControllerInfoUtil from '../util/ControllerInfoUtil'; import MethodInfoUtil from '../util/MethodInfoUtil'; import { AdviceInfoUtil } from '@eggjs/aop-decorator'; enum MiddlewareType { AOP = 'AOP', MiddlewareFunc = 'MiddlewareFunc', } function isAop(mw: MiddlewareFunc | EggProtoImplClass) { return is.class(mw) && AdviceInfoUtil.isAdvice(mw as EggProtoImplClass); } function isAopTypeOrMiddlewareType(middlewares: Array | Array>): MiddlewareType { const adviceCount = middlewares.filter(t => isAop(t)).length; if (adviceCount) { if (adviceCount === middlewares.length) { return MiddlewareType.AOP; } throw new Error('AOP and MiddlewareFunc can not be mixed'); } return MiddlewareType.MiddlewareFunc; } export function Middleware(...middlewares: Array | Array>) { function functionTypeClassMiddleware(constructor: EggProtoImplClass) { middlewares.forEach(mid => { ControllerInfoUtil.addControllerMiddleware(mid, constructor); }); } function aopTypeClassMiddleware(constructor: EggProtoImplClass) { for (const aopAdvice of middlewares as EggProtoImplClass[]) { ControllerInfoUtil.addControllerAopMiddleware(aopAdvice, constructor); } } function functionTypeMethodMiddleware(target: any, propertyKey: PropertyKey) { assert(typeof propertyKey === 'string', `[controller/${target.name}] expect method name be typeof string, but now is ${String(propertyKey)}`); const controllerClazz = target.constructor as EggProtoImplClass; const methodName = propertyKey as string; middlewares.forEach(mid => { MethodInfoUtil.addMethodMiddleware(mid, controllerClazz, methodName); }); } function aopTypeMethodMiddleware(target: any, propertyKey: PropertyKey) { const controllerClazz = target.constructor as EggProtoImplClass; const methodName = propertyKey as string; for (const aopAdvice of middlewares as EggProtoImplClass[]) { MethodInfoUtil.addMethodAopMiddleware(aopAdvice, controllerClazz, methodName); } } return function(target: any, propertyKey?: PropertyKey) { const type = isAopTypeOrMiddlewareType(middlewares); if (propertyKey === undefined) { if (type === MiddlewareType.AOP) { aopTypeClassMiddleware(target); } else { functionTypeClassMiddleware(target); } } else { if (type === MiddlewareType.AOP) { aopTypeMethodMiddleware(target, propertyKey); } else { functionTypeMethodMiddleware(target, propertyKey); } } }; } ================================================ FILE: core/controller-decorator/src/decorator/agent/AgentController.ts ================================================ import { PrototypeUtil, SingletonProto } from '@eggjs/core-decorator'; import { StackUtil } from '@eggjs/tegg-common-util'; import type { EggProtoImplClass } from '@eggjs/tegg-types'; import { AccessLevel, AGENT_CONTROLLER_PROTO_IMPL_TYPE, ControllerType, HTTPMethodEnum, HTTPParamType, } from '@eggjs/tegg-types'; import { AgentInfoUtil } from '../../util/AgentInfoUtil'; import ControllerInfoUtil from '../../util/ControllerInfoUtil'; import HTTPInfoUtil from '../../util/HTTPInfoUtil'; import MethodInfoUtil from '../../util/MethodInfoUtil'; interface AgentRouteParam { index: number; type: 'body' | 'pathParam' | 'query'; name?: string; } interface AgentRouteDefinition { methodName: string; httpMethod: HTTPMethodEnum; path: string; params: AgentRouteParam[]; } // Default implementations for unimplemented methods. // function.length must match the param count for framework param validation. // Stubs are marked with Symbol.for('AGENT_NOT_IMPLEMENTED') so agent-runtime // can distinguish them from user-defined methods at enhancement time. function createNotImplemented(methodName: string, paramCount: number) { let fn; if (paramCount >= 2) { // eslint-disable-next-line @typescript-eslint/no-unused-vars fn = async function(_a: unknown, _b: unknown) { throw new Error(`${methodName} not implemented`); }; } else if (paramCount === 1) { // eslint-disable-next-line @typescript-eslint/no-unused-vars fn = async function(_arg: unknown) { throw new Error(`${methodName} not implemented`); }; } else { fn = async function() { throw new Error(`${methodName} not implemented`); }; } AgentInfoUtil.setNotImplemented(fn); return fn; } const AGENT_ROUTES: AgentRouteDefinition[] = [ { methodName: 'createThread', httpMethod: HTTPMethodEnum.POST, path: '/threads', params: [], }, { methodName: 'getThread', httpMethod: HTTPMethodEnum.GET, path: '/threads/:id', params: [{ index: 0, type: 'pathParam', name: 'id' }], }, { methodName: 'asyncRun', httpMethod: HTTPMethodEnum.POST, path: '/runs', params: [{ index: 0, type: 'body' }], }, { methodName: 'streamRun', httpMethod: HTTPMethodEnum.POST, path: '/runs/stream', params: [{ index: 0, type: 'body' }], }, { methodName: 'getRunStream', httpMethod: HTTPMethodEnum.GET, path: '/runs/:id/stream', params: [ { index: 0, type: 'pathParam', name: 'id' }, { index: 1, type: 'query', name: 'lastSeq' }, ], }, { methodName: 'syncRun', httpMethod: HTTPMethodEnum.POST, path: '/runs/wait', params: [{ index: 0, type: 'body' }], }, { methodName: 'getRun', httpMethod: HTTPMethodEnum.GET, path: '/runs/:id', params: [{ index: 0, type: 'pathParam', name: 'id' }], }, { methodName: 'cancelRun', httpMethod: HTTPMethodEnum.POST, path: '/runs/:id/cancel', params: [{ index: 0, type: 'pathParam', name: 'id' }], }, ]; export function AgentController(): (constructor: EggProtoImplClass) => void { return function(constructor: EggProtoImplClass): void { // Set controller type as HTTP so existing infrastructure handles it ControllerInfoUtil.setControllerType(constructor, ControllerType.HTTP); // Set the fixed base HTTP path HTTPInfoUtil.setHTTPPath('/api/v1', constructor); // Apply SingletonProto with custom proto impl type const func = SingletonProto({ accessLevel: AccessLevel.PUBLIC, protoImplType: AGENT_CONTROLLER_PROTO_IMPL_TYPE, }); func(constructor); // Set file path for prototype // Stack depth 5: [0] getCalleeFromStack → [1] decorator fn → [2-4] reflect/oxc runtime → [5] user source PrototypeUtil.setFilePath(constructor, StackUtil.getCalleeFromStack(false, 5)); // Register each agent route for (const route of AGENT_ROUTES) { // Inject default implementation if method not defined if (!constructor.prototype[route.methodName]) { constructor.prototype[route.methodName] = createNotImplemented(route.methodName, route.params.length); } // Set method controller type MethodInfoUtil.setMethodControllerType(constructor, route.methodName, ControllerType.HTTP); // Set HTTP method (GET/POST) HTTPInfoUtil.setHTTPMethodMethod(route.httpMethod, constructor, route.methodName); // Set HTTP path HTTPInfoUtil.setHTTPMethodPath(route.path, constructor, route.methodName); // Set parameter metadata for (const param of route.params) { if (param.type === 'body') { HTTPInfoUtil.setHTTPMethodParamType(HTTPParamType.BODY, param.index, constructor, route.methodName); } else if (param.type === 'pathParam') { HTTPInfoUtil.setHTTPMethodParamType(HTTPParamType.PARAM, param.index, constructor, route.methodName); HTTPInfoUtil.setHTTPMethodParamName(param.name!, param.index, constructor, route.methodName); } else if (param.type === 'query') { HTTPInfoUtil.setHTTPMethodParamType(HTTPParamType.QUERY, param.index, constructor, route.methodName); HTTPInfoUtil.setHTTPMethodParamName(param.name!, param.index, constructor, route.methodName); } } } // Mark the class as an AgentController for precise detection AgentInfoUtil.setAgentController(constructor); }; } ================================================ FILE: core/controller-decorator/src/decorator/agent/AgentHandler.ts ================================================ import type { ThreadObject, ThreadObjectWithMessages, CreateRunInput, RunObject, AgentMessage, } from '@eggjs/tegg-types/agent-runtime'; // Interface for AgentController classes. The `execRun` method is required — // the framework uses it to auto-wire thread/run management, store persistence, // SSE streaming, async execution, and cancellation via smart defaults. export interface AgentHandler { execRun(input: CreateRunInput, signal?: AbortSignal): AsyncGenerator; /** Create the AgentStore used to persist threads and runs. */ createStore(): Promise; /** * Optional hook to decide whether the executor's underlying session has * been committed to persistent storage (for example the Claude Code SDK * jsonl file on disk). The runtime calls this each time a new message is * yielded from `execRun`; once it returns true, `cancelRun` is allowed to * abort and persist the thread. * * When not implemented, the runtime uses a default heuristic: any message * with `type !== 'system'` counts as committed (covers the Claude Code SDK * case where `system/init` is emitted before the session is fully written). */ isSessionCommitted?(msg: AgentMessage, history: AgentMessage[]): boolean | Promise; createThread?(): Promise; getThread?(threadId: string): Promise; asyncRun?(input: CreateRunInput): Promise; streamRun?(input: CreateRunInput): Promise; syncRun?(input: CreateRunInput): Promise; getRun?(runId: string): Promise; cancelRun?(runId: string): Promise; } ================================================ FILE: core/controller-decorator/src/decorator/agent/index.ts ================================================ export * from './AgentController'; export * from './AgentHandler'; ================================================ FILE: core/controller-decorator/src/decorator/http/HTTPController.ts ================================================ import { PrototypeUtil, SingletonProto } from '@eggjs/core-decorator'; import { StackUtil } from '@eggjs/tegg-common-util'; import type { EggProtoImplClass, HTTPControllerParams } from '@eggjs/tegg-types'; import { AccessLevel, ControllerType } from '@eggjs/tegg-types'; import ControllerInfoUtil from '../../util/ControllerInfoUtil'; import HTTPInfoUtil from '../../util/HTTPInfoUtil'; export function HTTPController(param?: HTTPControllerParams) { return function(constructor: EggProtoImplClass) { ControllerInfoUtil.setControllerType(constructor, ControllerType.HTTP); if (param?.controllerName) { ControllerInfoUtil.setControllerName(constructor, param.controllerName); } if (param?.timeout) { ControllerInfoUtil.setControllerTimeout(param.timeout, constructor); } if (param?.path) { HTTPInfoUtil.setHTTPPath(param.path, constructor); } // TODO elegant? const func = SingletonProto({ accessLevel: AccessLevel.PUBLIC, name: param?.protoName, }); func(constructor); // './tegg/core/common-util/src/StackUtil.ts', // './tegg/core/core-decorator/src/decorator/Prototype.ts', // './tegg/core/controller-decorator/src/decorator/http/HTTPController.ts', // './tegg/core/core-decorator/node_modules/_reflect-metadata@0.1.13@reflect-metadata/Reflect.js', // './tegg/core/core-decorator/node_modules/_reflect-metadata@0.1.13@reflect-metadata/Reflect.js', // './tegg/core/controller-decorator/test/fixtures/TRFooController.ts', PrototypeUtil.setFilePath(constructor, StackUtil.getCalleeFromStack(false, 5)); }; } ================================================ FILE: core/controller-decorator/src/decorator/http/HTTPMethod.ts ================================================ import assert from 'node:assert'; import { ControllerType } from '@eggjs/tegg-types'; import type { EggProtoImplClass, HTTPMethodParams } from '@eggjs/tegg-types'; import HTTPInfoUtil from '../../util/HTTPInfoUtil'; import MethodInfoUtil from '../../util/MethodInfoUtil'; export function HTTPMethod(param: HTTPMethodParams) { return function(target: any, propertyKey: PropertyKey) { assert(typeof propertyKey === 'string', `[controller/${target.name}] expect method name be typeof string, but now is ${String(propertyKey)}`); const controllerClazz = target.constructor as EggProtoImplClass; const methodName = propertyKey as string; MethodInfoUtil.setMethodControllerType(controllerClazz, methodName, ControllerType.HTTP); HTTPInfoUtil.setHTTPMethodPath(param.path, controllerClazz, methodName); HTTPInfoUtil.setHTTPMethodMethod(param.method, controllerClazz, methodName); if (param.timeout) { MethodInfoUtil.setMethodTimeout(param.timeout, controllerClazz, methodName); } if (param.priority !== undefined) { HTTPInfoUtil.setHTTPMethodPriority(param.priority, controllerClazz, methodName); } }; } ================================================ FILE: core/controller-decorator/src/decorator/http/HTTPParam.ts ================================================ import assert from 'node:assert'; import { HTTPParamType } from '@eggjs/tegg-types'; import type { EggProtoImplClass, HTTPParamParams, HTTPQueriesParams, HTTPQueryParams } from '@eggjs/tegg-types'; import HTTPInfoUtil from '../../util/HTTPInfoUtil'; import { ObjectUtils } from '@eggjs/tegg-common-util'; // TODO url params // /foo/:id // refactor HTTPQuery, HTTPBody, HTTPParam export function HTTPBody() { return function(target: any, propertyKey: PropertyKey, parameterIndex: number) { assert(typeof propertyKey === 'string', `[controller/${target.name}] expect method name be typeof string, but now is ${String(propertyKey)}`); const methodName = propertyKey as string; const controllerClazz = target.constructor as EggProtoImplClass; HTTPInfoUtil.setHTTPMethodParamType(HTTPParamType.BODY, parameterIndex, controllerClazz, methodName); }; } export function HTTPHeaders() { return function(target: any, propertyKey: PropertyKey, parameterIndex: number) { assert(typeof propertyKey === 'string', `[controller/${target.name}] expect method name be typeof string, but now is ${String(propertyKey)}`); const methodName = propertyKey as string; const controllerClazz = target.constructor as EggProtoImplClass; HTTPInfoUtil.setHTTPMethodParamType(HTTPParamType.HEADERS, parameterIndex, controllerClazz, methodName); }; } export function HTTPQuery(param?: HTTPQueryParams) { return function(target: any, propertyKey: PropertyKey, parameterIndex: number) { assert(typeof propertyKey === 'string', `[controller/${target.name}] expect method name be typeof string, but now is ${String(propertyKey)}`); const methodName = propertyKey as string; const controllerClazz = target.constructor as EggProtoImplClass; const argNames = ObjectUtils.getFunctionArgNameList(target[propertyKey]); const name = param?.name || argNames[parameterIndex]; HTTPInfoUtil.setHTTPMethodParamType(HTTPParamType.QUERY, parameterIndex, controllerClazz, methodName); HTTPInfoUtil.setHTTPMethodParamName(name, parameterIndex, controllerClazz, methodName); }; } export function HTTPQueries(param?: HTTPQueriesParams) { return function(target: any, propertyKey: PropertyKey, parameterIndex: number) { assert(typeof propertyKey === 'string', `[controller/${target.name}] expect method name be typeof string, but now is ${String(propertyKey)}`); const methodName = propertyKey as string; const controllerClazz = target.constructor as EggProtoImplClass; const argNames = ObjectUtils.getFunctionArgNameList(target[propertyKey]); const name = param?.name || argNames[parameterIndex]; HTTPInfoUtil.setHTTPMethodParamType(HTTPParamType.QUERIES, parameterIndex, controllerClazz, methodName); HTTPInfoUtil.setHTTPMethodParamName(name, parameterIndex, controllerClazz, methodName); }; } export function HTTPParam(param?: HTTPParamParams) { return function(target: any, propertyKey: PropertyKey, parameterIndex: number) { assert(typeof propertyKey === 'string', `[controller/${target.name}] expect method name be typeof string, but now is ${String(propertyKey)}`); const methodName = propertyKey as string; const controllerClazz = target.constructor as EggProtoImplClass; const argNames = ObjectUtils.getFunctionArgNameList(target[propertyKey]); const name = param?.name || argNames[parameterIndex]; HTTPInfoUtil.setHTTPMethodParamType(HTTPParamType.PARAM, parameterIndex, controllerClazz, methodName); HTTPInfoUtil.setHTTPMethodParamName(name, parameterIndex, controllerClazz, methodName); }; } export function Request() { return function(target: any, propertyKey: PropertyKey, parameterIndex: number) { const [ nodeMajor ] = process.versions.node.split('.').map(v => Number(v)); assert(nodeMajor >= 16, `[controller/${target.name}] expect node version >=16, but now is ${nodeMajor}`); assert(typeof propertyKey === 'string', `[controller/${target.name}] expect method name be typeof string, but now is ${String(propertyKey)}`); const methodName = propertyKey as string; const controllerClazz = target.constructor as EggProtoImplClass; HTTPInfoUtil.setHTTPMethodParamType(HTTPParamType.REQUEST, parameterIndex, controllerClazz, methodName); }; } export function Cookies() { return function(target: any, propertyKey: PropertyKey, parameterIndex: number) { assert(typeof propertyKey === 'string', `[controller/${target.name}] expect method name be typeof string, but now is ${String(propertyKey)}`); const methodName = propertyKey as string; const controllerClazz = target.constructor as EggProtoImplClass; HTTPInfoUtil.setHTTPMethodParamType(HTTPParamType.COOKIES, parameterIndex, controllerClazz, methodName); }; } ================================================ FILE: core/controller-decorator/src/decorator/http/Host.ts ================================================ import assert from 'node:assert'; import type { EggProtoImplClass, HostType } from '@eggjs/tegg-types'; import ControllerInfoUtil from '../../util/ControllerInfoUtil'; import MethodInfoUtil from '../../util/MethodInfoUtil'; export function Host(host: HostType) { function parseHost(): string[] { return Array.isArray(host) ? host : [ host ]; } function classHost(constructor: EggProtoImplClass) { ControllerInfoUtil.addControllerHosts(parseHost(), constructor); } function methodHost(target: any, propertyKey: PropertyKey) { assert(typeof propertyKey === 'string', `[controller/${target.name}] expect method name be typeof string, but now is ${String(propertyKey)}`); const controllerClazz = target.constructor as EggProtoImplClass; const methodName = propertyKey as string; MethodInfoUtil.setMethodHosts(parseHost(), controllerClazz, methodName); } return function(target: any, propertyKey?: PropertyKey) { if (propertyKey === undefined) { classHost(target); } else { methodHost(target, propertyKey); } }; } ================================================ FILE: core/controller-decorator/src/decorator/mcp/Extra.ts ================================================ import { EggProtoImplClass, } from '@eggjs/tegg-types'; import { MCPInfoUtil } from '../../../src/util/MCPInfoUtil'; export function Extra() { return function( target: any, propertyKey: PropertyKey, parameterIndex: number, ) { const controllerClazz = target.constructor as EggProtoImplClass; const methodName = propertyKey as string; MCPInfoUtil.setMCPExtra(parameterIndex, controllerClazz, methodName); }; } ================================================ FILE: core/controller-decorator/src/decorator/mcp/MCPController.ts ================================================ import { ControllerType, MCPControllerParams, AccessLevel, EggProtoImplClass } from '@eggjs/tegg-types'; import { PrototypeUtil, SingletonProto } from '@eggjs/core-decorator'; import ControllerInfoUtil from '../../util/ControllerInfoUtil'; import { StackUtil } from '@eggjs/tegg-common-util'; import { MCPInfoUtil } from '../../../src/util/MCPInfoUtil'; export function MCPController(param?: MCPControllerParams) { return function(constructor: EggProtoImplClass) { const func = SingletonProto({ accessLevel: AccessLevel.PUBLIC, name: param?.protoName, }); func(constructor); ControllerInfoUtil.setControllerType(constructor, ControllerType.MCP); if (param?.controllerName) { ControllerInfoUtil.setControllerName(constructor, param?.controllerName); } // './tegg/core/common-util/src/StackUtil.ts', // './tegg/core/core-decorator/src/decorator/Prototype.ts', // './tegg/core/controller-decorator/src/decorator/tr/TRController.ts', // './tegg/core/core-decorator/node_modules/_reflect-metadata@0.1.13@reflect-metadata/Reflect.js', // './tegg/core/core-decorator/node_modules/_reflect-metadata@0.1.13@reflect-metadata/Reflect.js', // './tegg/core/controller-decorator/test/fixtures/TRFooController.ts', PrototypeUtil.setFilePath(constructor, StackUtil.getCalleeFromStack(false, 5)); if (param?.name) { MCPInfoUtil.setMCPName(param.name, constructor); } if (param?.version) { MCPInfoUtil.setMCPVersion(param.version, constructor); } MCPInfoUtil.setMCPControllerParams(param, constructor); }; } ================================================ FILE: core/controller-decorator/src/decorator/mcp/MCPPrompt.ts ================================================ import { ControllerType, EggProtoImplClass, MCPPromptParams, } from '@eggjs/tegg-types'; import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { MCPInfoUtil } from '../../../src/util/MCPInfoUtil'; import MethodInfoUtil from '../../../src/util/MethodInfoUtil'; export function MCPPrompt(params?: MCPPromptParams) { return function( target: any, propertyKey: PropertyKey, ) { const controllerClazz = target.constructor as EggProtoImplClass; const methodName = propertyKey as string; MethodInfoUtil.setMethodControllerType( controllerClazz, methodName, ControllerType.MCP, ); MCPInfoUtil.setMCPPromptParams( { ...params, mcpName: params?.name, name: methodName, }, controllerClazz, methodName, ); MCPInfoUtil.setMCPPrompt(controllerClazz, methodName); }; } export function PromptArgsSchema(argsSchema: Parameters['2']) { return function( target: any, propertyKey: PropertyKey, parameterIndex: number, ) { const controllerClazz = target.constructor as EggProtoImplClass; const methodName = propertyKey as string; MCPInfoUtil.setMCPPromptArgsInArgs( { argsSchema, index: parameterIndex, }, controllerClazz, methodName, ); }; } ================================================ FILE: core/controller-decorator/src/decorator/mcp/MCPResource.ts ================================================ import { ControllerType, EggProtoImplClass, MCPResourceParams, } from '@eggjs/tegg-types'; import { MCPInfoUtil } from '../../../src/util/MCPInfoUtil'; import MethodInfoUtil from '../../../src/util/MethodInfoUtil'; export function MCPResource(params: MCPResourceParams) { return function( target: any, propertyKey: PropertyKey, ) { const controllerClazz = target.constructor as EggProtoImplClass; const methodName = propertyKey as string; MethodInfoUtil.setMethodControllerType( controllerClazz, methodName, ControllerType.MCP, ); MCPInfoUtil.setMCPResourceParams( { ...params, mcpName: params.name, name: methodName, }, controllerClazz, methodName, ); MCPInfoUtil.setMCPResource(controllerClazz, methodName); }; } ================================================ FILE: core/controller-decorator/src/decorator/mcp/MCPTool.ts ================================================ import { ControllerType, EggProtoImplClass, MCPToolParams, } from '@eggjs/tegg-types'; import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { MCPInfoUtil } from '../../../src/util/MCPInfoUtil'; import MethodInfoUtil from '../../../src/util/MethodInfoUtil'; export function MCPTool(params?: MCPToolParams) { return function( target: any, propertyKey: PropertyKey, ) { const controllerClazz = target.constructor as EggProtoImplClass; const methodName = propertyKey as string; MethodInfoUtil.setMethodControllerType( controllerClazz, methodName, ControllerType.MCP, ); MCPInfoUtil.setMCPToolParams( { ...params, mcpName: params?.name, name: methodName, }, controllerClazz, methodName, ); MCPInfoUtil.setMCPTool(controllerClazz, methodName); }; } export function ToolArgsSchema(argsSchema: Parameters['2']) { return function( target: any, propertyKey: PropertyKey, parameterIndex: number, ) { const controllerClazz = target.constructor as EggProtoImplClass; const methodName = propertyKey as string; MCPInfoUtil.setMCPToolArgsInArgs( { argsSchema, index: parameterIndex, }, controllerClazz, methodName, ); }; } ================================================ FILE: core/controller-decorator/src/impl/http/HTTPControllerMetaBuilder.ts ================================================ import assert from 'node:assert'; import { PrototypeUtil } from '@eggjs/core-decorator'; import { ObjectUtils } from '@eggjs/tegg-common-util'; import { ClassUtil } from '@eggjs/tegg-metadata'; import type { EggProtoImplClass } from '@eggjs/tegg-types'; import { ControllerType } from '@eggjs/tegg-types'; import { ControllerMetaBuilderFactory } from '../../builder/ControllerMetaBuilderFactory'; import { HTTPControllerMeta, HTTPMethodMeta } from '../../model'; import ControllerInfoUtil from '../../util/ControllerInfoUtil'; import { ControllerMetadataUtil } from '../../util/ControllerMetadataUtil'; import HTTPInfoUtil from '../../util/HTTPInfoUtil'; import { ControllerValidator } from '../../util/validator/ControllerValidator'; import { HTTPControllerMethodMetaBuilder } from './HTTPControllerMethodMetaBuilder'; export class HTTPControllerMetaBuilder { private readonly clazz: EggProtoImplClass; constructor(clazz: EggProtoImplClass) { this.clazz = clazz; } private buildMethod(): HTTPMethodMeta[] { const methodNames = ObjectUtils.getProperties(this.clazz.prototype); const methods: HTTPMethodMeta[] = []; for (const methodName of methodNames) { const builder = new HTTPControllerMethodMetaBuilder(this.clazz, methodName); const methodMeta = builder.build(); if (methodMeta) { methods.push(methodMeta); } } return methods; } build(): HTTPControllerMeta { ControllerValidator.validate(this.clazz); const controllerType = ControllerInfoUtil.getControllerType(this.clazz); assert(controllerType === ControllerType.HTTP, 'invalidate controller type'); const httpPath = HTTPInfoUtil.getHTTPPath(this.clazz); const httpMiddlewares = ControllerInfoUtil.getControllerMiddlewares(this.clazz); const methods = this.buildMethod(); const clazzName = this.clazz.name; const controllerName = ControllerInfoUtil.getControllerName(this.clazz) || clazzName; const property = PrototypeUtil.getProperty(this.clazz); const protoName = property!.name as string; const needAcl = ControllerInfoUtil.hasControllerAcl(this.clazz); const aclCode = ControllerInfoUtil.getControllerAcl(this.clazz); const hosts = ControllerInfoUtil.getControllerHosts(this.clazz); const timeout = ControllerInfoUtil.getControllerTimeout(this.clazz); const metadata = new HTTPControllerMeta( clazzName, protoName, controllerName, httpPath, httpMiddlewares, methods, needAcl, aclCode, hosts, timeout); ControllerMetadataUtil.setControllerMetadata(this.clazz, metadata); for (const method of metadata.methods) { const realPath = metadata.getMethodRealPath(method); if (!realPath.startsWith('/')) { const desc = ClassUtil.classDescription(this.clazz); throw new Error(`class ${desc} method ${method.name} path ${realPath} not start with /`); } } return metadata; } static create(clazz: EggProtoImplClass) { return new HTTPControllerMetaBuilder(clazz); } } ControllerMetaBuilderFactory.registerControllerMetaBuilder(ControllerType.HTTP, HTTPControllerMetaBuilder.create); ================================================ FILE: core/controller-decorator/src/impl/http/HTTPControllerMethodMetaBuilder.ts ================================================ import path from 'node:path'; import { ClassUtil } from '@eggjs/tegg-metadata'; import type { EggProtoImplClass } from '@eggjs/tegg-types'; import { HTTPMethodMeta, ParamMeta, ParamMetaUtil } from '../../model'; import { MethodValidator } from '../../util/validator/MethodValidator'; import HTTPInfoUtil from '../../util/HTTPInfoUtil'; import MethodInfoUtil from '../../util/MethodInfoUtil'; import { HTTPPriorityUtil } from '../../util/HTTPPriorityUtil'; export class HTTPControllerMethodMetaBuilder { private readonly clazz: EggProtoImplClass; private readonly methodName: string; constructor(clazz: EggProtoImplClass, methodName: string) { this.clazz = clazz; this.methodName = methodName; } // 检查 HTTP 方法上参数是否都加上了注解 // foo(a, b=233, c); foo.length = 2; // 由于这种情况的存在, 所以需要对 function 参数检查进行特殊处理 // 如果有注解的参数比 function.length 长, 长度应该取有注解参数总数(包括 @Context) // // 但是有两种特殊情况无法处理 // foo(@Context() ctx, @HTTPParam() id1, id2 = 233) // foo(@Context() ctx, @HTTPParam() id1, id2 = 233, id3) // 这两种情况均为默认值参数在 private checkParamDecorators() { const method = this.clazz.prototype[this.methodName]; // 获取函数参数长度 const functionLength = method.length; // 计算带注解参数 const paramIndexList = HTTPInfoUtil.getParamIndexList(this.clazz, this.methodName); const contextIndex = MethodInfoUtil.getMethodContextIndex(this.clazz, this.methodName); const hasAnnotationParamCount = typeof contextIndex === 'undefined' ? paramIndexList.length : paramIndexList.length + 1; const maxParamCount = Math.max(functionLength, hasAnnotationParamCount); for (let i = 0; i < maxParamCount; ++i) { // 上下文参数, 跳过检查 if (i === contextIndex) { continue; } const paramType = HTTPInfoUtil.getHTTPMethodParamType(i, this.clazz, this.methodName); if (!paramType) { const classDesc = ClassUtil.classDescription(this.clazz); throw new Error(`${classDesc}:${this.methodName} param ${i} has no http param type, Please add @HTTPBody, @HTTPParam, @HTTPQuery, @HTTPQueries`); } } } private buildParamType(httpPath: string): Map { this.checkParamDecorators(); const paramTypeMap = new Map(); const paramIndexList = HTTPInfoUtil.getParamIndexList(this.clazz, this.methodName); for (const paramIndex of paramIndexList) { const paramType = HTTPInfoUtil.getHTTPMethodParamType(paramIndex, this.clazz, this.methodName)!; const paramName = HTTPInfoUtil.getHTTPMethodParamName(paramIndex, this.clazz, this.methodName); const paramMeta = ParamMetaUtil.createParam(paramType, paramName); try { paramMeta.validate(httpPath); } catch (e) { const classDesc = ClassUtil.classDescription(this.clazz); e.message = `build controller ${classDesc} method ${this.methodName} param ${paramName} failed: ${e.message}`; throw e; } paramTypeMap.set(paramIndex, paramMeta); } return paramTypeMap; } getPriority() { const priority = HTTPInfoUtil.getHTTPMethodPriority(this.clazz, this.methodName); if (priority !== undefined) { return priority; } const controllerPath = HTTPInfoUtil.getHTTPPath(this.clazz); const methodPath = HTTPInfoUtil.getHTTPMethodPath(this.clazz, this.methodName)!; const realPath = controllerPath ? path.posix.join(controllerPath, methodPath) : methodPath; const defaultPriority = HTTPPriorityUtil.calcPathPriority(realPath); if (defaultPriority > HTTPPriorityUtil.DEFAULT_PRIORITY) { throw new Error(`path ${realPath} is too long, should set priority manually`); } return defaultPriority; } build(): HTTPMethodMeta | undefined { MethodValidator.validate(this.clazz, this.methodName); const controllerType = MethodInfoUtil.getMethodControllerType(this.clazz, this.methodName); if (!controllerType) { return undefined; } const httpMethod = HTTPInfoUtil.getHTTPMethodMethod(this.clazz, this.methodName); const parentPath = HTTPInfoUtil.getHTTPPath(this.clazz); const httpPath = HTTPInfoUtil.getHTTPMethodPath(this.clazz, this.methodName)!; const contextIndex = MethodInfoUtil.getMethodContextIndex(this.clazz, this.methodName); const middlewares = MethodInfoUtil.getMethodMiddlewares(this.clazz, this.methodName); const needAcl = MethodInfoUtil.hasMethodAcl(this.clazz, this.methodName); const aclCode = MethodInfoUtil.getMethodAcl(this.clazz, this.methodName); const hosts = MethodInfoUtil.getMethodHosts(this.clazz, this.methodName); const timeout = MethodInfoUtil.getMethodTimeout(this.clazz, this.methodName); const realPath = parentPath ? path.posix.join(parentPath, httpPath) : httpPath; const paramTypeMap = this.buildParamType(realPath); const priority = this.getPriority(); return new HTTPMethodMeta( this.methodName, httpPath!, httpMethod!, middlewares, contextIndex, paramTypeMap, priority, needAcl, aclCode, hosts, timeout); } } ================================================ FILE: core/controller-decorator/src/impl/mcp/MCPControllerMetaBuilder.ts ================================================ import assert from 'assert'; import { PrototypeUtil } from '@eggjs/core-decorator'; import { MCPPromptMeta, MCPResourceMeta, MCPToolMeta } from '../../model'; import { ControllerValidator } from '../../util/validator/ControllerValidator'; import ControllerInfoUtil from '../../util/ControllerInfoUtil'; import { MCPControllerPromptMetaBuilder } from './MCPControllerPromptMetaBuilder'; import { MCPControllerResourceMetaBuilder } from './MCPControllerResourceMetaBuilder'; import { MCPControllerToolMetaBuilder } from './MCPControllerToolMetaBuilder'; import { MCPInfoUtil } from '../../../src/util/MCPInfoUtil'; import { ControllerType, EggProtoImplClass } from '@eggjs/tegg-types'; import { MCPControllerMeta } from '../../../src/model/MCPControllerMeta'; import { ControllerMetaBuilderFactory } from '../../builder/ControllerMetaBuilderFactory'; export class MCPControllerMetaBuilder { private readonly clazz: EggProtoImplClass; constructor(clazz: EggProtoImplClass) { this.clazz = clazz; } private buildResource(): MCPResourceMeta[] { const methodNames = MCPInfoUtil.getMCPResource(this.clazz); const methods: MCPResourceMeta[] = []; for (const methodName of methodNames) { const builder = new MCPControllerResourceMetaBuilder( this.clazz, methodName, ); const methodMeta = builder.build(); if (methodMeta) { methods.push(methodMeta); } } return methods; } private buildPrompt(): MCPPromptMeta[] { const methodNames = MCPInfoUtil.getMCPPrompt(this.clazz); const methods: MCPPromptMeta[] = []; for (const methodName of methodNames) { const builder = new MCPControllerPromptMetaBuilder( this.clazz, methodName, ); const methodMeta = builder.build(); if (methodMeta) { methods.push(methodMeta); } } return methods; } private buildTool(): MCPToolMeta[] { const methodNames = MCPInfoUtil.getMCPTool(this.clazz); const methods: MCPToolMeta[] = []; for (const methodName of methodNames) { const builder = new MCPControllerToolMetaBuilder(this.clazz, methodName); const methodMeta = builder.build(); if (methodMeta) { methods.push(methodMeta); } } return methods; } build(): MCPControllerMeta { ControllerValidator.validate(this.clazz); const controllerType = ControllerInfoUtil.getControllerType(this.clazz); assert(controllerType === ControllerType.MCP, 'invalidate controller type'); const mcpMiddlewares = ControllerInfoUtil.getControllerMiddlewares( this.clazz, ); const resources = this.buildResource(); const prompts = this.buildPrompt(); const tools = this.buildTool(); const property = PrototypeUtil.getProperty(this.clazz); const protoName = property!.name as string; const clazzName = this.clazz.name; const controllerName = ControllerInfoUtil.getControllerName(this.clazz) || clazzName; const name = MCPInfoUtil.getMCPName(this.clazz); const version = MCPInfoUtil.getMCPVersion(this.clazz) || '1.0.0'; const needAcl = ControllerInfoUtil.hasControllerAcl(this.clazz); const aclCode = ControllerInfoUtil.getControllerAcl(this.clazz); const meta = MCPInfoUtil.getMCPControllerParams(this.clazz); return new MCPControllerMeta( clazzName, protoName, controllerName, version, tools, resources, prompts, mcpMiddlewares, name, needAcl, aclCode, meta, ); } static create(clazz: EggProtoImplClass) { return new MCPControllerMetaBuilder(clazz); } } ControllerMetaBuilderFactory.registerControllerMetaBuilder( ControllerType.MCP, MCPControllerMetaBuilder.create, ); ================================================ FILE: core/controller-decorator/src/impl/mcp/MCPControllerPromptMetaBuilder.ts ================================================ import type { EggProtoImplClass } from '@eggjs/tegg-types'; import { MCPPromptMeta } from '../../model'; import { MethodValidator } from '../../util/validator/MethodValidator'; import MethodInfoUtil from '../../util/MethodInfoUtil'; import { MCPInfoUtil } from '../../util/MCPInfoUtil'; export class MCPControllerPromptMetaBuilder { private readonly clazz: EggProtoImplClass; private readonly methodName: string; constructor(clazz: EggProtoImplClass, methodName: string) { this.clazz = clazz; this.methodName = methodName; } build(): MCPPromptMeta | undefined { MethodValidator.validate(this.clazz, this.methodName); const controllerType = MethodInfoUtil.getMethodControllerType(this.clazz, this.methodName); if (!controllerType) { return undefined; } const middlewares = MethodInfoUtil.getMethodMiddlewares(this.clazz, this.methodName); const needAcl = MethodInfoUtil.hasMethodAcl(this.clazz, this.methodName); const aclCode = MethodInfoUtil.getMethodAcl(this.clazz, this.methodName); const params = MCPInfoUtil.getMCPPromptParams(this.clazz, this.methodName); const detail = MCPInfoUtil.getMCPPromptArgsIndex(this.clazz, this.methodName); const extra = MCPInfoUtil.getMCPExtra(this.clazz, this.methodName); return new MCPPromptMeta({ name: this.methodName, middlewares, needAcl, aclCode, detail, extra, ...params, }); } } ================================================ FILE: core/controller-decorator/src/impl/mcp/MCPControllerResourceMetaBuilder.ts ================================================ import type { EggProtoImplClass } from '@eggjs/tegg-types'; import { MCPResourceMeta } from '../../model'; import { MethodValidator } from '../../util/validator/MethodValidator'; import MethodInfoUtil from '../../util/MethodInfoUtil'; import { MCPInfoUtil } from '../../util/MCPInfoUtil'; export class MCPControllerResourceMetaBuilder { private readonly clazz: EggProtoImplClass; private readonly methodName: string; constructor(clazz: EggProtoImplClass, methodName: string) { this.clazz = clazz; this.methodName = methodName; } build(): MCPResourceMeta | undefined { MethodValidator.validate(this.clazz, this.methodName); const controllerType = MethodInfoUtil.getMethodControllerType(this.clazz, this.methodName); if (!controllerType) { return undefined; } const middlewares = MethodInfoUtil.getMethodMiddlewares(this.clazz, this.methodName); const needAcl = MethodInfoUtil.hasMethodAcl(this.clazz, this.methodName); const aclCode = MethodInfoUtil.getMethodAcl(this.clazz, this.methodName); const params = MCPInfoUtil.getMCPResourceParams(this.clazz, this.methodName); const extra = MCPInfoUtil.getMCPExtra(this.clazz, this.methodName); return new MCPResourceMeta({ name: this.methodName, middlewares, needAcl, aclCode, extra, ...params, }); } } ================================================ FILE: core/controller-decorator/src/impl/mcp/MCPControllerToolMetaBuilder.ts ================================================ import type { EggProtoImplClass } from '@eggjs/tegg-types'; import { MCPToolMeta } from '../../model'; import { MethodValidator } from '../../util/validator/MethodValidator'; import MethodInfoUtil from '../../util/MethodInfoUtil'; import { MCPInfoUtil } from '../../util/MCPInfoUtil'; export class MCPControllerToolMetaBuilder { private readonly clazz: EggProtoImplClass; private readonly methodName: string; constructor(clazz: EggProtoImplClass, methodName: string) { this.clazz = clazz; this.methodName = methodName; } build(): MCPToolMeta | undefined { MethodValidator.validate(this.clazz, this.methodName); const controllerType = MethodInfoUtil.getMethodControllerType(this.clazz, this.methodName); if (!controllerType) { return undefined; } const middlewares = MethodInfoUtil.getMethodMiddlewares(this.clazz, this.methodName); const needAcl = MethodInfoUtil.hasMethodAcl(this.clazz, this.methodName); const aclCode = MethodInfoUtil.getMethodAcl(this.clazz, this.methodName); const params = MCPInfoUtil.getMCPToolParams(this.clazz, this.methodName); const detail = MCPInfoUtil.getMCPToolArgsIndex(this.clazz, this.methodName); const extra = MCPInfoUtil.getMCPExtra(this.clazz, this.methodName); return new MCPToolMeta({ name: this.methodName, middlewares, needAcl, aclCode, detail, extra, ...params, }); } } ================================================ FILE: core/controller-decorator/src/model/HTTPControllerMeta.ts ================================================ import path from 'node:path'; import type { ControllerMetadata, EggPrototypeName, MiddlewareFunc } from '@eggjs/tegg-types'; import { ControllerType } from '@eggjs/tegg-types'; import { HTTPMethodMeta } from './HTTPMethodMeta'; export class HTTPControllerMeta implements ControllerMetadata { readonly protoName: EggPrototypeName; readonly controllerName: string; readonly className: string; public readonly type = ControllerType.HTTP; public readonly path?: string; public readonly middlewares: readonly MiddlewareFunc[]; public readonly methods: readonly HTTPMethodMeta[]; public readonly needAcl: boolean; public readonly aclCode?: string; public readonly hosts?: string[]; public readonly timeout?: number; constructor( className: string, protoName: EggPrototypeName, controllerName: string, path: string | undefined, middlewares: MiddlewareFunc[], methods: HTTPMethodMeta[], needAcl: boolean, aclCode: string | undefined, hosts: string[] | undefined, timeout: number | undefined, ) { this.protoName = protoName; this.controllerName = controllerName; this.className = className; this.path = path; this.middlewares = middlewares; this.methods = methods; this.needAcl = needAcl; this.aclCode = aclCode; this.hosts = hosts; this.timeout = timeout; } getMethodRealPath(method: HTTPMethodMeta) { if (this.path) { return path.posix.join(this.path, method.path); } return method.path; } getMethodHosts(method: HTTPMethodMeta): string[] | undefined { if (this.hosts) { return this.hosts; } return method.hosts; } getMethodName(method: HTTPMethodMeta) { return `${method.method} ${this.controllerName}.${method.name}`; } getMethodMiddlewares(method: HTTPMethodMeta) { if (this.middlewares.length) { return [ ...this.middlewares, ...method.middlewares, ]; } return [ ...method.middlewares ]; } hasMethodAcl(method: HTTPMethodMeta): boolean { return method.needAcl || this.needAcl; } getMethodAcl(method: HTTPMethodMeta): string | undefined { if (method.aclCode) { return method.aclCode; } return this.aclCode; } getMethodTimeout(method: HTTPMethodMeta): number | undefined { return method.timeout || this.timeout; } } ================================================ FILE: core/controller-decorator/src/model/HTTPCookies.ts ================================================ import { Cookies } from '@eggjs/cookies'; export class HTTPCookies extends Cookies {} ================================================ FILE: core/controller-decorator/src/model/HTTPMethodMeta.ts ================================================ import assert from 'node:assert'; import pathToRegexp from 'path-to-regexp'; import { HTTPParamType } from '@eggjs/tegg-types'; import type { HTTPMethodEnum, MethodMeta, MiddlewareFunc } from '@eggjs/tegg-types'; export abstract class ParamMeta { type: HTTPParamType; abstract validate(httpPath: string); } export class RequestParamMeta extends ParamMeta { type = HTTPParamType.REQUEST; validate() { return; } } export class BodyParamMeta extends ParamMeta { type = HTTPParamType.BODY; validate() { return; } } export class HeadersParamMeta extends ParamMeta { type = HTTPParamType.HEADERS; validate() { return; } } export class QueryParamMeta extends ParamMeta { type = HTTPParamType.QUERY; name: string; constructor(name: string) { super(); this.name = name; } validate() { return; } } export class QueriesParamMeta extends ParamMeta { type = HTTPParamType.QUERIES; name: string; constructor(name: string) { super(); this.name = name; } validate() { return; } } export class PathParamMeta extends ParamMeta { type = HTTPParamType.PARAM; name: string; constructor(name: string) { super(); this.name = name; } validate(httpPath: string) { const names: pathToRegexp.Key[] = []; pathToRegexp(httpPath, names); if (!names.find(name => String(name.name) === this.name)) { throw new Error(`can not find param ${this.name} in path ${httpPath}`); } } } export class CookiesParamMeta extends ParamMeta { type = HTTPParamType.COOKIES; validate() { return; } } export class HTTPMethodMeta implements MethodMeta { public readonly name: string; public readonly path: string; public readonly method: HTTPMethodEnum; public readonly middlewares: readonly MiddlewareFunc[]; public readonly contextParamIndex: number | undefined; public readonly paramMap: Map; public readonly priority: number; public readonly needAcl: boolean; public readonly aclCode: string | undefined; public readonly hosts: string[] | undefined; public readonly timeout: number | undefined; constructor( name: string, path: string, method: HTTPMethodEnum, middlewares: MiddlewareFunc[], contextParamIndex: number | undefined, paramTypeMap: Map, priority: number, needAcl: boolean, aclCode: string | undefined, hosts: string[] | undefined, timeout: number | undefined, ) { this.name = name; this.path = path; this.method = method; this.middlewares = middlewares; this.contextParamIndex = contextParamIndex; this.paramMap = paramTypeMap; this.priority = priority; this.needAcl = needAcl; this.aclCode = aclCode; this.hosts = hosts; this.timeout = timeout; } } export class ParamMetaUtil { static createParam(type: HTTPParamType, name?: string) { switch (type) { case HTTPParamType.PARAM: { assert(name, 'path param must has name'); return new PathParamMeta(name!); } case HTTPParamType.BODY: { return new BodyParamMeta(); } case HTTPParamType.HEADERS: { return new HeadersParamMeta(); } case HTTPParamType.QUERIES: { assert(name, 'queries param must has name'); return new QueriesParamMeta(name!); } case HTTPParamType.QUERY: { assert(name, 'query param must has name'); return new QueryParamMeta(name!); } case HTTPParamType.REQUEST: { return new RequestParamMeta(); } case HTTPParamType.COOKIES: { return new CookiesParamMeta(); } default: assert.fail('never arrive'); } } } ================================================ FILE: core/controller-decorator/src/model/HTTPRequest.ts ================================================ import undici from 'undici'; // https://github.com/nodejs/undici/blob/main/index.js#L118 // 只有 nodejs >= 16 才支持 Request export class HTTPRequest extends (undici.Request || Object) {} ================================================ FILE: core/controller-decorator/src/model/HTTPResponse.ts ================================================ import undici from 'undici'; // https://github.com/nodejs/undici/blob/main/index.js#L118 // 只有 nodejs >= 16 才支持 Request export class HTTPResponse extends (undici.Response || Object) {} ================================================ FILE: core/controller-decorator/src/model/MCPControllerMeta.ts ================================================ import { ControllerType } from '@eggjs/tegg-types'; import type { ControllerMetadata, MiddlewareFunc, EggPrototypeName, MCPControllerParams, } from '@eggjs/tegg-types'; import { MCPToolMeta } from './MCPToolMeta'; import { MCPResourceMeta } from './MCPResourceMeta'; import { MCPPromptMeta } from './MCPPromptMeta'; export class MCPControllerMeta implements ControllerMetadata { readonly protoName: EggPrototypeName; readonly controllerName: string; readonly className: string; readonly methods: never[]; readonly middlewares: readonly MiddlewareFunc[]; readonly type = ControllerType.MCP; readonly name?: string; readonly version: string; readonly needAcl: boolean; readonly aclCode?: string; readonly tools: MCPToolMeta[]; readonly resources: MCPResourceMeta[]; readonly prompts: MCPPromptMeta[]; readonly timeout?: number; get id() { return `${this.name ?? this.controllerName}:${1.0}`; } constructor( className: string, protoName: EggPrototypeName, controllerName: string, version: string, tools: MCPToolMeta[], resources: MCPResourceMeta[], prompts: MCPPromptMeta[], middlewares: MiddlewareFunc[], name?: string, needAcl?: boolean, aclCode?: string, meta?: MCPControllerParams, ) { this.protoName = protoName; this.controllerName = controllerName; this.className = className; this.name = name; this.version = version; this.tools = tools; this.resources = resources; this.prompts = prompts; this.middlewares = middlewares; this.methods = []; this.needAcl = !!needAcl; this.aclCode = aclCode; this.timeout = meta?.timeout; } getMethodMiddlewares(method: MCPPromptMeta | MCPToolMeta | MCPResourceMeta) { if (this.middlewares.length) { return [ ...this.middlewares, ...method.middlewares ]; } return method.middlewares; } hasMethodAcl(method: MCPPromptMeta | MCPToolMeta | MCPResourceMeta): boolean { return method.needAcl || this.needAcl; } getMethodAcl( method: MCPPromptMeta | MCPToolMeta | MCPResourceMeta, ): string | undefined { return method.aclCode || this.aclCode; } } ================================================ FILE: core/controller-decorator/src/model/MCPPromptMeta.ts ================================================ import type { MiddlewareFunc } from '@eggjs/tegg-types'; import { PromptArgsSchemaDetail } from '../../src/util/MCPInfoUtil'; export class MCPPromptMeta { readonly name: string; readonly needAcl: boolean; readonly mcpName?: string; readonly aclCode?: string; readonly description?: string; readonly detail?: PromptArgsSchemaDetail; readonly middlewares: readonly MiddlewareFunc[]; readonly extra?: number; readonly title?: string; constructor(opt: { name: string; middlewares: MiddlewareFunc[]; needAcl?: boolean; aclCode?: string, description?: string; mcpName?: string; detail?: PromptArgsSchemaDetail; extra?: number; title?: string; }) { this.name = opt.name; this.needAcl = !!opt.needAcl; this.description = opt.description; this.mcpName = opt.mcpName; this.middlewares = opt.middlewares; this.aclCode = opt.aclCode; this.detail = opt.detail; this.extra = opt.extra; this.title = opt.title; } } ================================================ FILE: core/controller-decorator/src/model/MCPResourceMeta.ts ================================================ import type { MiddlewareFunc } from '@eggjs/tegg-types'; import { ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js'; import type { ResourceMetadata } from '@modelcontextprotocol/sdk/server/mcp.js'; export class MCPResourceMeta { readonly name: string; readonly needAcl: boolean; readonly aclCode?: string; readonly mcpName?: string; readonly uri?: string; readonly template?: ResourceTemplate; readonly metadata?: ResourceMetadata; readonly middlewares: readonly MiddlewareFunc[]; readonly extra?: number; constructor(opt: { name: string; middlewares: MiddlewareFunc[]; needAcl?: boolean; aclCode?: string, mcpName?: string; uri?: string; template?: ConstructorParameters; metadata?: ResourceMetadata; extra?: number; }) { this.name = opt.name; this.needAcl = !!opt.needAcl; this.uri = opt.uri; this.metadata = opt.metadata; if (opt.template) { this.template = new ResourceTemplate(opt.template[0], opt.template[1]); } this.middlewares = opt.middlewares; this.aclCode = opt.aclCode; this.mcpName = opt.mcpName; this.extra = opt.extra; } } ================================================ FILE: core/controller-decorator/src/model/MCPToolMeta.ts ================================================ import type { MiddlewareFunc } from '@eggjs/tegg-types'; import { ToolArgsSchemaDetail } from '../../src/util/MCPInfoUtil'; export class MCPToolMeta { readonly name: string; readonly needAcl: boolean; readonly aclCode?: string; readonly mcpName?: string; readonly description?: string; readonly detail?: ToolArgsSchemaDetail; readonly middlewares: readonly MiddlewareFunc[]; readonly extra?: number; constructor(opt: { name: string; middlewares: MiddlewareFunc[]; needAcl?: boolean; aclCode?: string, description?: string; mcpName?: string; detail?: ToolArgsSchemaDetail; extra?: number; }) { this.name = opt.name; this.needAcl = !!opt.needAcl; this.description = opt.description; this.mcpName = opt.mcpName; this.middlewares = opt.middlewares; this.aclCode = opt.aclCode; this.detail = opt.detail; this.extra = opt.extra; } } ================================================ FILE: core/controller-decorator/src/model/index.ts ================================================ export * from './HTTPMethodMeta'; export * from './HTTPControllerMeta'; export * from './HTTPRequest'; export * from './HTTPResponse'; export * from './HTTPCookies'; export * from './MCPControllerMeta'; export * from './MCPPromptMeta'; export * from './MCPResourceMeta'; export * from './MCPToolMeta'; ================================================ FILE: core/controller-decorator/src/util/AgentInfoUtil.ts ================================================ import { MetadataUtil } from '@eggjs/core-decorator'; import { CONTROLLER_AGENT_CONTROLLER, CONTROLLER_AGENT_NOT_IMPLEMENTED, CONTROLLER_AGENT_ENHANCED, } from '@eggjs/tegg-types'; import type { EggProtoImplClass } from '@eggjs/tegg-types'; export class AgentInfoUtil { static setAgentController(clazz: EggProtoImplClass): void { MetadataUtil.defineMetaData(CONTROLLER_AGENT_CONTROLLER, true, clazz); } static isAgentController(clazz: EggProtoImplClass): boolean { return MetadataUtil.getBooleanMetaData(CONTROLLER_AGENT_CONTROLLER, clazz); } static setNotImplemented(fn: Function): void { Reflect.defineMetadata(CONTROLLER_AGENT_NOT_IMPLEMENTED, true, fn); } static isNotImplemented(fn: Function): boolean { return !!Reflect.getMetadata(CONTROLLER_AGENT_NOT_IMPLEMENTED, fn); } static setEnhanced(clazz: EggProtoImplClass): void { MetadataUtil.defineMetaData(CONTROLLER_AGENT_ENHANCED, true, clazz); } static isEnhanced(clazz: EggProtoImplClass): boolean { return MetadataUtil.getBooleanMetaData(CONTROLLER_AGENT_ENHANCED, clazz); } } ================================================ FILE: core/controller-decorator/src/util/ControllerInfoUtil.ts ================================================ import { CONTROLLER_ACL, CONTROLLER_AOP_MIDDLEWARES, CONTROLLER_HOST, CONTROLLER_MIDDLEWARES, CONTROLLER_NAME, CONTROLLER_TIMEOUT_METADATA, CONTROLLER_TYPE, IAdvice, } from '@eggjs/tegg-types'; import type { ControllerTypeLike, EggProtoImplClass, MiddlewareFunc } from '@eggjs/tegg-types'; import { MetadataUtil } from '@eggjs/core-decorator'; export default class ControllerInfoUtil { static addControllerMiddleware(middleware: MiddlewareFunc, clazz: EggProtoImplClass) { const middlewares = MetadataUtil.initOwnArrayMetaData(CONTROLLER_MIDDLEWARES, clazz, []); middlewares.push(middleware); } static addControllerAopMiddleware(middleware: EggProtoImplClass, clazz: EggProtoImplClass) { const middlewares = MetadataUtil.initOwnArrayMetaData>(CONTROLLER_AOP_MIDDLEWARES, clazz, []); middlewares.push(middleware); } static getControllerMiddlewares(clazz: EggProtoImplClass): MiddlewareFunc[] { return MetadataUtil.getMetaData(CONTROLLER_MIDDLEWARES, clazz) || []; } static getControllerAopMiddlewares(clazz: EggProtoImplClass): EggProtoImplClass[] { return MetadataUtil.getMetaData(CONTROLLER_AOP_MIDDLEWARES, clazz) || []; } static setControllerType(clazz: EggProtoImplClass, controllerType: ControllerTypeLike) { MetadataUtil.defineMetaData(CONTROLLER_TYPE, controllerType, clazz); } static setControllerName(clazz: EggProtoImplClass, controllerName: string) { MetadataUtil.defineMetaData(CONTROLLER_NAME, controllerName, clazz); } static getControllerName(clazz: EggProtoImplClass): string | undefined { return MetadataUtil.getMetaData(CONTROLLER_NAME, clazz); } static getControllerType(clazz): ControllerTypeLike | undefined { return MetadataUtil.getMetaData(CONTROLLER_TYPE, clazz); } static setControllerAcl(code: string | undefined, clazz: EggProtoImplClass) { MetadataUtil.defineMetaData(CONTROLLER_ACL, code, clazz); } static hasControllerAcl(clazz: EggProtoImplClass): boolean { return MetadataUtil.hasMetaData(CONTROLLER_ACL, clazz); } static getControllerAcl(clazz: EggProtoImplClass): string | undefined { return MetadataUtil.getMetaData(CONTROLLER_ACL, clazz); } static addControllerHosts(hosts: string[], clazz: EggProtoImplClass) { MetadataUtil.defineMetaData(CONTROLLER_HOST, hosts, clazz); } static getControllerHosts(clazz: EggProtoImplClass): string[] | undefined { return MetadataUtil.getMetaData(CONTROLLER_HOST, clazz); } static setControllerTimeout(timeout: number, clazz: EggProtoImplClass) { MetadataUtil.defineMetaData(CONTROLLER_TIMEOUT_METADATA, timeout, clazz); } static getControllerTimeout(clazz: EggProtoImplClass): number | undefined { return MetadataUtil.getMetaData(CONTROLLER_TIMEOUT_METADATA, clazz); } } ================================================ FILE: core/controller-decorator/src/util/ControllerMetadataUtil.ts ================================================ import { CONTROLLER_META_DATA } from '@eggjs/tegg-types'; import type { ControllerMetadata, EggProtoImplClass } from '@eggjs/tegg-types'; import { MetadataUtil } from '@eggjs/core-decorator'; import { ControllerMetaBuilderFactory } from '../builder/ControllerMetaBuilderFactory'; export class ControllerMetadataUtil { static setControllerMetadata(clazz: EggProtoImplClass, metaData: ControllerMetadata) { MetadataUtil.defineMetaData(CONTROLLER_META_DATA, metaData, clazz); } static getControllerMetadata(clazz): ControllerMetadata | undefined { let metadata: ControllerMetadata | undefined = MetadataUtil.getOwnMetaData(CONTROLLER_META_DATA, clazz); if (metadata) { return metadata; } metadata = ControllerMetaBuilderFactory.build(clazz); if (metadata) { ControllerMetadataUtil.setControllerMetadata(clazz, metadata); } return metadata; } } ================================================ FILE: core/controller-decorator/src/util/HTTPInfoUtil.ts ================================================ import { MetadataUtil } from '@eggjs/core-decorator'; import { CONTROLLER_HTTP_PATH, CONTROLLER_METHOD_METHOD_MAP, CONTROLLER_METHOD_PARAM_NAME_MAP, CONTROLLER_METHOD_PARAM_TYPE_MAP, CONTROLLER_METHOD_PATH_MAP, CONTROLLER_METHOD_PRIORITY, } from '@eggjs/tegg-types'; import type { EggProtoImplClass, HTTPMethodEnum, HTTPParamType } from '@eggjs/tegg-types'; import { MapUtil } from '@eggjs/tegg-common-util'; type HTTPMethodPathMap = Map; type HTTPMethodMethodMap = Map; type HTTPMethodParamTypeMap = Map>; type HTTPMethodParamNameMap = Map>; type HTTPMethodPriorityMap = Map; export default class HTTPInfoUtil { static setHTTPPath(path: string, clazz: EggProtoImplClass) { MetadataUtil.defineMetaData(CONTROLLER_HTTP_PATH, path, clazz); } static getHTTPPath(clazz: EggProtoImplClass): string | undefined { return MetadataUtil.getMetaData(CONTROLLER_HTTP_PATH, clazz); } static setHTTPMethodPath(path: string, clazz: EggProtoImplClass, methodName: string) { const methodPathMap = MetadataUtil.initOwnMapMetaData(CONTROLLER_METHOD_PATH_MAP, clazz, new Map()); methodPathMap.set(methodName, path); } static getHTTPMethodPath(clazz: EggProtoImplClass, methodName: string): string | undefined { const methodPathMap: HTTPMethodPathMap | undefined = MetadataUtil.getMetaData(CONTROLLER_METHOD_PATH_MAP, clazz); return methodPathMap?.get(methodName); } static setHTTPMethodMethod(method: HTTPMethodEnum, clazz: EggProtoImplClass, methodName: string) { const methodMap: HTTPMethodMethodMap = MetadataUtil.initOwnMapMetaData(CONTROLLER_METHOD_METHOD_MAP, clazz, new Map()); methodMap.set(methodName, method); } static getHTTPMethodMethod(clazz: EggProtoImplClass, methodName: string): HTTPMethodEnum | undefined { const methodMap: HTTPMethodMethodMap | undefined = MetadataUtil.getMetaData(CONTROLLER_METHOD_METHOD_MAP, clazz); return methodMap?.get(methodName); } static setHTTPMethodParamType(paramType: HTTPParamType, parameterIndex: number, clazz: EggProtoImplClass, methodName: string) { const methodParamMap: HTTPMethodParamTypeMap = MetadataUtil.initOwnMapMetaData(CONTROLLER_METHOD_PARAM_TYPE_MAP, clazz, new Map()); const paramMap = MapUtil.getOrStore(methodParamMap, methodName, new Map()); paramMap.set(parameterIndex, paramType); } static getParamIndexList(clazz: EggProtoImplClass, methodName: string): number[] { const methodParamMap: HTTPMethodParamTypeMap | undefined = MetadataUtil.getMetaData(CONTROLLER_METHOD_PARAM_TYPE_MAP, clazz); const paramMap = methodParamMap?.get(methodName); if (!paramMap) { return []; } return Array.from(paramMap.keys()); } static getHTTPMethodParamType(parameterIndex: number, clazz: EggProtoImplClass, methodName: string): HTTPParamType | undefined { const methodParamMap: HTTPMethodParamTypeMap | undefined = MetadataUtil.getMetaData(CONTROLLER_METHOD_PARAM_TYPE_MAP, clazz); const paramMap = methodParamMap?.get(methodName); return paramMap?.get(parameterIndex); } static setHTTPMethodParamName(paramName: string, parameterIndex: number, clazz: EggProtoImplClass, methodName: string) { const methodParamNameMap: HTTPMethodParamNameMap = MetadataUtil.initOwnMapMetaData(CONTROLLER_METHOD_PARAM_NAME_MAP, clazz, new Map()); const paramMap = MapUtil.getOrStore(methodParamNameMap, methodName, new Map()); paramMap.set(parameterIndex, paramName); } static getHTTPMethodParamName(parameterIndex: number, clazz: EggProtoImplClass, methodName: string): string | undefined { const methodParamNameMap: HTTPMethodParamNameMap | undefined = MetadataUtil.getMetaData(CONTROLLER_METHOD_PARAM_NAME_MAP, clazz); const paramMap = methodParamNameMap?.get(methodName); return paramMap?.get(parameterIndex); } static getHTTPMethodPriority(clazz: EggProtoImplClass, methodName: string): number | undefined { const methodPriorityMap: HTTPMethodPriorityMap | undefined = MetadataUtil.getMetaData(CONTROLLER_METHOD_PRIORITY, clazz); return methodPriorityMap?.get(methodName); } static setHTTPMethodPriority(priority: number, clazz: EggProtoImplClass, methodName: string) { const methodPriorityMap: HTTPMethodPriorityMap = MetadataUtil.initOwnMapMetaData(CONTROLLER_METHOD_PRIORITY, clazz, new Map()); methodPriorityMap.set(methodName, priority); } } ================================================ FILE: core/controller-decorator/src/util/HTTPPriorityUtil.ts ================================================ import { parse } from 'path-to-regexp'; export class HTTPPriorityUtil { static readonly DEFAULT_PRIORITY = 100000; private static readonly TOKEN_PRIORITY = 1000; /** * | Path | RegExp index | priority | * | --- | --- | --- | * | /* | [0] | 0 | * | /hello/:name | [1] | 1000 | * | /hello/world/message/:message | [3] | 3000 | * | /hello/:name/message/:message | [1, 3] | 4000 | * | /hello/world | [] | 100000/Infinity? | * * priority = hasRegExp * : regexpIndex.reduce((p,c) => p + c * 1000, 0) * : 100000; * @param {string} path - */ static calcPathPriority(path: string): number { const tokens = parse(path); let priority = 0; let hasRegExp = false; let index = 0; let token; while ((token = tokens.shift())) { if (typeof token === 'string') { // /view/users/* // token is [ '/view/users', '*' ] index += (token.split('/').length - 1); } else { hasRegExp = true; priority += index++ * this.TOKEN_PRIORITY; } } if (!hasRegExp) { return this.DEFAULT_PRIORITY; } return priority; } } ================================================ FILE: core/controller-decorator/src/util/MCPInfoUtil.ts ================================================ import { CONTROLLER_MCP_NAME, CONTROLLER_MCP_PROMPT_MAP, CONTROLLER_MCP_RESOURCE_MAP, CONTROLLER_MCP_RESOURCE_PARAMS_MAP, CONTROLLER_MCP_TOOL_MAP, CONTROLLER_MCP_TOOL_PARAMS_MAP, CONTROLLER_MCP_VERSION, MCPPromptParams, MCPResourceParams, MCPToolParams, EggProtoImplClass, CONTROLLER_MCP_TOOL_ARGS_INDEX, CONTROLLER_MCP_PROMPT_ARGS_INDEX, CONTROLLER_MCP_EXTRA_INDEX, CONTROLLER_MCP_PROMPT_PARAMS_MAP, MCPControllerParams, CONTROLLER_MCP_CONTROLLER_PARAMS_MAP, } from '@eggjs/tegg-types'; import { MetadataUtil } from '@eggjs/core-decorator'; import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; type MCPMethodMap = Map; type MCPResourceMap = Map; type MCPToolMap = Map; type MCPPromptMap = Map; export interface ToolArgsSchemaDetail { argsSchema: Parameters['2']; index: number; } type MCPToolArgsSchemaMap = Map; type MCPExtraMap = Map; export interface PromptArgsSchemaDetail { argsSchema: Parameters['2']; index: number; } type MCPPromptArgsSchemaMap = Map; export class MCPInfoUtil { static setMCPName(name: string, clazz: EggProtoImplClass) { MetadataUtil.defineMetaData(CONTROLLER_MCP_NAME, name, clazz); } static getMCPName(clazz: EggProtoImplClass): string | undefined { return MetadataUtil.getMetaData(CONTROLLER_MCP_NAME, clazz); } static setMCPVersion(version: string, clazz: EggProtoImplClass) { MetadataUtil.defineMetaData(CONTROLLER_MCP_VERSION, version, clazz); } static getMCPVersion(clazz: EggProtoImplClass): string | undefined { return MetadataUtil.getMetaData(CONTROLLER_MCP_VERSION, clazz); } static setMCPControllerParams(params: MCPControllerParams | undefined, clazz: EggProtoImplClass) { MetadataUtil.defineMetaData(CONTROLLER_MCP_CONTROLLER_PARAMS_MAP, params, clazz); } static getMCPControllerParams(clazz: EggProtoImplClass): MCPControllerParams | undefined { return MetadataUtil.getMetaData(CONTROLLER_MCP_CONTROLLER_PARAMS_MAP, clazz); } static setMCPResource(clazz: EggProtoImplClass, methodName: string) { const methodMap: MCPMethodMap = MetadataUtil.initOwnMapMetaData(CONTROLLER_MCP_RESOURCE_MAP, clazz, new Map()); methodMap.set(methodName, true); } static getMCPResource(clazz: EggProtoImplClass): string[] { const methodMap: MCPMethodMap | undefined = MetadataUtil.getMetaData(CONTROLLER_MCP_RESOURCE_MAP, clazz); if (!methodMap) { return []; } return Array.from(methodMap.keys()); } static setMCPResourceParams(params: MCPResourceParams & { mcpName?: string }, clazz: EggProtoImplClass, resourceName: string) { const methodMap: MCPResourceMap = MetadataUtil.initOwnMapMetaData(CONTROLLER_MCP_RESOURCE_PARAMS_MAP, clazz, new Map()); methodMap.set(resourceName, params); } static getMCPResourceParams(clazz: EggProtoImplClass, resourceName: string): MCPResourceParams & { mcpName?: string } | undefined { const methodMap: MCPResourceMap | undefined = MetadataUtil.getMetaData(CONTROLLER_MCP_RESOURCE_PARAMS_MAP, clazz); return methodMap?.get(resourceName); } static setMCPTool(clazz: EggProtoImplClass, methodName: string) { const methodMap: MCPMethodMap = MetadataUtil.initOwnMapMetaData(CONTROLLER_MCP_TOOL_MAP, clazz, new Map()); methodMap.set(methodName, true); } static getMCPTool(clazz: EggProtoImplClass): string[] { const methodMap: MCPMethodMap | undefined = MetadataUtil.getMetaData(CONTROLLER_MCP_TOOL_MAP, clazz); if (!methodMap) { return []; } return Array.from(methodMap.keys()); } static getMCPToolParams(clazz: EggProtoImplClass, resourceName: string): MCPToolParams & { mcpName?: string } | undefined { const methodMap: MCPToolMap | undefined = MetadataUtil.getMetaData(CONTROLLER_MCP_TOOL_PARAMS_MAP, clazz); return methodMap?.get(resourceName); } static setMCPToolParams(params: MCPToolParams & { mcpName?: string }, clazz: EggProtoImplClass, resourceName: string) { const methodMap: MCPToolMap = MetadataUtil.initOwnMapMetaData(CONTROLLER_MCP_TOOL_PARAMS_MAP, clazz, new Map()); methodMap.set(resourceName, params); } static setMCPPrompt(clazz: EggProtoImplClass, methodName: string) { const methodMap: MCPMethodMap = MetadataUtil.initOwnMapMetaData(CONTROLLER_MCP_PROMPT_MAP, clazz, new Map()); methodMap.set(methodName, true); } static getMCPPrompt(clazz: EggProtoImplClass): string[] { const methodMap: MCPMethodMap | undefined = MetadataUtil.getMetaData(CONTROLLER_MCP_PROMPT_MAP, clazz); if (!methodMap) { return []; } return Array.from(methodMap.keys()); } static setMCPPromptParams(params: MCPPromptParams & { mcpName?: string }, clazz: EggProtoImplClass, resourceName: string) { const methodMap: MCPPromptMap = MetadataUtil.initOwnMapMetaData(CONTROLLER_MCP_PROMPT_PARAMS_MAP, clazz, new Map()); methodMap.set(resourceName, params); } static getMCPPromptParams(clazz: EggProtoImplClass, resourceName: string): MCPPromptParams & { mcpName?: string } | undefined { const methodMap: MCPPromptMap | undefined = MetadataUtil.getMetaData(CONTROLLER_MCP_PROMPT_PARAMS_MAP, clazz); return methodMap?.get(resourceName); } static setMCPToolArgsInArgs(detail: ToolArgsSchemaDetail, clazz: EggProtoImplClass, methodName: string) { const methodContextIndexMap: MCPToolArgsSchemaMap = MetadataUtil.initOwnMapMetaData(CONTROLLER_MCP_TOOL_ARGS_INDEX, clazz, new Map()); methodContextIndexMap.set(methodName, detail); } static getMCPToolArgsIndex(clazz: EggProtoImplClass, methodName: string): ToolArgsSchemaDetail | undefined { const methodContextIndexMap: MCPToolArgsSchemaMap | undefined = MetadataUtil.getMetaData(CONTROLLER_MCP_TOOL_ARGS_INDEX, clazz); return methodContextIndexMap?.get(methodName); } static setMCPExtra(index: number, clazz: EggProtoImplClass, methodName: string) { const methodContextIndexMap: MCPExtraMap = MetadataUtil.initOwnMapMetaData(CONTROLLER_MCP_EXTRA_INDEX, clazz, new Map()); methodContextIndexMap.set(methodName, index); } static getMCPExtra(clazz: EggProtoImplClass, methodName: string): number | undefined { const methodContextIndexMap: MCPExtraMap | undefined = MetadataUtil.getMetaData(CONTROLLER_MCP_EXTRA_INDEX, clazz); return methodContextIndexMap?.get(methodName); } static setMCPPromptArgsInArgs(detail: PromptArgsSchemaDetail, clazz: EggProtoImplClass, methodName: string) { const methodContextIndexMap: MCPPromptArgsSchemaMap = MetadataUtil.initOwnMapMetaData(CONTROLLER_MCP_PROMPT_ARGS_INDEX, clazz, new Map()); methodContextIndexMap.set(methodName, detail); } static getMCPPromptArgsIndex(clazz: EggProtoImplClass, methodName: string): PromptArgsSchemaDetail | undefined { const methodContextIndexMap: MCPPromptArgsSchemaMap | undefined = MetadataUtil.getMetaData(CONTROLLER_MCP_PROMPT_ARGS_INDEX, clazz); return methodContextIndexMap?.get(methodName); } } ================================================ FILE: core/controller-decorator/src/util/MethodInfoUtil.ts ================================================ import { MetadataUtil } from '@eggjs/core-decorator'; import { MapUtil } from '@eggjs/tegg-common-util'; import { IAdvice, METHOD_ACL, METHOD_AOP_MIDDLEWARES, METHOD_AOP_REGISTER_MAP, METHOD_CONTEXT_INDEX, METHOD_CONTROLLER_HOST, METHOD_CONTROLLER_TYPE_MAP, METHOD_MIDDLEWARES, METHOD_TIMEOUT_METADATA, } from '@eggjs/tegg-types'; import type { ControllerTypeLike, EggProtoImplClass, MiddlewareFunc } from '@eggjs/tegg-types'; type METHOD_MAP = Map; type MethodAopRegisterMap = Map; type MethodContextIndexMap = Map; type MethodMiddlewareMap = Map; type MethodAopMiddlewareMap = Map[]>; type MethodAclMap = Map; type MethodTimeoutMap = Map; export default class MethodInfoUtil { static setMethodControllerType(clazz: EggProtoImplClass, methodName: string, controllerType: ControllerTypeLike) { const methodControllerMap: METHOD_MAP = MetadataUtil.initOwnMapMetaData(METHOD_CONTROLLER_TYPE_MAP, clazz, new Map()); methodControllerMap.set(methodName, controllerType); } static getMethodControllerType(clazz: EggProtoImplClass, methodName: string): ControllerTypeLike | undefined { const methodControllerMap: METHOD_MAP | undefined = MetadataUtil.getMetaData(METHOD_CONTROLLER_TYPE_MAP, clazz); return methodControllerMap?.get(methodName) as ControllerTypeLike | undefined; } static setMethodContextIndexInArgs(index: number, clazz: EggProtoImplClass, methodName: string) { const methodContextIndexMap: MethodContextIndexMap = MetadataUtil.initOwnMapMetaData(METHOD_CONTEXT_INDEX, clazz, new Map()); methodContextIndexMap.set(methodName, index); } static getMethodContextIndex(clazz: EggProtoImplClass, methodName: string): number | undefined { const methodContextIndexMap: MethodContextIndexMap | undefined = MetadataUtil.getMetaData(METHOD_CONTEXT_INDEX, clazz); return methodContextIndexMap?.get(methodName); } static addMethodMiddleware(middleware: MiddlewareFunc, clazz: EggProtoImplClass, methodName: string) { const methodMiddlewareMap: MethodMiddlewareMap = MetadataUtil.initOwnMapMetaData(METHOD_MIDDLEWARES, clazz, new Map()); const methodMiddlewares = MapUtil.getOrStore(methodMiddlewareMap, methodName, []); methodMiddlewares.push(middleware); } static getMethodMiddlewares(clazz: EggProtoImplClass, methodName: string): MiddlewareFunc[] { const methodMiddlewareMap: MethodMiddlewareMap | undefined = MetadataUtil.getMetaData(METHOD_MIDDLEWARES, clazz); return methodMiddlewareMap?.get(methodName) || []; } static addMethodAopMiddleware(middleware: EggProtoImplClass, clazz: EggProtoImplClass, methodName: string) { const methodMiddlewareMap: MethodAopMiddlewareMap = MetadataUtil.initOwnMapMetaData(METHOD_AOP_MIDDLEWARES, clazz, new Map()); const methodMiddlewares = MapUtil.getOrStore(methodMiddlewareMap, methodName, []); methodMiddlewares.push(middleware); } static getMethodAopMiddlewares(clazz: EggProtoImplClass, methodName: string): EggProtoImplClass[] { const methodMiddlewareMap: MethodAopMiddlewareMap | undefined = MetadataUtil.getMetaData(METHOD_AOP_MIDDLEWARES, clazz); return methodMiddlewareMap?.get(methodName) || []; } static setMethodAcl(code: string | undefined, clazz: EggProtoImplClass, methodName: string) { const methodAclMap: MethodAclMap = MetadataUtil.initOwnMapMetaData(METHOD_ACL, clazz, new Map()); methodAclMap.set(methodName, code); } static hasMethodAcl(clazz: EggProtoImplClass, methodName: string): boolean { const methodAclMap: MethodAclMap | undefined = MetadataUtil.getMetaData(METHOD_ACL, clazz); return !!methodAclMap?.has(methodName); } static getMethodAcl(clazz: EggProtoImplClass, methodName: string): string | undefined { const methodAclMap: MethodAclMap | undefined = MetadataUtil.getMetaData(METHOD_ACL, clazz); return methodAclMap?.get(methodName); } static setMethodHosts(hosts: string[], clazz: EggProtoImplClass, methodName: string) { const methodControllerMap: METHOD_MAP = MetadataUtil.initOwnMapMetaData(METHOD_CONTROLLER_HOST, clazz, new Map()); methodControllerMap.set(methodName, hosts); } static getMethodHosts(clazz: EggProtoImplClass, methodName: string): string[] | undefined { const methodControllerMap: METHOD_MAP | undefined = MetadataUtil.getMetaData(METHOD_CONTROLLER_HOST, clazz); return methodControllerMap?.get(methodName) as string[] | undefined; } static getMethods(clazz: EggProtoImplClass): string[] { const methodControllerMap: METHOD_MAP | undefined = MetadataUtil.getMetaData(METHOD_CONTROLLER_TYPE_MAP, clazz); return Array.from(methodControllerMap?.keys() || []); } static shouldRegisterAopMiddlewarePointCut(clazz: EggProtoImplClass, methodName: string): boolean { const methodControllerMap: MethodAopRegisterMap | undefined = MetadataUtil.getMetaData(METHOD_AOP_REGISTER_MAP, clazz); return !(methodControllerMap && methodControllerMap.get(methodName)); } static registerAopMiddlewarePointcut(clazz: EggProtoImplClass, methodName: string) { const methodControllerMap: MethodAopRegisterMap = MetadataUtil.initOwnMapMetaData(METHOD_AOP_REGISTER_MAP, clazz, new Map()); methodControllerMap.set(methodName, true); } static setMethodTimeout(timeout: number, clazz: EggProtoImplClass, methodName: string) { const methodAclMap: MethodTimeoutMap = MetadataUtil.initOwnMapMetaData(METHOD_TIMEOUT_METADATA, clazz, new Map()); methodAclMap.set(methodName, timeout); } static getMethodTimeout(clazz: EggProtoImplClass, methodName: string): number | undefined { const methodAclMap: MethodTimeoutMap | undefined = MetadataUtil.getMetaData(METHOD_TIMEOUT_METADATA, clazz); return methodAclMap?.get(methodName); } } ================================================ FILE: core/controller-decorator/src/util/validator/ControllerValidator.ts ================================================ import type { EggProtoImplClass } from '@eggjs/tegg-types'; import { ClassUtil } from '@eggjs/tegg-metadata'; import ControllerInfoUtil from '../ControllerInfoUtil'; export class ControllerValidator { // should throw error // 1. use controller middleware but not has controller decorator static validate(clazz: EggProtoImplClass) { const controllerType = ControllerInfoUtil.getControllerType(clazz); const middlewares = ControllerInfoUtil.getControllerMiddlewares(clazz); if (middlewares.length && !controllerType) { const desc = ClassUtil.classDescription(clazz); throw new Error(`${desc} @Middleware should use with controller decorator`); } } } ================================================ FILE: core/controller-decorator/src/util/validator/MethodValidator.ts ================================================ import type { EggProtoImplClass } from '@eggjs/tegg-types'; import { ClassUtil } from '@eggjs/tegg-metadata'; import MethodInfoUtil from '../MethodInfoUtil'; import ControllerInfoUtil from '../ControllerInfoUtil'; export class MethodValidator { // should throw error // 1. use method middleware but not has method decorator // 2. use context decorator but not has method decorator // 3. method decorator type is not same as controller decorator type static validate(clazz: EggProtoImplClass, methodName: string) { const methodControllerType = MethodInfoUtil.getMethodControllerType(clazz, methodName); const methodMiddlewares = MethodInfoUtil.getMethodMiddlewares(clazz, methodName); const contextIndex = MethodInfoUtil.getMethodContextIndex(clazz, methodName); if (!methodControllerType) { if (methodMiddlewares.length) { const desc = ClassUtil.classDescription(clazz); throw new Error(`${desc}:${methodName} @Middleware should use with method decorator`); } if (contextIndex !== undefined) { const desc = ClassUtil.classDescription(clazz); throw new Error(`${desc}:${methodName} @Context should use with method decorator`); } return; } const controllerType = ControllerInfoUtil.getControllerType(clazz); if (methodControllerType !== controllerType) { const desc = ClassUtil.classDescription(clazz); throw new Error(`${desc}:${methodName} method decorator ${methodControllerType} can not be used with ${controllerType}`); } } } ================================================ FILE: core/controller-decorator/test/Acl.test.ts ================================================ import assert from 'node:assert'; import { ControllerType } from '@eggjs/tegg-types'; import { AclController } from './fixtures/AclController'; import { ControllerMetaBuilderFactory } from '../src/builder/ControllerMetaBuilderFactory'; import { HTTPControllerMeta } from '../src/model'; describe('test/Context.test.ts', () => { it('should work', () => { const builder = ControllerMetaBuilderFactory.createControllerMetaBuilder(AclController, ControllerType.HTTP)!; const aclControllerMeta = builder.build()! as HTTPControllerMeta; const fooMethod = aclControllerMeta.methods.find(t => t.name === 'foo')!; const barMethod = aclControllerMeta.methods.find(t => t.name === 'bar')!; const fooAcl = aclControllerMeta.getMethodAcl(fooMethod); const barAcl = aclControllerMeta.getMethodAcl(barMethod); assert(fooAcl === 'mock2'); assert(barAcl === 'mock1'); }); }); ================================================ FILE: core/controller-decorator/test/AgentController.test.ts ================================================ import assert from 'node:assert/strict'; import { ControllerType, HTTPMethodEnum } from '@eggjs/tegg-types'; import { AgentInfoUtil, ControllerMetaBuilderFactory, BodyParamMeta, PathParamMeta, QueryParamMeta, ControllerInfoUtil, MethodInfoUtil, } from '../index'; import HTTPInfoUtil from '../src/util/HTTPInfoUtil'; import { HTTPControllerMeta } from '../src/model/index'; import { AgentFooController } from './fixtures/AgentFooController'; describe('core/controller-decorator/test/AgentController.test.ts', () => { describe('decorator metadata', () => { it('should set ControllerType.HTTP on the class', () => { const controllerType = ControllerInfoUtil.getControllerType(AgentFooController); assert.strictEqual(controllerType, ControllerType.HTTP); }); it('should set AGENT_CONTROLLER metadata on the class', () => { assert.strictEqual(AgentInfoUtil.isAgentController(AgentFooController), true); }); it('should set fixed base path /api/v1', () => { const httpPath = HTTPInfoUtil.getHTTPPath(AgentFooController); assert.strictEqual(httpPath, '/api/v1'); }); }); describe('method HTTP metadata', () => { const methodRoutes = [ { methodName: 'createThread', httpMethod: HTTPMethodEnum.POST, path: '/threads' }, { methodName: 'getThread', httpMethod: HTTPMethodEnum.GET, path: '/threads/:id' }, { methodName: 'asyncRun', httpMethod: HTTPMethodEnum.POST, path: '/runs' }, { methodName: 'streamRun', httpMethod: HTTPMethodEnum.POST, path: '/runs/stream' }, { methodName: 'syncRun', httpMethod: HTTPMethodEnum.POST, path: '/runs/wait' }, { methodName: 'getRun', httpMethod: HTTPMethodEnum.GET, path: '/runs/:id' }, { methodName: 'cancelRun', httpMethod: HTTPMethodEnum.POST, path: '/runs/:id/cancel' }, ]; for (const route of methodRoutes) { it(`should set correct HTTP method for ${route.methodName}`, () => { const method = HTTPInfoUtil.getHTTPMethodMethod(AgentFooController, route.methodName); assert.strictEqual(method, route.httpMethod); }); it(`should set correct HTTP path for ${route.methodName}`, () => { const path = HTTPInfoUtil.getHTTPMethodPath(AgentFooController, route.methodName); assert.strictEqual(path, route.path); }); it(`should set ControllerType.HTTP on method ${route.methodName}`, () => { const controllerType = MethodInfoUtil.getMethodControllerType(AgentFooController, route.methodName); assert.strictEqual(controllerType, ControllerType.HTTP); }); } }); describe('parameter metadata', () => { it('should set BODY param at index 0 for asyncRun', () => { const paramType = HTTPInfoUtil.getHTTPMethodParamType(0, AgentFooController, 'asyncRun'); assert.strictEqual(paramType, 'BODY'); }); it('should set BODY param at index 0 for streamRun', () => { const paramType = HTTPInfoUtil.getHTTPMethodParamType(0, AgentFooController, 'streamRun'); assert.strictEqual(paramType, 'BODY'); }); it('should set BODY param at index 0 for syncRun', () => { const paramType = HTTPInfoUtil.getHTTPMethodParamType(0, AgentFooController, 'syncRun'); assert.strictEqual(paramType, 'BODY'); }); it('should set PARAM at index 0 with name "id" for getThread', () => { const paramType = HTTPInfoUtil.getHTTPMethodParamType(0, AgentFooController, 'getThread'); assert.strictEqual(paramType, 'PARAM'); const paramName = HTTPInfoUtil.getHTTPMethodParamName(0, AgentFooController, 'getThread'); assert.strictEqual(paramName, 'id'); }); it('should set PARAM at index 0 with name "id" for getRun', () => { const paramType = HTTPInfoUtil.getHTTPMethodParamType(0, AgentFooController, 'getRun'); assert.strictEqual(paramType, 'PARAM'); const paramName = HTTPInfoUtil.getHTTPMethodParamName(0, AgentFooController, 'getRun'); assert.strictEqual(paramName, 'id'); }); it('should set PARAM at index 0 with name "id" for cancelRun', () => { const paramType = HTTPInfoUtil.getHTTPMethodParamType(0, AgentFooController, 'cancelRun'); assert.strictEqual(paramType, 'PARAM'); const paramName = HTTPInfoUtil.getHTTPMethodParamName(0, AgentFooController, 'cancelRun'); assert.strictEqual(paramName, 'id'); }); it('should not have params for createThread', () => { const paramIndexList = HTTPInfoUtil.getParamIndexList(AgentFooController, 'createThread'); assert.strictEqual(paramIndexList.length, 0); }); }); describe('context index', () => { it('should not set contextIndex on any method', () => { const methods = [ 'createThread', 'getThread', 'asyncRun', 'streamRun', 'syncRun', 'getRun', 'cancelRun' ]; for (const methodName of methods) { const contextIndex = MethodInfoUtil.getMethodContextIndex(AgentFooController, methodName); assert.strictEqual(contextIndex, undefined, `${methodName} should not have contextIndex`); } }); }); describe('AgentInfoUtil.setEnhanced / isEnhanced', () => { it('should return false before setEnhanced is called', () => { class NotEnhanced {} assert.strictEqual(AgentInfoUtil.isEnhanced(NotEnhanced), false); }); it('should return true after setEnhanced is called', () => { class ToBeEnhanced {} AgentInfoUtil.setEnhanced(ToBeEnhanced); assert.strictEqual(AgentInfoUtil.isEnhanced(ToBeEnhanced), true); }); }); describe('default implementations', () => { it('should inject default stubs for all 8 route methods', () => { // AgentFooController only implements execRun (smart defaults pattern) // All 8 route methods should have stub defaults that throw const proto = AgentFooController.prototype as any; const routeMethods = [ 'createThread', 'getThread', 'asyncRun', 'streamRun', 'getRunStream', 'syncRun', 'getRun', 'cancelRun' ]; for (const methodName of routeMethods) { assert(typeof proto[methodName] === 'function', `${methodName} should be a function`); assert.strictEqual( AgentInfoUtil.isNotImplemented(proto[methodName]), true, `${methodName} should be marked as AGENT_NOT_IMPLEMENTED`, ); } }); const stubMethods = [ { name: 'createThread', args: [] }, { name: 'getThread', args: [ 'thread_1' ] }, { name: 'asyncRun', args: [{ input: { messages: [] } }] }, { name: 'streamRun', args: [{ input: { messages: [] } }] }, { name: 'getRunStream', args: [ 'run_1', '0' ] }, { name: 'syncRun', args: [{ input: { messages: [] } }] }, { name: 'getRun', args: [ 'run_1' ] }, { name: 'cancelRun', args: [ 'run_1' ] }, ]; for (const { name, args } of stubMethods) { it(`should throw for unimplemented ${name}`, async () => { const instance = new AgentFooController() as any; await assert.rejects(() => instance[name](...args), new RegExp(`${name} not implemented`)); }); } }); describe('HTTPControllerMetaBuilder integration', () => { it('should build metadata with 8 HTTPMethodMeta entries', () => { const meta = ControllerMetaBuilderFactory.build(AgentFooController, ControllerType.HTTP) as HTTPControllerMeta; assert(meta); assert.strictEqual(meta.methods.length, 8); assert.strictEqual(meta.path, '/api/v1'); }); it('should produce correct route metadata for each method', () => { const meta = ControllerMetaBuilderFactory.build(AgentFooController, ControllerType.HTTP) as HTTPControllerMeta; const createThread = meta.methods.find(m => m.name === 'createThread')!; assert.strictEqual(createThread.path, '/threads'); assert.strictEqual(createThread.method, HTTPMethodEnum.POST); assert.strictEqual(createThread.paramMap.size, 0); const getThread = meta.methods.find(m => m.name === 'getThread')!; assert.strictEqual(getThread.path, '/threads/:id'); assert.strictEqual(getThread.method, HTTPMethodEnum.GET); assert.deepStrictEqual(getThread.paramMap, new Map([[ 0, new PathParamMeta('id') ]])); const asyncRun = meta.methods.find(m => m.name === 'asyncRun')!; assert.strictEqual(asyncRun.path, '/runs'); assert.strictEqual(asyncRun.method, HTTPMethodEnum.POST); assert.deepStrictEqual(asyncRun.paramMap, new Map([[ 0, new BodyParamMeta() ]])); const streamRun = meta.methods.find(m => m.name === 'streamRun')!; assert.strictEqual(streamRun.path, '/runs/stream'); assert.strictEqual(streamRun.method, HTTPMethodEnum.POST); assert.deepStrictEqual(streamRun.paramMap, new Map([[ 0, new BodyParamMeta() ]])); const getRunStream = meta.methods.find(m => m.name === 'getRunStream')!; assert.strictEqual(getRunStream.path, '/runs/:id/stream'); assert.strictEqual(getRunStream.method, HTTPMethodEnum.GET); assert.deepStrictEqual(getRunStream.paramMap, new Map([ [ 0, new PathParamMeta('id') ], [ 1, new QueryParamMeta('lastSeq') ], ])); const syncRun = meta.methods.find(m => m.name === 'syncRun')!; assert.strictEqual(syncRun.path, '/runs/wait'); assert.strictEqual(syncRun.method, HTTPMethodEnum.POST); assert.deepStrictEqual(syncRun.paramMap, new Map([[ 0, new BodyParamMeta() ]])); const getRun = meta.methods.find(m => m.name === 'getRun')!; assert.strictEqual(getRun.path, '/runs/:id'); assert.strictEqual(getRun.method, HTTPMethodEnum.GET); assert.deepStrictEqual(getRun.paramMap, new Map([[ 0, new PathParamMeta('id') ]])); const cancelRun = meta.methods.find(m => m.name === 'cancelRun')!; assert.strictEqual(cancelRun.path, '/runs/:id/cancel'); assert.strictEqual(cancelRun.method, HTTPMethodEnum.POST); assert.deepStrictEqual(cancelRun.paramMap, new Map([[ 0, new PathParamMeta('id') ]])); }); it('should have all real paths start with /', () => { const meta = ControllerMetaBuilderFactory.build(AgentFooController, ControllerType.HTTP) as HTTPControllerMeta; for (const method of meta.methods) { const realPath = meta.getMethodRealPath(method); assert(realPath.startsWith('/'), `${method.name} real path "${realPath}" should start with /`); } }); }); }); ================================================ FILE: core/controller-decorator/test/Context.test.ts ================================================ import assert from 'node:assert'; import { ContextController } from './fixtures/ContextController'; import MethodInfoUtil from '../src/util/MethodInfoUtil'; describe('test/Context.test.ts', () => { it('should work', () => { const contextIndex = MethodInfoUtil.getMethodContextIndex(ContextController, 'hello'); assert(contextIndex === 0); }); }); ================================================ FILE: core/controller-decorator/test/MCPMeta.test.ts ================================================ import assert from 'assert'; import { ControllerType } from '@eggjs/tegg-types'; import { MCPFooController, ToolType, PromptType } from './fixtures/MCPController'; import { ControllerMetaBuilderFactory, MCPControllerMeta } from '..'; import { ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js'; describe('test/MCPMeta.test.ts', () => { it('should work', () => { const builder = ControllerMetaBuilderFactory.createControllerMetaBuilder(MCPFooController, ControllerType.MCP)!; const fooControllerMetaData = builder.build()! as MCPControllerMeta; assert(fooControllerMetaData); assert(fooControllerMetaData.type === ControllerType.MCP); assert(fooControllerMetaData.name === 'HelloChairMCP'); assert(fooControllerMetaData.version === '1.0.0'); assert(fooControllerMetaData.protoName === 'mCPFooController'); assert(fooControllerMetaData.controllerName === 'MCPFooController'); assert(fooControllerMetaData.className === 'MCPFooController'); assert(fooControllerMetaData.prompts[0].name === 'foo'); assert(fooControllerMetaData.tools[0].name === 'bar'); assert(fooControllerMetaData.resources[0].name === 'car'); assert(fooControllerMetaData.resources[0].template instanceof ResourceTemplate); assert(fooControllerMetaData.tools[0].detail?.argsSchema === ToolType); assert(fooControllerMetaData.prompts[0].detail?.argsSchema === PromptType); assert(fooControllerMetaData.timeout === 60000); }); }); ================================================ FILE: core/controller-decorator/test/Middleware.test.ts ================================================ import assert from 'node:assert'; import { MiddlewareController, MiddlewaresController } from './fixtures/MiddlewareController'; import ControllerInfoUtil from '../src/util/ControllerInfoUtil'; import MethodInfoUtil from '../src/util/MethodInfoUtil'; import { AopMiddlewareController, BarAdvice, BarMethodAdvice, FooAdvice, FooMethodAdvice, } from './fixtures/AopMiddlewareController'; describe('test/Middleware.test.ts', () => { it('should work', () => { const controllerMws = ControllerInfoUtil.getControllerMiddlewares(MiddlewareController); const methodMws = MethodInfoUtil.getMethodMiddlewares(MiddlewareController, 'hello'); assert(controllerMws.length === 1); assert(methodMws.length === 2); }); it('Middleware with muti params should work', () => { const controllerMws = ControllerInfoUtil.getControllerMiddlewares(MiddlewaresController); const methodMws = MethodInfoUtil.getMethodMiddlewares(MiddlewaresController, 'hello'); assert(controllerMws.length === 1); assert(methodMws.length === 2); }); it('controller Aop Middleware should work', () => { const controllerAopMws = ControllerInfoUtil.getControllerAopMiddlewares(AopMiddlewareController); const helloMethodMws = MethodInfoUtil.getMethodAopMiddlewares(AopMiddlewareController, 'hello'); const byeMethodMws = MethodInfoUtil.getMethodAopMiddlewares(AopMiddlewareController, 'bye'); assert.deepStrictEqual(controllerAopMws, [ FooAdvice, BarAdvice ]); assert.deepStrictEqual(helloMethodMws, [ FooMethodAdvice, BarMethodAdvice ]); assert.deepStrictEqual(byeMethodMws, []); }); }); ================================================ FILE: core/controller-decorator/test/decorators.test.ts ================================================ import assert from 'node:assert'; import { PrototypeUtil } from '@eggjs/core-decorator'; import { FooController } from './fixtures/HTTPFooController'; describe('test/decorators.test.ts', () => { describe('', () => { it('should get the right file path', () => { assert(PrototypeUtil.getFilePath(FooController) === FooController.fileName); }); }); }); ================================================ FILE: core/controller-decorator/test/fixtures/AclController.ts ================================================ import { HTTPMethodEnum } from '@eggjs/tegg-types'; import { Acl } from '../../src/decorator/Acl'; import { HTTPController } from '../../src/decorator/http/HTTPController'; import { HTTPMethod } from '../../src/decorator/http/HTTPMethod'; @Acl('mock1') @HTTPController() export class AclController { @Acl('mock2') @HTTPMethod({ path: '/foo', method: HTTPMethodEnum.GET, }) async foo() { console.log('hello,acl'); } @HTTPMethod({ path: '/bar', method: HTTPMethodEnum.GET, }) async bar() { console.log('hello,acl'); } } ================================================ FILE: core/controller-decorator/test/fixtures/AgentFooController.ts ================================================ import type { CreateRunInput, AgentMessage } from '@eggjs/tegg-types/agent-runtime'; import { AgentController } from '../../src/decorator/agent/AgentController'; import type { AgentHandler } from '../../src/decorator/agent/AgentHandler'; // AgentController that only implements execRun (smart defaults pattern) @AgentController() export class AgentFooController implements AgentHandler { async createStore(): Promise { return new Map(); } async *execRun(input: CreateRunInput): AsyncGenerator { const messages = input.input.messages; yield { type: 'assistant', message: { role: 'assistant', content: [{ type: 'text', text: `Processed ${messages.length} messages` }], }, }; } } ================================================ FILE: core/controller-decorator/test/fixtures/AopMiddlewareController.ts ================================================ import { HTTPMethodEnum, IAdvice, ObjectInitType } from '@eggjs/tegg-types'; import { Middleware } from '../../src/decorator/Middleware'; import { Advice } from '@eggjs/aop-decorator'; import { HTTPController } from '../../src/decorator/http/HTTPController'; import { HTTPMethod } from '../../src/decorator/http/HTTPMethod'; @Advice({ initType: ObjectInitType.SINGLETON, }) export class FooAdvice implements IAdvice { async beforeCall(): Promise { // ... } } @Advice({ initType: ObjectInitType.SINGLETON, }) export class BarAdvice implements IAdvice { async beforeCall(): Promise { // ... } } @Advice({ initType: ObjectInitType.SINGLETON, }) export class FooMethodAdvice implements IAdvice { async beforeCall(): Promise { // ... } } @Advice({ initType: ObjectInitType.SINGLETON, }) export class BarMethodAdvice implements IAdvice { async beforeCall(): Promise { // ... } } @Middleware(FooAdvice, BarAdvice) @HTTPController() export class AopMiddlewareController { @Middleware(FooMethodAdvice, BarMethodAdvice) @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/hello', }) async hello(): Promise { return; } @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/bye', }) async bye(): Promise { return; } } ================================================ FILE: core/controller-decorator/test/fixtures/ContextController.ts ================================================ import { Context } from '../../src/decorator/Context'; export class ContextController { async hello(@Context() ctx: object) { console.log('ctx:', ctx); } } ================================================ FILE: core/controller-decorator/test/fixtures/HTTPFooController.ts ================================================ import type { EggContext, IncomingHttpHeaders, Next } from '@eggjs/tegg-types'; import { HTTPMethodEnum } from '@eggjs/tegg-types' import { HTTPController } from '../../src/decorator/http/HTTPController'; import { Context } from '../../src/decorator/Context'; import { Middleware } from '../../src/decorator/Middleware'; import { HTTPBody, HTTPHeaders, HTTPParam, HTTPQueries, HTTPQuery } from '../../src/decorator/http/HTTPParam'; import { HTTPMethod } from '../../src/decorator/http/HTTPMethod'; async function middleware1(ctx: EggContext, next: Next) { console.log(ctx, next); } async function middleware2(ctx: EggContext, next: Next) { console.log(ctx, next); } async function middleware3(ctx: EggContext, next: Next) { console.log(ctx, next); } @HTTPController({ path: '/foo', }) @Middleware(middleware1) export class FooController { static fileName = __filename; @HTTPMethod({ path: '/bar/:id', method: HTTPMethodEnum.POST, }) @Middleware(middleware2) @Middleware(middleware3) async bar(@Context() ctx: EggContext, @HTTPBody() body, @HTTPQuery() query, @HTTPQueries() queries, @HTTPParam() id) { console.log(ctx, body, query, queries, id); } } @HTTPController({ path: '/foo/:fooId', }) export class ControllerWithParam { static fileName = __filename; @HTTPMethod({ path: '/bar/:id', method: HTTPMethodEnum.GET, }) async bar(@Context() ctx: EggContext, @HTTPParam() id: string, @HTTPParam() fooId: string, @HTTPHeaders() headers: IncomingHttpHeaders) { console.log(ctx, id, fooId, headers); } } @HTTPController({ controllerName: 'FxxController', }) export class FoxController { @HTTPMethod({ path: '/bar/:id', method: HTTPMethodEnum.POST, }) @Middleware(middleware2) @Middleware(middleware3) async bar(@Context() ctx: EggContext, @HTTPBody() body, @HTTPQuery() query, @HTTPQueries() queries, @HTTPParam() id) { console.log(ctx, body, query, queries, id); } } @HTTPController({ protoName: 'FooController', }) export class FxxController { @HTTPMethod({ path: '/bar/:id', method: HTTPMethodEnum.POST, }) @Middleware(middleware2) @Middleware(middleware3) async bar(@Context() ctx: EggContext, @HTTPBody() body, @HTTPQuery() query, @HTTPQueries() queries, @HTTPParam() id) { console.log(ctx, body, query, queries, id); } } @HTTPController() export class ParentController { } @HTTPController() export class ChildController extends ParentController { } @HTTPController() export class DefaultValueController { @HTTPMethod({ path: '/default/:id', method: HTTPMethodEnum.GET, }) @Middleware(middleware2) @Middleware(middleware3) async bar(@Context() ctx: EggContext, @HTTPParam() id = 233, @HTTPQuery() query, @HTTPQueries() queries) { console.log(ctx, id, query, queries); } } @HTTPController() export class Error1Controller { @HTTPMethod({ path: '/error', method: HTTPMethodEnum.GET, }) @Middleware(middleware2) @Middleware(middleware3) async bar(@Context() ctx: EggContext, id) { console.log(ctx, id); } } @HTTPController() export class Error2Controller { @HTTPMethod({ path: '/error', method: HTTPMethodEnum.GET, }) @Middleware(middleware2) @Middleware(middleware3) async bar(@Context() ctx: EggContext, id = 233, @HTTPParam() id2 = 233) { console.log(ctx, id, id2); } } @HTTPController({ timeout: 1000 }) export class TimeoutController { @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/timeout-1' }) async timeout1() {} @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/timeout-2', timeout: 2000 }) async timeout2() {} } ================================================ FILE: core/controller-decorator/test/fixtures/HTTPPriorityController.ts ================================================ import { HTTPMethodEnum } from '@eggjs/tegg-types'; import { HTTPController } from '../../src/decorator/http/HTTPController'; import { HTTPMethod } from '../..'; @HTTPController({ path: '/foo', }) export class PriorityController { @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/*', }) async regexpMethod() { return Promise.resolve(); } @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/users/:id', }) async paramMethod() { return Promise.resolve(); } @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/web/users/*', }) async regexpMethod2() { return Promise.resolve(); } } @HTTPController() export class TooLongController { @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/:id1/:id2/:id3/:id4/:id5/:id6/:id7/:id8/:id9/:id10/:id11/:id12/:id13/:id14/:id15', }) async tooLongMethod() { return Promise.resolve(); } } ================================================ FILE: core/controller-decorator/test/fixtures/HostController.ts ================================================ import { Host } from '../../src/decorator/http/Host'; @Host('foo.eggjs.com') export class HostController { async hello(): Promise { return; } @Host('bar.eggjs.com') async bar(): Promise { return; } } ================================================ FILE: core/controller-decorator/test/fixtures/MCPController.ts ================================================ import { MCPPromptResponse, MCPResourceResponse, MCPToolResponse, PromptArgs, ToolArgs } from "@eggjs/tegg-types"; import { MCPPrompt, PromptArgsSchema } from "../../src/decorator/mcp/MCPPrompt"; import { MCPTool, ToolArgsSchema } from "../../src/decorator/mcp/MCPTool"; import { MCPController } from "../../src/decorator/mcp/MCPController"; import * as z from 'zod/v4'; import { MCPResource } from "../../src/decorator/mcp/MCPResource"; import { Extra } from "../../src/decorator/mcp/Extra"; export const PromptType = { name: z.string(), } export const ToolType = { name: z.string().describe('npm package name'), } @MCPController({ name: "HelloChairMCP", timeout: 60000, }) export class MCPFooController { @MCPPrompt() async foo(@PromptArgsSchema(PromptType) args: PromptArgs): Promise { return { messages: [ { role: "user", content: { type: "text", text: `Generate a concise but descriptive commit message for these changes:\n\n${args.name}`, }, }, ], }; } @MCPTool() async bar(@ToolArgsSchema(ToolType) args: ToolArgs): Promise { return { content: [ { type: 'text', text: `海兔 npm 包: ${args.name} 不存在`, }, ], }; } @MCPResource({ template: [ "hitu://npm/{name}", { list: undefined, }, ], }) async car(uri: URL, @Extra() extra): Promise { console.log(extra); return { contents: [{ uri: uri.toString(), text: `MOCK TEXT`, }], }; } } ================================================ FILE: core/controller-decorator/test/fixtures/MiddlewareController.ts ================================================ import type { EggContext, Next } from '@eggjs/tegg-types'; import { Middleware } from '../../src/decorator/Middleware'; async function middleware1(ctx: EggContext, next: Next) { console.log(ctx, next); } async function middleware2(ctx: EggContext, next: Next) { console.log(ctx, next); } async function middleware3(ctx: EggContext, next: Next) { console.log(ctx, next); } @Middleware(middleware1) export class MiddlewareController { @Middleware(middleware2) @Middleware(middleware3) async hello(): Promise { return; } } @Middleware(middleware1) export class MiddlewaresController { @Middleware(middleware2, middleware3) async hello(): Promise { return; } } ================================================ FILE: core/controller-decorator/test/http/HTTPMeta.test.ts ================================================ import assert from 'node:assert'; import { ControllerType, HTTPMethodEnum } from '@eggjs/tegg-types'; import { ControllerWithParam, DefaultValueController, Error1Controller, Error2Controller, FooController, FoxController, FxxController, TimeoutController, } from '../fixtures/HTTPFooController'; import { BodyParamMeta, ControllerMetaBuilderFactory, ParamMeta, HeadersParamMeta, PathParamMeta, QueriesParamMeta, QueryParamMeta, } from '../..'; import { HTTPControllerMeta } from '../../src/model'; import { PriorityController, TooLongController } from '../fixtures/HTTPPriorityController'; import { AopMiddlewareController, BarAdvice, BarMethodAdvice, FooAdvice, FooMethodAdvice, } from '../fixtures/AopMiddlewareController'; import { PointcutAdviceInfoUtil } from '@eggjs/aop-decorator'; describe('core/controller-decorator/test/http/HTTPMeta.test.ts', () => { it('should work', () => { const fooControllerMetaData = ControllerMetaBuilderFactory.build(FooController, ControllerType.HTTP)! as HTTPControllerMeta; assert(fooControllerMetaData.protoName === 'fooController'); assert(fooControllerMetaData.controllerName === 'FooController'); assert(fooControllerMetaData.className === 'FooController'); assert(fooControllerMetaData.path === '/foo'); assert(fooControllerMetaData.middlewares.length === 1); assert(fooControllerMetaData.methods.length === 1); const barMethodMetaData = fooControllerMetaData.methods[0]; assert(barMethodMetaData.name === 'bar'); assert(barMethodMetaData.path === '/bar/:id'); assert(barMethodMetaData.method === HTTPMethodEnum.POST); assert(barMethodMetaData.contextParamIndex === 0); assert(barMethodMetaData.middlewares.length === 2); const expectParamTypeMap = new Map([ [ 1, new BodyParamMeta() ], [ 2, new QueryParamMeta('query') ], [ 3, new QueriesParamMeta('queries') ], [ 4, new PathParamMeta('id') ], ]); assert.deepStrictEqual(barMethodMetaData.paramMap, expectParamTypeMap); }); it('controller name should work', () => { const fxxControllerMetaData = ControllerMetaBuilderFactory.build(FoxController, ControllerType.HTTP)! as HTTPControllerMeta; assert(fxxControllerMetaData.controllerName === 'FxxController'); assert(fxxControllerMetaData.protoName === 'foxController'); assert(fxxControllerMetaData.className === 'FoxController'); }); it('proto name should work', () => { const fxxControllerMetaData = ControllerMetaBuilderFactory.build(FxxController, ControllerType.HTTP)! as HTTPControllerMeta; assert(fxxControllerMetaData.protoName === 'FooController'); assert(fxxControllerMetaData.className === 'FxxController'); assert(fxxControllerMetaData.controllerName === 'FxxController'); }); it('should support param with default value', () => { const controllerMeta = ControllerMetaBuilderFactory.build(DefaultValueController)! as HTTPControllerMeta; const methodMeta = controllerMeta.methods[0]; assert(methodMeta.paramMap.size === 3); }); describe('param has no decorator', () => { it('should throw error', () => { assert.throws(() => { const builder = ControllerMetaBuilderFactory.createControllerMetaBuilder(Error1Controller)!; builder.build(); }, /param 1 has no http param type/); }); }); describe('controller has param', () => { it('should throw error', () => { const controllerMeta = ControllerMetaBuilderFactory.build(ControllerWithParam)! as HTTPControllerMeta; const methodMeta = controllerMeta.methods[0]; const expectParamTypeMap = new Map([ [ 3, new HeadersParamMeta() ], [ 2, new PathParamMeta('fooId') ], [ 1, new PathParamMeta('id') ], ]); assert.deepStrictEqual(methodMeta.paramMap, expectParamTypeMap); }); }); describe('param after default param has no decorator', () => { it('should throw error', () => { assert.throws(() => { const builder = ControllerMetaBuilderFactory.createControllerMetaBuilder(Error1Controller)!; builder.build(); }, /param 1 has no http param type/); }); }); it('should check decorator', () => { assert.throws(() => { const builder = ControllerMetaBuilderFactory.createControllerMetaBuilder(Error2Controller)!; builder.build(); }, /param 1 has no http param type/); }); describe('priority', () => { let priorityMeta: HTTPControllerMeta; beforeEach(() => { priorityMeta = ControllerMetaBuilderFactory.build(PriorityController, ControllerType.HTTP)! as HTTPControllerMeta; }); describe('path is /foo/*', () => { it('should equals 1000', () => { const methodMeta = priorityMeta.methods.find(t => t.name === 'regexpMethod')!; assert(methodMeta.priority === 1000); }); }); describe('path is /foo/users/:id', () => { it('should equals 2000', () => { const methodMeta = priorityMeta.methods.find(t => t.name === 'paramMethod')!; assert(methodMeta.priority === 2000); }); }); describe('path is /web/users/*', () => { it('should equals 3000', () => { const methodMeta = priorityMeta.methods.find(t => t.name === 'regexpMethod2')!; assert(methodMeta.priority === 3000); }); }); describe('too long path', () => { it('should throw error', () => { const builder = ControllerMetaBuilderFactory.createControllerMetaBuilder(TooLongController, ControllerType.HTTP)!; assert.throws(() => { builder.build(); }, /path \/:id1\/:id2\/:id3\/:id4\/:id5\/:id6\/:id7\/:id8\/:id9\/:id10\/:id11\/:id12\/:id13\/:id14\/:id15 is too long, should set priority manually/); }); }); }); it('aop middleware should work', () => { ControllerMetaBuilderFactory.build(AopMiddlewareController, ControllerType.HTTP); const helloAdvices = PointcutAdviceInfoUtil.getPointcutAdviceInfoList(AopMiddlewareController, 'hello'); const byeAdvices = PointcutAdviceInfoUtil.getPointcutAdviceInfoList(AopMiddlewareController, 'bye'); assert.deepStrictEqual(helloAdvices, [ { clazz: FooMethodAdvice, order: 1000, adviceParams: undefined, }, { clazz: BarMethodAdvice, order: 1000, adviceParams: undefined, }, { clazz: FooAdvice, order: 1000, adviceParams: undefined, }, { clazz: BarAdvice, order: 1000, adviceParams: undefined, }, ]); assert.deepStrictEqual(byeAdvices, [ { clazz: FooAdvice, order: 1000, adviceParams: undefined }, { clazz: BarAdvice, order: 1000, adviceParams: undefined }, ]); }); describe('timeout', () => { it('should work', () => { const controllerMeta = ControllerMetaBuilderFactory.build(TimeoutController)! as HTTPControllerMeta; const methodMeta1 = controllerMeta.methods.find(m => m.path === '/timeout-1')!; const methodMeta2 = controllerMeta.methods.find(m => m.path === '/timeout-2')!; assert.strictEqual(controllerMeta.getMethodTimeout(methodMeta1), 1000); assert.strictEqual(controllerMeta.getMethodTimeout(methodMeta2), 2000); }); it('should default be undefined', () => { const controllerMeta = ControllerMetaBuilderFactory.build(FooController)! as HTTPControllerMeta; const methodMeta = controllerMeta.methods[0]; assert.strictEqual(controllerMeta.getMethodTimeout(methodMeta), undefined); }); }); }); ================================================ FILE: core/controller-decorator/test/http/Host.test.ts ================================================ import assert from 'node:assert'; import { HostController } from '../fixtures/HostController'; import ControllerInfoUtil from '../../src/util/ControllerInfoUtil'; import MethodInfoUtil from '../../src/util/MethodInfoUtil'; describe('test/Host.test.ts', () => { it('controller Host work', () => { const controllerHost = ControllerInfoUtil.getControllerHosts(HostController); assert(controllerHost![0] === 'foo.eggjs.com'); }); it('method Host work', () => { const methodHost = MethodInfoUtil.getMethodHosts(HostController, 'bar'); assert(methodHost![0] === 'bar.eggjs.com'); }); }); ================================================ FILE: core/controller-decorator/test/util/ControllerMetadataUtil.test.ts ================================================ import assert from 'node:assert'; import { MetadataUtil } from '@eggjs/core-decorator'; import { CONTROLLER_META_DATA } from '@eggjs/tegg-types'; import { FooController, ParentController, ChildController } from '../fixtures/HTTPFooController'; import { ControllerMetadataUtil } from '../../src/util/ControllerMetadataUtil'; import '../../src/impl/http/HTTPControllerMetaBuilder'; describe('test/util/ControllerMetadataUtil.test.ts', () => { describe('get metadata', () => { beforeEach(() => { MetadataUtil.deleteMetaData(CONTROLLER_META_DATA, FooController); }); it('should work', () => { const metadata = ControllerMetadataUtil.getControllerMetadata(FooController)!; assert(metadata); assert(metadata.controllerName === 'FooController'); }); }); describe('inherit case', () => { beforeEach(() => { MetadataUtil.deleteMetaData(CONTROLLER_META_DATA, ParentController); MetadataUtil.deleteMetaData(CONTROLLER_META_DATA, ChildController); }); it('should work', () => { const parentMetadata = ControllerMetadataUtil.getControllerMetadata(ParentController)!; assert(parentMetadata); assert(parentMetadata.controllerName === 'ParentController'); const childMetadata = ControllerMetadataUtil.getControllerMetadata(ChildController)!; assert(childMetadata); assert(childMetadata.controllerName === 'ChildController'); }); }); }); ================================================ FILE: core/controller-decorator/test/util/HTTPPriority.test.ts ================================================ import assert from 'node:assert'; import { HTTPPriorityUtil } from '../../src/util/HTTPPriorityUtil'; describe('test/util/HTTPPriority.test.ts', () => { describe('path has no regexp', () => { it('should eqs 100000', () => { assert(HTTPPriorityUtil.calcPathPriority('/') === HTTPPriorityUtil.DEFAULT_PRIORITY); assert(HTTPPriorityUtil.calcPathPriority('/users') === HTTPPriorityUtil.DEFAULT_PRIORITY); }); }); describe('path has regexp', () => { describe('path has less than 10 /', () => { it('should works', () => { assert(HTTPPriorityUtil.calcPathPriority('/*') === 0); assert(HTTPPriorityUtil.calcPathPriority('/users/:id') === 1000); assert(HTTPPriorityUtil.calcPathPriority('/users/:id/moments/:momentId') === 4000); }); }); }); }); ================================================ FILE: core/controller-decorator/tsconfig.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "dist", "baseUrl": "./" }, "exclude": [ "dist", "node_modules", "test" ] } ================================================ FILE: core/controller-decorator/tsconfig.pub.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "dist", "baseUrl": "./" }, "exclude": [ "dist", "node_modules", "test" ] } ================================================ FILE: core/core-decorator/CHANGELOG.md ================================================ # Change Log All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. ## [3.78.15](https://github.com/eggjs/tegg/compare/v3.78.14...v3.78.15) (2026-04-23) **Note:** Version bump only for package @eggjs/core-decorator ## [3.78.14](https://github.com/eggjs/tegg/compare/v3.78.13...v3.78.14) (2026-04-21) **Note:** Version bump only for package @eggjs/core-decorator ## [3.78.13](https://github.com/eggjs/tegg/compare/v3.78.12...v3.78.13) (2026-04-21) **Note:** Version bump only for package @eggjs/core-decorator ## [3.78.12](https://github.com/eggjs/tegg/compare/v3.78.11...v3.78.12) (2026-04-18) **Note:** Version bump only for package @eggjs/core-decorator ## [3.78.11](https://github.com/eggjs/tegg/compare/v3.78.10...v3.78.11) (2026-04-18) **Note:** Version bump only for package @eggjs/core-decorator ## [3.78.10](https://github.com/eggjs/tegg/compare/v3.78.9...v3.78.10) (2026-04-18) **Note:** Version bump only for package @eggjs/core-decorator ## [3.78.9](https://github.com/eggjs/tegg/compare/v3.78.8...v3.78.9) (2026-04-18) **Note:** Version bump only for package @eggjs/core-decorator ## [3.78.8](https://github.com/eggjs/tegg/compare/v3.78.7...v3.78.8) (2026-04-18) **Note:** Version bump only for package @eggjs/core-decorator ## [3.78.7](https://github.com/eggjs/tegg/compare/v3.78.6...v3.78.7) (2026-04-18) **Note:** Version bump only for package @eggjs/core-decorator ## [3.78.6](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.6) (2026-04-18) **Note:** Version bump only for package @eggjs/core-decorator ## [3.78.5](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.5) (2026-04-18) **Note:** Version bump only for package @eggjs/core-decorator ## [3.78.4](https://github.com/eggjs/tegg/compare/v3.78.3...v3.78.4) (2026-04-18) **Note:** Version bump only for package @eggjs/core-decorator ## [3.78.3](https://github.com/eggjs/tegg/compare/v3.78.2...v3.78.3) (2026-04-15) **Note:** Version bump only for package @eggjs/core-decorator ## [3.78.2](https://github.com/eggjs/tegg/compare/v3.78.1...v3.78.2) (2026-04-09) **Note:** Version bump only for package @eggjs/core-decorator ## [3.78.1](https://github.com/eggjs/tegg/compare/v3.78.0...v3.78.1) (2026-04-09) **Note:** Version bump only for package @eggjs/core-decorator # [3.78.0](https://github.com/eggjs/tegg/compare/v3.77.2...v3.78.0) (2026-04-09) **Note:** Version bump only for package @eggjs/core-decorator ## [3.77.2](https://github.com/eggjs/tegg/compare/v3.77.1...v3.77.2) (2026-04-07) **Note:** Version bump only for package @eggjs/core-decorator ## [3.77.1](https://github.com/eggjs/tegg/compare/v3.77.0...v3.77.1) (2026-04-01) **Note:** Version bump only for package @eggjs/core-decorator # [3.77.0](https://github.com/eggjs/tegg/compare/v3.76.1...v3.77.0) (2026-04-01) **Note:** Version bump only for package @eggjs/core-decorator ## [3.76.1](https://github.com/eggjs/tegg/compare/v3.76.0...v3.76.1) (2026-04-01) **Note:** Version bump only for package @eggjs/core-decorator # [3.76.0](https://github.com/eggjs/tegg/compare/v3.75.1...v3.76.0) (2026-04-01) **Note:** Version bump only for package @eggjs/core-decorator ## [3.75.1](https://github.com/eggjs/tegg/compare/v3.75.0...v3.75.1) (2026-04-01) **Note:** Version bump only for package @eggjs/core-decorator # [3.75.0](https://github.com/eggjs/tegg/compare/v3.74.0...v3.75.0) (2026-03-30) **Note:** Version bump only for package @eggjs/core-decorator # [3.74.0](https://github.com/eggjs/tegg/compare/v3.73.0...v3.74.0) (2026-03-30) **Note:** Version bump only for package @eggjs/core-decorator # [3.73.0](https://github.com/eggjs/tegg/compare/v3.72.0...v3.73.0) (2026-03-25) **Note:** Version bump only for package @eggjs/core-decorator # [3.72.0](https://github.com/eggjs/tegg/compare/v3.71.2...v3.72.0) (2026-02-04) **Note:** Version bump only for package @eggjs/core-decorator ## [3.71.2](https://github.com/eggjs/tegg/compare/v3.71.1...v3.71.2) (2026-01-30) **Note:** Version bump only for package @eggjs/core-decorator ## [3.71.1](https://github.com/eggjs/tegg/compare/v3.71.0...v3.71.1) (2026-01-28) **Note:** Version bump only for package @eggjs/core-decorator # [3.71.0](https://github.com/eggjs/tegg/compare/v3.70.1...v3.71.0) (2026-01-26) **Note:** Version bump only for package @eggjs/core-decorator ## [3.70.1](https://github.com/eggjs/tegg/compare/v3.70.0...v3.70.1) (2026-01-14) **Note:** Version bump only for package @eggjs/core-decorator # [3.70.0](https://github.com/eggjs/tegg/compare/v3.69.0...v3.70.0) (2026-01-07) **Note:** Version bump only for package @eggjs/core-decorator # [3.69.0](https://github.com/eggjs/tegg/compare/v3.68.0...v3.69.0) (2026-01-06) **Note:** Version bump only for package @eggjs/core-decorator # [3.68.0](https://github.com/eggjs/tegg/compare/v3.67.2...v3.68.0) (2025-12-29) **Note:** Version bump only for package @eggjs/core-decorator ## [3.67.2](https://github.com/eggjs/tegg/compare/v3.67.1...v3.67.2) (2025-12-24) **Note:** Version bump only for package @eggjs/core-decorator ## [3.67.1](https://github.com/eggjs/tegg/compare/v3.67.0...v3.67.1) (2025-12-23) **Note:** Version bump only for package @eggjs/core-decorator # [3.67.0](https://github.com/eggjs/tegg/compare/v3.66.0...v3.67.0) (2025-12-18) **Note:** Version bump only for package @eggjs/core-decorator # [3.66.0](https://github.com/eggjs/tegg/compare/v3.65.3...v3.66.0) (2025-12-16) **Note:** Version bump only for package @eggjs/core-decorator ## [3.65.3](https://github.com/eggjs/tegg/compare/v3.65.2...v3.65.3) (2025-12-16) **Note:** Version bump only for package @eggjs/core-decorator ## [3.65.2](https://github.com/eggjs/tegg/compare/v3.65.1...v3.65.2) (2025-12-16) **Note:** Version bump only for package @eggjs/core-decorator ## [3.65.1](https://github.com/eggjs/tegg/compare/v3.65.0...v3.65.1) (2025-12-16) **Note:** Version bump only for package @eggjs/core-decorator # [3.65.0](https://github.com/eggjs/tegg/compare/v3.64.5...v3.65.0) (2025-12-10) **Note:** Version bump only for package @eggjs/core-decorator ## [3.64.5](https://github.com/eggjs/tegg/compare/v3.64.4...v3.64.5) (2025-12-08) **Note:** Version bump only for package @eggjs/core-decorator ## [3.64.4](https://github.com/eggjs/tegg/compare/v3.64.3...v3.64.4) (2025-12-06) **Note:** Version bump only for package @eggjs/core-decorator ## [3.64.3](https://github.com/eggjs/tegg/compare/v3.64.2...v3.64.3) (2025-12-06) **Note:** Version bump only for package @eggjs/core-decorator ## [3.64.2](https://github.com/eggjs/tegg/compare/v3.64.1...v3.64.2) (2025-11-27) **Note:** Version bump only for package @eggjs/core-decorator ## [3.64.1](https://github.com/eggjs/tegg/compare/v3.64.0...v3.64.1) (2025-11-27) **Note:** Version bump only for package @eggjs/core-decorator # [3.64.0](https://github.com/eggjs/tegg/compare/v3.63.2...v3.64.0) (2025-11-27) **Note:** Version bump only for package @eggjs/core-decorator ## [3.63.2](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.2) (2025-11-18) **Note:** Version bump only for package @eggjs/core-decorator ## [3.63.1](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.1) (2025-11-18) **Note:** Version bump only for package @eggjs/core-decorator # [3.63.0](https://github.com/eggjs/tegg/compare/v3.62.3...v3.63.0) (2025-11-18) **Note:** Version bump only for package @eggjs/core-decorator ## [3.62.3](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.3) (2025-11-05) **Note:** Version bump only for package @eggjs/core-decorator ## [3.62.2](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.2) (2025-11-05) **Note:** Version bump only for package @eggjs/core-decorator ## [3.62.1](https://github.com/eggjs/tegg/compare/v3.62.0...v3.62.1) (2025-11-03) **Note:** Version bump only for package @eggjs/core-decorator # [3.62.0](https://github.com/eggjs/tegg/compare/v3.61.0...v3.62.0) (2025-09-24) **Note:** Version bump only for package @eggjs/core-decorator # [3.61.0](https://github.com/eggjs/tegg/compare/v3.60.3...v3.61.0) (2025-08-15) **Note:** Version bump only for package @eggjs/core-decorator ## [3.60.3](https://github.com/eggjs/tegg/compare/v3.60.2...v3.60.3) (2025-08-06) **Note:** Version bump only for package @eggjs/core-decorator ## [3.60.2](https://github.com/eggjs/tegg/compare/v3.60.1...v3.60.2) (2025-08-06) **Note:** Version bump only for package @eggjs/core-decorator ## [3.60.1](https://github.com/eggjs/tegg/compare/v3.60.0...v3.60.1) (2025-07-28) **Note:** Version bump only for package @eggjs/core-decorator # [3.60.0](https://github.com/eggjs/tegg/compare/v3.59.1...v3.60.0) (2025-07-28) **Note:** Version bump only for package @eggjs/core-decorator ## [3.59.1](https://github.com/eggjs/tegg/compare/v3.59.0...v3.59.1) (2025-07-16) **Note:** Version bump only for package @eggjs/core-decorator # [3.59.0](https://github.com/eggjs/tegg/compare/v3.58.0...v3.59.0) (2025-07-07) **Note:** Version bump only for package @eggjs/core-decorator # [3.58.0](https://github.com/eggjs/tegg/compare/v3.57.14...v3.58.0) (2025-07-01) **Note:** Version bump only for package @eggjs/core-decorator ## [3.57.14](https://github.com/eggjs/tegg/compare/v3.57.13...v3.57.14) (2025-06-18) **Note:** Version bump only for package @eggjs/core-decorator ## [3.57.13](https://github.com/eggjs/tegg/compare/v3.57.12...v3.57.13) (2025-06-18) **Note:** Version bump only for package @eggjs/core-decorator ## [3.57.12](https://github.com/eggjs/tegg/compare/v3.57.11...v3.57.12) (2025-06-16) **Note:** Version bump only for package @eggjs/core-decorator ## [3.57.11](https://github.com/eggjs/tegg/compare/v3.57.10...v3.57.11) (2025-06-16) **Note:** Version bump only for package @eggjs/core-decorator ## [3.57.10](https://github.com/eggjs/tegg/compare/v3.57.9...v3.57.10) (2025-06-12) **Note:** Version bump only for package @eggjs/core-decorator ## [3.57.9](https://github.com/eggjs/tegg/compare/v3.57.8...v3.57.9) (2025-05-29) **Note:** Version bump only for package @eggjs/core-decorator ## [3.57.8](https://github.com/eggjs/tegg/compare/v3.57.7...v3.57.8) (2025-05-29) **Note:** Version bump only for package @eggjs/core-decorator ## [3.57.7](https://github.com/eggjs/tegg/compare/v3.57.6...v3.57.7) (2025-05-28) **Note:** Version bump only for package @eggjs/core-decorator ## [3.57.6](https://github.com/eggjs/tegg/compare/v3.57.5...v3.57.6) (2025-05-27) **Note:** Version bump only for package @eggjs/core-decorator ## [3.57.5](https://github.com/eggjs/tegg/compare/v3.57.4...v3.57.5) (2025-05-15) **Note:** Version bump only for package @eggjs/core-decorator ## [3.57.4](https://github.com/eggjs/tegg/compare/v3.57.3...v3.57.4) (2025-05-14) **Note:** Version bump only for package @eggjs/core-decorator ## [3.57.3](https://github.com/eggjs/tegg/compare/v3.57.2...v3.57.3) (2025-05-13) **Note:** Version bump only for package @eggjs/core-decorator ## [3.57.2](https://github.com/eggjs/tegg/compare/v3.57.1...v3.57.2) (2025-05-13) **Note:** Version bump only for package @eggjs/core-decorator ## [3.57.1](https://github.com/eggjs/tegg/compare/v3.57.0...v3.57.1) (2025-05-13) **Note:** Version bump only for package @eggjs/core-decorator # [3.57.0](https://github.com/eggjs/tegg/compare/v3.56.3...v3.57.0) (2025-05-09) **Note:** Version bump only for package @eggjs/core-decorator ## [3.56.3](https://github.com/eggjs/tegg/compare/v3.56.2...v3.56.3) (2025-05-07) **Note:** Version bump only for package @eggjs/core-decorator ## [3.56.2](https://github.com/eggjs/tegg/compare/v3.56.1...v3.56.2) (2025-05-01) **Note:** Version bump only for package @eggjs/core-decorator ## [3.56.1](https://github.com/eggjs/tegg/compare/v3.56.0...v3.56.1) (2025-04-29) **Note:** Version bump only for package @eggjs/core-decorator # [3.56.0](https://github.com/eggjs/tegg/compare/v3.55.0...v3.56.0) (2025-04-29) **Note:** Version bump only for package @eggjs/core-decorator # [3.55.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.55.0) (2025-04-29) **Note:** Version bump only for package @eggjs/core-decorator # [3.54.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.54.0) (2025-04-29) **Note:** Version bump only for package @eggjs/core-decorator # [3.53.0](https://github.com/eggjs/tegg/compare/v3.52.1...v3.53.0) (2025-04-21) ### Features * add timeout metadata for http controller ([#301](https://github.com/eggjs/tegg/issues/301)) ([68980c2](https://github.com/eggjs/tegg/commit/68980c23de81dbc9bd86c1d8df7b3952f52aa5ce)) ## [3.52.1](https://github.com/eggjs/tegg/compare/v3.52.0...v3.52.1) (2025-03-14) ### Bug Fixes * add qualifier check ([#295](https://github.com/eggjs/tegg/issues/295)) ([6744088](https://github.com/eggjs/tegg/commit/674408810d77fe0f4b95b25790bcb3975e543e26)) # [3.52.0](https://github.com/eggjs/tegg/compare/v3.51.2...v3.52.0) (2024-12-30) **Note:** Version bump only for package @eggjs/core-decorator ## [3.51.2](https://github.com/eggjs/tegg/compare/v3.51.1...v3.51.2) (2024-12-09) **Note:** Version bump only for package @eggjs/core-decorator ## [3.51.1](https://github.com/eggjs/tegg/compare/v3.51.0...v3.51.1) (2024-11-06) **Note:** Version bump only for package @eggjs/core-decorator # [3.51.0](https://github.com/eggjs/tegg/compare/v3.50.1...v3.51.0) (2024-10-30) ### Features * add default inject init type qualifier ([#255](https://github.com/eggjs/tegg/issues/255)) ([538ae80](https://github.com/eggjs/tegg/commit/538ae8033ff102ac0b1d141c6495058a800e46f1)) * support optional inject ([#254](https://github.com/eggjs/tegg/issues/254)) ([260470b](https://github.com/eggjs/tegg/commit/260470b766d5fdb323c1bd72cc6260a90468a161)) ## [3.50.1](https://github.com/eggjs/tegg/compare/v3.50.0...v3.50.1) (2024-10-23) **Note:** Version bump only for package @eggjs/core-decorator # [3.50.0](https://github.com/eggjs/tegg/compare/v3.49.0...v3.50.0) (2024-10-22) **Note:** Version bump only for package @eggjs/core-decorator # [3.49.0](https://github.com/eggjs/tegg/compare/v3.48.1...v3.49.0) (2024-10-21) **Note:** Version bump only for package @eggjs/core-decorator ## [3.48.1](https://github.com/eggjs/tegg/compare/v3.48.0...v3.48.1) (2024-10-14) ### Bug Fixes * fix merge qualifier ([#250](https://github.com/eggjs/tegg/issues/250)) ([d5a8a93](https://github.com/eggjs/tegg/commit/d5a8a93abad570f69881f9fa42f39d7b5cd436be)) # [3.48.0](https://github.com/eggjs/tegg/compare/v3.47.2...v3.48.0) (2024-10-10) **Note:** Version bump only for package @eggjs/core-decorator ## [3.47.2](https://github.com/eggjs/tegg/compare/v3.47.1...v3.47.2) (2024-10-10) **Note:** Version bump only for package @eggjs/core-decorator ## [3.47.1](https://github.com/eggjs/tegg/compare/v3.47.0...v3.47.1) (2024-10-10) **Note:** Version bump only for package @eggjs/core-decorator # [3.47.0](https://github.com/eggjs/tegg/compare/v3.46.4...v3.47.0) (2024-10-10) ### Features * export ProtoDescriptorHelper ([#245](https://github.com/eggjs/tegg/issues/245)) ([f01fb63](https://github.com/eggjs/tegg/commit/f01fb639b153a907fd9c951d4b1e40ba101b43d0)) ## [3.46.4](https://github.com/eggjs/tegg/compare/v3.46.3...v3.46.4) (2024-10-09) **Note:** Version bump only for package @eggjs/core-decorator ## [3.46.3](https://github.com/eggjs/tegg/compare/v3.46.2...v3.46.3) (2024-10-08) ### Bug Fixes * Prototype should not be inherited ([#243](https://github.com/eggjs/tegg/issues/243)) ([6e7017a](https://github.com/eggjs/tegg/commit/6e7017a48d395fba6525e0b31c848a257eb171ef)) ## [3.46.2](https://github.com/eggjs/tegg/compare/v3.46.1...v3.46.2) (2024-10-07) **Note:** Version bump only for package @eggjs/core-decorator ## [3.46.1](https://github.com/eggjs/tegg/compare/v3.46.0...v3.46.1) (2024-09-30) ### Bug Fixes * fix miss MultiInstance proper qualifiers ([#241](https://github.com/eggjs/tegg/issues/241)) ([15666d3](https://github.com/eggjs/tegg/commit/15666d36c18b99eccc4f1a11d8e7702503694ee1)) # [3.46.0](https://github.com/eggjs/tegg/compare/v3.45.0...v3.46.0) (2024-09-29) **Note:** Version bump only for package @eggjs/core-decorator # [3.45.0](https://github.com/eggjs/tegg/compare/v3.44.1...v3.45.0) (2024-09-29) ### Features * impl MultiInstanceInfo decorator ([#239](https://github.com/eggjs/tegg/issues/239)) ([70d4d95](https://github.com/eggjs/tegg/commit/70d4d95bca4a0c3e11d0d7cc4f292b1315e49e81)) ## [3.44.1](https://github.com/eggjs/tegg/compare/v3.44.0...v3.44.1) (2024-09-29) **Note:** Version bump only for package @eggjs/core-decorator # [3.44.0](https://github.com/eggjs/tegg/compare/v3.43.2...v3.44.0) (2024-09-29) ### Features * support inject in constructor ([#237](https://github.com/eggjs/tegg/issues/237)) ([e68b1ed](https://github.com/eggjs/tegg/commit/e68b1ed6a90432f1cb35a6f562914b7b04cb5114)) ## [3.43.2](https://github.com/eggjs/tegg/compare/v3.43.1...v3.43.2) (2024-09-14) **Note:** Version bump only for package @eggjs/core-decorator ## [3.43.1](https://github.com/eggjs/tegg/compare/v3.43.0...v3.43.1) (2024-09-14) **Note:** Version bump only for package @eggjs/core-decorator # [3.43.0](https://github.com/eggjs/tegg/compare/v3.42.0...v3.43.0) (2024-09-13) **Note:** Version bump only for package @eggjs/core-decorator # [3.42.0](https://github.com/eggjs/tegg/compare/v3.41.0...v3.42.0) (2024-09-10) **Note:** Version bump only for package @eggjs/core-decorator # [3.41.0](https://github.com/eggjs/tegg/compare/v3.40.1...v3.41.0) (2024-08-26) **Note:** Version bump only for package @eggjs/core-decorator ## [3.40.1](https://github.com/eggjs/tegg/compare/v3.40.0...v3.40.1) (2024-08-23) **Note:** Version bump only for package @eggjs/core-decorator # [3.40.0](https://github.com/eggjs/tegg/compare/v3.39.5...v3.40.0) (2024-08-22) **Note:** Version bump only for package @eggjs/core-decorator ## [3.39.5](https://github.com/eggjs/tegg/compare/v3.39.4...v3.39.5) (2024-08-09) **Note:** Version bump only for package @eggjs/core-decorator ## [3.39.4](https://github.com/eggjs/tegg/compare/v3.39.3...v3.39.4) (2024-07-09) **Note:** Version bump only for package @eggjs/core-decorator ## [3.39.3](https://github.com/eggjs/tegg/compare/v3.39.2...v3.39.3) (2024-04-28) **Note:** Version bump only for package @eggjs/core-decorator ## [3.39.2](https://github.com/eggjs/tegg/compare/v3.39.1...v3.39.2) (2024-04-28) **Note:** Version bump only for package @eggjs/core-decorator ## [3.39.1](https://github.com/eggjs/tegg/compare/v3.39.0...v3.39.1) (2024-04-28) **Note:** Version bump only for package @eggjs/core-decorator # [3.39.0](https://github.com/eggjs/tegg/compare/v3.38.0...v3.39.0) (2024-04-19) **Note:** Version bump only for package @eggjs/core-decorator # [3.38.0](https://github.com/eggjs/tegg/compare/v3.37.3...v3.38.0) (2024-04-18) **Note:** Version bump only for package @eggjs/core-decorator ## [3.37.3](https://github.com/eggjs/tegg/compare/v3.37.2...v3.37.3) (2024-04-17) **Note:** Version bump only for package @eggjs/core-decorator ## [3.37.2](https://github.com/eggjs/tegg/compare/v3.37.1...v3.37.2) (2024-04-17) **Note:** Version bump only for package @eggjs/core-decorator ## [3.37.1](https://github.com/eggjs/tegg/compare/v3.37.0...v3.37.1) (2024-04-16) **Note:** Version bump only for package @eggjs/core-decorator # [3.37.0](https://github.com/eggjs/tegg/compare/v3.36.3...v3.37.0) (2024-04-16) **Note:** Version bump only for package @eggjs/core-decorator ## [3.36.3](https://github.com/eggjs/tegg/compare/v3.36.2...v3.36.3) (2024-04-10) **Note:** Version bump only for package @eggjs/core-decorator ## [3.36.2](https://github.com/eggjs/tegg/compare/v3.36.1...v3.36.2) (2024-04-08) **Note:** Version bump only for package @eggjs/core-decorator ## [3.36.1](https://github.com/eggjs/tegg/compare/v3.36.0...v3.36.1) (2024-04-07) **Note:** Version bump only for package @eggjs/core-decorator # [3.36.0](https://github.com/eggjs/tegg/compare/v3.35.1...v3.36.0) (2024-04-02) ### Features * impl ajv + typebox Validator ([#201](https://github.com/eggjs/tegg/issues/201)) ([9fd585d](https://github.com/eggjs/tegg/commit/9fd585de9b613466c96b73494a08a494db34ea57)) ## [3.35.1](https://github.com/eggjs/tegg/compare/v3.35.0...v3.35.1) (2024-03-26) **Note:** Version bump only for package @eggjs/core-decorator # [3.35.0](https://github.com/eggjs/tegg/compare/v3.34.0...v3.35.0) (2024-03-26) **Note:** Version bump only for package @eggjs/core-decorator # [3.34.0](https://github.com/eggjs/tegg/compare/v3.33.1...v3.34.0) (2024-03-22) **Note:** Version bump only for package @eggjs/core-decorator ## [3.33.1](https://github.com/eggjs/tegg/compare/v3.33.0...v3.33.1) (2024-03-22) **Note:** Version bump only for package @eggjs/core-decorator # [3.33.0](https://github.com/eggjs/tegg/compare/v3.32.0...v3.33.0) (2024-03-22) ### Features * impl dal ([#192](https://github.com/eggjs/tegg/issues/192)) ([1c7d145](https://github.com/eggjs/tegg/commit/1c7d1454bc8c600cd58c3ec7b9cda4e8a98c7287)) # [3.32.0](https://github.com/eggjs/tegg/compare/v3.31.0...v3.32.0) (2024-02-19) **Note:** Version bump only for package @eggjs/core-decorator # [3.31.0](https://github.com/eggjs/tegg/compare/v3.30.1...v3.31.0) (2024-01-31) **Note:** Version bump only for package @eggjs/core-decorator ## [3.30.1](https://github.com/eggjs/tegg/compare/v3.30.0...v3.30.1) (2024-01-25) **Note:** Version bump only for package @eggjs/core-decorator # [3.30.0](https://github.com/eggjs/tegg/compare/v3.29.0...v3.30.0) (2024-01-17) **Note:** Version bump only for package @eggjs/core-decorator # [3.29.0](https://github.com/eggjs/tegg/compare/v3.28.2...v3.29.0) (2023-12-26) **Note:** Version bump only for package @eggjs/core-decorator ## [3.28.2](https://github.com/eggjs/tegg/compare/v3.28.1...v3.28.2) (2023-12-12) **Note:** Version bump only for package @eggjs/core-decorator ## [3.28.1](https://github.com/eggjs/tegg/compare/v3.28.0...v3.28.1) (2023-12-11) **Note:** Version bump only for package @eggjs/core-decorator # [3.28.0](https://github.com/eggjs/tegg/compare/v3.27.0...v3.28.0) (2023-12-10) **Note:** Version bump only for package @eggjs/core-decorator # [3.27.0](https://github.com/eggjs/tegg/compare/v3.26.0...v3.27.0) (2023-11-23) **Note:** Version bump only for package @eggjs/core-decorator ## [3.25.2](https://github.com/eggjs/tegg/compare/v3.25.1...v3.25.2) (2023-11-06) **Note:** Version bump only for package @eggjs/core-decorator ## [3.25.1](https://github.com/eggjs/tegg/compare/v3.25.0...v3.25.1) (2023-11-03) **Note:** Version bump only for package @eggjs/core-decorator # [3.25.0](https://github.com/eggjs/tegg/compare/v3.24.0...v3.25.0) (2023-11-03) ### Features * tegg plugin support ModuleConfig ([#162](https://github.com/eggjs/tegg/issues/162)) ([58bd9fa](https://github.com/eggjs/tegg/commit/58bd9fafdd0d56aabdde5f7c33f17c45568bada8)) # [3.24.0](https://github.com/eggjs/tegg/compare/v3.23.0...v3.24.0) (2023-10-26) **Note:** Version bump only for package @eggjs/core-decorator # [3.23.0](https://github.com/eggjs/tegg/compare/v3.22.0...v3.23.0) (2023-09-20) ### Features * add className property to EggPrototypeInfo ([#158](https://github.com/eggjs/tegg/issues/158)) ([bddac97](https://github.com/eggjs/tegg/commit/bddac97a9f575c9f13b794246a7e8346c58d1a09)) # [3.20.0](https://github.com/eggjs/tegg/compare/v3.19.0...v3.20.0) (2023-09-07) **Note:** Version bump only for package @eggjs/core-decorator # [3.19.0](https://github.com/eggjs/tegg/compare/v3.18.1...v3.19.0) (2023-08-31) **Note:** Version bump only for package @eggjs/core-decorator ## [3.18.1](https://github.com/eggjs/tegg/compare/v3.18.0...v3.18.1) (2023-08-29) ### Bug Fixes * fix use MultiInstanceProto from other modules ([#147](https://github.com/eggjs/tegg/issues/147)) ([b71af60](https://github.com/eggjs/tegg/commit/b71af60ce6d1da0d778f5e712633b8c15052bd70)) # [3.18.0](https://github.com/eggjs/tegg/compare/v3.17.0...v3.18.0) (2023-08-29) **Note:** Version bump only for package @eggjs/core-decorator # [3.17.0](https://github.com/eggjs/tegg/compare/v3.16.0...v3.17.0) (2023-08-24) ### Features * impl MultiInstanceProto ([#145](https://github.com/eggjs/tegg/issues/145)) ([12fd5cf](https://github.com/eggjs/tegg/commit/12fd5cff4004578bcc737dcdf4f7e9d1159f5633)) # [3.16.0](https://github.com/eggjs/tegg/compare/v3.15.0...v3.16.0) (2023-08-24) **Note:** Version bump only for package @eggjs/core-decorator ## [3.14.3](https://github.com/eggjs/tegg/compare/v3.14.2...v3.14.3) (2023-08-14) **Note:** Version bump only for package @eggjs/core-decorator ## [3.14.2](https://github.com/eggjs/tegg/compare/v3.14.1...v3.14.2) (2023-08-14) **Note:** Version bump only for package @eggjs/core-decorator ## [3.14.1](https://github.com/eggjs/tegg/compare/v3.14.0...v3.14.1) (2023-08-11) **Note:** Version bump only for package @eggjs/core-decorator # [3.14.0](https://github.com/eggjs/tegg/compare/v3.13.0...v3.14.0) (2023-07-31) **Note:** Version bump only for package @eggjs/core-decorator # [3.13.0](https://github.com/eggjs/tegg/compare/v3.12.0...v3.13.0) (2023-07-25) **Note:** Version bump only for package @eggjs/core-decorator # [3.12.0](https://github.com/eggjs/tegg/compare/v3.11.1...v3.12.0) (2023-07-13) **Note:** Version bump only for package @eggjs/core-decorator ## [3.11.1](https://github.com/eggjs/tegg/compare/v3.11.0...v3.11.1) (2023-06-29) **Note:** Version bump only for package @eggjs/core-decorator # [3.11.0](https://github.com/eggjs/tegg/compare/v3.10.0...v3.11.0) (2023-06-29) **Note:** Version bump only for package @eggjs/core-decorator # [3.10.0](https://github.com/eggjs/tegg/compare/v3.9.0...v3.10.0) (2023-06-28) **Note:** Version bump only for package @eggjs/core-decorator # [3.8.0](https://github.com/eggjs/tegg/compare/v3.7.0...v3.8.0) (2023-05-30) ### Features * impl EggObjectLifecycle hook in decorator ([#119](https://github.com/eggjs/tegg/issues/119)) ([cced8a2](https://github.com/eggjs/tegg/commit/cced8a2e009c33d5040fa21d00409fddef471b0e)) # [3.7.0](https://github.com/eggjs/tegg/compare/v3.6.3...v3.7.0) (2023-04-03) **Note:** Version bump only for package @eggjs/core-decorator # [3.5.0](https://github.com/eggjs/tegg/compare/v3.4.1...v3.5.0) (2023-02-10) ### Features * use SingletonProto for egg ctx object ([#92](https://github.com/eggjs/tegg/issues/92)) ([3385d57](https://github.com/eggjs/tegg/commit/3385d571b076d3148978f252188f29d9cf2c6781)) ## [3.2.3](https://github.com/eggjs/tegg/compare/v3.2.2...v3.2.3) (2023-01-16) **Note:** Version bump only for package @eggjs/core-decorator ## [3.2.2](https://github.com/eggjs/tegg/compare/v3.2.1...v3.2.2) (2023-01-06) **Note:** Version bump only for package @eggjs/core-decorator # [3.1.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.1.0) (2022-12-27) ### Features * **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) # [3.0.0](https://github.com/eggjs/tegg/compare/v3.0.0-alpha.0...v3.0.0) (2022-12-26) **Note:** Version bump only for package @eggjs/core-decorator # [3.0.0-alpha.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.0.0-alpha.0) (2022-12-22) ### Features * **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) # [1.3.0](https://github.com/eggjs/tegg/compare/v1.2.0...v1.3.0) (2022-07-01) ### Features * allow inject proto and name ([#40](https://github.com/eggjs/tegg/issues/40)) ([abd1766](https://github.com/eggjs/tegg/commit/abd17665af2528c4c2e33f4c6b0fceddd8a4e76b)) # [0.2.0](https://github.com/eggjs/tegg/compare/v0.1.19...v0.2.0) (2022-01-20) ### Features * impl aop ([c53df00](https://github.com/eggjs/tegg/commit/c53df001d1455a0a105689694775d880541d9d2f)) ================================================ FILE: core/core-decorator/README.md ================================================ # `@eggjs/core-decorator` ## Usage Please read [@eggjs/tegg-plugin](../../plugin/tegg) ================================================ FILE: core/core-decorator/index.ts ================================================ export * from '@eggjs/tegg-types/core-decorator'; export * from './src/decorator/Inject'; export * from './src/decorator/Prototype'; export * from './src/decorator/InitTypeQualifier'; export * from './src/decorator/ModuleQualifier'; export * from './src/decorator/ContextProto'; export * from './src/decorator/SingletonProto'; export * from './src/decorator/EggQualifier'; export * from './src/decorator/MultiInstanceProto'; export * from './src/decorator/MultiInstanceInfo'; export * from './src/decorator/ConfigSource'; export * from './src/util/MetadataUtil'; export * from './src/util/PrototypeUtil'; export * from './src/util/QualifierUtil'; ================================================ FILE: core/core-decorator/package.json ================================================ { "name": "@eggjs/core-decorator", "version": "3.78.15", "description": "tegg core decorator", "keywords": [ "egg", "typescript", "decorator", "tegg" ], "main": "dist/index.js", "files": [ "dist/**/*.js", "dist/**/*.d.ts" ], "typings": "dist/index.d.ts", "scripts": { "test": "cross-env NODE_ENV=test NODE_OPTIONS='--no-deprecation' mocha", "clean": "tsc -b --clean", "tsc": "ut run clean && tsc -p ./tsconfig.json", "tsc:pub": "ut run clean && tsc -p ./tsconfig.pub.json", "prepublishOnly": "ut tsc:pub" }, "author": "killagu ", "license": "MIT", "homepage": "https://github.com/eggjs/tegg", "bugs": { "url": "https://github.com/eggjs/tegg/issues" }, "repository": { "type": "git", "url": "git@github.com:eggjs/tegg.git", "directory": "core/core-decorator" }, "engines": { "node": ">=14.0.0" }, "dependencies": { "@eggjs/tegg-common-util": "^3.78.15", "@eggjs/tegg-types": "^3.78.15", "reflect-metadata": "^0.1.13" }, "publishConfig": { "access": "public" }, "devDependencies": { "@types/mocha": "^10.0.1", "@types/node": "^20.2.4", "cross-env": "^7.0.3", "mocha": "^10.2.0", "ts-node": "^10.9.1", "typescript": "^5.0.4" }, "gitHead": "240a3e9f40fda65ebb7589727d197db5ce17916c" } ================================================ FILE: core/core-decorator/src/decorator/ConfigSource.ts ================================================ import { QualifierUtil } from '../util/QualifierUtil'; import { ConfigSourceQualifierAttribute } from '@eggjs/tegg-types'; import type { EggProtoImplClass } from '@eggjs/tegg-types'; export function ConfigSourceQualifier(moduleName: string) { return function(target: any, propertyKey?: PropertyKey, parameterIndex?: number) { QualifierUtil.addInjectQualifier(target as EggProtoImplClass, propertyKey, parameterIndex, ConfigSourceQualifierAttribute, moduleName); }; } ================================================ FILE: core/core-decorator/src/decorator/ContextProto.ts ================================================ import { Prototype } from './Prototype'; import { AccessLevel, ObjectInitType } from '@eggjs/tegg-types'; import type { ContextProtoParams } from '@eggjs/tegg-types'; export function ContextProto(params?: ContextProtoParams) { return Prototype({ initType: ObjectInitType.CONTEXT, accessLevel: params?.accessLevel || AccessLevel.PRIVATE, ...params, }); } ================================================ FILE: core/core-decorator/src/decorator/EggQualifier.ts ================================================ import { EggQualifierAttribute } from '@eggjs/tegg-types'; import type { EggProtoImplClass, EggType } from '@eggjs/tegg-types'; import { QualifierUtil } from '../util/QualifierUtil'; export function EggQualifier(eggType: EggType) { return function(target: any, propertyKey?: PropertyKey, parameterIndex?: number) { QualifierUtil.addInjectQualifier(target as EggProtoImplClass, propertyKey, parameterIndex, EggQualifierAttribute, eggType); }; } ================================================ FILE: core/core-decorator/src/decorator/InitTypeQualifier.ts ================================================ import { InitTypeQualifierAttribute } from '@eggjs/tegg-types'; import type { EggProtoImplClass, ObjectInitTypeLike } from '@eggjs/tegg-types'; import { QualifierUtil } from '../util/QualifierUtil'; export function InitTypeQualifier(initType: ObjectInitTypeLike) { return function(target: any, propertyKey?: PropertyKey, parameterIndex?: number) { QualifierUtil.addInjectQualifier(target as EggProtoImplClass, propertyKey, parameterIndex, InitTypeQualifierAttribute, initType); }; } ================================================ FILE: core/core-decorator/src/decorator/Inject.ts ================================================ import { EggProtoImplClass, InjectObjectInfo, InjectConstructorInfo, InjectParams, InjectType, InitTypeQualifierAttribute, } from '@eggjs/tegg-types'; import { PrototypeUtil } from '../util/PrototypeUtil'; import { ObjectUtils } from '@eggjs/tegg-common-util'; import { QualifierUtil } from '../util/QualifierUtil'; function guessInjectInfo(clazz: EggProtoImplClass, name: PropertyKey, proto: any) { let objName: PropertyKey | undefined; let initType: string | undefined; if (typeof proto === 'function' && proto !== Object) { // if property type is function and not Object( means maybe proto class ), then try to read EggPrototypeInfo.name as obj name const info = PrototypeUtil.getProperty(proto as EggProtoImplClass); objName = info?.name; // try to read EggPrototypeInfo.initType as qualifier if (info?.initType) { const customInitType = QualifierUtil.getProperQualifier(clazz, name, InitTypeQualifierAttribute); if (!customInitType) { initType = info.initType; } } } return { objName, initType, }; } export function Inject(param?: InjectParams | string) { const injectParam = typeof param === 'string' ? { name: param } : param; function propertyInject(target: any, propertyKey: PropertyKey) { let objName: PropertyKey | undefined; let initType: string | undefined; if (!injectParam) { // try to read design:type from proto const proto = PrototypeUtil.getDesignType(target, propertyKey); ({ objName, initType } = guessInjectInfo(target.constructor, propertyKey, proto)); } else { // params allow string or object objName = injectParam?.name; } const injectObject: InjectObjectInfo = { refName: propertyKey, objName: objName || propertyKey, }; if (injectParam?.optional) { injectObject.optional = true; } PrototypeUtil.setInjectType(target.constructor, InjectType.PROPERTY); PrototypeUtil.addInjectObject(target.constructor as EggProtoImplClass, injectObject); if (initType) { QualifierUtil.addProperQualifier(target.constructor, propertyKey, InitTypeQualifierAttribute, initType); } } function constructorInject(target: any, parameterIndex: number) { const argNames = ObjectUtils.getConstructorArgNameList(target); const argName = argNames[parameterIndex]; let objName: PropertyKey | undefined; let initType: string | undefined; if (!injectParam) { // try to read proto from design:paramtypes const protos = PrototypeUtil.getDesignParamtypes(target); ({ objName, initType } = guessInjectInfo(target, argName, protos?.[parameterIndex])); } else { // params allow string or object objName = injectParam?.name; } const injectObject: InjectConstructorInfo = { refIndex: parameterIndex, refName: argName, objName: objName || argName, }; if (injectParam?.optional) { injectObject.optional = true; } PrototypeUtil.setInjectType(target, InjectType.CONSTRUCTOR); PrototypeUtil.addInjectConstructor(target as EggProtoImplClass, injectObject); if (initType) { QualifierUtil.addProperQualifier(target, argName, InitTypeQualifierAttribute, initType); } } return function(target: any, propertyKey?: PropertyKey, parameterIndex?: number) { if (typeof parameterIndex === 'undefined') { propertyInject(target, propertyKey!); } else { constructorInject(target, parameterIndex!); } }; } export function InjectOptional(param?: Omit | string) { const injectParam = typeof param === 'string' ? { name: param } : param; return Inject({ ...injectParam, optional: true, }); } ================================================ FILE: core/core-decorator/src/decorator/ModuleQualifier.ts ================================================ import { LoadUnitNameQualifierAttribute } from '@eggjs/tegg-types'; import type { EggProtoImplClass } from '@eggjs/tegg-types'; import { QualifierUtil } from '../util/QualifierUtil'; export function ModuleQualifier(moduleName: string) { return function(target: any, propertyKey?: PropertyKey, parameterIndex?: number) { QualifierUtil.addInjectQualifier(target as EggProtoImplClass, propertyKey, parameterIndex, LoadUnitNameQualifierAttribute, moduleName); }; } ================================================ FILE: core/core-decorator/src/decorator/MultiInstanceInfo.ts ================================================ import { PrototypeUtil } from '../util/PrototypeUtil'; import { QualifierAttribute } from '@eggjs/tegg-types'; export function MultiInstanceInfo(attributes: QualifierAttribute[]) { return function(target: any, _propertyKey: PropertyKey | undefined, parameterIndex: number) { PrototypeUtil.setMultiInstanceConstructorIndex(target, parameterIndex); PrototypeUtil.setMultiInstanceConstructorAttributes(target, attributes); }; } ================================================ FILE: core/core-decorator/src/decorator/MultiInstanceProto.ts ================================================ import { ObjectInitType, AccessLevel, DEFAULT_PROTO_IMPL_TYPE } from '@eggjs/tegg-types'; import type { EggMultiInstanceCallbackPrototypeInfo, EggMultiInstancePrototypeInfo, EggProtoImplClass, MultiInstancePrototypeParams, MultiInstancePrototypeStaticParams, MultiInstancePrototypeCallbackParams, } from '@eggjs/tegg-types'; import { PrototypeUtil } from '../util/PrototypeUtil'; import { StackUtil } from '@eggjs/tegg-common-util'; const DEFAULT_PARAMS = { initType: ObjectInitType.SINGLETON, accessLevel: AccessLevel.PRIVATE, protoImplType: DEFAULT_PROTO_IMPL_TYPE, }; export function MultiInstanceProto(param: MultiInstancePrototypeParams) { return function(clazz: EggProtoImplClass) { PrototypeUtil.setIsEggMultiInstancePrototype(clazz); if ((param as MultiInstancePrototypeStaticParams).objects) { const property: EggMultiInstancePrototypeInfo = { ...DEFAULT_PARAMS, ...param as MultiInstancePrototypeStaticParams, className: clazz.name, }; PrototypeUtil.setMultiInstanceStaticProperty(clazz, property); } else if ((param as MultiInstancePrototypeCallbackParams).getObjects) { const property: EggMultiInstanceCallbackPrototypeInfo = { ...DEFAULT_PARAMS, ...param as MultiInstancePrototypeCallbackParams, className: clazz.name, }; PrototypeUtil.setMultiInstanceCallbackProperty(clazz, property); } // './tegg/core/common-util/src/StackUtil.ts', // './tegg/core/core-decorator/src/decorator/Prototype.ts', // './tegg/core/core-decorator/node_modules/_reflect-metadata@0.1.13@reflect-metadata/Reflect.js', // './tegg/core/core-decorator/node_modules/_reflect-metadata@0.1.13@reflect-metadata/Reflect.js', // './tegg/core/core-decorator/test/fixtures/decators/CacheService.ts', PrototypeUtil.setFilePath(clazz, StackUtil.getCalleeFromStack(false, 4)); }; } ================================================ FILE: core/core-decorator/src/decorator/Prototype.ts ================================================ import { NameUtil, StackUtil } from '@eggjs/tegg-common-util'; import { AccessLevel, DEFAULT_PROTO_IMPL_TYPE, ObjectInitType } from '@eggjs/tegg-types'; import type { EggProtoImplClass, EggPrototypeInfo, PrototypeParams } from '@eggjs/tegg-types'; import { PrototypeUtil } from '../util/PrototypeUtil'; const DEFAULT_PARAMS = { initType: ObjectInitType.SINGLETON, accessLevel: AccessLevel.PRIVATE, protoImplType: DEFAULT_PROTO_IMPL_TYPE, }; export function Prototype(param?: PrototypeParams) { return function(clazz: EggProtoImplClass) { PrototypeUtil.setIsEggPrototype(clazz); const property: Partial = { ...DEFAULT_PARAMS, ...param, className: clazz.name, }; if (!property.name) { property.name = NameUtil.getClassName(clazz); } PrototypeUtil.setProperty(clazz, property as EggPrototypeInfo); // './tegg/core/common-util/src/StackUtil.ts', // './tegg/core/core-decorator/src/decorator/Prototype.ts', // './tegg/core/core-decorator/node_modules/_reflect-metadata@0.1.13@reflect-metadata/Reflect.js', // './tegg/core/core-decorator/node_modules/_reflect-metadata@0.1.13@reflect-metadata/Reflect.js', // './tegg/core/core-decorator/test/fixtures/decators/CacheService.ts', PrototypeUtil.setFilePath(clazz, StackUtil.getCalleeFromStack(false, 4)); }; } ================================================ FILE: core/core-decorator/src/decorator/SingletonProto.ts ================================================ import { AccessLevel, ObjectInitType } from '@eggjs/tegg-types'; import type { SingletonProtoParams } from '@eggjs/tegg-types'; import { Prototype } from './Prototype'; export function SingletonProto(params?: SingletonProtoParams) { return Prototype({ initType: ObjectInitType.SINGLETON, accessLevel: params?.accessLevel || AccessLevel.PRIVATE, ...params, }); } ================================================ FILE: core/core-decorator/src/util/MetadataUtil.ts ================================================ import 'reflect-metadata'; import type { EggProtoImplClass, MetaDataKey } from '@eggjs/tegg-types'; export class MetadataUtil { static deleteMetaData(metadataKey: MetaDataKey, clazz: EggProtoImplClass) { Reflect.deleteMetadata(metadataKey, clazz); } static defineMetaData(metadataKey: MetaDataKey, metadataValue: T, clazz: EggProtoImplClass) { Reflect.defineMetadata(metadataKey, metadataValue, clazz); } static getOwnMetaData(metadataKey: MetaDataKey, clazz: EggProtoImplClass): T | undefined { return Reflect.getOwnMetadata(metadataKey, clazz); } static hasMetaData(metadataKey: MetaDataKey, clazz: EggProtoImplClass, propKey?: PropertyKey): boolean { return Reflect.hasMetadata(metadataKey, clazz, propKey as string); } static getMetaData(metadataKey: MetaDataKey, clazz: EggProtoImplClass, propKey?: PropertyKey): T | undefined { return Reflect.getMetadata(metadataKey, clazz, propKey as string); } static getBooleanMetaData(metadataKey: MetaDataKey, clazz: EggProtoImplClass): boolean { return !!this.getMetaData(metadataKey, clazz); } static getOwnBooleanMetaData(metadataKey: MetaDataKey, clazz: EggProtoImplClass): boolean { return !!this.getOwnMetaData(metadataKey, clazz); } static getArrayMetaData(metadataKey: MetaDataKey, clazz: EggProtoImplClass): Array { return this.getMetaData(metadataKey, clazz) || []; } /** * init array metadata * not inherit parent metadata * return value true means use default value * return value false means use map value */ static initArrayMetaData(metadataKey: MetaDataKey, clazz: EggProtoImplClass, defaultValue: Array): Array { const ownMetaData: Array | undefined = this.getOwnMetaData(metadataKey, clazz); if (!ownMetaData) { this.defineMetaData(metadataKey, defaultValue, clazz); } return this.getOwnMetaData>(metadataKey, clazz)!; } /** * init own array metadata * if parent metadata exists, inherit * if parent metadata not exits, use default value * return value true means use default value * return value false means use map value */ static initOwnArrayMetaData(metadataKey: MetaDataKey, clazz: EggProtoImplClass, defaultValue: Array): Array { const ownMetaData: Array | undefined = this.getOwnMetaData(metadataKey, clazz); if (!ownMetaData) { const parentValue: Array | undefined = this.getMetaData(metadataKey, clazz); const ownDefaultValue = parentValue || defaultValue; const selfValue = ownDefaultValue.slice(); this.defineMetaData(metadataKey, selfValue, clazz); } return this.getOwnMetaData>(metadataKey, clazz)!; } /** * init own map metadata * if parent metadata exists, inherit * if parent metadata not exits, use default value * return value true means use default value * return value false means use map value */ static initOwnMapMetaData(metadataKey: MetaDataKey, clazz: EggProtoImplClass, defaultValue: Map): Map { const ownMetaData: Map | undefined = this.getOwnMetaData(metadataKey, clazz); if (!ownMetaData) { const parentValue: Map | undefined = this.getMetaData(metadataKey, clazz); const selfValue = new Map(); const ownDefaultValue = parentValue || defaultValue; for (const [ k, v ] of ownDefaultValue) { selfValue.set(k, v); } this.defineMetaData(metadataKey, selfValue, clazz); } return this.getOwnMetaData>(metadataKey, clazz)!; } static getOrStoreMetaData(metadataKey: MetaDataKey, clazz: EggProtoImplClass, metadataValue: T): T { if (!Reflect.hasMetadata(metadataKey, clazz)) { Reflect.defineMetadata(metadataKey, metadataValue, clazz); } return Reflect.getMetadata(metadataKey, clazz); } } ================================================ FILE: core/core-decorator/src/util/PrototypeUtil.ts ================================================ import { EggMultiInstanceCallbackPrototypeInfo, EggMultiInstancePrototypeInfo, EggProtoImplClass, EggPrototypeInfo, EggPrototypeName, InitTypeQualifierAttribute, InjectConstructorInfo, InjectObjectInfo, InjectType, LoadUnitNameQualifierAttribute, MultiInstancePrototypeGetObjectsContext, MultiInstanceType, QualifierAttribute, } from '@eggjs/tegg-types'; import { MetadataUtil } from './MetadataUtil'; export class PrototypeUtil { static readonly IS_EGG_OBJECT_PROTOTYPE = Symbol.for('EggPrototype#isEggPrototype'); static readonly IS_EGG_OBJECT_MULTI_INSTANCE_PROTOTYPE = Symbol.for('EggPrototype#isEggMultiInstancePrototype'); static readonly FILE_PATH = Symbol.for('EggPrototype.filePath'); static readonly PROTOTYPE_PROPERTY = Symbol.for('EggPrototype.Property'); static readonly MULTI_INSTANCE_PROTOTYPE_STATIC_PROPERTY = Symbol.for('EggPrototype.MultiInstanceStaticProperty'); static readonly MULTI_INSTANCE_PROTOTYPE_CALLBACK_PROPERTY = Symbol.for('EggPrototype.MultiInstanceCallbackProperty'); static readonly INJECT_OBJECT_NAME_SET = Symbol.for('EggPrototype.injectObjectNames'); static readonly INJECT_TYPE = Symbol.for('EggPrototype.injectType'); static readonly INJECT_CONSTRUCTOR_NAME_SET = Symbol.for('EggPrototype.injectConstructorNames'); static readonly CLAZZ_PROTO = Symbol.for('EggPrototype.clazzProto'); static readonly MULTI_INSTANCE_CONSTRUCTOR_INDEX = Symbol.for('EggPrototype#multiInstanceConstructorIndex'); static readonly MULTI_INSTANCE_CONSTRUCTOR_ATTRIBUTES = Symbol.for('EggPrototype#multiInstanceConstructorAttributes'); /** * Mark class is egg object prototype * @param {Function} clazz - */ static setIsEggPrototype(clazz: EggProtoImplClass) { MetadataUtil.defineMetaData(PrototypeUtil.IS_EGG_OBJECT_PROTOTYPE, true, clazz); } /** * If class is egg object prototype, return true * @param {Function} clazz - */ static isEggPrototype(clazz: EggProtoImplClass): boolean { return MetadataUtil.getOwnBooleanMetaData(PrototypeUtil.IS_EGG_OBJECT_PROTOTYPE, clazz); } /** * Mark class is egg object multi instance prototype * @param {Function} clazz - */ static setIsEggMultiInstancePrototype(clazz: EggProtoImplClass) { MetadataUtil.defineMetaData(PrototypeUtil.IS_EGG_OBJECT_MULTI_INSTANCE_PROTOTYPE, true, clazz); } /** * If class is egg object multi instance prototype, return true * @param {Function} clazz - */ static isEggMultiInstancePrototype(clazz: EggProtoImplClass): boolean { return MetadataUtil.getOwnBooleanMetaData(PrototypeUtil.IS_EGG_OBJECT_MULTI_INSTANCE_PROTOTYPE, clazz); } /** * Get the type of the egg multi-instance prototype. * @param {Function} clazz - */ static getEggMultiInstancePrototypeType(clazz: EggProtoImplClass): MultiInstanceType | undefined { if (!PrototypeUtil.isEggMultiInstancePrototype(clazz)) { return; } const metadata = MetadataUtil.getOwnMetaData(PrototypeUtil.MULTI_INSTANCE_PROTOTYPE_STATIC_PROPERTY, clazz); if (metadata) { return MultiInstanceType.STATIC; } return MultiInstanceType.DYNAMIC; } /** * set class file path * @param {Function} clazz - * @param {string} filePath - */ static setFilePath(clazz: EggProtoImplClass, filePath: string) { MetadataUtil.defineMetaData(PrototypeUtil.FILE_PATH, filePath, clazz); } /** * get class file path * @param {Function} clazz - */ static getFilePath(clazz: EggProtoImplClass): string | undefined { return MetadataUtil.getOwnMetaData(PrototypeUtil.FILE_PATH, clazz); } /** * set class property * @param {EggProtoImplClass} clazz - * @param {EggPrototypeInfo} property - */ static setProperty(clazz: EggProtoImplClass, property: EggPrototypeInfo) { MetadataUtil.defineMetaData(PrototypeUtil.PROTOTYPE_PROPERTY, property, clazz); } /** * get class property * @param {EggProtoImplClass} clazz - * @return {EggPrototypeInfo} - */ static getProperty(clazz: EggProtoImplClass): EggPrototypeInfo | undefined { return MetadataUtil.getOwnMetaData(PrototypeUtil.PROTOTYPE_PROPERTY, clazz); } static getInitType(clazz: EggProtoImplClass, ctx: MultiInstancePrototypeGetObjectsContext): string | undefined { const property = PrototypeUtil.getProperty(clazz) ?? PrototypeUtil.getMultiInstanceProperty(clazz, ctx); return property?.initType; } static getAccessLevel(clazz: EggProtoImplClass, ctx: MultiInstancePrototypeGetObjectsContext): string | undefined { const property = PrototypeUtil.getProperty(clazz) ?? PrototypeUtil.getMultiInstanceProperty(clazz, ctx); return property?.accessLevel; } static getObjNames(clazz: EggProtoImplClass, ctx: MultiInstancePrototypeGetObjectsContext): EggPrototypeName[] { const property = PrototypeUtil.getProperty(clazz); if (property) { return [ property.name ]; } const multiInstanceProperty = PrototypeUtil.getMultiInstanceProperty(clazz, ctx); return multiInstanceProperty?.objects.map(t => t.name) || []; } /** * set class property * @param {EggProtoImplClass} clazz - * @param {EggPrototypeInfo} property - */ static setMultiInstanceStaticProperty(clazz: EggProtoImplClass, property: EggMultiInstancePrototypeInfo) { MetadataUtil.defineMetaData(PrototypeUtil.MULTI_INSTANCE_PROTOTYPE_STATIC_PROPERTY, property, clazz); } /** * set class property * @param {EggProtoImplClass} clazz - * @param {EggPrototypeInfo} property - */ static setMultiInstanceCallbackProperty(clazz: EggProtoImplClass, property: EggMultiInstanceCallbackPrototypeInfo) { MetadataUtil.defineMetaData(PrototypeUtil.MULTI_INSTANCE_PROTOTYPE_CALLBACK_PROPERTY, property, clazz); } /** * Get instance property of Static multi-instance prototype. * @param {EggProtoImplClass} clazz - */ static getStaticMultiInstanceProperty(clazz: EggProtoImplClass): EggMultiInstancePrototypeInfo | undefined { const metadata = MetadataUtil.getOwnMetaData(PrototypeUtil.MULTI_INSTANCE_PROTOTYPE_STATIC_PROPERTY, clazz); if (metadata) { return metadata; } } /** * Get instance property of Dynamic multi-instance prototype. * @param {EggProtoImplClass} clazz - * @param {MultiInstancePrototypeGetObjectsContext} ctx - */ static getDynamicMultiInstanceProperty(clazz: EggProtoImplClass, ctx: MultiInstancePrototypeGetObjectsContext): EggMultiInstancePrototypeInfo | undefined { const callBackMetadata = MetadataUtil.getOwnMetaData(PrototypeUtil.MULTI_INSTANCE_PROTOTYPE_CALLBACK_PROPERTY, clazz); if (callBackMetadata) { const objects = callBackMetadata.getObjects(ctx); return { ...callBackMetadata, objects, }; } } /** * get class property * @param {EggProtoImplClass} clazz - * @param {MultiInstancePrototypeGetObjectsContext} ctx - */ static getMultiInstanceProperty(clazz: EggProtoImplClass, ctx: MultiInstancePrototypeGetObjectsContext): EggMultiInstancePrototypeInfo | undefined { const metadata = MetadataUtil.getOwnMetaData(PrototypeUtil.MULTI_INSTANCE_PROTOTYPE_STATIC_PROPERTY, clazz); if (metadata) { return metadata; } const callBackMetadata = MetadataUtil.getOwnMetaData(PrototypeUtil.MULTI_INSTANCE_PROTOTYPE_CALLBACK_PROPERTY, clazz); if (callBackMetadata) { const objects = callBackMetadata.getObjects(ctx); // TODO delete in next major version, default qualifier be added in ProtoDescriptorHelper.addDefaultQualifier const defaultQualifier = [{ attribute: InitTypeQualifierAttribute, value: callBackMetadata.initType, }, { attribute: LoadUnitNameQualifierAttribute, value: ctx.moduleName, }]; for (const object of objects) { defaultQualifier.forEach(qualifier => { if (!object.qualifiers.find(t => t.attribute === qualifier.attribute)) { object.qualifiers.push(qualifier); } }); } return { ...callBackMetadata, objects, }; } } static setMultiInstanceConstructorAttributes(clazz: EggProtoImplClass, attributes: QualifierAttribute[]) { MetadataUtil.defineMetaData(PrototypeUtil.MULTI_INSTANCE_CONSTRUCTOR_ATTRIBUTES, attributes, clazz); } static getMultiInstanceConstructorAttributes(clazz: EggProtoImplClass): QualifierAttribute[] { return MetadataUtil.getMetaData(PrototypeUtil.MULTI_INSTANCE_CONSTRUCTOR_ATTRIBUTES, clazz) || []; } static setMultiInstanceConstructorIndex(clazz: EggProtoImplClass, index: number) { MetadataUtil.defineMetaData(PrototypeUtil.MULTI_INSTANCE_CONSTRUCTOR_INDEX, index, clazz); } static getMultiInstanceConstructorIndex(clazz: EggProtoImplClass): number | undefined { return MetadataUtil.getMetaData(PrototypeUtil.MULTI_INSTANCE_CONSTRUCTOR_INDEX, clazz); } static setInjectType(clazz: EggProtoImplClass, type: InjectType) { const injectType: InjectType | undefined = MetadataUtil.getMetaData(PrototypeUtil.INJECT_TYPE, clazz); if (!injectType) { MetadataUtil.defineMetaData(PrototypeUtil.INJECT_TYPE, type, clazz); } else if (injectType !== type) { throw new Error(`class ${clazz.name} already use inject type ${injectType} can not use ${type}`); } } static addInjectObject(clazz: EggProtoImplClass, injectObject: InjectObjectInfo) { const objs: InjectObjectInfo[] = MetadataUtil.initOwnArrayMetaData(PrototypeUtil.INJECT_OBJECT_NAME_SET, clazz, []); objs.push(injectObject); MetadataUtil.defineMetaData(PrototypeUtil.INJECT_OBJECT_NAME_SET, objs, clazz); } static addInjectConstructor(clazz: EggProtoImplClass, injectConstructorInfo: InjectConstructorInfo) { const objs: InjectConstructorInfo[] = MetadataUtil.initArrayMetaData(PrototypeUtil.INJECT_CONSTRUCTOR_NAME_SET, clazz, []); objs.push(injectConstructorInfo); MetadataUtil.defineMetaData(PrototypeUtil.INJECT_CONSTRUCTOR_NAME_SET, objs, clazz); } static getInjectType(clazz: EggProtoImplClass): InjectType | undefined { const injectType: InjectType | undefined = MetadataUtil.getMetaData(PrototypeUtil.INJECT_TYPE, clazz); return injectType; } static getInjectObjects(clazz: EggProtoImplClass): Array { const injectType: InjectType | undefined = MetadataUtil.getMetaData(PrototypeUtil.INJECT_TYPE, clazz); if (!injectType) { return []; } if (injectType === InjectType.CONSTRUCTOR) { return MetadataUtil.getArrayMetaData(PrototypeUtil.INJECT_CONSTRUCTOR_NAME_SET, clazz) .sort((a, b) => { return a.refIndex - b.refIndex; }); } return MetadataUtil.getArrayMetaData(PrototypeUtil.INJECT_OBJECT_NAME_SET, clazz); } // static getInjectConstructors(clazz: EggProtoImplClass): Array { // return MetadataUtil.getArrayMetaData(PrototypeUtil.INJECT_CONSTRUCTOR_NAME_SET, clazz) // .sort((a, b) => { // return a.refIndex - b.refIndex; // }); // } // TODO fix proto type static getClazzProto(clazz: EggProtoImplClass): object | undefined { return MetadataUtil.getMetaData(PrototypeUtil.CLAZZ_PROTO, clazz); } // TODO fix proto type static setClazzProto(clazz: EggProtoImplClass, proto: object) { return MetadataUtil.defineMetaData(PrototypeUtil.CLAZZ_PROTO, proto, clazz); } static getDesignType(clazz: EggProtoImplClass, propKey?: PropertyKey) { return MetadataUtil.getMetaData('design:type', clazz, propKey); } static getDesignParamtypes(clazz: EggProtoImplClass, propKey?: PropertyKey) { return MetadataUtil.getMetaData('design:paramtypes', clazz, propKey); } } ================================================ FILE: core/core-decorator/src/util/QualifierUtil.ts ================================================ import { MapUtil, ObjectUtils } from '@eggjs/tegg-common-util'; import { PROPERTY_QUALIFIER_META_DATA, QUALIFIER_META_DATA } from '@eggjs/tegg-types'; import type { EggProtoImplClass, QualifierAttribute, QualifierInfo, QualifierValue } from '@eggjs/tegg-types'; import { MetadataUtil } from './MetadataUtil'; export class QualifierUtil { static addProtoQualifier(clazz: EggProtoImplClass, attribute: QualifierAttribute, value: QualifierValue) { const qualifiers = MetadataUtil.initOwnMapMetaData(QUALIFIER_META_DATA, clazz, new Map()); qualifiers.set(attribute, value); } static getProtoQualifiers(clazz: EggProtoImplClass): QualifierInfo[] { const qualifiers: Map | undefined = MetadataUtil.getMetaData(QUALIFIER_META_DATA, clazz); if (!qualifiers) { return []; } const res: QualifierInfo[] = []; for (const [ attribute, value ] of qualifiers) { res.push({ attribute, value, }); } return res; } static addInjectQualifier(clazz: EggProtoImplClass, property: PropertyKey | undefined, parameterIndex: number | undefined, attribute: QualifierAttribute, value: QualifierValue) { if (typeof parameterIndex === 'number') { const argNames = ObjectUtils.getConstructorArgNameList(clazz); const argName = argNames[parameterIndex]; QualifierUtil.addProperQualifier(clazz, argName, attribute, value); } else { QualifierUtil.addProperQualifier((clazz as any).constructor, property!, attribute, value); } } static addProperQualifier(clazz: EggProtoImplClass, property: PropertyKey, attribute: QualifierAttribute, value: QualifierValue) { const properQualifiers = MetadataUtil.initOwnMapMetaData(PROPERTY_QUALIFIER_META_DATA, clazz, new Map>()); const qualifiers = MapUtil.getOrStore(properQualifiers, property, new Map()); qualifiers.set(attribute, value); } static getProperQualifiers(clazz: EggProtoImplClass, property: PropertyKey): QualifierInfo[] { const properQualifiers: Map> | undefined = MetadataUtil.getMetaData(PROPERTY_QUALIFIER_META_DATA, clazz); const qualifiers = properQualifiers?.get(property); if (!qualifiers) { return []; } const res: QualifierInfo[] = []; for (const [ attribute, value ] of qualifiers) { res.push({ attribute, value, }); } return res; } static getQualifierValue(clazz: EggProtoImplClass, attribute: QualifierAttribute): QualifierValue | undefined { const qualifiers: Map | undefined = MetadataUtil.getMetaData(QUALIFIER_META_DATA, clazz); return qualifiers?.get(attribute); } static getProperQualifier(clazz: EggProtoImplClass, property: PropertyKey, attribute: QualifierAttribute): QualifierValue | undefined { const properQualifiers: Map> | undefined = MetadataUtil.getMetaData(PROPERTY_QUALIFIER_META_DATA, clazz); const qualifiers = properQualifiers?.get(property); return qualifiers?.get(attribute); } static matchQualifiers(clazzQualifiers: QualifierInfo[], requestQualifiers: QualifierInfo[]): boolean { for (const request of requestQualifiers) { if (!clazzQualifiers.find(t => t.attribute === request.attribute && t.value === request.value)) { return false; } } return true; } static equalQualifiers(clazzQualifiers: QualifierInfo[], requestQualifiers: QualifierInfo[]): boolean { if (clazzQualifiers.length !== requestQualifiers.length) return false; return QualifierUtil.matchQualifiers(clazzQualifiers, requestQualifiers); } static mergeQualifiers(...qualifiers: QualifierInfo[][]): QualifierInfo[] { const result: QualifierInfo[] = []; const temp: Record = {}; for (const qualifierList of qualifiers) { for (const { attribute, value } of qualifierList) { temp[attribute] = value; } } for (const key of Reflect.ownKeys(temp)) { result.push({ attribute: key, value: temp[key], }); } return result; } } ================================================ FILE: core/core-decorator/test/decorators.test.ts ================================================ import assert from 'node:assert'; import { AccessLevel, ObjectInitType, LoadUnitNameQualifierAttribute, InitTypeQualifierAttribute, DEFAULT_PROTO_IMPL_TYPE, MultiInstanceType, } from '@eggjs/tegg-types'; import type { EggPrototypeInfo, EggMultiInstancePrototypeInfo, InjectObjectInfo } from '@eggjs/tegg-types'; import CacheService from './fixtures/decators/CacheService'; import ContextCache from './fixtures/decators/ContextCache'; import SingletonCache from './fixtures/decators/SingletonCache'; import { PrototypeUtil, QualifierUtil } from '..'; import QualifierCacheService from './fixtures/decators/QualifierCacheService'; import { FOO_ATTRIBUTE, FooLogger } from './fixtures/decators/FooLogger'; import { ConstructorObject, ConstructorQualifierObject } from './fixtures/decators/ConstructorObject'; import { ChildDynamicMultiInstanceProto, ChildSingletonProto, ChildStaticMultiInstanceProto, ParentDynamicMultiInstanceProto, ParentSingletonProto, ParentStaticMultiInstanceProto, } from './fixtures/decators/ChildService'; describe('test/decorator.test.ts', () => { describe('ContextProto', () => { it('should work', () => { assert(PrototypeUtil.isEggPrototype(ContextCache)); const expectObjectProperty: EggPrototypeInfo = { name: 'cache', initType: ObjectInitType.CONTEXT, accessLevel: AccessLevel.PUBLIC, protoImplType: DEFAULT_PROTO_IMPL_TYPE, className: 'ContextCache', }; assert.deepStrictEqual(PrototypeUtil.getProperty(ContextCache), expectObjectProperty); }); }); describe('SingletonProto', () => { it('should work', () => { assert(PrototypeUtil.isEggPrototype(SingletonCache)); const expectObjectProperty: EggPrototypeInfo = { name: 'cache', initType: ObjectInitType.SINGLETON, accessLevel: AccessLevel.PUBLIC, protoImplType: DEFAULT_PROTO_IMPL_TYPE, className: 'SingletonCache', }; assert.deepStrictEqual(PrototypeUtil.getProperty(SingletonCache), expectObjectProperty); }); }); describe('Inject', () => { it('should work', () => { assert(PrototypeUtil.isEggPrototype(CacheService)); const expectInjectInfo: InjectObjectInfo[] = [{ refName: 'cache', objName: 'fooCache', }, { refName: 'testService', objName: 'testService', }, { objName: 'abcabc', refName: 'testService2', }, { objName: 'testService3', refName: 'otherService', }, { objName: 'testService4', refName: 'testService4', }, { objName: 'optionalService1', refName: 'optionalService1', optional: true, }, { objName: 'optionalService2', refName: 'optionalService2', optional: true, }]; assert.deepStrictEqual(PrototypeUtil.getInjectObjects(CacheService), expectInjectInfo); }); it('constructor should work', () => { const injectConstructors = PrototypeUtil.getInjectObjects(ConstructorObject); assert.deepStrictEqual(injectConstructors, [ { refIndex: 0, refName: 'xCache', objName: 'fooCache' }, { refIndex: 1, refName: 'cache', objName: 'cache' }, { refIndex: 2, refName: 'otherCache', objName: 'cacheService' }, { refIndex: 3, refName: 'optional1', objName: 'optional1', optional: true }, { refIndex: 4, refName: 'optional2', objName: 'optional2', optional: true }, ]); }); }); describe('Qualifier', () => { it('should work', () => { assert(PrototypeUtil.isEggPrototype(QualifierCacheService)); const property = 'cache'; assert( QualifierUtil.getProperQualifier(QualifierCacheService, property, LoadUnitNameQualifierAttribute) === 'foo', ); assert( QualifierUtil.getProperQualifier(QualifierCacheService, property, InitTypeQualifierAttribute) === ObjectInitType.SINGLETON, ); }); it('should set default initType in inject', () => { const properties = [ { property: 'interfaceService', expected: undefined }, { property: 'testContextService', expected: ObjectInitType.CONTEXT }, { property: 'testSingletonService', expected: ObjectInitType.SINGLETON }, { property: 'customNameService', expected: undefined }, { property: 'customQualifierService1', expected: ObjectInitType.CONTEXT }, { property: 'customQualifierService2', expected: ObjectInitType.CONTEXT }, ]; for (const { property, expected } of properties) { const qualifier = QualifierUtil.getProperQualifier(QualifierCacheService, property, InitTypeQualifierAttribute); assert.strictEqual(qualifier, expected, `expect initType for ${property} to be ${expected}`); } }); it('should work use Symbol.for', () => { assert(PrototypeUtil.isEggPrototype(QualifierCacheService)); const property = 'cache'; assert( QualifierUtil.getProperQualifier(QualifierCacheService, property, Symbol.for('Qualifier.LoadUnitName')) === 'foo', ); assert( QualifierUtil.getProperQualifier(QualifierCacheService, property, Symbol.for('Qualifier.InitType')) === ObjectInitType.SINGLETON, ); }); it('constructor should work', () => { const constructorQualifiers = QualifierUtil.getProperQualifiers(ConstructorObject, 'xCache'); const constructorQualifiers2 = QualifierUtil.getProperQualifiers(ConstructorObject, 'cache'); assert.deepStrictEqual(constructorQualifiers, [ { attribute: Symbol.for('Qualifier.LoadUnitName'), value: 'foo' }, { attribute: Symbol.for('Qualifier.InitType'), value: ObjectInitType.SINGLETON }, ]); assert.deepStrictEqual(constructorQualifiers2, []); }); it('should set default initType in constructor inject', () => { const properties = [ { property: 'xCache', expected: undefined }, { property: 'cache', expected: ObjectInitType.SINGLETON }, { property: 'ContextCache', expected: ObjectInitType.CONTEXT }, { property: 'customNameCache', expected: undefined }, { property: 'customQualifierCache1', expected: ObjectInitType.CONTEXT }, { property: 'customQualifierCache2', expected: ObjectInitType.CONTEXT }, ]; for (const { property, expected } of properties) { const qualifier = QualifierUtil.getProperQualifier(ConstructorQualifierObject, property, InitTypeQualifierAttribute); assert.strictEqual(qualifier, expected, `expect initType for ${property} to be ${expected}`); } }); }); describe('MultiInstanceProto', () => { it('should work', () => { assert(PrototypeUtil.isEggMultiInstancePrototype(FooLogger)); const expectObjectProperty: EggMultiInstancePrototypeInfo = { initType: ObjectInitType.SINGLETON, accessLevel: AccessLevel.PUBLIC, protoImplType: 'foo', objects: [{ name: 'foo', qualifiers: [{ attribute: FOO_ATTRIBUTE, value: 'foo1', }], }, { name: 'foo', qualifiers: [{ attribute: FOO_ATTRIBUTE, value: 'foo2', }], }], className: 'FooLogger', }; assert.deepStrictEqual(PrototypeUtil.getMultiInstanceProperty(FooLogger, { unitPath: 'foo', moduleName: '', }), expectObjectProperty); }); }); it('should get the right file path', () => { assert(PrototypeUtil.getFilePath(CacheService) === CacheService.fileName); }); describe('inherited', () => { const fakeCtx = { unitPath: 'foo', moduleName: '', }; it('Prototype should not be inherited', () => { assert(PrototypeUtil.isEggPrototype(ParentSingletonProto)); assert(PrototypeUtil.getProperty(ParentSingletonProto)); assert(PrototypeUtil.getFilePath(ParentSingletonProto)); assert.strictEqual(PrototypeUtil.isEggPrototype(ChildSingletonProto), false); assert.strictEqual(PrototypeUtil.getProperty(ChildSingletonProto), undefined); assert.strictEqual(PrototypeUtil.getFilePath(ChildSingletonProto), undefined); }); it('static multiInstanceProto should not be inherited', () => { assert(PrototypeUtil.isEggMultiInstancePrototype(ParentStaticMultiInstanceProto)); assert.strictEqual( PrototypeUtil.getEggMultiInstancePrototypeType(ParentStaticMultiInstanceProto), MultiInstanceType.STATIC, ); assert(PrototypeUtil.getStaticMultiInstanceProperty(ParentStaticMultiInstanceProto)); assert(PrototypeUtil.getMultiInstanceProperty(ParentStaticMultiInstanceProto, fakeCtx)); assert(PrototypeUtil.getFilePath(ParentStaticMultiInstanceProto)); assert.strictEqual(PrototypeUtil.isEggMultiInstancePrototype(ChildStaticMultiInstanceProto), false); assert.strictEqual(PrototypeUtil.getEggMultiInstancePrototypeType(ChildStaticMultiInstanceProto), undefined); assert.strictEqual(PrototypeUtil.getStaticMultiInstanceProperty(ChildStaticMultiInstanceProto), undefined); assert.strictEqual(PrototypeUtil.getMultiInstanceProperty(ChildStaticMultiInstanceProto, fakeCtx), undefined); assert.strictEqual(PrototypeUtil.getFilePath(ChildStaticMultiInstanceProto), undefined); }); it('dynamic multipleInstanceProto should not be inherited', () => { assert.strictEqual( PrototypeUtil.getEggMultiInstancePrototypeType(ParentDynamicMultiInstanceProto), MultiInstanceType.DYNAMIC, ); assert(PrototypeUtil.getDynamicMultiInstanceProperty(ParentDynamicMultiInstanceProto, fakeCtx)); assert(PrototypeUtil.getMultiInstanceProperty(ParentDynamicMultiInstanceProto, fakeCtx)); assert.strictEqual(PrototypeUtil.getEggMultiInstancePrototypeType(ChildDynamicMultiInstanceProto), undefined); assert.strictEqual(PrototypeUtil.getDynamicMultiInstanceProperty(ChildDynamicMultiInstanceProto, fakeCtx), undefined); assert.strictEqual(PrototypeUtil.getMultiInstanceProperty(ChildDynamicMultiInstanceProto, fakeCtx), undefined); }); }); }); ================================================ FILE: core/core-decorator/test/fixtures/decators/CacheService.ts ================================================ import { ContextProto } from '../../../src/decorator/ContextProto'; import { Inject, InjectOptional } from '../../../src/decorator/Inject'; import { ICache } from './ICache'; import { TestService, TestService2 } from './OtherService'; @ContextProto() export class TestService3 { sayHi() { console.info('hi'); } } @ContextProto() export class TestService4 { sayHi() { console.info('hi'); } } @ContextProto() export default class CacheService { static fileName = __filename; @Inject({ name: 'fooCache', }) cache: ICache; @Inject('testService') testService: TestService; @Inject() testService2: TestService2; @Inject() otherService: TestService3; @Inject() testService4: any; @Inject({ optional: true }) optionalService1?: any; @InjectOptional() optionalService2?: any; } ================================================ FILE: core/core-decorator/test/fixtures/decators/ChildService.ts ================================================ import { MultiInstanceProto, SingletonProto } from '../../..'; @SingletonProto() export class ParentSingletonProto {} export class ChildSingletonProto extends ParentSingletonProto {} @MultiInstanceProto({ objects: [], }) export class ParentStaticMultiInstanceProto {} export class ChildStaticMultiInstanceProto extends ParentStaticMultiInstanceProto {} @MultiInstanceProto({ getObjects: () => [], }) export class ParentDynamicMultiInstanceProto {} export class ChildDynamicMultiInstanceProto extends ParentDynamicMultiInstanceProto {} ================================================ FILE: core/core-decorator/test/fixtures/decators/ConstructorObject.ts ================================================ import { ObjectInitType } from '@eggjs/tegg-types'; import { SingletonProto } from '../../../src/decorator/SingletonProto'; import { Inject, InjectOptional } from '../../../src/decorator/Inject'; import { InitTypeQualifier } from '../../../src/decorator/InitTypeQualifier'; import { ModuleQualifier } from '../../../src/decorator/ModuleQualifier'; import { ContextProto } from '../../../src/decorator/ContextProto'; import { ICache } from './ICache'; @SingletonProto() export class CacheService {} @ContextProto() export class CacheContextService {} @SingletonProto() export class ConstructorObject { constructor( @InitTypeQualifier(ObjectInitType.SINGLETON) @ModuleQualifier('foo') @Inject({ name: 'fooCache'}) readonly xCache: ICache, @Inject() readonly cache: ICache, @Inject() readonly otherCache: CacheService, @Inject({ optional: true }) readonly optional1?: ICache, @InjectOptional() readonly optional2?: ICache, ) {} } @SingletonProto() export class ConstructorQualifierObject { constructor( @Inject() readonly xCache: ICache, @Inject() readonly cache: CacheService, @Inject() readonly ContextCache: CacheContextService, @Inject('cacheService') readonly customNameCache: CacheService, @InitTypeQualifier(ObjectInitType.CONTEXT) @Inject() readonly customQualifierCache1: CacheService, @Inject() @InitTypeQualifier(ObjectInitType.CONTEXT) readonly customQualifierCache2: CacheService, ) {} } ================================================ FILE: core/core-decorator/test/fixtures/decators/ContextCache.ts ================================================ import { AccessLevel } from '@eggjs/tegg-types'; import { ContextProto } from '../../..'; import { ICache } from './ICache'; @ContextProto({ name: 'cache', accessLevel: AccessLevel.PUBLIC, }) export default class ContextCache implements ICache { } ================================================ FILE: core/core-decorator/test/fixtures/decators/FooLogger.ts ================================================ import { AccessLevel, ObjectInitType } from '@eggjs/tegg-types'; import { MultiInstanceProto } from '../../../src/decorator/MultiInstanceProto'; export const FOO_ATTRIBUTE = Symbol.for('FOO_ATTRIBUTE'); @MultiInstanceProto({ accessLevel: AccessLevel.PUBLIC, initType: ObjectInitType.SINGLETON, protoImplType: 'foo', objects: [{ name: 'foo', qualifiers: [{ attribute: FOO_ATTRIBUTE, value: 'foo1', }], }, { name: 'foo', qualifiers: [{ attribute: FOO_ATTRIBUTE, value: 'foo2', }], }], }) export class FooLogger { } ================================================ FILE: core/core-decorator/test/fixtures/decators/ICache.ts ================================================ export interface ICache { } ================================================ FILE: core/core-decorator/test/fixtures/decators/OtherService.ts ================================================ import { ContextProto } from '../../..'; @ContextProto() export class TestService { sayHi() { console.info('hi'); } } @ContextProto({ name: 'abcabc' }) export class TestService2 { sayHi() { console.info('hi'); } } ================================================ FILE: core/core-decorator/test/fixtures/decators/QualifierCacheService.ts ================================================ import { ObjectInitType } from '@eggjs/tegg-types'; import { ContextProto, InitTypeQualifier, Inject, ModuleQualifier, SingletonProto } from '../../..'; import { ICache } from './ICache'; @ContextProto() export class TestContextService {} @SingletonProto() export class TestSingletonService {} @ContextProto() export default class CacheService { @Inject({ name: 'fooCache', }) @InitTypeQualifier(ObjectInitType.SINGLETON) @ModuleQualifier('foo') cache: ICache; @Inject() interfaceService: ICache; @Inject() testContextService: TestContextService; @Inject() testSingletonService: TestSingletonService; @Inject('testSingletonService') customNameService: TestSingletonService; @InitTypeQualifier(ObjectInitType.CONTEXT) @Inject() customQualifierService1: TestSingletonService; @Inject() @InitTypeQualifier(ObjectInitType.CONTEXT) customQualifierService2: TestSingletonService; } ================================================ FILE: core/core-decorator/test/fixtures/decators/SingletonCache.ts ================================================ import { AccessLevel } from '@eggjs/tegg-types'; import { SingletonProto } from '../../..'; import { ICache } from './ICache'; @SingletonProto({ name: 'cache', accessLevel: AccessLevel.PUBLIC, }) export default class SingletonCache implements ICache { } ================================================ FILE: core/core-decorator/test/util/MetadataUtil.test.ts ================================================ import assert from 'node:assert'; import { MetadataUtil } from '../..'; class Parent { } class Child extends Parent { } describe('test/util/MetadataUtil.test.ts', () => { describe('initOwnArrayMetaData', () => { it('class extends should work', () => { const TEST_KEY = 'test_array_key'; const parentVal: string[] = MetadataUtil.initOwnArrayMetaData(TEST_KEY, Parent, []); parentVal.push('parent_data'); const childVal: string[] = MetadataUtil.initOwnArrayMetaData(TEST_KEY, Child, []); assert.deepStrictEqual(childVal, [ 'parent_data' ]); }); }); describe('initOwnMapMetaData', () => { it('class extends should work', () => { const TEST_KEY = 'test_map_key'; const parentVal: Map = MetadataUtil.initOwnMapMetaData(TEST_KEY, Parent, new Map()); parentVal.set('parent_data_key', 'parent_data_key'); const childVal: Map = MetadataUtil.initOwnMapMetaData(TEST_KEY, Child, new Map()); assert(childVal.get('parent_data_key') === 'parent_data_key'); }); }); }); ================================================ FILE: core/core-decorator/tsconfig.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "dist", "baseUrl": "./" }, "exclude": [ "dist", "node_modules", "test" ] } ================================================ FILE: core/core-decorator/tsconfig.pub.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "dist", "baseUrl": "./" }, "exclude": [ "dist", "node_modules", "test" ] } ================================================ FILE: core/dal-decorator/CHANGELOG.md ================================================ # Change Log All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. ## [3.78.15](https://github.com/eggjs/tegg/compare/v3.78.14...v3.78.15) (2026-04-23) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.78.14](https://github.com/eggjs/tegg/compare/v3.78.13...v3.78.14) (2026-04-21) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.78.13](https://github.com/eggjs/tegg/compare/v3.78.12...v3.78.13) (2026-04-21) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.78.12](https://github.com/eggjs/tegg/compare/v3.78.11...v3.78.12) (2026-04-18) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.78.11](https://github.com/eggjs/tegg/compare/v3.78.10...v3.78.11) (2026-04-18) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.78.10](https://github.com/eggjs/tegg/compare/v3.78.9...v3.78.10) (2026-04-18) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.78.9](https://github.com/eggjs/tegg/compare/v3.78.8...v3.78.9) (2026-04-18) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.78.8](https://github.com/eggjs/tegg/compare/v3.78.7...v3.78.8) (2026-04-18) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.78.7](https://github.com/eggjs/tegg/compare/v3.78.6...v3.78.7) (2026-04-18) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.78.6](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.6) (2026-04-18) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.78.5](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.5) (2026-04-18) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.78.4](https://github.com/eggjs/tegg/compare/v3.78.3...v3.78.4) (2026-04-18) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.78.3](https://github.com/eggjs/tegg/compare/v3.78.2...v3.78.3) (2026-04-15) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.78.2](https://github.com/eggjs/tegg/compare/v3.78.1...v3.78.2) (2026-04-09) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.78.1](https://github.com/eggjs/tegg/compare/v3.78.0...v3.78.1) (2026-04-09) **Note:** Version bump only for package @eggjs/dal-decorator # [3.78.0](https://github.com/eggjs/tegg/compare/v3.77.2...v3.78.0) (2026-04-09) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.77.2](https://github.com/eggjs/tegg/compare/v3.77.1...v3.77.2) (2026-04-07) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.77.1](https://github.com/eggjs/tegg/compare/v3.77.0...v3.77.1) (2026-04-01) **Note:** Version bump only for package @eggjs/dal-decorator # [3.77.0](https://github.com/eggjs/tegg/compare/v3.76.1...v3.77.0) (2026-04-01) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.76.1](https://github.com/eggjs/tegg/compare/v3.76.0...v3.76.1) (2026-04-01) **Note:** Version bump only for package @eggjs/dal-decorator # [3.76.0](https://github.com/eggjs/tegg/compare/v3.75.1...v3.76.0) (2026-04-01) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.75.1](https://github.com/eggjs/tegg/compare/v3.75.0...v3.75.1) (2026-04-01) **Note:** Version bump only for package @eggjs/dal-decorator # [3.75.0](https://github.com/eggjs/tegg/compare/v3.74.0...v3.75.0) (2026-03-30) **Note:** Version bump only for package @eggjs/dal-decorator # [3.74.0](https://github.com/eggjs/tegg/compare/v3.73.0...v3.74.0) (2026-03-30) **Note:** Version bump only for package @eggjs/dal-decorator # [3.73.0](https://github.com/eggjs/tegg/compare/v3.72.0...v3.73.0) (2026-03-25) **Note:** Version bump only for package @eggjs/dal-decorator # [3.72.0](https://github.com/eggjs/tegg/compare/v3.71.2...v3.72.0) (2026-02-04) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.71.2](https://github.com/eggjs/tegg/compare/v3.71.1...v3.71.2) (2026-01-30) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.71.1](https://github.com/eggjs/tegg/compare/v3.71.0...v3.71.1) (2026-01-28) **Note:** Version bump only for package @eggjs/dal-decorator # [3.71.0](https://github.com/eggjs/tegg/compare/v3.70.1...v3.71.0) (2026-01-26) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.70.1](https://github.com/eggjs/tegg/compare/v3.70.0...v3.70.1) (2026-01-14) **Note:** Version bump only for package @eggjs/dal-decorator # [3.70.0](https://github.com/eggjs/tegg/compare/v3.69.0...v3.70.0) (2026-01-07) **Note:** Version bump only for package @eggjs/dal-decorator # [3.69.0](https://github.com/eggjs/tegg/compare/v3.68.0...v3.69.0) (2026-01-06) **Note:** Version bump only for package @eggjs/dal-decorator # [3.68.0](https://github.com/eggjs/tegg/compare/v3.67.2...v3.68.0) (2025-12-29) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.67.2](https://github.com/eggjs/tegg/compare/v3.67.1...v3.67.2) (2025-12-24) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.67.1](https://github.com/eggjs/tegg/compare/v3.67.0...v3.67.1) (2025-12-23) **Note:** Version bump only for package @eggjs/dal-decorator # [3.67.0](https://github.com/eggjs/tegg/compare/v3.66.0...v3.67.0) (2025-12-18) **Note:** Version bump only for package @eggjs/dal-decorator # [3.66.0](https://github.com/eggjs/tegg/compare/v3.65.3...v3.66.0) (2025-12-16) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.65.3](https://github.com/eggjs/tegg/compare/v3.65.2...v3.65.3) (2025-12-16) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.65.2](https://github.com/eggjs/tegg/compare/v3.65.1...v3.65.2) (2025-12-16) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.65.1](https://github.com/eggjs/tegg/compare/v3.65.0...v3.65.1) (2025-12-16) **Note:** Version bump only for package @eggjs/dal-decorator # [3.65.0](https://github.com/eggjs/tegg/compare/v3.64.5...v3.65.0) (2025-12-10) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.64.5](https://github.com/eggjs/tegg/compare/v3.64.4...v3.64.5) (2025-12-08) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.64.4](https://github.com/eggjs/tegg/compare/v3.64.3...v3.64.4) (2025-12-06) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.64.3](https://github.com/eggjs/tegg/compare/v3.64.2...v3.64.3) (2025-12-06) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.64.2](https://github.com/eggjs/tegg/compare/v3.64.1...v3.64.2) (2025-11-27) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.64.1](https://github.com/eggjs/tegg/compare/v3.64.0...v3.64.1) (2025-11-27) **Note:** Version bump only for package @eggjs/dal-decorator # [3.64.0](https://github.com/eggjs/tegg/compare/v3.63.2...v3.64.0) (2025-11-27) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.63.2](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.2) (2025-11-18) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.63.1](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.1) (2025-11-18) **Note:** Version bump only for package @eggjs/dal-decorator # [3.63.0](https://github.com/eggjs/tegg/compare/v3.62.3...v3.63.0) (2025-11-18) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.62.3](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.3) (2025-11-05) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.62.2](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.2) (2025-11-05) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.62.1](https://github.com/eggjs/tegg/compare/v3.62.0...v3.62.1) (2025-11-03) **Note:** Version bump only for package @eggjs/dal-decorator # [3.62.0](https://github.com/eggjs/tegg/compare/v3.61.0...v3.62.0) (2025-09-24) **Note:** Version bump only for package @eggjs/dal-decorator # [3.61.0](https://github.com/eggjs/tegg/compare/v3.60.3...v3.61.0) (2025-08-15) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.60.3](https://github.com/eggjs/tegg/compare/v3.60.2...v3.60.3) (2025-08-06) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.60.2](https://github.com/eggjs/tegg/compare/v3.60.1...v3.60.2) (2025-08-06) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.60.1](https://github.com/eggjs/tegg/compare/v3.60.0...v3.60.1) (2025-07-28) **Note:** Version bump only for package @eggjs/dal-decorator # [3.60.0](https://github.com/eggjs/tegg/compare/v3.59.1...v3.60.0) (2025-07-28) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.59.1](https://github.com/eggjs/tegg/compare/v3.59.0...v3.59.1) (2025-07-16) **Note:** Version bump only for package @eggjs/dal-decorator # [3.59.0](https://github.com/eggjs/tegg/compare/v3.58.0...v3.59.0) (2025-07-07) **Note:** Version bump only for package @eggjs/dal-decorator # [3.58.0](https://github.com/eggjs/tegg/compare/v3.57.14...v3.58.0) (2025-07-01) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.57.14](https://github.com/eggjs/tegg/compare/v3.57.13...v3.57.14) (2025-06-18) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.57.13](https://github.com/eggjs/tegg/compare/v3.57.12...v3.57.13) (2025-06-18) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.57.12](https://github.com/eggjs/tegg/compare/v3.57.11...v3.57.12) (2025-06-16) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.57.11](https://github.com/eggjs/tegg/compare/v3.57.10...v3.57.11) (2025-06-16) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.57.10](https://github.com/eggjs/tegg/compare/v3.57.9...v3.57.10) (2025-06-12) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.57.9](https://github.com/eggjs/tegg/compare/v3.57.8...v3.57.9) (2025-05-29) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.57.8](https://github.com/eggjs/tegg/compare/v3.57.7...v3.57.8) (2025-05-29) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.57.7](https://github.com/eggjs/tegg/compare/v3.57.6...v3.57.7) (2025-05-28) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.57.6](https://github.com/eggjs/tegg/compare/v3.57.5...v3.57.6) (2025-05-27) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.57.5](https://github.com/eggjs/tegg/compare/v3.57.4...v3.57.5) (2025-05-15) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.57.4](https://github.com/eggjs/tegg/compare/v3.57.3...v3.57.4) (2025-05-14) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.57.3](https://github.com/eggjs/tegg/compare/v3.57.2...v3.57.3) (2025-05-13) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.57.2](https://github.com/eggjs/tegg/compare/v3.57.1...v3.57.2) (2025-05-13) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.57.1](https://github.com/eggjs/tegg/compare/v3.57.0...v3.57.1) (2025-05-13) **Note:** Version bump only for package @eggjs/dal-decorator # [3.57.0](https://github.com/eggjs/tegg/compare/v3.56.3...v3.57.0) (2025-05-09) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.56.3](https://github.com/eggjs/tegg/compare/v3.56.2...v3.56.3) (2025-05-07) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.56.2](https://github.com/eggjs/tegg/compare/v3.56.1...v3.56.2) (2025-05-01) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.56.1](https://github.com/eggjs/tegg/compare/v3.56.0...v3.56.1) (2025-04-29) **Note:** Version bump only for package @eggjs/dal-decorator # [3.56.0](https://github.com/eggjs/tegg/compare/v3.55.0...v3.56.0) (2025-04-29) **Note:** Version bump only for package @eggjs/dal-decorator # [3.55.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.55.0) (2025-04-29) **Note:** Version bump only for package @eggjs/dal-decorator # [3.54.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.54.0) (2025-04-29) **Note:** Version bump only for package @eggjs/dal-decorator # [3.53.0](https://github.com/eggjs/tegg/compare/v3.52.1...v3.53.0) (2025-04-21) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.52.1](https://github.com/eggjs/tegg/compare/v3.52.0...v3.52.1) (2025-03-14) **Note:** Version bump only for package @eggjs/dal-decorator # [3.52.0](https://github.com/eggjs/tegg/compare/v3.51.2...v3.52.0) (2024-12-30) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.51.2](https://github.com/eggjs/tegg/compare/v3.51.1...v3.51.2) (2024-12-09) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.51.1](https://github.com/eggjs/tegg/compare/v3.51.0...v3.51.1) (2024-11-06) **Note:** Version bump only for package @eggjs/dal-decorator # [3.51.0](https://github.com/eggjs/tegg/compare/v3.50.1...v3.51.0) (2024-10-30) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.50.1](https://github.com/eggjs/tegg/compare/v3.50.0...v3.50.1) (2024-10-23) **Note:** Version bump only for package @eggjs/dal-decorator # [3.50.0](https://github.com/eggjs/tegg/compare/v3.49.0...v3.50.0) (2024-10-22) **Note:** Version bump only for package @eggjs/dal-decorator # [3.49.0](https://github.com/eggjs/tegg/compare/v3.48.1...v3.49.0) (2024-10-21) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.48.1](https://github.com/eggjs/tegg/compare/v3.48.0...v3.48.1) (2024-10-14) **Note:** Version bump only for package @eggjs/dal-decorator # [3.48.0](https://github.com/eggjs/tegg/compare/v3.47.2...v3.48.0) (2024-10-10) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.47.2](https://github.com/eggjs/tegg/compare/v3.47.1...v3.47.2) (2024-10-10) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.47.1](https://github.com/eggjs/tegg/compare/v3.47.0...v3.47.1) (2024-10-10) **Note:** Version bump only for package @eggjs/dal-decorator # [3.47.0](https://github.com/eggjs/tegg/compare/v3.46.4...v3.47.0) (2024-10-10) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.46.4](https://github.com/eggjs/tegg/compare/v3.46.3...v3.46.4) (2024-10-09) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.46.3](https://github.com/eggjs/tegg/compare/v3.46.2...v3.46.3) (2024-10-08) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.46.2](https://github.com/eggjs/tegg/compare/v3.46.1...v3.46.2) (2024-10-07) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.46.1](https://github.com/eggjs/tegg/compare/v3.46.0...v3.46.1) (2024-09-30) **Note:** Version bump only for package @eggjs/dal-decorator # [3.46.0](https://github.com/eggjs/tegg/compare/v3.45.0...v3.46.0) (2024-09-29) **Note:** Version bump only for package @eggjs/dal-decorator # [3.45.0](https://github.com/eggjs/tegg/compare/v3.44.1...v3.45.0) (2024-09-29) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.44.1](https://github.com/eggjs/tegg/compare/v3.44.0...v3.44.1) (2024-09-29) ### Bug Fixes * fix DataSourceQualifier ([#238](https://github.com/eggjs/tegg/issues/238)) ([7b1ebe7](https://github.com/eggjs/tegg/commit/7b1ebe718736d93e548f531bf99c5d2d38b41046)) # [3.44.0](https://github.com/eggjs/tegg/compare/v3.43.2...v3.44.0) (2024-09-29) ### Features * support inject in constructor ([#237](https://github.com/eggjs/tegg/issues/237)) ([e68b1ed](https://github.com/eggjs/tegg/commit/e68b1ed6a90432f1cb35a6f562914b7b04cb5114)) ## [3.43.2](https://github.com/eggjs/tegg/compare/v3.43.1...v3.43.2) (2024-09-14) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.43.1](https://github.com/eggjs/tegg/compare/v3.43.0...v3.43.1) (2024-09-14) **Note:** Version bump only for package @eggjs/dal-decorator # [3.43.0](https://github.com/eggjs/tegg/compare/v3.42.0...v3.43.0) (2024-09-13) **Note:** Version bump only for package @eggjs/dal-decorator # [3.42.0](https://github.com/eggjs/tegg/compare/v3.41.0...v3.42.0) (2024-09-10) **Note:** Version bump only for package @eggjs/dal-decorator # [3.41.0](https://github.com/eggjs/tegg/compare/v3.40.1...v3.41.0) (2024-08-26) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.40.1](https://github.com/eggjs/tegg/compare/v3.40.0...v3.40.1) (2024-08-23) **Note:** Version bump only for package @eggjs/dal-decorator # [3.40.0](https://github.com/eggjs/tegg/compare/v3.39.5...v3.40.0) (2024-08-22) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.39.5](https://github.com/eggjs/tegg/compare/v3.39.4...v3.39.5) (2024-08-09) ### Bug Fixes * generate index name with column name ([#230](https://github.com/eggjs/tegg/issues/230)) ([82ec72d](https://github.com/eggjs/tegg/commit/82ec72d4fb8628c847b32d0ddf23a95119ca6ccf)) ## [3.39.4](https://github.com/eggjs/tegg/compare/v3.39.3...v3.39.4) (2024-07-09) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.39.3](https://github.com/eggjs/tegg/compare/v3.39.2...v3.39.3) (2024-04-28) ### Bug Fixes * mount clazzExtension/clazzExtension/tableSql to BaseDao ([#220](https://github.com/eggjs/tegg/issues/220)) ([ac322cf](https://github.com/eggjs/tegg/commit/ac322cfc4100841a1483b04b99e04d553af323eb)) ## [3.39.2](https://github.com/eggjs/tegg/compare/v3.39.1...v3.39.2) (2024-04-28) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.39.1](https://github.com/eggjs/tegg/compare/v3.39.0...v3.39.1) (2024-04-28) **Note:** Version bump only for package @eggjs/dal-decorator # [3.39.0](https://github.com/eggjs/tegg/compare/v3.38.0...v3.39.0) (2024-04-19) **Note:** Version bump only for package @eggjs/dal-decorator # [3.38.0](https://github.com/eggjs/tegg/compare/v3.37.3...v3.38.0) (2024-04-18) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.37.3](https://github.com/eggjs/tegg/compare/v3.37.2...v3.37.3) (2024-04-17) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.37.2](https://github.com/eggjs/tegg/compare/v3.37.1...v3.37.2) (2024-04-17) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.37.1](https://github.com/eggjs/tegg/compare/v3.37.0...v3.37.1) (2024-04-16) **Note:** Version bump only for package @eggjs/dal-decorator # [3.37.0](https://github.com/eggjs/tegg/compare/v3.36.3...v3.37.0) (2024-04-16) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.36.3](https://github.com/eggjs/tegg/compare/v3.36.2...v3.36.3) (2024-04-10) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.36.2](https://github.com/eggjs/tegg/compare/v3.36.1...v3.36.2) (2024-04-08) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.36.1](https://github.com/eggjs/tegg/compare/v3.36.0...v3.36.1) (2024-04-07) **Note:** Version bump only for package @eggjs/dal-decorator # [3.36.0](https://github.com/eggjs/tegg/compare/v3.35.1...v3.36.0) (2024-04-02) ### Features * impl Date/timestamp on update ([#203](https://github.com/eggjs/tegg/issues/203)) ([e5c7b8d](https://github.com/eggjs/tegg/commit/e5c7b8d529f2854b77de2e99369c781a4ea9e070)) ## [3.35.1](https://github.com/eggjs/tegg/compare/v3.35.0...v3.35.1) (2024-03-26) **Note:** Version bump only for package @eggjs/dal-decorator # [3.35.0](https://github.com/eggjs/tegg/compare/v3.34.0...v3.35.0) (2024-03-26) ### Features * dal-runtime templates support pkg alias ([#198](https://github.com/eggjs/tegg/issues/198)) ([cecef78](https://github.com/eggjs/tegg/commit/cecef781bd134b629fc835063a351460aceb340c)) # [3.34.0](https://github.com/eggjs/tegg/compare/v3.33.1...v3.34.0) (2024-03-22) **Note:** Version bump only for package @eggjs/dal-decorator ## [3.33.1](https://github.com/eggjs/tegg/compare/v3.33.0...v3.33.1) (2024-03-22) **Note:** Version bump only for package @eggjs/dal-decorator # [3.33.0](https://github.com/eggjs/tegg/compare/v3.32.0...v3.33.0) (2024-03-22) ### Bug Fixes * set column canNull default to false ([#195](https://github.com/eggjs/tegg/issues/195)) ([24628ec](https://github.com/eggjs/tegg/commit/24628ec5a3cd167dc44a50017450d0dedec2c9ce)) ### Features * impl dal ([#192](https://github.com/eggjs/tegg/issues/192)) ([1c7d145](https://github.com/eggjs/tegg/commit/1c7d1454bc8c600cd58c3ec7b9cda4e8a98c7287)) ================================================ FILE: core/dal-decorator/README.md ================================================ # `@eggjs/dal-decorator` ## Usage Please read [@eggjs/tegg-dal-plugin](../../plugin/dal-plugin) ================================================ FILE: core/dal-decorator/index.ts ================================================ export * from '@eggjs/tegg-types/dal'; export * from './src/decorator/Index'; export * from './src/decorator/Table'; export * from './src/decorator/Column'; export * from './src/decorator/DataSourceQualifier'; export * from './src/decorator/Dao'; export * from './src/util/ColumnInfoUtil'; export * from './src/util/IndexInfoUtil'; export * from './src/util/TableInfoUtil'; export * from './src/util/DaoInfoUtil'; export * from './src/model/ColumnModel'; export * from './src/model/IndexModel'; export * from './src/model/TableModel'; export * from './src/type/Spatial'; export * from './src/type/MySql'; ================================================ FILE: core/dal-decorator/package.json ================================================ { "name": "@eggjs/dal-decorator", "version": "3.78.15", "description": "tegg dal decorator", "keywords": [ "egg", "typescript", "decorator", "tegg", "dal" ], "main": "dist/index.js", "files": [ "dist/**/*.js", "dist/**/*.d.ts" ], "typings": "dist/index.d.ts", "scripts": { "test": "cross-env NODE_ENV=test NODE_OPTIONS='--no-deprecation' mocha", "clean": "tsc -b --clean", "tsc": "ut run clean && tsc -p ./tsconfig.json", "tsc:pub": "ut run clean && tsc -p ./tsconfig.pub.json", "prepublishOnly": "ut tsc:pub" }, "author": "killagu ", "license": "MIT", "homepage": "https://github.com/eggjs/tegg", "bugs": { "url": "https://github.com/eggjs/tegg/issues" }, "repository": { "type": "git", "url": "git@github.com:eggjs/tegg.git", "directory": "core/dal-decorator" }, "engines": { "node": ">=14.0.0" }, "dependencies": { "@eggjs/core-decorator": "^3.78.15", "@eggjs/tegg-common-util": "^3.78.15", "@eggjs/tegg-types": "^3.78.15", "lodash.snakecase": "^4.1.1", "pluralize": "^8.0.0" }, "publishConfig": { "access": "public" }, "devDependencies": { "@types/mocha": "^10.0.1", "@types/node": "^20.2.4", "cross-env": "^7.0.3", "mocha": "^10.2.0", "ts-node": "^10.9.1", "typescript": "^5.0.4" }, "gitHead": "240a3e9f40fda65ebb7589727d197db5ce17916c" } ================================================ FILE: core/dal-decorator/src/decorator/Column.ts ================================================ import assert from 'node:assert'; import type { ColumnParams, ColumnTypeParams, EggProtoImplClass } from '@eggjs/tegg-types'; import { ColumnInfoUtil } from '../util/ColumnInfoUtil'; export function Column(type: ColumnTypeParams, params?: ColumnParams) { return function(target: any, propertyKey: PropertyKey) { assert(typeof propertyKey === 'string', `[Column/${target.name}] expect column name be typeof string, but now is ${String(propertyKey)}`); const tableClazz = target.constructor as EggProtoImplClass; const columnName = propertyKey as string; ColumnInfoUtil.addColumnType(tableClazz, columnName, type); if (params) { ColumnInfoUtil.addColumnInfo(tableClazz, columnName, params); } }; } ================================================ FILE: core/dal-decorator/src/decorator/Dao.ts ================================================ import { Prototype, PrototypeUtil } from '@eggjs/core-decorator'; import { StackUtil } from '@eggjs/tegg-common-util'; import { AccessLevel, ObjectInitType } from '@eggjs/tegg-types'; import type { EggProtoImplClass } from '@eggjs/tegg-types'; import { DaoInfoUtil } from '../util/DaoInfoUtil'; export function Dao() { return function(constructor: EggProtoImplClass) { DaoInfoUtil.setIsDao(constructor); const func = Prototype({ accessLevel: AccessLevel.PUBLIC, initType: ObjectInitType.SINGLETON, }); func(constructor); PrototypeUtil.setFilePath(constructor, StackUtil.getCalleeFromStack(false, 5)); }; } ================================================ FILE: core/dal-decorator/src/decorator/DataSourceQualifier.ts ================================================ import { DataSourceQualifierAttribute } from '@eggjs/tegg-types'; import { QualifierUtil } from '@eggjs/core-decorator'; export function DataSourceQualifier(dataSourceName: string) { return function(target: any, propertyKey: PropertyKey, parameterIndex?: number) { QualifierUtil.addInjectQualifier(target, propertyKey, parameterIndex, DataSourceQualifierAttribute, dataSourceName); }; } ================================================ FILE: core/dal-decorator/src/decorator/Index.ts ================================================ import type { EggProtoImplClass, IndexParams } from '@eggjs/tegg-types'; import { IndexInfoUtil } from '../util/IndexInfoUtil'; export function Index(params: IndexParams) { return function(constructor: EggProtoImplClass) { IndexInfoUtil.addIndex(constructor, params); }; } ================================================ FILE: core/dal-decorator/src/decorator/Table.ts ================================================ import { Prototype, PrototypeUtil } from '@eggjs/core-decorator'; import { StackUtil } from '@eggjs/tegg-common-util'; import { AccessLevel, ObjectInitType } from '@eggjs/tegg-types'; import type { EggProtoImplClass, TableParams } from '@eggjs/tegg-types'; import { TableInfoUtil } from '../util/TableInfoUtil'; export function Table(params?: TableParams) { return function(constructor: EggProtoImplClass) { TableInfoUtil.setIsTable(constructor); if (params) { TableInfoUtil.setTableParams(constructor, params); } const func = Prototype({ accessLevel: AccessLevel.PUBLIC, initType: ObjectInitType.ALWAYS_NEW, }); func(constructor); PrototypeUtil.setFilePath(constructor, StackUtil.getCalleeFromStack(false, 5)); }; } ================================================ FILE: core/dal-decorator/src/model/ColumnModel.ts ================================================ import snakecase from 'lodash.snakecase'; import type { ColumnFormat, ColumnParams, ColumnTypeParams } from '@eggjs/tegg-types'; export class ColumnModel { columnName: string; propertyName: string; type: ColumnTypeParams; canNull: boolean; default?: string; comment?: string; visible?: boolean; autoIncrement?: boolean; uniqueKey?: boolean; primaryKey?: boolean; collate?: string; columnFormat?: ColumnFormat; engineAttribute?: string; secondaryEngineAttribute?: string; constructor(params: { columnName: string; propertyName: string; type: ColumnTypeParams; canNull: boolean; default?: string; comment?: string; visible?: boolean; autoIncrement?: boolean; uniqueKey?: boolean; primaryKey?: boolean; collate?: string; columnFormat?: ColumnFormat; engineAttribute?: string; secondaryEngineAttribute?: string; }) { this.columnName = params.columnName; this.propertyName = params.propertyName; this.type = params.type; this.canNull = params.canNull; this.default = params.default; this.comment = params.comment; this.visible = params.visible; this.autoIncrement = params.autoIncrement; this.uniqueKey = params.uniqueKey; this.primaryKey = params.primaryKey; this.collate = params.collate; this.columnFormat = params.columnFormat; this.engineAttribute = params.engineAttribute; this.secondaryEngineAttribute = params.secondaryEngineAttribute; } static build(property: string, type: ColumnTypeParams, params?: ColumnParams) { const columnName = params?.name ?? snakecase(property); let canNull = params?.canNull ?? false; if (params?.primaryKey) { canNull = false; } return new ColumnModel({ columnName, propertyName: property, type, canNull, default: params?.default, comment: params?.comment, visible: params?.visible, autoIncrement: params?.autoIncrement, uniqueKey: params?.uniqueKey, primaryKey: params?.primaryKey, collate: params?.collate, columnFormat: params?.columnFormat, engineAttribute: params?.engineAttribute, secondaryEngineAttribute: params?.secondaryEngineAttribute, }); } } ================================================ FILE: core/dal-decorator/src/model/IndexModel.ts ================================================ import { EggProtoImplClass, IndexType } from '@eggjs/tegg-types'; import type { IndexParams, IndexStoreType } from '@eggjs/tegg-types'; import { ColumnModel } from './ColumnModel'; export interface IndexKey { columnName: string; propertyName: string; } export class IndexModel { name: string; keys: IndexKey[]; type: IndexType; storeType?: IndexStoreType; comment?: string; engineAttribute?: string; secondaryEngineAttribute?: string; parser?: string; constructor(params: { name: string; keys: IndexKey[]; type: IndexType; storeType?: IndexStoreType; comment?: string; engineAttribute?: string; secondaryEngineAttribute?: string; parser?: string; }) { this.name = params.name; this.keys = params.keys; this.type = params.type; this.storeType = params.storeType; this.comment = params.comment; this.engineAttribute = params.engineAttribute; this.secondaryEngineAttribute = params.secondaryEngineAttribute; this.parser = params.parser; } static buildIndexName(keys: string[], type: IndexType) { const prefix = type === IndexType.UNIQUE ? 'uk_' : 'idx_'; return prefix + keys.join('_'); } static build(params: IndexParams, columns: ColumnModel[], clazz: EggProtoImplClass) { const type = params.type ?? IndexType.INDEX; const keys: Array = params.keys.map(t => { const column = columns.find(c => c.propertyName === t); if (!column) { throw new Error(`Table ${clazz.name} index configuration error: has no property named "${t}"`); } return { propertyName: column!.propertyName, columnName: column!.columnName, }; }); const name = params.name ?? IndexModel.buildIndexName(keys.map(t => t.columnName), type); return new IndexModel({ name, keys, type, storeType: params.storeType, comment: params.comment, engineAttribute: params.engineAttribute, secondaryEngineAttribute: params.secondaryEngineAttribute, parser: params.parser, }); } } ================================================ FILE: core/dal-decorator/src/model/TableModel.ts ================================================ import assert from 'node:assert'; import pluralize from 'pluralize'; import snakecase from 'lodash.snakecase'; import { IndexType } from '@eggjs/tegg-types'; import type { CompressionType, EggProtoImplClass, InsertMethod, RowFormat } from '@eggjs/tegg-types'; import { ColumnModel } from './ColumnModel'; import { IndexModel } from './IndexModel'; import { TableInfoUtil } from '../util/TableInfoUtil'; import { ColumnInfoUtil } from '../util/ColumnInfoUtil'; import { IndexInfoUtil } from '../util/IndexInfoUtil'; export class TableModel { clazz: EggProtoImplClass; name: string; columns: Array; indices: Array; dataSourceName: string; comment?: string; autoExtendSize?: number; autoIncrement?: number; avgRowLength?: number; characterSet?: string; collate?: string; compression?: CompressionType; encryption?: boolean; engine?: string; engineAttribute?: string; insertMethod?: InsertMethod; keyBlockSize?: number; maxRows?: number; minRows?: number; rowFormat?: RowFormat; secondaryEngineAttribute?: string; constructor(params: { clazz: EggProtoImplClass; name: string; dataSourceName: string; columns: Array; indices: Array; comment?: string; autoExtendSize?: number; autoIncrement?: number; avgRowLength?: number; characterSet?: string; collate?: string; compression?: CompressionType; encryption?: boolean; engine?: string; engineAttribute?: string; insertMethod?: InsertMethod; keyBlockSize?: number; maxRows?: number; minRows?: number; rowFormat?: RowFormat; secondaryEngineAttribute?: string; }) { this.clazz = params.clazz; this.name = params.name; this.dataSourceName = params.dataSourceName; this.columns = params.columns; this.indices = params.indices; this.comment = params.comment; this.autoExtendSize = params.autoExtendSize; this.autoIncrement = params.autoIncrement; this.avgRowLength = params.avgRowLength; this.characterSet = params.characterSet; this.collate = params.collate; this.compression = params.compression; this.encryption = params.encryption; this.engine = params.engine; this.engineAttribute = params.engineAttribute; this.insertMethod = params.insertMethod; this.keyBlockSize = params.keyBlockSize; this.maxRows = params.maxRows; this.minRows = params.minRows; this.rowFormat = params.rowFormat; this.secondaryEngineAttribute = params.secondaryEngineAttribute; } getPrimary(): IndexModel | undefined { const index = this.indices.find(t => t.type === IndexType.PRIMARY); if (index) { return index; } const primaryColumn = this.columns.filter(t => t.primaryKey === true); return new IndexModel({ name: 'PRIMARY', type: IndexType.PRIMARY, keys: primaryColumn.map(t => { return { columnName: t.columnName, propertyName: t.propertyName, }; }), }); } static build(clazz: EggProtoImplClass): TableModel { const params = TableInfoUtil.getTableParams(clazz as EggProtoImplClass); const name = params?.name ?? snakecase(pluralize(clazz.name)); const columnInfoMap = ColumnInfoUtil.getColumnInfoMap(clazz as EggProtoImplClass); const columnTypeMap = ColumnInfoUtil.getColumnTypeMap(clazz as EggProtoImplClass); const dataSourceName = params?.dataSourceName ?? 'default'; assert(TableInfoUtil.getIsTable(clazz as EggProtoImplClass), `${name} is not Table`); assert(columnTypeMap, `${name} has no columns`); const columns: Array = []; const indices: Array = []; for (const [ property, columnType ] of columnTypeMap?.entries()) { const columnParam = columnInfoMap?.get(property); columns.push(ColumnModel.build(property, columnType, columnParam)); } const indexList = IndexInfoUtil.getIndexList(clazz as EggProtoImplClass); for (const index of indexList) { indices.push(IndexModel.build(index, columns, clazz)); } return new TableModel({ clazz, name, columns, indices, dataSourceName, comment: params?.comment, autoExtendSize: params?.autoExtendSize, autoIncrement: params?.autoIncrement, avgRowLength: params?.avgRowLength, characterSet: params?.characterSet, collate: params?.collate, compression: params?.compression, encryption: params?.encryption, engine: params?.engine, engineAttribute: params?.engineAttribute, insertMethod: params?.insertMethod, keyBlockSize: params?.keyBlockSize, maxRows: params?.maxRows, minRows: params?.minRows, rowFormat: params?.rowFormat, secondaryEngineAttribute: params?.secondaryEngineAttribute, }); } } ================================================ FILE: core/dal-decorator/src/type/MySql.ts ================================================ export { InsertResult, UpdateResult, DeleteResult } from '@eggjs/rds'; ================================================ FILE: core/dal-decorator/src/type/Spatial.ts ================================================ import { ColumnType } from '@eggjs/tegg-types'; import type { Geometry, GeometryCollection } from '@eggjs/tegg-types'; export class SpatialHelper { static isPoint(t: Geometry) { return typeof Reflect.get(t, 'x') === 'number' && typeof Reflect.get(t, 'y') === 'number'; } static isLine(t: Geometry) { return Array.isArray(t) && t[0] && SpatialHelper.isPoint(t[0]); } static isPolygon(t: Geometry) { return Array.isArray(t) && t[0] && SpatialHelper.isLine(t[0]); } static getGeometryType(t: Geometry) { if (SpatialHelper.isPoint(t)) { return ColumnType.POINT; } else if (SpatialHelper.isLine(t)) { return ColumnType.LINESTRING; } return ColumnType.POLYGON; } static isMultiPoint(t: GeometryCollection) { return Array.isArray(t) && t[0] && SpatialHelper.isPoint(t[0]); } static isMultiLine(t: GeometryCollection) { return Array.isArray(t) && t[0] && SpatialHelper.isLine(t[0]); } static isMultiPolygon(t: GeometryCollection) { return Array.isArray(t) && t[0] && SpatialHelper.isPolygon(t[0]); } } ================================================ FILE: core/dal-decorator/src/util/ColumnInfoUtil.ts ================================================ import { DAL_COLUMN_INFO_MAP, DAL_COLUMN_TYPE_MAP } from '@eggjs/tegg-types'; import type { ColumnParams, ColumnTypeParams, EggProtoImplClass } from '@eggjs/tegg-types'; import { MetadataUtil } from '@eggjs/core-decorator'; export type ColumnInfoMap = Map; export type ColumnTypeMap = Map; export class ColumnInfoUtil { static addColumnInfo(clazz: EggProtoImplClass, property: string, column: ColumnInfoUtil) { const columnInfoMap = MetadataUtil.initOwnMapMetaData(DAL_COLUMN_INFO_MAP, clazz, new Map()); columnInfoMap.set(property, column); } static addColumnType(clazz: EggProtoImplClass, property: string, type: ColumnTypeParams) { const columnInfoMap = MetadataUtil.initOwnMapMetaData(DAL_COLUMN_TYPE_MAP, clazz, new Map()); columnInfoMap.set(property, type); } static getColumnInfoMap(clazz: EggProtoImplClass): ColumnInfoMap | undefined { return MetadataUtil.getMetaData(DAL_COLUMN_INFO_MAP, clazz); } static getColumnTypeMap(clazz: EggProtoImplClass): ColumnTypeMap | undefined { return MetadataUtil.getMetaData(DAL_COLUMN_TYPE_MAP, clazz); } } ================================================ FILE: core/dal-decorator/src/util/DaoInfoUtil.ts ================================================ import { BaseDaoType, DAL_IS_DAO } from '@eggjs/tegg-types'; import type { EggProtoImplClass } from '@eggjs/tegg-types'; import { MetadataUtil } from '@eggjs/core-decorator'; export class DaoInfoUtil { static setIsDao(clazz: EggProtoImplClass) { MetadataUtil.defineMetaData(DAL_IS_DAO, true, clazz); } static getIsDao(clazz: EggProtoImplClass): clazz is BaseDaoType { return MetadataUtil.getOwnMetaData(DAL_IS_DAO, clazz) === true; } } ================================================ FILE: core/dal-decorator/src/util/IndexInfoUtil.ts ================================================ import { DAL_INDEX_LIST } from '@eggjs/tegg-types'; import type { EggProtoImplClass, IndexParams } from '@eggjs/tegg-types'; import { MetadataUtil } from '@eggjs/core-decorator'; export class IndexInfoUtil { static addIndex(clazz: EggProtoImplClass, index: IndexParams) { const indexList: Array = MetadataUtil.initOwnArrayMetaData(DAL_INDEX_LIST, clazz, []); indexList.push(index); } static getIndexList(clazz: EggProtoImplClass): Array { return MetadataUtil.getMetaData(DAL_INDEX_LIST, clazz) || []; } } ================================================ FILE: core/dal-decorator/src/util/TableInfoUtil.ts ================================================ import { DAL_IS_TABLE, DAL_TABLE_PARAMS } from '@eggjs/tegg-types'; import type { EggProtoImplClass, TableParams } from '@eggjs/tegg-types'; import { MetadataUtil } from '@eggjs/core-decorator'; export const TABLE_CLAZZ_LIST: Array = []; export class TableInfoUtil { static setIsTable(clazz: EggProtoImplClass) { TABLE_CLAZZ_LIST.push(clazz); MetadataUtil.defineMetaData(DAL_IS_TABLE, true, clazz); } // TODO del static getClazzList() { return TABLE_CLAZZ_LIST; } static getIsTable(clazz: EggProtoImplClass) { return MetadataUtil.getMetaData(DAL_IS_TABLE, clazz) === true; } static setTableParams(clazz: EggProtoImplClass, params: TableParams) { MetadataUtil.defineMetaData(DAL_TABLE_PARAMS, params, clazz); } static getTableParams(clazz: EggProtoImplClass): TableParams | undefined { return MetadataUtil.getMetaData(DAL_TABLE_PARAMS, clazz); } } ================================================ FILE: core/dal-decorator/test/fixtures/modules/dal/Foo.ts ================================================ import { ColumnType, IndexType } from '@eggjs/tegg-types'; import { Table, Index, Column } from '../../../..'; @Table({ comment: 'foo table', }) @Index({ keys: [ 'name' ], type: IndexType.UNIQUE, }) export class Foo { @Column({ type: ColumnType.INT, }, { primaryKey: true, }) id: number; @Column({ type: ColumnType.VARCHAR, length: 100, }) name: string; } ================================================ FILE: core/dal-decorator/test/fixtures/modules/dal/package.json ================================================ { "name": "dal", "eggModule": { "name": "dal" } } ================================================ FILE: core/dal-decorator/test/index.test.ts ================================================ import assert from 'node:assert'; import { Foo } from './fixtures/modules/dal/Foo'; import { ColumnType, IndexType } from '@eggjs/tegg-types'; import { ColumnInfoUtil, IndexInfoUtil, TableInfoUtil } from '..'; import { TableModel } from '../src/model/TableModel'; describe('test/dal/index.test.ts', () => { it('decorator should work', () => { const columnInfoMap = ColumnInfoUtil.getColumnInfoMap(Foo); const columnTypeMap = ColumnInfoUtil.getColumnTypeMap(Foo); const indexList = IndexInfoUtil.getIndexList(Foo); const tableInfo = TableInfoUtil.getTableParams(Foo); const isTable = TableInfoUtil.getIsTable(Foo); assert.deepStrictEqual(columnInfoMap, new Map([ [ 'id', { primaryKey: true, }, ], ])); assert.deepStrictEqual(columnTypeMap, new Map([ [ 'id', { type: ColumnType.INT, }, ], [ 'name', { type: ColumnType.VARCHAR, length: 100, }, ], ])); assert.deepStrictEqual(indexList, [{ keys: [ 'name' ], type: IndexType.UNIQUE, }]); assert.deepStrictEqual(tableInfo, { comment: 'foo table', }); assert.equal(isTable, true); }); it('model should work', () => { const table = TableModel.build(Foo); assert(table); assert(table.name === 'foos'); assert.equal(table.columns.length, 2); assert.equal(table.indices.length, 1); }); }); ================================================ FILE: core/dal-decorator/tsconfig.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "dist", "baseUrl": "./" }, "exclude": [ "dist", "node_modules", "test" ] } ================================================ FILE: core/dal-decorator/tsconfig.pub.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "dist", "baseUrl": "./" }, "exclude": [ "dist", "node_modules", "test" ] } ================================================ FILE: core/dal-runtime/CHANGELOG.md ================================================ # Change Log All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. ## [3.78.15](https://github.com/eggjs/tegg/compare/v3.78.14...v3.78.15) (2026-04-23) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.78.14](https://github.com/eggjs/tegg/compare/v3.78.13...v3.78.14) (2026-04-21) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.78.13](https://github.com/eggjs/tegg/compare/v3.78.12...v3.78.13) (2026-04-21) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.78.12](https://github.com/eggjs/tegg/compare/v3.78.11...v3.78.12) (2026-04-18) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.78.11](https://github.com/eggjs/tegg/compare/v3.78.10...v3.78.11) (2026-04-18) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.78.10](https://github.com/eggjs/tegg/compare/v3.78.9...v3.78.10) (2026-04-18) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.78.9](https://github.com/eggjs/tegg/compare/v3.78.8...v3.78.9) (2026-04-18) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.78.8](https://github.com/eggjs/tegg/compare/v3.78.7...v3.78.8) (2026-04-18) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.78.7](https://github.com/eggjs/tegg/compare/v3.78.6...v3.78.7) (2026-04-18) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.78.6](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.6) (2026-04-18) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.78.5](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.5) (2026-04-18) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.78.4](https://github.com/eggjs/tegg/compare/v3.78.3...v3.78.4) (2026-04-18) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.78.3](https://github.com/eggjs/tegg/compare/v3.78.2...v3.78.3) (2026-04-15) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.78.2](https://github.com/eggjs/tegg/compare/v3.78.1...v3.78.2) (2026-04-09) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.78.1](https://github.com/eggjs/tegg/compare/v3.78.0...v3.78.1) (2026-04-09) **Note:** Version bump only for package @eggjs/dal-runtime # [3.78.0](https://github.com/eggjs/tegg/compare/v3.77.2...v3.78.0) (2026-04-09) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.77.2](https://github.com/eggjs/tegg/compare/v3.77.1...v3.77.2) (2026-04-07) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.77.1](https://github.com/eggjs/tegg/compare/v3.77.0...v3.77.1) (2026-04-01) **Note:** Version bump only for package @eggjs/dal-runtime # [3.77.0](https://github.com/eggjs/tegg/compare/v3.76.1...v3.77.0) (2026-04-01) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.76.1](https://github.com/eggjs/tegg/compare/v3.76.0...v3.76.1) (2026-04-01) **Note:** Version bump only for package @eggjs/dal-runtime # [3.76.0](https://github.com/eggjs/tegg/compare/v3.75.1...v3.76.0) (2026-04-01) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.75.1](https://github.com/eggjs/tegg/compare/v3.75.0...v3.75.1) (2026-04-01) **Note:** Version bump only for package @eggjs/dal-runtime # [3.75.0](https://github.com/eggjs/tegg/compare/v3.74.0...v3.75.0) (2026-03-30) **Note:** Version bump only for package @eggjs/dal-runtime # [3.74.0](https://github.com/eggjs/tegg/compare/v3.73.0...v3.74.0) (2026-03-30) **Note:** Version bump only for package @eggjs/dal-runtime # [3.73.0](https://github.com/eggjs/tegg/compare/v3.72.0...v3.73.0) (2026-03-25) **Note:** Version bump only for package @eggjs/dal-runtime # [3.72.0](https://github.com/eggjs/tegg/compare/v3.71.2...v3.72.0) (2026-02-04) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.71.2](https://github.com/eggjs/tegg/compare/v3.71.1...v3.71.2) (2026-01-30) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.71.1](https://github.com/eggjs/tegg/compare/v3.71.0...v3.71.1) (2026-01-28) **Note:** Version bump only for package @eggjs/dal-runtime # [3.71.0](https://github.com/eggjs/tegg/compare/v3.70.1...v3.71.0) (2026-01-26) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.70.1](https://github.com/eggjs/tegg/compare/v3.70.0...v3.70.1) (2026-01-14) **Note:** Version bump only for package @eggjs/dal-runtime # [3.70.0](https://github.com/eggjs/tegg/compare/v3.69.0...v3.70.0) (2026-01-07) ### Features * set default retry time to 3 for dal init ([#390](https://github.com/eggjs/tegg/issues/390)) ([afde48c](https://github.com/eggjs/tegg/commit/afde48c39990f5b000520cb1ba3ba1a336222ce2)) # [3.69.0](https://github.com/eggjs/tegg/compare/v3.68.0...v3.69.0) (2026-01-06) **Note:** Version bump only for package @eggjs/dal-runtime # [3.68.0](https://github.com/eggjs/tegg/compare/v3.67.2...v3.68.0) (2025-12-29) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.67.2](https://github.com/eggjs/tegg/compare/v3.67.1...v3.67.2) (2025-12-24) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.67.1](https://github.com/eggjs/tegg/compare/v3.67.0...v3.67.1) (2025-12-23) **Note:** Version bump only for package @eggjs/dal-runtime # [3.67.0](https://github.com/eggjs/tegg/compare/v3.66.0...v3.67.0) (2025-12-18) **Note:** Version bump only for package @eggjs/dal-runtime # [3.66.0](https://github.com/eggjs/tegg/compare/v3.65.3...v3.66.0) (2025-12-16) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.65.3](https://github.com/eggjs/tegg/compare/v3.65.2...v3.65.3) (2025-12-16) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.65.2](https://github.com/eggjs/tegg/compare/v3.65.1...v3.65.2) (2025-12-16) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.65.1](https://github.com/eggjs/tegg/compare/v3.65.0...v3.65.1) (2025-12-16) **Note:** Version bump only for package @eggjs/dal-runtime # [3.65.0](https://github.com/eggjs/tegg/compare/v3.64.5...v3.65.0) (2025-12-10) ### Features * add parameterized query ([#366](https://github.com/eggjs/tegg/issues/366)) ([6d7d8d8](https://github.com/eggjs/tegg/commit/6d7d8d8383f4eea574d13e87ee03c57a33a319e7)) ## [3.64.5](https://github.com/eggjs/tegg/compare/v3.64.4...v3.64.5) (2025-12-08) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.64.4](https://github.com/eggjs/tegg/compare/v3.64.3...v3.64.4) (2025-12-06) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.64.3](https://github.com/eggjs/tegg/compare/v3.64.2...v3.64.3) (2025-12-06) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.64.2](https://github.com/eggjs/tegg/compare/v3.64.1...v3.64.2) (2025-11-27) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.64.1](https://github.com/eggjs/tegg/compare/v3.64.0...v3.64.1) (2025-11-27) **Note:** Version bump only for package @eggjs/dal-runtime # [3.64.0](https://github.com/eggjs/tegg/compare/v3.63.2...v3.64.0) (2025-11-27) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.63.2](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.2) (2025-11-18) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.63.1](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.1) (2025-11-18) **Note:** Version bump only for package @eggjs/dal-runtime # [3.63.0](https://github.com/eggjs/tegg/compare/v3.62.3...v3.63.0) (2025-11-18) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.62.3](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.3) (2025-11-05) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.62.2](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.2) (2025-11-05) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.62.1](https://github.com/eggjs/tegg/compare/v3.62.0...v3.62.1) (2025-11-03) **Note:** Version bump only for package @eggjs/dal-runtime # [3.62.0](https://github.com/eggjs/tegg/compare/v3.61.0...v3.62.0) (2025-09-24) **Note:** Version bump only for package @eggjs/dal-runtime # [3.61.0](https://github.com/eggjs/tegg/compare/v3.60.3...v3.61.0) (2025-08-15) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.60.3](https://github.com/eggjs/tegg/compare/v3.60.2...v3.60.3) (2025-08-06) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.60.2](https://github.com/eggjs/tegg/compare/v3.60.1...v3.60.2) (2025-08-06) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.60.1](https://github.com/eggjs/tegg/compare/v3.60.0...v3.60.1) (2025-07-28) **Note:** Version bump only for package @eggjs/dal-runtime # [3.60.0](https://github.com/eggjs/tegg/compare/v3.59.1...v3.60.0) (2025-07-28) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.59.1](https://github.com/eggjs/tegg/compare/v3.59.0...v3.59.1) (2025-07-16) **Note:** Version bump only for package @eggjs/dal-runtime # [3.59.0](https://github.com/eggjs/tegg/compare/v3.58.0...v3.59.0) (2025-07-07) **Note:** Version bump only for package @eggjs/dal-runtime # [3.58.0](https://github.com/eggjs/tegg/compare/v3.57.14...v3.58.0) (2025-07-01) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.57.14](https://github.com/eggjs/tegg/compare/v3.57.13...v3.57.14) (2025-06-18) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.57.13](https://github.com/eggjs/tegg/compare/v3.57.12...v3.57.13) (2025-06-18) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.57.12](https://github.com/eggjs/tegg/compare/v3.57.11...v3.57.12) (2025-06-16) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.57.11](https://github.com/eggjs/tegg/compare/v3.57.10...v3.57.11) (2025-06-16) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.57.10](https://github.com/eggjs/tegg/compare/v3.57.9...v3.57.10) (2025-06-12) ### Bug Fixes * muliti column primary generator code error ([#326](https://github.com/eggjs/tegg/issues/326)) ([7b8e1de](https://github.com/eggjs/tegg/commit/7b8e1de5b990574f7b907f3d7a3f68ecd54f8a86)) ## [3.57.9](https://github.com/eggjs/tegg/compare/v3.57.8...v3.57.9) (2025-05-29) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.57.8](https://github.com/eggjs/tegg/compare/v3.57.7...v3.57.8) (2025-05-29) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.57.7](https://github.com/eggjs/tegg/compare/v3.57.6...v3.57.7) (2025-05-28) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.57.6](https://github.com/eggjs/tegg/compare/v3.57.5...v3.57.6) (2025-05-27) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.57.5](https://github.com/eggjs/tegg/compare/v3.57.4...v3.57.5) (2025-05-15) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.57.4](https://github.com/eggjs/tegg/compare/v3.57.3...v3.57.4) (2025-05-14) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.57.3](https://github.com/eggjs/tegg/compare/v3.57.2...v3.57.3) (2025-05-13) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.57.2](https://github.com/eggjs/tegg/compare/v3.57.1...v3.57.2) (2025-05-13) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.57.1](https://github.com/eggjs/tegg/compare/v3.57.0...v3.57.1) (2025-05-13) **Note:** Version bump only for package @eggjs/dal-runtime # [3.57.0](https://github.com/eggjs/tegg/compare/v3.56.3...v3.57.0) (2025-05-09) ### Features * preserve SQL hint in minify function ([#314](https://github.com/eggjs/tegg/issues/314)) ([145bcf3](https://github.com/eggjs/tegg/commit/145bcf37bcd1ba86084cc304d15f0993abf0ebc8)) ## [3.56.3](https://github.com/eggjs/tegg/compare/v3.56.2...v3.56.3) (2025-05-07) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.56.2](https://github.com/eggjs/tegg/compare/v3.56.1...v3.56.2) (2025-05-01) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.56.1](https://github.com/eggjs/tegg/compare/v3.56.0...v3.56.1) (2025-04-29) **Note:** Version bump only for package @eggjs/dal-runtime # [3.56.0](https://github.com/eggjs/tegg/compare/v3.55.0...v3.56.0) (2025-04-29) **Note:** Version bump only for package @eggjs/dal-runtime # [3.55.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.55.0) (2025-04-29) **Note:** Version bump only for package @eggjs/dal-runtime # [3.54.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.54.0) (2025-04-29) **Note:** Version bump only for package @eggjs/dal-runtime # [3.53.0](https://github.com/eggjs/tegg/compare/v3.52.1...v3.53.0) (2025-04-21) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.52.1](https://github.com/eggjs/tegg/compare/v3.52.0...v3.52.1) (2025-03-14) **Note:** Version bump only for package @eggjs/dal-runtime # [3.52.0](https://github.com/eggjs/tegg/compare/v3.51.2...v3.52.0) (2024-12-30) ### Features * dal retry when init failed ([#260](https://github.com/eggjs/tegg/issues/260)) ([74e7c06](https://github.com/eggjs/tegg/commit/74e7c067c3ff7ae0ed705abaaa8a91f804e487e3)) ## [3.51.2](https://github.com/eggjs/tegg/compare/v3.51.1...v3.51.2) (2024-12-09) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.51.1](https://github.com/eggjs/tegg/compare/v3.51.0...v3.51.1) (2024-11-06) **Note:** Version bump only for package @eggjs/dal-runtime # [3.51.0](https://github.com/eggjs/tegg/compare/v3.50.1...v3.51.0) (2024-10-30) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.50.1](https://github.com/eggjs/tegg/compare/v3.50.0...v3.50.1) (2024-10-23) **Note:** Version bump only for package @eggjs/dal-runtime # [3.50.0](https://github.com/eggjs/tegg/compare/v3.49.0...v3.50.0) (2024-10-22) **Note:** Version bump only for package @eggjs/dal-runtime # [3.49.0](https://github.com/eggjs/tegg/compare/v3.48.1...v3.49.0) (2024-10-21) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.48.1](https://github.com/eggjs/tegg/compare/v3.48.0...v3.48.1) (2024-10-14) **Note:** Version bump only for package @eggjs/dal-runtime # [3.48.0](https://github.com/eggjs/tegg/compare/v3.47.2...v3.48.0) (2024-10-10) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.47.2](https://github.com/eggjs/tegg/compare/v3.47.1...v3.47.2) (2024-10-10) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.47.1](https://github.com/eggjs/tegg/compare/v3.47.0...v3.47.1) (2024-10-10) **Note:** Version bump only for package @eggjs/dal-runtime # [3.47.0](https://github.com/eggjs/tegg/compare/v3.46.4...v3.47.0) (2024-10-10) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.46.4](https://github.com/eggjs/tegg/compare/v3.46.3...v3.46.4) (2024-10-09) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.46.3](https://github.com/eggjs/tegg/compare/v3.46.2...v3.46.3) (2024-10-08) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.46.2](https://github.com/eggjs/tegg/compare/v3.46.1...v3.46.2) (2024-10-07) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.46.1](https://github.com/eggjs/tegg/compare/v3.46.0...v3.46.1) (2024-09-30) **Note:** Version bump only for package @eggjs/dal-runtime # [3.46.0](https://github.com/eggjs/tegg/compare/v3.45.0...v3.46.0) (2024-09-29) **Note:** Version bump only for package @eggjs/dal-runtime # [3.45.0](https://github.com/eggjs/tegg/compare/v3.44.1...v3.45.0) (2024-09-29) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.44.1](https://github.com/eggjs/tegg/compare/v3.44.0...v3.44.1) (2024-09-29) **Note:** Version bump only for package @eggjs/dal-runtime # [3.44.0](https://github.com/eggjs/tegg/compare/v3.43.2...v3.44.0) (2024-09-29) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.43.2](https://github.com/eggjs/tegg/compare/v3.43.1...v3.43.2) (2024-09-14) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.43.1](https://github.com/eggjs/tegg/compare/v3.43.0...v3.43.1) (2024-09-14) **Note:** Version bump only for package @eggjs/dal-runtime # [3.43.0](https://github.com/eggjs/tegg/compare/v3.42.0...v3.43.0) (2024-09-13) **Note:** Version bump only for package @eggjs/dal-runtime # [3.42.0](https://github.com/eggjs/tegg/compare/v3.41.0...v3.42.0) (2024-09-10) **Note:** Version bump only for package @eggjs/dal-runtime # [3.41.0](https://github.com/eggjs/tegg/compare/v3.40.1...v3.41.0) (2024-08-26) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.40.1](https://github.com/eggjs/tegg/compare/v3.40.0...v3.40.1) (2024-08-23) **Note:** Version bump only for package @eggjs/dal-runtime # [3.40.0](https://github.com/eggjs/tegg/compare/v3.39.5...v3.40.0) (2024-08-22) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.39.5](https://github.com/eggjs/tegg/compare/v3.39.4...v3.39.5) (2024-08-09) ### Bug Fixes * generate index name with column name ([#230](https://github.com/eggjs/tegg/issues/230)) ([82ec72d](https://github.com/eggjs/tegg/commit/82ec72d4fb8628c847b32d0ddf23a95119ca6ccf)) ## [3.39.4](https://github.com/eggjs/tegg/compare/v3.39.3...v3.39.4) (2024-07-09) ### Bug Fixes * fix total type in paginate ([#228](https://github.com/eggjs/tegg/issues/228)) ([e57b91e](https://github.com/eggjs/tegg/commit/e57b91ee64e89487a3cc1663868d9b819e6e60c0)) ## [3.39.3](https://github.com/eggjs/tegg/compare/v3.39.2...v3.39.3) (2024-04-28) ### Bug Fixes * mount clazzExtension/clazzExtension/tableSql to BaseDao ([#220](https://github.com/eggjs/tegg/issues/220)) ([ac322cf](https://github.com/eggjs/tegg/commit/ac322cfc4100841a1483b04b99e04d553af323eb)) ## [3.39.2](https://github.com/eggjs/tegg/compare/v3.39.1...v3.39.2) (2024-04-28) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.39.1](https://github.com/eggjs/tegg/compare/v3.39.0...v3.39.1) (2024-04-28) ### Bug Fixes * not overwrite extension file ([#218](https://github.com/eggjs/tegg/issues/218)) ([f915f08](https://github.com/eggjs/tegg/commit/f915f08dc61b01f10400ad844496f5b227f377eb)) # [3.39.0](https://github.com/eggjs/tegg/compare/v3.38.0...v3.39.0) (2024-04-19) ### Bug Fixes * not overwrite dao file ([#215](https://github.com/eggjs/tegg/issues/215)) ([0856bf1](https://github.com/eggjs/tegg/commit/0856bf189b160c7209bc24cf7eb911ec2f5875d1)) # [3.38.0](https://github.com/eggjs/tegg/compare/v3.37.3...v3.38.0) (2024-04-18) ### Features * impl dal transaction ([#214](https://github.com/eggjs/tegg/issues/214)) ([b8b67dd](https://github.com/eggjs/tegg/commit/b8b67dd7e0fb282d78de7e68e68834ff79d30732)) ## [3.37.3](https://github.com/eggjs/tegg/compare/v3.37.2...v3.37.3) (2024-04-17) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.37.2](https://github.com/eggjs/tegg/compare/v3.37.1...v3.37.2) (2024-04-17) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.37.1](https://github.com/eggjs/tegg/compare/v3.37.0...v3.37.1) (2024-04-16) ### Bug Fixes * fix dal runtime dep ([#210](https://github.com/eggjs/tegg/issues/210)) ([5ad7f45](https://github.com/eggjs/tegg/commit/5ad7f4537114217924ae8dc7445e8fc77eee0b5a)) # [3.37.0](https://github.com/eggjs/tegg/compare/v3.36.3...v3.37.0) (2024-04-16) **Note:** Version bump only for package @eggjs/dal-runtime ## [3.36.3](https://github.com/eggjs/tegg/compare/v3.36.2...v3.36.3) (2024-04-10) ### Bug Fixes * fix custom sql extension ([#207](https://github.com/eggjs/tegg/issues/207)) ([a405233](https://github.com/eggjs/tegg/commit/a405233d11323cd8a51c6197e855f0c3ff98337d)) ## [3.36.2](https://github.com/eggjs/tegg/compare/v3.36.1...v3.36.2) (2024-04-08) ### Bug Fixes * fix dao extension in prod ([#206](https://github.com/eggjs/tegg/issues/206)) ([0498e9d](https://github.com/eggjs/tegg/commit/0498e9d11bd9e4d186160e8b6af07e627dde6a20)) ## [3.36.1](https://github.com/eggjs/tegg/compare/v3.36.0...v3.36.1) (2024-04-07) **Note:** Version bump only for package @eggjs/dal-runtime # [3.36.0](https://github.com/eggjs/tegg/compare/v3.35.1...v3.36.0) (2024-04-02) ### Features * impl dal forkDb ([#202](https://github.com/eggjs/tegg/issues/202)) ([a411f04](https://github.com/eggjs/tegg/commit/a411f04e074425419b5b348a362f120bf8189541)) * impl Date/timestamp on update ([#203](https://github.com/eggjs/tegg/issues/203)) ([e5c7b8d](https://github.com/eggjs/tegg/commit/e5c7b8d529f2854b77de2e99369c781a4ea9e070)) ## [3.35.1](https://github.com/eggjs/tegg/compare/v3.35.0...v3.35.1) (2024-03-26) ### Bug Fixes * fix dal templates build ([#199](https://github.com/eggjs/tegg/issues/199)) ([17afe8c](https://github.com/eggjs/tegg/commit/17afe8c98929c7613739e32e897e881619bbdb2a)) # [3.35.0](https://github.com/eggjs/tegg/compare/v3.34.0...v3.35.0) (2024-03-26) ### Features * dal-runtime templates support pkg alias ([#198](https://github.com/eggjs/tegg/issues/198)) ([cecef78](https://github.com/eggjs/tegg/commit/cecef781bd134b629fc835063a351460aceb340c)) # [3.34.0](https://github.com/eggjs/tegg/compare/v3.33.1...v3.34.0) (2024-03-22) ### Features * impl dal for standalone tegg ([#197](https://github.com/eggjs/tegg/issues/197)) ([56b259d](https://github.com/eggjs/tegg/commit/56b259d7215a9d9542b36e421996623819369846)) ## [3.33.1](https://github.com/eggjs/tegg/compare/v3.33.0...v3.33.1) (2024-03-22) ### Bug Fixes * add dal templates ([#196](https://github.com/eggjs/tegg/issues/196)) ([49ba4f9](https://github.com/eggjs/tegg/commit/49ba4f9db3d9313654674f813c0358dc0774fd10)) # [3.33.0](https://github.com/eggjs/tegg/compare/v3.32.0...v3.33.0) (2024-03-22) ### Bug Fixes * set column canNull default to false ([#195](https://github.com/eggjs/tegg/issues/195)) ([24628ec](https://github.com/eggjs/tegg/commit/24628ec5a3cd167dc44a50017450d0dedec2c9ce)) ### Features * impl dal ([#192](https://github.com/eggjs/tegg/issues/192)) ([1c7d145](https://github.com/eggjs/tegg/commit/1c7d1454bc8c600cd58c3ec7b9cda4e8a98c7287)) ================================================ FILE: core/dal-runtime/README.md ================================================ # `@eggjs/dal-runtime` ## Usage Please read [@eggjs/tegg-dal-plugin](../../plugin/dal-plugin) ================================================ FILE: core/dal-runtime/index.ts ================================================ export * from './src/SqlGenerator'; export * from './src/CodeGenerator'; export { TableSqlMap } from './src/TableSqlMap'; export * from './src/SqlMapLoader'; export * from './src/DataSource'; export * from './src/MySqlDataSource'; export * from './src/TableModelInstanceBuilder'; export * from './src/DatabaseForker'; export * from './src/DaoLoader'; ================================================ FILE: core/dal-runtime/package.json ================================================ { "name": "@eggjs/dal-runtime", "version": "3.78.15", "description": "tegg dal decorator", "keywords": [ "egg", "typescript", "decorator", "tegg", "dal" ], "main": "dist/index.js", "files": [ "dist/**/*.js", "dist/**/*.d.ts", "dist/**/*.njk" ], "typings": "dist/index.d.ts", "scripts": { "test": "cross-env NODE_ENV=test NODE_OPTIONS='--no-deprecation' mocha", "clean": "tsc -b --clean", "tsc": "ut run clean && tsc -p ./tsconfig.json", "tsc:pub": "ut run clean && tsc -p ./tsconfig.pub.json && ut cp:template", "cp:template": "rm -rf dist/src/templates && cp -r src/templates dist/src/templates", "prepublishOnly": "ut tsc:pub" }, "author": "killagu ", "license": "MIT", "homepage": "https://github.com/eggjs/tegg", "bugs": { "url": "https://github.com/eggjs/tegg/issues" }, "repository": { "type": "git", "url": "git@github.com:eggjs/tegg.git", "directory": "core/dal-decorator" }, "engines": { "node": ">=14.0.0" }, "dependencies": { "@eggjs/rds": "^1.0.0", "@eggjs/tegg": "^3.78.15", "@eggjs/tegg-types": "^3.78.15", "js-beautify": "^1.15.1", "lodash": "^4.17.21", "nunjucks": "^3.2.4", "sdk-base": "^4.2.1", "sqlstring": "^2.3.3" }, "publishConfig": { "access": "public" }, "devDependencies": { "@types/lodash": "^4.17.0", "@types/mocha": "^10.0.1", "@types/node": "^20.2.4", "@types/nunjucks": "^3.2.6", "cross-env": "^7.0.3", "mm": "^3.2.1", "mocha": "^10.2.0", "ts-node": "^10.9.1", "typescript": "^5.0.4" }, "gitHead": "240a3e9f40fda65ebb7589727d197db5ce17916c" } ================================================ FILE: core/dal-runtime/src/BaseSqlMap.ts ================================================ import _ from 'lodash'; import { TableModel } from '@eggjs/tegg/dal'; import { ColumnType, IndexType, SqlType, SqlMap } from '@eggjs/tegg-types'; import type { Logger, GenerateSqlMap } from '@eggjs/tegg-types'; import { TemplateUtil } from './TemplateUtil'; export class BaseSqlMapGenerator { private readonly tableModel: TableModel; private readonly logger: Logger; constructor(tableModel: TableModel, logger: Logger) { this.tableModel = tableModel; this.logger = logger; } generateAllColumns(countIf: boolean): string { const str = this.tableModel.columns.map(t => `\`${t.columnName}\``) .join(','); return countIf ? `{% if $$count == true %}0{% else %}${str}{% endif %}` : str; } generateFindByPrimary(): Array { const result: Array = []; const primary = this.tableModel.getPrimary(); if (!primary) { this.logger.warn(`表 \`${this.tableModel.name}\` 没有主键,无法生成主键查询语句。`); return result; } let sql = `SELECT ${this.generateAllColumns(true)} FROM \`${this.tableModel.name}\` WHERE `; sql += primary.keys.map(indexKey => `\`${indexKey.columnName}\` = {{$${indexKey.propertyName} | param}}`) .join(' AND '); if (primary.keys.length === 1) { result.push({ type: SqlType.SELECT, name: `findBy${_.upperFirst(primary.keys[0].propertyName)}`, sql, }); } result.push({ name: 'findByPrimary', type: SqlType.SELECT, sql, }); return result; } // TODO index 的左匹配 generateFindByIndexes() { const sqlMaps: Array = []; for (const index of this.tableModel.indices) { if (index.type === IndexType.PRIMARY) continue; let sql = `SELECT ${this.generateAllColumns(true)} FROM \`${this.tableModel.name}\` WHERE `; sql += index.keys.map(indexKey => { const s = `\`${indexKey.columnName}\` {{ "IS" if $${indexKey.propertyName} == null else "=" }} {{$${indexKey.propertyName} | param}}`; return s; }) .join(' AND '); const tempName = _.upperFirst(_.camelCase(index.keys.length === 1 ? index.keys[0].propertyName : index.name)); sqlMaps.push({ name: `findBy${tempName}`, type: SqlType.SELECT, sql, }); sqlMaps.push({ name: `findOneBy${tempName}`, type: SqlType.SELECT, sql: `${sql} LIMIT 0, 1`, }); } return sqlMaps; } generateInsert() { let sql = `INSERT INTO \`${this.tableModel.name}\` `; sql += '{% set ___first = true %}'; const keys: string[] = []; const values: string[] = []; for (const column of this.tableModel.columns) { const { propertyName, columnName, type } = column; if (column.propertyName !== 'gmtCreate' && column.propertyName !== 'gmtModified') { // Add filter for Spatial Type // - toPoint // - toLine // - toPolygon // - toGeometry // - toMultiPoint // - toMultiLine // - toMultiPolygon // - toGeometryCollection keys.push((` {% if $${propertyName} !== undefined %} {% if ___first %} {% set ___first = false %} {% else %} , {% endif %} \`${columnName}\` {% endif %} `).trim()); if (TemplateUtil.isSpatialType(column)) { const filter = TemplateUtil.getSpatialFilter(column.type.type); values.push((` {% if $${propertyName} !== undefined %} {% if ___first %} {% set ___first = false %} {% else %} , {% endif %} {{$${propertyName} | ${filter}}} {% endif %} `).trim()); } else if (column.type.type === ColumnType.JSON) { values.push((` {% if $${propertyName} !== undefined %} {% if ___first %} {% set ___first = false %} {% else %} , {% endif %} {{$${propertyName} | toJson}} {% endif %} `).trim()); } else { values.push((` {% if $${propertyName} !== undefined %} {% if ___first %} {% set ___first = false %} {% else %} , {% endif %} {{$${propertyName} | param}} {% endif %} `).trim()); } } else { let now; // Default value for gmtCreate/gmtModified // int:UNIX_TEIMESTAMP // bigint: ROUND(UNIX_TIMESTAMP(CURTIME(4)) * 1000) // datetime/timestamp Now() if (type.type === ColumnType.INT) { // 秒级时间戳 now = 'UNIX_TIMESTAMP()'; } else if (type.type === ColumnType.BIGINT) { // 毫秒级时间戳 now = 'ROUND(UNIX_TIMESTAMP(CURTIME(4)) * 1000)'; } else if (type.type === ColumnType.DATETIME || type.type === ColumnType.TIMESTAMP) { now = type.precision ? `NOW(${type.precision})` : 'NOW()'; } else { this.logger.warn(`unknown type ${type.type} for ${propertyName}`); } keys.push((` {% if ___first %} {% set ___first = false %} {% else %} , {% endif %} \`${columnName}\` `).trim()); values.push((` {% if ___first %} {% set ___first = false %} {% else %} , {% endif %} {{ ($${propertyName} | param) if $${propertyName} !== undefined else '${now}' }} `).trim()); } } sql += `(${keys.join('')})`; sql += '{% set ___first = true %}'; sql += `VALUES(${values.join('')});`; return sql; } generateUpdate() { const primary = this.tableModel.getPrimary(); if (!primary) { this.logger.warn(`表 \`${this.tableModel.name}\` 没有主键,无法生成主键更新语句。`); return; } let sql = `UPDATE \`${this.tableModel.name}\` SET`; sql += '{% set ___first = true %}'; const kv: string[] = []; for (const column of this.tableModel.columns) { const { type, propertyName, columnName } = column; let now; if (type.type === ColumnType.INT) { // 秒级时间戳 now = 'UNIX_TIMESTAMP()'; } else if (type.type === ColumnType.BIGINT) { // 毫秒级时间戳 now = 'ROUND(UNIX_TIMESTAMP(CURTIME(4)) * 1000)'; } else if (type.type === ColumnType.TIMESTAMP || type.type === ColumnType.DATETIME) { now = type.precision ? `NOW(${type.precision})` : 'NOW()'; } // 若无更新时间字段,则自动更新该字段 const temp = propertyName !== 'gmtModified' ? ` {% if $${propertyName} !== undefined %} {% if ___first %} {% set ___first = false %} {% else %} , {% endif %} \`${columnName}\` = {{$${propertyName} | param}} {% endif %} ` : ` {% if ___first %} {% set ___first = false %} {% else %} , {% endif %} \`${columnName}\` = {{ ($${propertyName} | param) if $${propertyName} !== undefined else '${now}' }} `; kv.push(temp); } sql += kv.join(''); sql += `WHERE ${primary.keys.map(indexKey => `\`${indexKey.columnName}\` = {{primary.${indexKey.propertyName} | param}}`) .join(' AND ')}`; return sql; } generateDelete() { const primary = this.tableModel.getPrimary(); if (!primary) { this.logger.warn(`表 \`${this.tableModel.name}\` 没有主键,无法生成主键删除语句。`); return; } let sql = `DELETE FROM \`${this.tableModel.name}\` WHERE `; sql += primary.keys.map(indexKey => `\`${indexKey.columnName}\` = {{${indexKey.propertyName} | param}}`) .join(' AND '); return sql; } load(): Record { const map: Record = {}; map.allColumns = { type: SqlType.BLOCK, content: this.generateAllColumns(false), }; const sqlMaps: Array = [ /** * 以主键进行索引 * * + `findByPrimary` * + 若为单主键,则再加 `findBy键名` */ ...this.generateFindByPrimary(), /** * findBy 各索引 * * + 若为多列索引,则为 `findBy索引名` * + 若为单列索引,则为 `findBy列名` */ ...this.generateFindByIndexes(), /** * 插入 */ { name: 'insert', type: SqlType.INSERT, sql: this.generateInsert(), } as GenerateSqlMap, /** * 主键更新 */ { name: 'update', type: SqlType.UPDATE, sql: this.generateUpdate(), } as GenerateSqlMap, /** * 主键删除 */ { name: 'delete', type: SqlType.DELETE, sql: this.generateDelete(), } as GenerateSqlMap, ]; for (const sqlMap of sqlMaps) { map[sqlMap.name] = { type: sqlMap.type, sql: sqlMap.sql, }; } return map; } } ================================================ FILE: core/dal-runtime/src/CodeGenerator.ts ================================================ import fs from 'node:fs/promises'; import path from 'node:path'; import { js_beautify } from 'js-beautify'; import _ from 'lodash'; import nunjucks, { type Environment } from 'nunjucks'; import { Templates } from '@eggjs/tegg-types'; import type { CodeGeneratorOptions } from '@eggjs/tegg-types'; import { ColumnModel, TableModel } from '@eggjs/tegg/dal'; import { PrototypeUtil } from '@eggjs/tegg'; import { SqlGenerator } from './SqlGenerator'; import { TemplateUtil } from './TemplateUtil'; export class CodeGenerator { private readonly moduleDir: string; private readonly moduleName: string; private readonly teggPkg: string; private readonly dalPkg: string; constructor(options: CodeGeneratorOptions) { this.moduleDir = options.moduleDir; this.moduleName = options.moduleName; this.teggPkg = options.teggPkg ?? '@eggjs/tegg'; this.dalPkg = options.dalPkg ?? '@eggjs/tegg/dal'; this.createNunjucksEnv(); } private njkEnv: Environment; createNunjucksEnv() { this.njkEnv = nunjucks.configure(path.join(__dirname, './templates'), { autoescape: false, }); this.njkEnv.addFilter('pascalCase', name => _.upperFirst(_.camelCase(name))); this.njkEnv.addFilter('camelCase', name => _.camelCase(name)); this.njkEnv.addFilter('dbTypeToTSType', TemplateUtil.dbTypeToTsType); } genCode(tplName: Templates, filePath: string, tableModel: TableModel) { let tableModelAbsolutePath = PrototypeUtil.getFilePath(tableModel.clazz)!; tableModelAbsolutePath = tableModelAbsolutePath.substring(0, tableModelAbsolutePath.length - 3); const data = { table: tableModel, file: filePath, fileName: path.basename(filePath), clazzName: tableModel.clazz.name, moduleName: this.moduleName, teggPkg: this.teggPkg, dalPkg: this.dalPkg, id: tableModel.columns.find(t => t.propertyName === 'id'), primaryIndex: tableModel.getPrimary(), tableModelPath: TemplateUtil.importPath(tableModelAbsolutePath, path.dirname(filePath)), extensionPath: `../../extension/${tableModel.clazz.name}Extension`, structurePath: `../../structure/${tableModel.clazz.name}.json`, sqlPath: `../../structure/${tableModel.clazz.name}.sql`, columnMap: tableModel.columns.reduce>((p, c) => { p[c.propertyName] = c; return p; }, {}), }; return this.njkEnv.render(`${tplName}.njk`, data); } async generate(tableModel: TableModel) { let dalDir: string; try { await fs.access(path.join(this.moduleDir, 'src')); dalDir = path.join(this.moduleDir, 'src/dal'); } catch { dalDir = path.join(this.moduleDir, 'dal'); } // const tableName = tableModel.name; // const clazzName = tableModel.clazz.name; const clazzFileName = path.basename(PrototypeUtil.getFilePath(tableModel.clazz)!); const baseFileName = path.basename(clazzFileName, '.ts'); // 要动的一些文件 const paths = { // e.g. app/dal/dao/base/example.ts baseBizDAO: path.join(dalDir, `dao/base/Base${baseFileName}DAO.ts`), // e.g. app/dal/dao/example.ts bizDAO: path.join(dalDir, `dao/${baseFileName}DAO.ts`), // e.g. app/dal/extension/example.ts extension: path.join(dalDir, `extension/${baseFileName}Extension.ts`), // e.g. app/dal/structure/example.json structure: path.join(dalDir, `structure/${baseFileName}.json`), // e.g. app/dal/structure/example.sql structureSql: path.join(dalDir, `structure/${baseFileName}.sql`), }; // 建立 structure 文件 await fs.mkdir(path.dirname(paths.structure), { recursive: true, }); await fs.writeFile(paths.structure, JSON.stringify(tableModel, null, 2), 'utf8'); const sqlGenerator = new SqlGenerator(); const structureSql = sqlGenerator.generate(tableModel); await fs.writeFile(paths.structureSql, structureSql, 'utf8'); const codes = [{ templates: Templates.BASE_DAO, filePath: paths.baseBizDAO, beautify: true, overwrite: true, }, { templates: Templates.DAO, filePath: paths.bizDAO, beautify: true, overwrite: false, }, { templates: Templates.EXTENSION, filePath: paths.extension, beautify: false, overwrite: false, }]; for (const { templates, filePath, beautify, overwrite } of codes) { await fs.mkdir(path.dirname(filePath), { recursive: true, }); const code = this.genCode(templates, filePath, tableModel); let beautified: string; if (beautify) { beautified = js_beautify(code, { brace_style: 'preserve-inline', indent_size: 2, jslint_happy: true, preserve_newlines: false, }); } else { beautified = code; } beautified = beautified .replace(/( )*\/\/ empty-line( )*/g, '') .replace(/Promise( )*<( )*(.+?)( )*>/g, 'Promise<$3>') .replace(/Optional( )*<( )*(.+?)( )*>/g, 'Optional<$3>') .replace(/Record( )*<( )*(.+?)( )*>/g, 'Record<$3>') .replace(/Partial( )*<( )*(.+?)( )*>/g, 'Partial<$3>') .replace(/DataSource( )*<( )*(.+?)( )*>/g, 'DataSource<$3>') .replace(/ \? :/g, '?:'); if (overwrite !== true) { try { await fs.access(filePath); continue; } catch { // file not exists } } await fs.writeFile(filePath, beautified, 'utf8'); } } } ================================================ FILE: core/dal-runtime/src/DaoLoader.ts ================================================ import { EggLoadUnitType } from '@eggjs/tegg-types'; import { DaoInfoUtil } from '@eggjs/tegg/dal'; import { BaseDaoType } from '@eggjs/tegg-types/dal'; import { LoaderFactory } from '@eggjs/tegg/helper'; export class DaoLoader { static loadDaos(moduleDir: string): Array { const loader = LoaderFactory.createLoader(moduleDir, EggLoadUnitType.MODULE); const clazzList = loader.load(); return clazzList.filter((t): t is BaseDaoType => { return DaoInfoUtil.getIsDao(t); }); } } ================================================ FILE: core/dal-runtime/src/DataSource.ts ================================================ import { TableModel } from '@eggjs/tegg/dal'; import type { DataSource as IDataSource, PaginateData, SqlType } from '@eggjs/tegg-types'; import { EggQueryOptions, MysqlDataSource } from './MySqlDataSource'; import { TableSqlMap } from './TableSqlMap'; import { TableModelInstanceBuilder } from './TableModelInstanceBuilder'; export interface ExecuteSql { sql: string; template: string; sqlType: SqlType; params: any[]; } const PAGINATE_COUNT_WRAPPER = [ 'SELECT COUNT(0) as count FROM (', ') AS T' ]; export class DataSource implements IDataSource { private readonly tableModel: TableModel; private readonly mysqlDataSource: MysqlDataSource; private readonly sqlMap: TableSqlMap; constructor(tableModel: TableModel, mysqlDataSource: MysqlDataSource, sqlMap: TableSqlMap) { this.tableModel = tableModel; this.mysqlDataSource = mysqlDataSource; this.sqlMap = sqlMap; } /** * public for aop execute to implement sql hint append * @param sqlName * @param data */ async generateSql(sqlName: string, data: object): Promise { const { sql, params } = this.sqlMap.generate(sqlName, data, this.mysqlDataSource.timezone!); const sqlType = this.sqlMap.getType(sqlName); const template = this.sqlMap.getTemplateString(sqlName); return { sql, sqlType, template, params, }; } async count(sqlName: string, data?: any, options?: EggQueryOptions): Promise { const newData = Object.assign({ $$count: true }, data); const executeSql = await this.generateSql(sqlName, newData); return await this.#paginateCount(executeSql.sql, executeSql.params, options); } async execute(sqlName: string, data?: any, options?: EggQueryOptions): Promise> { const executeSql = await this.generateSql(sqlName, data); const rows = await this.mysqlDataSource.query(executeSql.sql, executeSql.params, options); return rows.map(t => { return TableModelInstanceBuilder.buildInstance(this.tableModel, t); }); } async executeRaw(sqlName: string, data?: any, options?: EggQueryOptions): Promise> { const executeSql = await this.generateSql(sqlName, data); return await this.mysqlDataSource.query(executeSql.sql, executeSql.params, options); } async executeScalar(sqlName: string, data?: any, options?: EggQueryOptions): Promise { const ret = await this.execute(sqlName, data, options); if (!Array.isArray(ret)) return ret || null; return ret[0] || null; } async executeRawScalar(sqlName: string, data?: any, options?: EggQueryOptions): Promise { const ret = await this.executeRaw(sqlName, data, options); if (!Array.isArray(ret)) return (ret || null) as any; return ret[0] || null; } async paginate(sqlName: string, data: any, currentPage: number, perPageCount: number, options?: EggQueryOptions): Promise> { const limit = `LIMIT ${(currentPage - 1) * perPageCount}, ${perPageCount}`; const executeSql = await this.generateSql(sqlName, data); const sql = executeSql.sql + ' ' + limit; const countExecuteSql = await this.generateSql(sqlName, Object.assign({ $$count: true }, data)); const ret = await Promise.all([ this.mysqlDataSource.query(sql, executeSql.params, options), this.#paginateCount(countExecuteSql.sql, countExecuteSql.params, options), ]); return { total: Number(ret[1]), pageNum: currentPage, rows: ret[0].map(t => TableModelInstanceBuilder.buildInstance(this.tableModel, t)), }; } async #paginateCount(baseSQL: string, params: any[], options?: EggQueryOptions): Promise { const sql = `${PAGINATE_COUNT_WRAPPER[0]}${baseSQL}${PAGINATE_COUNT_WRAPPER[1]}`; const result = await this.mysqlDataSource.query(sql, params, options); return result[0].count; } } ================================================ FILE: core/dal-runtime/src/DatabaseForker.ts ================================================ import assert from 'node:assert'; import { RDSClient } from '@eggjs/rds'; import { DataSourceOptions } from './MySqlDataSource'; import { DaoLoader } from './DaoLoader'; export class DatabaseForker { private readonly env: string; private readonly options: DataSourceOptions; constructor(env: string, options: DataSourceOptions) { this.env = env; this.options = options; } shouldFork() { return this.env === 'unittest' && this.options.forkDb; } async forkDb(moduleDir: string) { assert(this.shouldFork(), 'fork db only run in unittest'); // 尽早判断不应该 fork,避免对 rds pool 配置造成污染 // eslint-disable-next-line @typescript-eslint/no-unused-vars const { name, initSql, forkDb, database, ...mysqlOptions } = this.options; const client = new RDSClient(Object.assign(mysqlOptions)); const conn = await client.getConnection(); await this.doCreateUtDb(conn); await this.forkTables(conn, moduleDir); conn.release(); await client.end(); } private async forkTables(conn, moduleDir: string) { const daoClazzList = DaoLoader.loadDaos(moduleDir); for (const clazz of daoClazzList) { await this.doForkTable(conn, clazz.tableSql); } } private async doForkTable(conn, sqlFile: string) { const sqls = sqlFile.split(';').filter(t => !!t.trim()); for (const sql of sqls) { await conn.query(sql); } } private async doCreateUtDb(conn) { await conn.query(`CREATE DATABASE IF NOT EXISTS ${this.options.database};`); await conn.query(`use ${this.options.database};`); } async destroy() { assert(this.shouldFork(), 'fork db only run in unittest'); // eslint-disable-next-line @typescript-eslint/no-unused-vars const { name, initSql, forkDb, database, ...mysqlOptions } = this.options; const client = new RDSClient(Object.assign(mysqlOptions)); await client.query(`DROP DATABASE ${database}`); await client.end(); } } ================================================ FILE: core/dal-runtime/src/MySqlDataSource.ts ================================================ import { RDSClient } from '@eggjs/rds'; import type { QueryOptions, RDSClientOptions } from '@eggjs/rds'; import Base from 'sdk-base'; import { Logger } from '@eggjs/tegg-types'; export interface DataSourceOptions extends RDSClientOptions { name: string; // default is select 1 + 1; initSql?: string; forkDb?: boolean; initRetryTimes?: number; logger?: Logger; executeType?: 'execute' | 'query'; } const DEFAULT_OPTIONS: RDSClientOptions = { supportBigNumbers: true, bigNumberStrings: true, trace: true, }; const DEFAULT_RETRY_TIMES = 3; export interface EggQueryOptions extends QueryOptions { executeType?: 'execute' | 'query'; } export class MysqlDataSource extends Base { private client: RDSClient; private readonly initSql: string; readonly name: string; readonly timezone?: string; readonly rdsOptions: RDSClientOptions; readonly forkDb?: boolean; readonly #initRetryTimes?: number; readonly #logger?: Logger; private readonly executeType: 'execute' | 'query'; constructor(options: DataSourceOptions) { super({ initMethod: '_init' }); const { name, initSql, forkDb, initRetryTimes, logger, executeType, ...mysqlOptions } = options; this.#logger = logger; this.forkDb = forkDb; this.initSql = initSql ?? 'SELECT 1 + 1'; this.#initRetryTimes = initRetryTimes ?? DEFAULT_RETRY_TIMES; this.name = name; this.timezone = options.timezone; this.executeType = executeType ?? 'query'; this.rdsOptions = Object.assign({}, DEFAULT_OPTIONS, mysqlOptions); this.client = new RDSClient(this.rdsOptions); } protected async _init() { if (this.initSql) { await this.#doInit(1); } } async #doInit(tryTimes: number): Promise { try { this.#logger?.log(`${tryTimes} try to initialize dataSource ${this.name}`); const st = Date.now(); await this.client.query(this.initSql); this.#logger?.info(`dataSource initialization cost: ${Date.now() - st}, tryTimes: ${tryTimes}`); } catch (e) { this.#logger?.warn(`failed to initialize dataSource ${this.name}, tryTimes ${tryTimes}`, e); if (!this.#initRetryTimes || tryTimes >= this.#initRetryTimes) { throw e; } await this.#doInit(tryTimes + 1); } } async query(sql: string, params?: any[], options?: EggQueryOptions): Promise { const executeType = options?.executeType || this.executeType; if (executeType === 'execute') { return this.client.execute(sql, params, options); } return this.client.query(sql, params, options); } async beginTransactionScope(scope: () => Promise): Promise { return await this.client.beginTransactionScope(scope); } } ================================================ FILE: core/dal-runtime/src/NunjucksConverter.ts ================================================ export class NunjucksConverter { /** * 将变量 HTML 转义的逻辑改为 MySQL 防注入转义 * * eg: * * output += runtime.suppressValue(runtime.contextOrFrameLookup(context, frame, "allColumns") * * 转换为 * * output += runtime.escapeSQL.call(this, "allColumns", runtime.contextOrFrameLookup(context, frame, "allColumns") * * @param {String} code 转换前的代码 * @return {String} 转换后的代码 */ static convertNormalVariableCode(code: string) { return code.replace( /\Woutput\W*?\+=\W*?runtime\.suppressValue\(runtime\.contextOrFrameLookup\((.+?),(.*?),\W*?"(.+?)"\)/g, '\noutput += runtime.escapeSQL.call(this, "$3", runtime.contextOrFrameLookup($1, $2, "$3")'); } /** * 三目运算的 MySQL 防注入转义 * * eg: * * output += runtime.suppressValue((runtime.contextOrFrameLookup(context, frame, "$gmtCreate") !== \ * runtime.contextOrFrameLookup(context, frame, "undefined")?runtime.contextOrFrameLookup(context,\ * frame, "$gmtCreate"):"NOW()"), env.opts.autoescape); * * 转换为 * * output += runtime.suppressValue((runtime.contextOrFrameLookup(...) != ...) ? * runtime.escapeSQL.call(this, "...", runtime.contextOrFrameLookup(...)) : * ...) * * @param {String} code 转换前的代码 * @return {String} 转换后的代码 */ static convertTernaryCode(code: string) { // 先找到所有的 runtime.suppressValue((...?...:...), env...) const ternaryBefore = code.match( /\Woutput\W*?\+=\W*?runtime\.suppressValue\(\(.*\W*?\?\W*?.*?:.*\),\W*?env\.opts\.autoescape/g) || []; // 进行逐一处理 const ternaryAfter = ternaryBefore.map(str => { return str.replace( /([\?:])runtime\.contextOrFrameLookup\((.+?),(.*?),\W*?"(.+?)"\)/g, '$1runtime.escapeSQL.call(this, "$4", runtime.contextOrFrameLookup($2, $3, "$4"))', ) .replace( /env.opts.autoescape$/g, 'false', ); }); // 统一替换 for (let i = 0; i < ternaryBefore.length; i++) { code = code.replace(ternaryBefore[i], ternaryAfter[i]); } return code; } /** * 对象的属性,如 `user.id` 防注入转义 * * eg: * output += runtime.suppressValue(runtime.memberLookup(\ * (runtime.contextOrFrameLookup(context, frame, "user")),"id"), env.opts.autoescape); * * 转换为 * * output += runtime.escapeSQL.call(this, "<...>", runtime.memberLookup(...), env.opts.autoescape); * * 由于 escapeSQL 中是根据 key 与预定义 block 匹配决定是否转义,而 memberLookup 的状态下总的 key 肯定不会匹配, * 所以找一个绝对不会匹配的 "<...>" 传入。事实上它可以是任意一个不会被匹配的字符串,比如说 ">_<" 等。 * * @param {String} code 转换前的代码 * @return {String} 转换后的代码 */ static convertNestedObjectCode(code: string) { return code.replace( /\Woutput\W*?\+=\W*?runtime\.suppressValue\(runtime\.memberLookup\((.+?)\), env\.opts\.autoescape\)/g, '\noutput += runtime.escapeSQL.call(this, "<...>", runtime.memberLookup($1), env.opts.autoescape)'); } /** * For 中的 `t_xxx` 要被转义: * * eg: * frame.set("...", t_...); * ... * output += runtime.suppressValue(t_.., env.opts.autoscape); * * 转换为 * * output += runtime.escapeSQL.call(this, "for.t_...", t_..., env.opts.autoescape); * * 由于 escapeSQL 中是根据 key 与预定义 block 匹配决定是否转义,而 memberLookup 的状态下总的 key 肯定不会匹配, * 所以找一个绝对不会匹配的 "for.t_..." 传入。事实上它可以是任意一个不会被匹配的字符串,比如说 ">_<" 等。 * * @param {String} code 转换前的代码 * @return {String} 转换后的代码 */ static convertValueInsideFor(code: string) { return code.replace( /\Woutput\W*?\+=\W*?runtime\.suppressValue\((t_\d+), env\.opts\.autoescape\)/g, '\noutput += runtime.escapeSQL.call(this, "for.$1", $1, env.opts.autoescape)'); } } ================================================ FILE: core/dal-runtime/src/NunjucksUtil.ts ================================================ import nunjucks, { Template, type Environment } from 'nunjucks'; import sqlstring from 'sqlstring'; import { NunjucksConverter } from './NunjucksConverter'; import { SqlUtil } from './SqlUtil'; const compiler = (nunjucks as any).compiler; const envs: Record = {}; const ROOT_RENDER_FUNC = Symbol('rootRenderFunc'); const RUNTIME = Object.assign({}, nunjucks.runtime, { escapeSQL: function escapeSQL(this: any, key, value) { // 如果是预定义 block 则不转义 if (this.env.globals[key]) return value; return sqlstring.escape(value, true, this.env.timezone); }, }); function _replaceCodeWithSQLFeature(source) { const funcs = [ 'convertNormalVariableCode', // 普通变量 'convertTernaryCode', // 三目运算 'convertNestedObjectCode', // 对象中的变量,如 `user.id` 'convertValueInsideFor', // for 中的值需要转义 ]; return funcs.reduce((source, func) => NunjucksConverter[func](source), source); } /** * compile the string into function * @see https://github.com/mozilla/nunjucks/blob/2fd547f/src/environment.js#L571-L592 */ function _compile(this: any) { let source = compiler.compile( this.tmplStr, this.env.asyncFilters, this.env.extensionsList, this.path, this.env.opts); /** * 将一些 Nunjucks 的 HTML 转义的代码转换成 SQL 防注入的代码 */ source = _replaceCodeWithSQLFeature(source); // eslint-disable-next-line const props = (new Function(source))(); this.blocks = this._getBlocks(props); this[ROOT_RENDER_FUNC] = props.root; this.rootRenderFunc = function(env, context, frame, _runtime, cb) { /** * 1. 将 runtime 遗弃,用新的 * 2. 移除 SQL 语句中多余空白符 */ return this[ROOT_RENDER_FUNC](env, context, frame, RUNTIME, function(err, ret) { // istanbul ignore if if (err) return cb(err, ret); return cb(err, SqlUtil.minify(ret || '')); }); }; this.compiled = true; } export class NunjucksUtils { static createEnv(modelName: string) { if (envs[modelName]) return envs[modelName]; const env = envs[modelName] = nunjucks.configure({ autoescape: false, }); return env; } static compile(modelName: string, sqlName: string, sql: string) { // istanbul ignore if if (!envs[modelName]) { throw new Error(`you should create an Environment for ${modelName} first.`); } const template = new Template(sql, envs[modelName], `egg-dal:MySQL:${modelName}:${sqlName}`, false); // 做一些 hack,使得支持 MySQL 的一些 Escape (template as any)._compile = _compile; (template as any).compile(); return template; } } ================================================ FILE: core/dal-runtime/src/SqlGenerator.ts ================================================ import { ColumnModel, IndexModel, TableModel, } from '@eggjs/tegg/dal'; import { ColumnType, IndexType } from '@eggjs/tegg-types'; import type { BaseSpatialParams, ColumnTypeParams } from '@eggjs/tegg-types'; // TODO diff 实现 export class SqlGenerator { private formatComment(comment: string) { return comment.replace(/\n/g, '\\n'); } private generateColumn(column: ColumnModel) { const sqls: string[] = [ ' ', column.columnName, this.generateColumnType(column.type), ]; if (column.canNull) { sqls.push('NULL'); } else { sqls.push('NOT NULL'); } if (([ ColumnType.POINT, ColumnType.GEOMETRY, ColumnType.POINT, ColumnType.LINESTRING, ColumnType.POLYGON, ColumnType.MULTIPOINT, ColumnType.MULTILINESTRING, ColumnType.MULTIPOLYGON, ColumnType.GEOMETRYCOLLECTION, ] as ColumnType[]).includes(column.type.type)) { const SRID = (column.type as BaseSpatialParams).SRID; if (SRID) { sqls.push(`SRID ${SRID}`); } } if (typeof column.default !== 'undefined') { sqls.push(`DEFAULT ${column.default}`); } if (column.autoIncrement) { sqls.push('AUTO_INCREMENT'); } if (column.uniqueKey) { sqls.push('UNIQUE KEY'); } if (column.primaryKey) { sqls.push('PRIMARY KEY'); } if (column.comment) { sqls.push(`COMMENT '${this.formatComment(column.comment)}'`); } if (column.collate) { sqls.push(`COLLATE ${column.collate}`); } if (column.columnFormat) { sqls.push(`COLUMN_FORMAT ${column.columnFormat}`); } if (column.engineAttribute) { sqls.push(`ENGINE_ATTRIBUTE='${column.engineAttribute}'`); } if (column.secondaryEngineAttribute) { sqls.push(`SECONDARY_ENGINE_ATTRIBUTE='${column.secondaryEngineAttribute}'`); } return sqls.join(' '); } private generateColumnType(columnType: ColumnTypeParams) { const sqls: string[] = []; switch (columnType.type) { case ColumnType.BOOL: { sqls.push('BOOL'); break; } case ColumnType.BIT: { if (columnType.length) { sqls.push(`BIT(${columnType.length})`); } else { sqls.push('BIT'); } break; } case ColumnType.TINYINT: case ColumnType.SMALLINT: case ColumnType.MEDIUMINT: case ColumnType.INT: case ColumnType.BIGINT: { if (typeof columnType.length === 'number') { sqls.push(`${columnType.type}(${columnType.length})`); } else { sqls.push(columnType.type); } if (columnType.unsigned) { sqls.push('UNSIGNED'); } if (columnType.zeroFill) { sqls.push('ZEROFILL'); } break; } case ColumnType.DECIMAL: case ColumnType.FLOAT: case ColumnType.DOUBLE: { if (typeof columnType.length === 'number' && typeof columnType.fractionalLength === 'number') { sqls.push(`${columnType.type}(${columnType.length},${columnType.fractionalLength})`); } else if (typeof columnType.length === 'number') { sqls.push(`${columnType.type}(${columnType.length})`); } else { sqls.push('TINYINT'); } if (columnType.unsigned) { sqls.push('UNSIGNED'); } if (columnType.zeroFill) { sqls.push('ZEROFILL'); } break; } case ColumnType.DATE: { sqls.push('DATE'); break; } case ColumnType.DATETIME: case ColumnType.TIMESTAMP: { if (columnType.precision) { sqls.push(`${columnType.type}(${columnType.precision})`); } else { sqls.push(columnType.type); } if (columnType.autoUpdate) { if (columnType.precision) { sqls.push(`ON UPDATE CURRENT_TIMESTAMP(${columnType.precision})`); } else { sqls.push('ON UPDATE CURRENT_TIMESTAMP'); } } break; } case ColumnType.TIME: { if (columnType.precision) { sqls.push(`${columnType.type}(${columnType.precision})`); } else { sqls.push(columnType.type); } break; } case ColumnType.YEAR: { sqls.push('YEAR'); break; } case ColumnType.CHAR: case ColumnType.TEXT: { if (columnType.length) { sqls.push(`${columnType.type}(${columnType.length})`); } else { sqls.push(columnType.type); } if (columnType.characterSet) { sqls.push(`CHARACTER SET ${columnType.characterSet}`); } if (columnType.collate) { sqls.push(`COLLATE ${columnType.collate}`); } break; } case ColumnType.VARCHAR: { sqls.push(`${columnType.type}(${columnType.length})`); if (columnType.characterSet) { sqls.push(`CHARACTER SET ${columnType.characterSet}`); } if (columnType.collate) { sqls.push(`COLLATE ${columnType.collate}`); } break; } case ColumnType.BINARY: { if (columnType.length) { sqls.push(`${columnType.type}(${columnType.length})`); } else { sqls.push(columnType.type); } break; } case ColumnType.VARBINARY: { sqls.push(`${columnType.type}(${columnType.length})`); break; } case ColumnType.TINYBLOB: { sqls.push('TINYBLOB'); break; } case ColumnType.TINYTEXT: case ColumnType.MEDIUMTEXT: case ColumnType.LONGTEXT: { sqls.push(columnType.type); if (columnType.characterSet) { sqls.push(`CHARACTER SET ${columnType.characterSet}`); } if (columnType.collate) { sqls.push(`COLLATE ${columnType.collate}`); } break; } case ColumnType.BLOB: { if (columnType.length) { sqls.push(`${columnType.type}(${columnType.length})`); } else { sqls.push(columnType.type); } break; } case ColumnType.MEDIUMBLOB: { sqls.push('MEDIUMBLOB'); break; } case ColumnType.LONGBLOB: { sqls.push('LONGBLOB'); break; } case ColumnType.ENUM: { const enumValue: string = columnType.enums.map(t => `'${t}'`).join(','); sqls.push(`ENUM(${enumValue})`); if (columnType.characterSet) { sqls.push(`CHARACTER SET ${columnType.characterSet}`); } if (columnType.collate) { sqls.push(`COLLATE ${columnType.collate}`); } break; } case ColumnType.SET: { const enumValue: string = columnType.enums.map(t => `'${t}'`).join(','); sqls.push(`SET(${enumValue})`); if (columnType.characterSet) { sqls.push(`CHARACTER SET ${columnType.characterSet}`); } if (columnType.collate) { sqls.push(`COLLATE ${columnType.collate}`); } break; } case ColumnType.JSON: { sqls.push('JSON'); break; } case ColumnType.GEOMETRY: case ColumnType.POINT: case ColumnType.LINESTRING: case ColumnType.POLYGON: case ColumnType.MULTIPOINT: case ColumnType.MULTILINESTRING: case ColumnType.MULTIPOLYGON: case ColumnType.GEOMETRYCOLLECTION: { sqls.push(columnType.type); break; } default: { throw new Error(`unknown ColumnType ${columnType}`); } } return sqls.join(' '); } private generateIndex(indexModel: IndexModel) { const indexSql: string[] = [ ' ', ]; switch (indexModel.type) { case IndexType.INDEX: { indexSql.push('KEY'); break; } case IndexType.UNIQUE: { indexSql.push('UNIQUE KEY'); break; } case IndexType.PRIMARY: { indexSql.push('PRIMARY KEY'); break; } case IndexType.FULLTEXT: { indexSql.push('FULLTEXT KEY'); break; } case IndexType.SPATIAL: { indexSql.push('SPATIAL KEY'); break; } default: { throw new Error(`unknown IndexType ${indexModel.type}`); } } indexSql.push(indexModel.name); indexSql.push(`(${indexModel.keys.map(t => t.columnName).join(',')})`); if (indexModel.storeType) { indexSql.push(`USING ${indexModel.storeType}`); } if (indexModel.parser) { indexSql.push(`WITH PARSER ${indexModel.parser}`); } if (indexModel.comment) { indexSql.push(`COMMENT '${this.formatComment(indexModel.comment)}'`); } if (indexModel.engineAttribute) { indexSql.push(`ENGINE_ATTRIBUTE='${indexModel.engineAttribute}'`); } if (indexModel.secondaryEngineAttribute) { indexSql.push(`SECONDARY_ENGINE_ATTRIBUTE='${indexModel.secondaryEngineAttribute}'`); } return indexSql.join(' '); } private generateTableOptions(tableModel: TableModel) { const sqls: string[] = []; if (tableModel.autoExtendSize) { sqls.push(`AUTOEXTEND_SIZE=${tableModel.autoExtendSize}`); } if (tableModel.autoIncrement) { sqls.push(`AUTO_INCREMENT=${tableModel.autoIncrement}`); } if (tableModel.avgRowLength) { sqls.push(`AVG_ROW_LENGTH=${tableModel.avgRowLength}`); } if (tableModel.characterSet) { sqls.push(`DEFAULT CHARACTER SET ${tableModel.characterSet}`); } if (tableModel.collate) { sqls.push(`DEFAULT COLLATE ${tableModel.collate}`); } if (tableModel.comment) { sqls.push(`COMMENT='${this.formatComment(tableModel.comment)}'`); } if (tableModel.compression) { sqls.push(`COMPRESSION='${tableModel.compression}'`); } if (typeof tableModel.encryption !== 'undefined') { sqls.push(`ENCRYPTION='${tableModel.encryption ? 'Y' : 'N'}'`); } if (typeof tableModel.engine !== 'undefined') { sqls.push(`ENGINE=${tableModel.engine}`); } if (tableModel.engineAttribute) { sqls.push(`ENGINE_ATTRIBUTE='${tableModel.engineAttribute}'`); } if (tableModel.secondaryEngineAttribute) { sqls.push(`SECONDARY_ENGINE_ATTRIBUTE = '${tableModel.secondaryEngineAttribute}'`); } if (tableModel.insertMethod) { sqls.push(`INSERT_METHOD=${tableModel.insertMethod}`); } if (tableModel.keyBlockSize) { sqls.push(`KEY_BLOCK_SIZE=${tableModel.keyBlockSize}`); } if (tableModel.maxRows) { sqls.push(`MAX_ROWS=${tableModel.maxRows}`); } if (tableModel.minRows) { sqls.push(`MIN_ROWS=${tableModel.minRows}`); } if (tableModel.rowFormat) { sqls.push(`ROW_FORMAT=${tableModel.rowFormat}`); } return sqls.join(', '); } generate(tableModel: TableModel) { const createSql: string[] = []; createSql.push(`CREATE TABLE IF NOT EXISTS ${tableModel.name} (`); const columnSql: string[] = []; for (const column of tableModel.columns) { columnSql.push(this.generateColumn(column)); } const indexSql: string[] = []; for (const index of tableModel.indices) { indexSql.push(this.generateIndex(index)); } if (indexSql.length) { createSql.push(columnSql.join(',\n') + ','); createSql.push(indexSql.join(',\n')); } else { createSql.push(columnSql.join(',\n')); } createSql.push(`) ${this.generateTableOptions(tableModel)};`); return createSql.join('\n'); } } ================================================ FILE: core/dal-runtime/src/SqlMapLoader.ts ================================================ import { BaseDaoType, TableModel } from '@eggjs/tegg/dal'; import type { Logger, SqlMap } from '@eggjs/tegg-types'; import { BaseSqlMapGenerator } from './BaseSqlMap'; import { TableSqlMap } from './TableSqlMap'; export class SqlMapLoader { private readonly logger: Logger; private readonly tableModel: TableModel; private readonly clazzExtension: Record; constructor(tableModel: TableModel, baseDaoClazz: BaseDaoType, logger: Logger) { this.clazzExtension = baseDaoClazz.clazzExtension; this.logger = logger; this.tableModel = tableModel; } load(): TableSqlMap { const baseSqlMapGenerator = new BaseSqlMapGenerator(this.tableModel, this.logger); const baseSqlMap = baseSqlMapGenerator.load(); const sqlMap = { ...baseSqlMap, ...this.clazzExtension, }; return new TableSqlMap(this.tableModel.clazz.name, sqlMap); } } ================================================ FILE: core/dal-runtime/src/SqlUtil.ts ================================================ function isWhiteChar(ch) { return ch === ' ' || ch === '\n' || ch === '\r' || ch === '\t'; } const COMMENT_CHARS = '-#/'; const MUL_CHAR_LEADING_COMMENT_FIRST_CHAR = { MAY_BE_FIRST_COMMENT: '-', MAY_BE_FIRST_BLOCK_COMMENT: '/', }; const MUL_CHAR_LEADING_COMMENT_VERIFIER = { MAY_BE_FIRST_COMMENT: '-', MAY_BE_FIRST_BLOCK_COMMENT: '*', }; const MUL_CHAR_LEADING_COMMENT_NEXT_STATE = { MAY_BE_FIRST_COMMENT: 'IN_COMMENT_WAIT_HINT', MAY_BE_FIRST_BLOCK_COMMENT: 'IN_BLOCK_COMMENT_WAIT_HINT', }; export class SqlUtil { static minify(sql: string) { let ret = ''; let state = 'START'; let tempNextState; for (let i = 0; i < sql.length; i++) { const ch = sql[i]; switch (state) { case 'MAY_BE_FIRST_COMMENT': case 'MAY_BE_FIRST_BLOCK_COMMENT': switch (ch) { case '"': tempNextState = 'DOUBLE_QUOTE'; break; case '\'': tempNextState = 'SINGLE_QUOTE'; break; case MUL_CHAR_LEADING_COMMENT_VERIFIER[state]: tempNextState = MUL_CHAR_LEADING_COMMENT_NEXT_STATE[state]; break; default: tempNextState = 'CONTENT'; break; } if (ch !== MUL_CHAR_LEADING_COMMENT_VERIFIER[state]) { ret += `${MUL_CHAR_LEADING_COMMENT_FIRST_CHAR[state]}${ch}`; } state = tempNextState; break; case 'IN_COMMENT_WAIT_HINT': if (ch !== '+') { state = 'IN_COMMENT'; } else { state = 'IN_COMMENT_HINT'; ret += '--+'; } break; case 'IN_BLOCK_COMMENT_WAIT_HINT': if (ch !== '+') { state = 'IN_BLOCK_COMMENT'; } else { state = 'IN_BLOCK_COMMENT_HINT'; ret += '/*+'; } break; case 'MAY_BE_LAST_BLOCK_COMMENT': if (ch === '/') { if (ret && !isWhiteChar(ret[ret.length - 1])) ret += ' '; state = 'IN_SPACE'; } else { state = 'IN_BLOCK_COMMENT'; } break; case 'MAY_BE_LAST_BLOCK_COMMENT_HINT': ret += ch; if (ch === '/') { state = 'IN_SPACE'; if (isWhiteChar(sql[i + 1])) ret += sql[i + 1]; } else { state = 'IN_BLOCK_COMMENT_HINT'; } break; case 'IN_COMMENT': if (ch === '\n' || ch === '\r') { if (ret && !isWhiteChar(ret[ret.length - 1])) ret += ' '; state = 'IN_SPACE'; } break; case 'IN_COMMENT_HINT': ret += ch; if (ch === '\n' || ch === '\r') { state = 'IN_SPACE'; } break; case 'IN_BLOCK_COMMENT': if (ch === '*') { state = 'MAY_BE_LAST_BLOCK_COMMENT'; } break; case 'IN_BLOCK_COMMENT_HINT': ret += ch; if (ch === '*') { state = 'MAY_BE_LAST_BLOCK_COMMENT_HINT'; } break; case 'START': if (isWhiteChar(ch)) continue; switch (ch) { case '"': state = 'DOUBLE_QUOTE'; break; case '\'': state = 'SINGLE_QUOTE'; break; case '-': state = 'MAY_BE_FIRST_COMMENT'; break; case '#': state = 'IN_COMMENT'; break; case '/': if (sql[i + 1] === '+') { state = 'IN_BLOCK_COMMENT_HINT'; ret += '/*+'; i++; } else { state = 'IN_BLOCK_COMMENT'; } break; default: state = 'CONTENT'; break; } if (!COMMENT_CHARS.includes(ch)) ret += ch; break; case 'DOUBLE_QUOTE': case 'SINGLE_QUOTE': switch (ch) { case '\\': state = `BACKSLASH_AFTER_${state}`; break; case '\'': if (state === 'SINGLE_QUOTE') { state = 'QUOTE_DONE'; } break; case '"': if (state === 'DOUBLE_QUOTE') { state = 'QUOTE_DONE'; } break; default: break; } ret += ch; break; case 'BACKSLASH_AFTER_SINGLE_QUOTE': case 'BACKSLASH_AFTER_DOUBLE_QUOTE': ret += ch; state = state.substr(16); break; case 'QUOTE_DONE': case 'CONTENT': switch (ch) { case '\'': state = 'SINGLE_QUOTE'; break; case '"': state = 'DOUBLE_QUOTE'; break; case '-': state = 'MAY_BE_FIRST_COMMENT'; break; case '#': state = 'IN_COMMENT'; break; case '/': state = 'MAY_BE_FIRST_BLOCK_COMMENT'; break; default: if (isWhiteChar(ch)) { state = 'IN_SPACE'; ret += ' '; continue; } state = 'CONTENT'; } if (!COMMENT_CHARS.includes(ch)) ret += ch; break; case 'IN_SPACE': switch (ch) { case '\'': state = 'SINGLE_QUOTE'; break; case '"': state = 'DOUBLE_QUOTE'; break; case '-': state = 'MAY_BE_FIRST_COMMENT'; break; case '#': state = 'IN_COMMENT'; break; case '/': state = 'MAY_BE_FIRST_BLOCK_COMMENT'; break; default: if (isWhiteChar(ch)) continue; state = 'CONTENT'; } if (!COMMENT_CHARS.includes(ch)) ret += ch; break; default: throw new Error('Unexpected state machine while minifying SQL.'); } } return ret.trim(); } } ================================================ FILE: core/dal-runtime/src/TableModelInstanceBuilder.ts ================================================ import { TableModel } from '@eggjs/tegg/dal'; export class TableModelInstanceBuilder { constructor(tableModel: TableModel, row: Record) { for (const [ key, value ] of Object.entries(row)) { const column = tableModel.columns.find(t => t.columnName === key); Reflect.set(this, column?.propertyName ?? key, value); } } static buildInstance(tableModel: TableModel, row: Record) { return Reflect.construct(TableModelInstanceBuilder, [ tableModel, row ], tableModel.clazz); } static buildRow(instance: T, tableModel: TableModel) { const result: any = {}; for (const column of tableModel.columns) { const columnValue = Reflect.get(instance, column.propertyName); if (typeof columnValue !== 'undefined') { result[`$${column.propertyName}`] = columnValue; } } return result; } } ================================================ FILE: core/dal-runtime/src/TableSqlMap.ts ================================================ // const nunjucks = require('./NunjucksUtil'); import { Template } from 'nunjucks'; import { NunjucksUtils } from './NunjucksUtil'; import { TemplateUtil } from './TemplateUtil'; import { SqlType } from '@eggjs/tegg-types'; import type { SqlMap } from '@eggjs/tegg-types'; const SQL_PARAMS = '$$__sql_params'; export interface SqlGenerator { type: SqlType; template: Template, raw: string, } export interface GeneratedSql { sql: string; params: any[]; } export class TableSqlMap { readonly name: string; private readonly map: Record; private readonly blocks: Record; private readonly sqlGenerator: Record; constructor(name: string, map: Record) { this.name = name; this.map = map; const env = NunjucksUtils.createEnv(name); const extracted = this.#extract(this.map); this.blocks = extracted.blocks; this.sqlGenerator = extracted.sqlGenerator; for (const key in this.blocks) { // istanbul ignore if if (!this.blocks.hasOwnProperty(key)) continue; env.addGlobal(key, this.blocks[key]); } env.addFilter('toJson', TemplateUtil.toJson); env.addFilter('toPoint', TemplateUtil.toPoint); env.addFilter('toLine', TemplateUtil.toLine); env.addFilter('toPolygon', TemplateUtil.toPolygon); env.addFilter('toGeometry', TemplateUtil.toGeometry); env.addFilter('toMultiPoint', TemplateUtil.toMultiPoint); env.addFilter('toMultiLine', TemplateUtil.toMultiLine); env.addFilter('toMultiPolygon', TemplateUtil.toMultiPolygon); env.addFilter('toGeometryCollection', TemplateUtil.toGeometryCollection); // Add param filter for parameterized queries env.addFilter('param', function(this: Template, value: any) { if ((this as any).ctx[SQL_PARAMS]) { (this as any).ctx[SQL_PARAMS].push(value); } return '?'; }); } #extract(map: Record) { const ret = { blocks: {}, sqlGenerator: {}, }; for (const key in map) { // istanbul ignore if if (!map.hasOwnProperty(key)) continue; const sqlMap = map[key]; switch (sqlMap.type) { case SqlType.BLOCK: ret.blocks[key] = sqlMap.content || ''; break; case SqlType.INSERT: case SqlType.SELECT: case SqlType.UPDATE: case SqlType.DELETE: default: ret.sqlGenerator[key] = { type: sqlMap.type, template: NunjucksUtils.compile(this.name, key, sqlMap.sql || ''), raw: sqlMap.sql, }; break; } } return ret; } generate(name: string, data: object, timezone: string): GeneratedSql { const generator = this.sqlGenerator[name]; // istanbul ignore if if (!generator) { throw new Error(`No sql map named '${name}' in '${name}'.`); } const template = generator.template; const params: any[] = []; // Set timezone and params collector on env (template as any).env.timezone = timezone; const context = { ...data, [SQL_PARAMS]: params, }; const sql = template.render(context); return { sql, params }; } getType(name: string): SqlType { const generator = this.sqlGenerator[name]; // istanbul ignore if if (!generator) { throw new Error(`No sql map named '${name}' in '${name}'.`); } return generator.type; } getTemplateString(name: string) { const generator = this.sqlGenerator[name]; // istanbul ignore if if (!generator) { throw new Error(`No sql map named '${name}' in '${name}'.`); } return generator.raw; } } ================================================ FILE: core/dal-runtime/src/TemplateUtil.ts ================================================ import path from 'node:path'; import { ColumnModel, SpatialHelper } from '@eggjs/tegg/dal'; import { ColumnType } from '@eggjs/tegg-types'; import type { Geometry, GeometryCollection, Line, MultiLine, MultiPoint, MultiPolygon, Point, Polygon, } from '@eggjs/tegg-types'; export class TemplateUtil { static isSpatialType(columnModel: ColumnModel): boolean { switch (columnModel.type.type) { case ColumnType.GEOMETRY: case ColumnType.POINT: case ColumnType.LINESTRING: case ColumnType.POLYGON: case ColumnType.MULTIPOINT: case ColumnType.MULTILINESTRING: case ColumnType.MULTIPOLYGON: case ColumnType.GEOMETRYCOLLECTION: { return true; } default: { return false; } } } static importPath(tableModelPath: string, currentPath: string) { return path.relative(currentPath, tableModelPath); } static dbTypeToTsType(columnType: ColumnType): string { return `ColumnTsType['${columnType}']`; } static toJson(value: any): string { return JSON.stringify(JSON.stringify(value)); } static toPoint(point: Point): string { if (typeof point.x !== 'number' || typeof point.y !== 'number') { throw new Error(`invalidate point ${JSON.stringify(point)}`); } return `Point(${point.x}, ${point.y})`; } static toLine(val: Line): string { const points = val.map(t => TemplateUtil.toPoint(t)); return `LINESTRING(${points.join(',')})`; } static toPolygon(val: Polygon): string { const lines = val.map(t => TemplateUtil.toLine(t)); return `POLYGON(${lines.join(',')})`; } static toGeometry(val: Geometry): string { const type = SpatialHelper.getGeometryType(val); const filterName = TemplateUtil.getSpatialFilter(type); return TemplateUtil[filterName](val); } static toMultiPoint(val: MultiPoint): string { const points = val.map(t => TemplateUtil.toPoint(t)); return `MULTIPOINT(${points.join(',')})`; } static toMultiLine(val: MultiLine): string { const lines = val.map(t => TemplateUtil.toLine(t)); return `MULTILINESTRING(${lines.join(',')})`; } static toMultiPolygon(val: MultiPolygon): string { const polygon = val.map(t => TemplateUtil.toPolygon(t)); return `MULTIPOLYGON(${polygon.join(',')})`; } static toGeometryCollection(val: GeometryCollection): string { const geometries = val.map(t => { return TemplateUtil.toGeometry(t); }); return `GEOMETRYCOLLECTION(${geometries.join(',')})`; } static spatialFilter = { [ColumnType.POINT]: 'toPoint', [ColumnType.LINESTRING]: 'toLine', [ColumnType.POLYGON]: 'toPolygon', [ColumnType.GEOMETRY]: 'toGeometry', [ColumnType.MULTIPOINT]: 'toMultiPoint', [ColumnType.MULTILINESTRING]: 'toMultiLine', [ColumnType.MULTIPOLYGON]: 'toMultiPolygon', [ColumnType.GEOMETRYCOLLECTION]: 'toGeometryCollection', }; static getSpatialFilter(columnType: ColumnType) { const filter = TemplateUtil.spatialFilter[columnType]; if (!filter) { throw new Error(`type ${columnType} is not spatial type`); } return filter; } } ================================================ FILE: core/dal-runtime/src/templates/base_dao.njk ================================================ {% macro newDataLogic(columns, old, new) %} let tmp; {% for column in table.columns %} // empty-line tmp = {{ old }}.{{ column.propertyName }}; if (tmp !== undefined) { {{ new }}.${{ column.propertyName }} = tmp; } {% endfor %} {% endmacro %} {% macro findLogic(funcName, sqlName, idx, uniq) %} public async {{ funcName }}( {% for key in idx.keys %} ${{ key.propertyName }}: {{columnMap[key.propertyName].type.type | dbTypeToTSType}}{% if loop.last !== true %},{% endif%} {% endfor %} ): Promise<{{ clazzName }}{{ '| null' if uniq else '[]' }}> { return this.dataSource.{{ 'executeScalar' if uniq else 'execute' }}('{{ sqlName }}', { {% for key in idx.keys %} ${{ key.propertyName }}, {% endfor %} }); } {% endmacro %} {% macro generatePrimaryType(primary) %} {% if (primary.keys | length) === 1 %} {{primary.keys[0].propertyName}}: {{ columnMap[primary.keys[0].propertyName].type.type | dbTypeToTSType}} {% else %} primary: { {% for key in primary.keys %} {{ key.propertyName }}: {{columnMap[key.propertyName].type.type | dbTypeToTSType}}{% if loop.last !== true %},{% endif%} {% endfor %} } {% endif %} {% endmacro %} {% macro generateUpdateValue(primary) %} {% if (primary.keys | length) === 1 %} const newData: Record = { primary: { {{primary.keys[0].propertyName}}, }, }; {% else %} const newData: Record = { primary, }; {% endif %} {% endmacro %} {% macro generateDeleteValue(primary) %} {% if (primary.keys | length) === 1 %} { {{primary.keys[0].propertyName}}, } {% else %} primary {% endif %} {% endmacro %} {% macro generateInsertType(primary) %} Optional<{{clazzName}}, {% for key in primary.keys %} '{{ key.propertyName }}'{% if loop.last !== true %}|{% endif%} {% endfor %} > {% endmacro %} import fs from 'node:fs'; import path from 'node:path'; import type { InsertResult, UpdateResult, DeleteResult } from '{{dalPkg}}'; import { Inject } from '{{teggPkg}}'; import { Dao } from '{{teggPkg}}/dal'; import { DataSource, DataSourceInjectName, DataSourceQualifier, ColumnTsType } from '{{dalPkg}}'; import { {{ clazzName }} } from '{{ tableModelPath }}'; import {{ clazzName }}Extension from '{{ extensionPath }}'; import Structure from '{{ structurePath }}'; const SQL = Symbol('Dao#sql'); // empty-line type Optional = Omit & Partial; /** * 自动生成的 {{ clazzName }}DAO 基类 * @class Base{{ clazzName }}DAO * @classdesc 该文件由 {{teggPkg}} 自动生成,请**不要**修改它! */ /* istanbul ignore next */ @Dao() export class Base{{ clazzName }}DAO { static clazzModel = {{ clazzName }}; static clazzExtension = {{ clazzName }}Extension; static tableStature = Structure; static get tableSql() { if (!this[SQL]) { this[SQL] = fs.readFileSync(path.join(__dirname, '../../structure/{{clazzName}}.sql'), 'utf8'); } return this[SQL]; } @Inject({ name: DataSourceInjectName, }) @DataSourceQualifier('{{moduleName}}.{{ table.dataSourceName }}.{{ clazzName }}') protected readonly dataSource: DataSource<{{clazzName}}>; // empty-line {# insert: 插入 #} public async insert(raw: {{generateInsertType(primaryIndex)}}): Promise { const data: Record = {}; {{ newDataLogic(columns, 'raw', 'data') }} // empty-line return this.dataSource.executeRawScalar('insert', data); } // empty-line {# update: 更新 #} public async update({{generatePrimaryType(primaryIndex)}}, data: Partial<{{ ((clazzName)) }}>): Promise { // empty-line {{ generateUpdateValue(primaryIndex) }} {{ newDataLogic(columns, 'data', 'newData') }} // empty-line return this.dataSource.executeRawScalar('update', newData); } {% for funcName in [ 'delete', 'del' ] %} // empty-line {# delete: 删除 #} public async {{ funcName }}({{generatePrimaryType(primaryIndex)}}): Promise { return this.dataSource.executeRawScalar('delete', {{generateDeleteValue(primaryIndex)}}); } {% endfor %} {% for idx in table.indices %} // empty-line {# 某个索引 #} {% if idx.type !== 'PRIMARY' %} {% set tmpName = ((idx.keys[0].propertyName if (idx.keys | length) === 1 else idx.name) | pascalCase) %} {% set findName = 'findBy' + tmpName %} {% set findOneName = 'findOneBy' + tmpName %} {{ findLogic(findName, findName, idx, false) }} // empty-line {{ findLogic(findOneName, findOneName, idx, true) }} {% endif %} {% endfor %} // empty-line {# 某个索引 #} {% if primaryIndex %} {% set tmpName = ((primaryIndex.keys[0].propertyName if (primaryIndex.keys | length) === 1 else primaryIndex.name) | pascalCase) %} {% set findName = 'findBy' + tmpName %} {% set findOneName = 'findOneBy' + tmpName %} {{ findLogic(findName, 'findByPrimary', primaryIndex, true) }} {% if (primaryIndex.keys | length) > 0 %} // empty-line {{ findLogic('findByPrimary', 'findByPrimary', primaryIndex, true) }} {% endif %} {% endif %} } // empty-line ================================================ FILE: core/dal-runtime/src/templates/dao.njk ================================================ import { SingletonProto, AccessLevel } from '{{teggPkg}}'; import { Base{{ clazzName }}DAO } from './base/Base{{ clazzName }}DAO'; // empty-line /** * {{ clazzName }}DAO 类 {% if user.name %} * @author {{ user.name }} {% if user.email %}<{{ user.email }}>{% endif %} {% endif %} * @class {{ clazzName }}DAO * @classdesc 在此扩展关于 {{ clazzName }} 数据的一切操作 * @extends Base{{ clazzName }}DAO */ @SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) export default class {{ clazzName }}DAO extends Base{{ clazzName }}DAO { // empty-line } // empty-line ================================================ FILE: core/dal-runtime/src/templates/extension.njk ================================================ import { SqlMap } from '{{dalPkg}}'; // empty-line /** * Define Custom SQLs * * import { SqlMap, SqlType } from '{{dalPkg}}'; * * export default { * findByName: { * type: SqlType.SELECT, * sql: 'SELECT {{ allColumns }} from foo where name = {{ name }}' * }, * } */ export default { // empty-line } as Record; ================================================ FILE: core/dal-runtime/test/CodeGenerator.test.ts ================================================ import assert from 'node:assert'; import path from 'node:path'; import fs from 'node:fs/promises'; import { Foo } from './fixtures/modules/generate_codes/Foo'; import { MultiPrimaryKey } from './fixtures/modules/generate_codes/MultiPrimaryKey'; import { TableModel } from '@eggjs/dal-decorator'; import { CodeGenerator } from '../src/CodeGenerator'; describe('test/CodeGenerator.test.ts', () => { it('BaseDao should work', async () => { const generator = new CodeGenerator({ moduleDir: path.join(__dirname, './fixtures/modules/generate_codes'), moduleName: 'dal', dalPkg: '@eggjs/dal-decorator', }); const fooModel = TableModel.build(Foo); await generator.generate(fooModel); const multiPrimaryKeyTableModel = TableModel.build(MultiPrimaryKey); await generator.generate(multiPrimaryKeyTableModel); assert(fooModel); }); it('should not overwrite Dao file', async () => { const generator = new CodeGenerator({ moduleDir: path.join(__dirname, './fixtures/modules/generate_codes_not_overwrite_dao'), moduleName: 'dal', dalPkg: '@eggjs/dal-decorator', }); const fooModel = TableModel.build(Foo); await generator.generate(fooModel); const multiPrimaryKeyTableModel = TableModel.build(MultiPrimaryKey); await generator.generate(multiPrimaryKeyTableModel); const daoFile = await fs.readFile(path.join(__dirname, './fixtures/modules/generate_codes_not_overwrite_dao/dal/dao/FooDAO.ts'), 'utf8'); assert(/customFind/.test(daoFile)); const extensionFile = await fs.readFile(path.join(__dirname, './fixtures/modules/generate_codes_not_overwrite_dao/dal/extension/FooExtension.ts'), 'utf8'); assert(/customFind/.test(extensionFile)); }); it('should generate to src first', async () => { const generator = new CodeGenerator({ moduleDir: path.join(__dirname, './fixtures/modules/generate_codes_to_src'), moduleName: 'dal', dalPkg: '@eggjs/dal-decorator', }); const fooModel = TableModel.build(Foo); await generator.generate(fooModel); const daoFile = await fs.readFile(path.join(__dirname, './fixtures/modules/generate_codes_to_src/src/dal/dao/FooDAO.ts'), 'utf8'); assert(daoFile); }); }); ================================================ FILE: core/dal-runtime/test/DAO.test.ts ================================================ import assert from 'node:assert'; import path from 'node:path'; import { TableModel } from '@eggjs/dal-decorator'; import { MysqlDataSource } from '../src/MySqlDataSource'; import { SqlMapLoader } from '../src/SqlMapLoader'; import { Foo } from './fixtures/modules/dal/Foo'; import { DataSource } from '../src/DataSource'; import FooDAO from './fixtures/modules/dal/dal/dao/FooDAO'; import { DatabaseForker } from '../src/DatabaseForker'; import { BaseFooDAO } from './fixtures/modules/dal/dal/dao/base/BaseFooDAO'; describe('test/DAO.test.ts', () => { let dataSource: DataSource; let tableModel: TableModel; let forker: DatabaseForker; before(async () => { const mysqlOptions = { name: 'foo', host: '127.0.0.1', user: 'root', database: 'test_runtime', timezone: '+08:00', initSql: 'SET GLOBAL time_zone = \'+08:00\';', forkDb: true, }; forker = new DatabaseForker('unittest', mysqlOptions); await forker.forkDb(path.join(__dirname, './fixtures/modules/dal')); const mysql = new MysqlDataSource(mysqlOptions); await mysql.ready(); tableModel = TableModel.build(Foo); const sqlMapLoader = new SqlMapLoader(tableModel, BaseFooDAO, console as any); const sqlMap = sqlMapLoader.load(); dataSource = new DataSource(tableModel, mysql, sqlMap); }); after(async () => { await forker.destroy(); }); it('execute should work', async () => { const foo = new Foo(); foo.name = 'name'; foo.col1 = 'col1'; foo.bitColumn = Buffer.from([ 0, 0 ]); foo.boolColumn = 0; foo.tinyIntColumn = 0; foo.smallIntColumn = 1; foo.mediumIntColumn = 3; foo.intColumn = 3; foo.bigIntColumn = '00099'; foo.decimalColumn = '00002.33333'; foo.floatColumn = 2.3; foo.doubleColumn = 2.3; foo.dateColumn = new Date('2024-03-16T16:00:00.000Z'); foo.dateTimeColumn = new Date('2024-03-16T01:26:58.677Z'); foo.timestampColumn = new Date('2024-03-16T01:26:58.677Z'); foo.timeColumn = '838:59:50.123'; foo.yearColumn = 2024; foo.varCharColumn = 'var_char'; foo.binaryColumn = Buffer.from('b'); foo.varBinaryColumn = Buffer.from('var_binary'); foo.tinyBlobColumn = Buffer.from('tiny_blob'); foo.tinyTextColumn = 'text'; foo.blobColumn = Buffer.from('blob'); foo.textColumn = 'text'; foo.mediumBlobColumn = Buffer.from('medium_blob'); foo.longBlobColumn = Buffer.from('long_blob'); foo.mediumTextColumn = 'medium_text'; foo.longTextColumn = 'long_text'; foo.enumColumn = 'A'; foo.setColumn = 'B'; foo.geometryColumn = { x: 10, y: 10 }; foo.pointColumn = { x: 10, y: 10 }; foo.lineStringColumn = [ { x: 15, y: 15 }, { x: 20, y: 20 }, ]; foo.polygonColumn = [ [ { x: 0, y: 0 }, { x: 10, y: 0 }, { x: 10, y: 10 }, { x: 0, y: 10 }, { x: 0, y: 0 }, ], [ { x: 5, y: 5 }, { x: 7, y: 5 }, { x: 7, y: 7 }, { x: 5, y: 7 }, { x: 5, y: 5 }, ], ]; foo.multipointColumn = [ { x: 0, y: 0 }, { x: 20, y: 20 }, { x: 60, y: 60 }, ]; foo.multiLineStringColumn = [ [ { x: 10, y: 10 }, { x: 20, y: 20 }, ], [ { x: 15, y: 15 }, { x: 30, y: 15 }, ], ]; foo.multiPolygonColumn = [ [ [ { x: 0, y: 0 }, { x: 10, y: 0 }, { x: 10, y: 10 }, { x: 0, y: 10 }, { x: 0, y: 0 }, ], ], [ [ { x: 5, y: 5 }, { x: 7, y: 5 }, { x: 7, y: 7 }, { x: 5, y: 7 }, { x: 5, y: 5 }, ], ], ]; foo.geometryCollectionColumn = [ { x: 10, y: 10 }, { x: 30, y: 30 }, [ { x: 15, y: 15 }, { x: 20, y: 20 }, ], ]; foo.jsonColumn = { hello: 'json', }; const fooDao = new FooDAO(); (fooDao as any).dataSource = dataSource; const insertResult = await fooDao.insert(foo); assert(insertResult); foo.id = insertResult.insertId; const updateResult = await fooDao.update(foo.id, { name: 'update_name_2', }); assert(updateResult); assert.equal(updateResult.affectedRows, 1); foo.name = 'update_name_2'; const fooRow = await fooDao.findByPrimary(foo.id); assert.deepStrictEqual(fooRow, foo); const fooRows = await fooDao.findByCol1(foo.col1); assert.equal(fooRows.length, 1); assert.deepStrictEqual(fooRows[0], foo); await fooDao.delete(foo.id); const fooRow2 = await fooDao.findByPrimary(foo.id); assert.equal(fooRow2, null); }); }); ================================================ FILE: core/dal-runtime/test/DataSource.test.ts ================================================ import assert from 'node:assert'; import path from 'node:path'; import mm from 'mm'; import { RDSClient } from '@eggjs/rds'; import { DeleteResult, InsertResult, UpdateResult } from '@eggjs/rds/lib/types'; import { TableModel } from '@eggjs/dal-decorator'; import { MysqlDataSource } from '../src/MySqlDataSource'; import { SqlMapLoader } from '../src/SqlMapLoader'; import { Foo } from './fixtures/modules/dal/Foo'; import { DataSource } from '../src/DataSource'; import { TableModelInstanceBuilder } from '../src/TableModelInstanceBuilder'; import { DatabaseForker } from '../src/DatabaseForker'; import { BaseFooDAO } from './fixtures/modules/dal/dal/dao/base/BaseFooDAO'; describe('test/Datasource.test.ts', () => { const mysqlOptions = { name: 'foo', host: '127.0.0.1', user: 'root', database: 'test_runtime', timezone: '+08:00', initSql: 'SET GLOBAL time_zone = \'+08:00\';', forkDb: true, }; describe('init', () => { afterEach(() => { mm.restore(); }); it('init failed should throw error', async () => { mm.errorOnce(RDSClient.prototype, 'query', new Error('fake error')); const query: any = RDSClient.prototype.query; const mysql = new MysqlDataSource({ ...mysqlOptions, initRetryTimes: 0 }); await assert.rejects(mysql.ready(), /fake error/); assert.strictEqual(query.called, 1); assert.deepStrictEqual(query.lastCalledArguments, [ mysqlOptions.initSql ]); }); it('init should retry', async () => { let i = 0; mm(RDSClient.prototype, 'query', () => { throw new Error(`fake error ${++i}`); }); const query: any = RDSClient.prototype.query; const mysql = new MysqlDataSource({ ...mysqlOptions, initRetryTimes: 3 }); await assert.rejects(mysql.ready(), /fake error 3/); assert.strictEqual(query.called, 3); }); it('should success after retry', async () => { let i = 0; mm(RDSClient.prototype, 'query', async () => { if (i === 0) { i++; throw new Error('fake error'); } }); const query: any = RDSClient.prototype.query; const mysql = new MysqlDataSource({ ...mysqlOptions, initRetryTimes: 2 }); await assert.doesNotReject(mysql.ready()); assert.strictEqual(query.called, 2); }); }); describe('execute', () => { let dataSource: DataSource; let tableModel: TableModel; let forker: DatabaseForker; before(async () => { forker = new DatabaseForker('unittest', mysqlOptions); await forker.forkDb(path.join(__dirname, './fixtures/modules/dal')); const mysql = new MysqlDataSource(mysqlOptions); await mysql.ready(); tableModel = TableModel.build(Foo); const sqlMapLoader = new SqlMapLoader(tableModel, BaseFooDAO, console as any); const sqlMap = sqlMapLoader.load(); dataSource = new DataSource(tableModel, mysql, sqlMap); }); after(async () => { await forker.destroy(); }); it('execute should work', async () => { const foo = new Foo(); foo.name = 'name'; foo.col1 = 'col1'; foo.bitColumn = Buffer.from([ 0, 0 ]); foo.boolColumn = 0; foo.tinyIntColumn = 0; foo.smallIntColumn = 1; foo.mediumIntColumn = 3; foo.intColumn = 3; foo.bigIntColumn = '00099'; foo.decimalColumn = '00002.33333'; foo.floatColumn = 2.3; foo.doubleColumn = 2.3; foo.dateColumn = new Date('2024-03-16T16:00:00.000Z'); foo.dateTimeColumn = new Date('2024-03-16T01:26:58.677Z'); foo.timestampColumn = new Date('2024-03-16T01:26:58.677Z'); foo.timeColumn = '838:59:50.123'; foo.yearColumn = 2024; foo.varCharColumn = 'var_char'; foo.binaryColumn = Buffer.from('b'); foo.varBinaryColumn = Buffer.from('var_binary'); foo.tinyBlobColumn = Buffer.from('tiny_blob'); foo.tinyTextColumn = 'text'; foo.blobColumn = Buffer.from('blob'); foo.textColumn = 'text'; foo.mediumBlobColumn = Buffer.from('medium_blob'); foo.longBlobColumn = Buffer.from('long_blob'); foo.mediumTextColumn = 'medium_text'; foo.longTextColumn = 'long_text'; foo.enumColumn = 'A'; foo.setColumn = 'B'; foo.geometryColumn = { x: 10, y: 10 }; foo.pointColumn = { x: 10, y: 10 }; foo.lineStringColumn = [ { x: 15, y: 15 }, { x: 20, y: 20 }, ]; foo.polygonColumn = [ [ { x: 0, y: 0 }, { x: 10, y: 0 }, { x: 10, y: 10 }, { x: 0, y: 10 }, { x: 0, y: 0 }, ], [ { x: 5, y: 5 }, { x: 7, y: 5 }, { x: 7, y: 7 }, { x: 5, y: 7 }, { x: 5, y: 5 }, ], ]; foo.multipointColumn = [ { x: 0, y: 0 }, { x: 20, y: 20 }, { x: 60, y: 60 }, ]; foo.multiLineStringColumn = [ [ { x: 10, y: 10 }, { x: 20, y: 20 }, ], [ { x: 15, y: 15 }, { x: 30, y: 15 }, ], ]; foo.multiPolygonColumn = [ [ [ { x: 0, y: 0 }, { x: 10, y: 0 }, { x: 10, y: 10 }, { x: 0, y: 10 }, { x: 0, y: 0 }, ], ], [ [ { x: 5, y: 5 }, { x: 7, y: 5 }, { x: 7, y: 7 }, { x: 5, y: 7 }, { x: 5, y: 5 }, ], ], ]; foo.geometryCollectionColumn = [ { x: 10, y: 10 }, { x: 30, y: 30 }, [ { x: 15, y: 15 }, { x: 20, y: 20 }, ], ]; foo.jsonColumn = { hello: 'json', }; const rowValue = TableModelInstanceBuilder.buildRow(foo, tableModel); const insertResult: InsertResult = await dataSource.executeRawScalar('insert', rowValue); assert(insertResult.insertId); foo.id = insertResult.insertId; const updateResult: UpdateResult = await dataSource.executeRawScalar('update', { primary: { id: insertResult.insertId, }, $name: 'update_name', }); assert.equal(updateResult.affectedRows, 1); foo.name = 'update_name'; const findRow = await dataSource.executeScalar('findByPrimary', { $id: insertResult.insertId, }); assert(findRow); assert.deepStrictEqual(findRow, foo); const deleteRow: DeleteResult = await dataSource.executeRawScalar('delete', { id: insertResult.insertId, }); assert.equal(deleteRow.affectedRows, 1); const findRow2 = await dataSource.executeScalar('findByPrimary', { $id: insertResult.insertId, }); assert.equal(findRow2, null); const res = await dataSource.paginate('findByPrimary', {}, 1, 10); assert(res.total === 0); }); it('executeType should work', async () => { const mysql = new MysqlDataSource({ ...mysqlOptions, supportBigNumbers: true, bigNumberStrings: true, executeType: 'execute', }); await mysql.ready(); const originExecute = (mysql as any).client.execute; let executeCalled = false; mm((mysql as any).client, 'execute', async (sql, params) => { executeCalled = true; return Reflect.apply(originExecute, (mysql as any).client, [ sql, params ]); }); tableModel = TableModel.build(Foo); const sqlMapLoader = new SqlMapLoader(tableModel, BaseFooDAO, console as any); const sqlMap = sqlMapLoader.load(); dataSource = new DataSource(tableModel, mysql, sqlMap); const foo = new Foo(); foo.name = 'name'; foo.col1 = 'col1'; foo.bitColumn = Buffer.from([ 0, 0 ]); foo.boolColumn = 0; foo.tinyIntColumn = 0; foo.smallIntColumn = 1; foo.mediumIntColumn = 3; foo.intColumn = 3; foo.bigIntColumn = '99'; foo.decimalColumn = '00002.33333'; foo.floatColumn = 0; foo.doubleColumn = 2.3; foo.dateColumn = new Date('2024-03-16T16:00:00.000Z'); foo.dateTimeColumn = new Date('2024-03-16T01:26:58.677Z'); foo.timestampColumn = new Date('2024-03-16T01:26:58.677Z'); foo.timeColumn = '838:59:50.123'; foo.yearColumn = 2024; foo.varCharColumn = 'var_char'; foo.binaryColumn = Buffer.from('b'); foo.varBinaryColumn = Buffer.from('var_binary'); foo.tinyBlobColumn = Buffer.from('tiny_blob'); foo.tinyTextColumn = 'text'; foo.blobColumn = Buffer.from('blob'); foo.textColumn = 'text'; foo.mediumBlobColumn = Buffer.from('medium_blob'); foo.longBlobColumn = Buffer.from('long_blob'); foo.mediumTextColumn = 'medium_text'; foo.longTextColumn = 'long_text'; foo.enumColumn = 'A'; foo.setColumn = 'B'; foo.geometryColumn = { x: 10, y: 10 }; foo.pointColumn = { x: 10, y: 10 }; foo.lineStringColumn = [ { x: 15, y: 15 }, { x: 20, y: 20 }, ]; foo.polygonColumn = [ [ { x: 0, y: 0 }, { x: 10, y: 0 }, { x: 10, y: 10 }, { x: 0, y: 10 }, { x: 0, y: 0 }, ], [ { x: 5, y: 5 }, { x: 7, y: 5 }, { x: 7, y: 7 }, { x: 5, y: 7 }, { x: 5, y: 5 }, ], ]; foo.multipointColumn = [ { x: 0, y: 0 }, { x: 20, y: 20 }, { x: 60, y: 60 }, ]; foo.multiLineStringColumn = [ [ { x: 10, y: 10 }, { x: 20, y: 20 }, ], [ { x: 15, y: 15 }, { x: 30, y: 15 }, ], ]; foo.multiPolygonColumn = [ [ [ { x: 0, y: 0 }, { x: 10, y: 0 }, { x: 10, y: 10 }, { x: 0, y: 10 }, { x: 0, y: 0 }, ], ], [ [ { x: 5, y: 5 }, { x: 7, y: 5 }, { x: 7, y: 7 }, { x: 5, y: 7 }, { x: 5, y: 5 }, ], ], ]; foo.geometryCollectionColumn = [ { x: 10, y: 10 }, { x: 30, y: 30 }, [ { x: 15, y: 15 }, { x: 20, y: 20 }, ], ]; foo.jsonColumn = { hello: 'json', }; const rowValue = TableModelInstanceBuilder.buildRow(foo, tableModel); const insertResult: InsertResult = await dataSource.executeRawScalar('insert', rowValue); assert(insertResult.insertId); foo.id = insertResult.insertId; const updateResult: UpdateResult = await dataSource.executeRawScalar('update', { primary: { id: insertResult.insertId, }, $name: 'update_name', }); assert.equal(updateResult.affectedRows, 1); foo.name = 'update_name'; const findRow = await dataSource.executeScalar('findByPrimary', { $id: insertResult.insertId, }); assert(findRow); assert.deepStrictEqual(findRow, foo); const deleteRow: DeleteResult = await dataSource.executeRawScalar('delete', { id: insertResult.insertId, }); assert.equal(deleteRow.affectedRows, 1); const findRow2 = await dataSource.executeScalar('findByPrimary', { $id: insertResult.insertId, }); assert.equal(findRow2, null); const res = await dataSource.paginate('findByPrimary', { $id: null, }, 1, 10); assert(res.total === 0); assert(executeCalled); }); }); }); ================================================ FILE: core/dal-runtime/test/SqlGenerator.test.ts ================================================ import assert from 'node:assert'; import { TableModel } from '@eggjs/dal-decorator'; import { Foo } from './fixtures/modules/dal/Foo'; import { SqlGenerator } from '../src/SqlGenerator'; import { AutoUpdateTime } from './fixtures/modules/dal/AutoUpdateTime'; import { FooIndexName } from './fixtures/modules/dal/FooIndexName'; describe('test/SqlGenerator.test.ts', () => { it('generator should work', () => { const generator = new SqlGenerator(); const fooModel = TableModel.build(Foo); const sql = generator.generate(fooModel); assert.equal(sql, 'CREATE TABLE IF NOT EXISTS egg_foo (\n' + ' id INT NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT \'the primary key\',\n' + ' name VARCHAR(100) NOT NULL UNIQUE KEY,\n' + ' col1 VARCHAR(100) NOT NULL,\n' + ' bit_column BIT(10) NOT NULL,\n' + ' bool_column BOOL NOT NULL,\n' + ' tiny_int_column TINYINT(5) UNSIGNED ZEROFILL NOT NULL,\n' + ' small_int_column SMALLINT(5) UNSIGNED ZEROFILL NOT NULL,\n' + ' medium_int_column MEDIUMINT(5) UNSIGNED ZEROFILL NOT NULL,\n' + ' int_column INT(5) UNSIGNED ZEROFILL NOT NULL,\n' + ' big_int_column BIGINT(5) UNSIGNED ZEROFILL NOT NULL,\n' + ' decimal_column DECIMAL(10,5) UNSIGNED ZEROFILL NOT NULL,\n' + ' float_column FLOAT(10,5) UNSIGNED ZEROFILL NOT NULL,\n' + ' double_column DOUBLE(10,5) UNSIGNED ZEROFILL NOT NULL,\n' + ' date_column DATE NOT NULL,\n' + ' date_time_column DATETIME(3) NOT NULL,\n' + ' timestamp_column TIMESTAMP(3) NULL,\n' + ' time_column TIME(3) NOT NULL,\n' + ' year_column YEAR NOT NULL,\n' + ' var_char_column VARCHAR(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,\n' + ' binary_column BINARY NOT NULL,\n' + ' var_binary_column VARBINARY(100) NOT NULL,\n' + ' tiny_blob_column TINYBLOB NOT NULL,\n' + ' tiny_text_column TINYTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,\n' + ' blob_column BLOB(100) NOT NULL,\n' + ' text_column TEXT(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,\n' + ' medium_blob_column MEDIUMBLOB NOT NULL,\n' + ' long_blob_column LONGBLOB NOT NULL,\n' + ' medium_text_column MEDIUMTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,\n' + ' long_text_column LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,\n' + ' enum_column ENUM(\'A\',\'B\') CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,\n' + ' set_column SET(\'A\',\'B\') CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,\n' + ' geometry_column GEOMETRY NOT NULL,\n' + ' point_column POINT NOT NULL,\n' + ' line_string_column LINESTRING NOT NULL,\n' + ' polygon_column POLYGON NOT NULL,\n' + ' multipoint_column MULTIPOINT NOT NULL,\n' + ' multi_line_string_column MULTILINESTRING NOT NULL,\n' + ' multi_polygon_column MULTIPOLYGON NOT NULL,\n' + ' geometry_collection_column GEOMETRYCOLLECTION NOT NULL,\n' + ' json_column JSON NOT NULL,\n' + ' FULLTEXT KEY idx_col1 (col1) COMMENT \'index comment\\n\',\n' + ' UNIQUE KEY uk_name_col1 (name,col1) USING BTREE COMMENT \'index comment\\n\'\n' + ') DEFAULT CHARACTER SET utf8mb4, DEFAULT COLLATE utf8mb4_unicode_ci, COMMENT=\'foo table\';'); }); it('generator auto update should work', () => { const generator = new SqlGenerator(); const autoUpdateTimeTableModel = TableModel.build(AutoUpdateTime); const sql = generator.generate(autoUpdateTimeTableModel); assert.equal(sql, 'CREATE TABLE IF NOT EXISTS auto_update_times (\n' + ' id INT NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT \'the primary key\',\n' + ' date DATETIME ON UPDATE CURRENT_TIMESTAMP NOT NULL UNIQUE KEY,\n' + ' date_2 DATETIME(3) ON UPDATE CURRENT_TIMESTAMP(3) NOT NULL UNIQUE KEY,\n' + ' date_3 TIMESTAMP ON UPDATE CURRENT_TIMESTAMP NOT NULL UNIQUE KEY,\n' + ' date_4 TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) NOT NULL UNIQUE KEY\n' + ') ;'); }); it('generator index name should work', () => { const generator = new SqlGenerator(); const fooIndexNameTableModel = TableModel.build(FooIndexName); const sql = generator.generate(fooIndexNameTableModel); assert.equal(sql, 'CREATE TABLE IF NOT EXISTS egg_foo (\n' + ' id INT NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT \'the primary key\',\n' + ' name VARCHAR(100) NOT NULL UNIQUE KEY,\n' + ' col1 VARCHAR(100) NOT NULL,\n' + ' bit_column BIT(10) NOT NULL,\n' + ' bool_column BOOL NOT NULL,\n' + ' FULLTEXT KEY idx_col1_bool_column (col1,bool_column) COMMENT \'index comment\\n\',\n' + ' UNIQUE KEY uk_name_col1_bit_column (name,col1,bit_column) USING BTREE COMMENT \'index comment\\n\'\n' + ') DEFAULT CHARACTER SET utf8mb4, DEFAULT COLLATE utf8mb4_unicode_ci, COMMENT=\'foo table\';', ); }); }); ================================================ FILE: core/dal-runtime/test/SqlUtil.test.ts ================================================ import { SqlUtil } from '../src/SqlUtil'; import assert from 'node:assert/strict'; it('should preserve SQL hint and remove normal comments', () => { const sql = 'SELECT /*+ INDEX(a) */ * FROM table /* this is a comment */ WHERE id = 1'; const result = SqlUtil.minify(sql); assert.strictEqual(result, 'SELECT /*+ INDEX(a) */ * FROM table WHERE id = 1'); }); ================================================ FILE: core/dal-runtime/test/TableSqlMap.test.ts ================================================ import assert from 'node:assert'; import { TableModel } from '@eggjs/dal-decorator'; import { Foo } from './fixtures/modules/dal/Foo'; import { SqlMapLoader } from '../src/SqlMapLoader'; import { BaseFooDAO } from './fixtures/modules/dal/dal/dao/base/BaseFooDAO'; describe('test/TableSqlMap.test.ts', () => { it('custom sql should work', () => { // const generator = new SqlGenerator(); const fooModel = TableModel.build(Foo); // const sql = generator.generate(fooModel); const sqlMapLoader = new SqlMapLoader(fooModel, BaseFooDAO, console); const tableSqlMap = sqlMapLoader.load(); const result = tableSqlMap.generate('findAll', {}, 'UTC'); assert.equal(result.sql, 'SELECT `id`,`name`,`col1`,`bit_column`,`bool_column`,`tiny_int_column`,`small_int_column`,`medium_int_column`,`int_column`,`big_int_column`,`decimal_column`,`float_column`,`double_column`,`date_column`,`date_time_column`,`timestamp_column`,`time_column`,`year_column`,`var_char_column`,`binary_column`,`var_binary_column`,`tiny_blob_column`,`tiny_text_column`,`blob_column`,`text_column`,`medium_blob_column`,`long_blob_column`,`medium_text_column`,`long_text_column`,`enum_column`,`set_column`,`geometry_column`,`point_column`,`line_string_column`,`polygon_column`,`multipoint_column`,`multi_line_string_column`,`multi_polygon_column`,`geometry_collection_column`,`json_column` from egg_foo;'); assert.deepEqual(result.params, []); }); it('param filter should work for parameterized queries', () => { const fooModel = TableModel.build(Foo); const sqlMapLoader = new SqlMapLoader(fooModel, BaseFooDAO, console); const tableSqlMap = sqlMapLoader.load(); const result = tableSqlMap.generate('customFind', { name: 'test' }, 'UTC'); assert(result.sql.includes('name = ?'), 'SQL should contain ? placeholder'); assert.deepEqual(result.params, [ 'test' ], 'params should contain the test value'); }); }); ================================================ FILE: core/dal-runtime/test/fixtures/modules/dal/AutoUpdateTime.ts ================================================ import { Column, ColumnType, Table, } from '@eggjs/dal-decorator'; @Table() export class AutoUpdateTime { @Column({ type: ColumnType.INT, }, { primaryKey: true, autoIncrement: true, comment: 'the primary key', }) id: number; @Column({ type: ColumnType.DATETIME, autoUpdate: true, }, { uniqueKey: true, }) date: Date; @Column({ type: ColumnType.DATETIME, precision: 3, autoUpdate: true, }, { uniqueKey: true, }) date2: Date; @Column({ type: ColumnType.TIMESTAMP, autoUpdate: true, }, { uniqueKey: true, }) date3: Date; @Column({ type: ColumnType.TIMESTAMP, precision: 3, autoUpdate: true, }, { uniqueKey: true, }) date4: Date; } ================================================ FILE: core/dal-runtime/test/fixtures/modules/dal/Foo.ts ================================================ import { Column, ColumnType, Geometry, GeometryCollection, Index, IndexStoreType, IndexType, Line, MultiLine, MultiPoint, MultiPolygon, Point, Polygon, Table, } from '@eggjs/dal-decorator'; @Table({ name: 'egg_foo', comment: 'foo table', // autoExtendSize: 1024, // autoIncrement: 100, // avgRowLength: 1024, characterSet: 'utf8mb4', collate: 'utf8mb4_unicode_ci', // compression: CompressionType.ZLIB, // encryption: true, // engine: 'NDB', // engineAttribute: '{"key":"value"}', // secondaryEngineAttribute: '{"key2":"value2"}', // insertMethod: InsertMethod.FIRST, // keyBlockSize: 1024, // maxRows: 1000000, // minRows: 100, // rowFormat: RowFormat.COMPRESSED, }) @Index({ keys: [ 'name', 'col1' ], type: IndexType.UNIQUE, storeType: IndexStoreType.BTREE, comment: 'index comment\n', // engineAttribute: '{"key":"value"}', // secondaryEngineAttribute: '{"key2":"value2"}', }) @Index({ keys: [ 'col1' ], type: IndexType.FULLTEXT, comment: 'index comment\n', // engineAttribute: '{"key":"value"}', // secondaryEngineAttribute: '{"key2":"value2"}', // parser: 'foo', }) export class Foo { @Column({ type: ColumnType.INT, }, { primaryKey: true, autoIncrement: true, comment: 'the primary key', }) id: number; @Column({ type: ColumnType.VARCHAR, length: 100, }, { uniqueKey: true, }) name: string; @Column({ type: ColumnType.VARCHAR, length: 100, }, { name: 'col1', }) col1: string; @Column({ type: ColumnType.BIT, length: 10, }) bitColumn: Buffer; @Column({ type: ColumnType.BOOL, }) boolColumn: 0 | 1; @Column({ type: ColumnType.TINYINT, length: 5, unsigned: true, zeroFill: true, }) tinyIntColumn: number; @Column({ type: ColumnType.SMALLINT, length: 5, unsigned: true, zeroFill: true, }) smallIntColumn: number; @Column({ type: ColumnType.MEDIUMINT, length: 5, unsigned: true, zeroFill: true, }) mediumIntColumn: number; @Column({ type: ColumnType.INT, length: 5, unsigned: true, zeroFill: true, }) intColumn: number; @Column({ type: ColumnType.BIGINT, length: 5, unsigned: true, zeroFill: true, }) bigIntColumn: string; @Column({ type: ColumnType.DECIMAL, length: 10, fractionalLength: 5, unsigned: true, zeroFill: true, }) decimalColumn: string; @Column({ type: ColumnType.FLOAT, length: 10, fractionalLength: 5, unsigned: true, zeroFill: true, }) floatColumn: number; @Column({ type: ColumnType.DOUBLE, length: 10, fractionalLength: 5, unsigned: true, zeroFill: true, }) doubleColumn: number; @Column({ type: ColumnType.DATE, }) dateColumn: Date; @Column({ type: ColumnType.DATETIME, precision: 3, }) dateTimeColumn: Date; @Column({ type: ColumnType.TIMESTAMP, precision: 3, }, { canNull: true, }) timestampColumn: Date; @Column({ type: ColumnType.TIME, precision: 3, }) timeColumn: string; @Column({ type: ColumnType.YEAR, }) yearColumn: number; @Column({ type: ColumnType.VARCHAR, length: 100, characterSet: 'utf8mb4', collate: 'utf8mb4_unicode_ci', }) varCharColumn: string; @Column({ type: ColumnType.BINARY, }) binaryColumn: Buffer; @Column({ type: ColumnType.VARBINARY, length: 100, }) varBinaryColumn: Buffer; @Column({ type: ColumnType.TINYBLOB, }) tinyBlobColumn: Buffer; @Column({ type: ColumnType.TINYTEXT, characterSet: 'utf8mb4', collate: 'utf8mb4_unicode_ci', }) tinyTextColumn: string; @Column({ type: ColumnType.BLOB, length: 100, }) blobColumn: Buffer; @Column({ type: ColumnType.TEXT, length: 100, characterSet: 'utf8mb4', collate: 'utf8mb4_unicode_ci', }) textColumn: string; @Column({ type: ColumnType.MEDIUMBLOB, }) mediumBlobColumn: Buffer; @Column({ type: ColumnType.LONGBLOB, }) longBlobColumn: Buffer; @Column({ type: ColumnType.MEDIUMTEXT, characterSet: 'utf8mb4', collate: 'utf8mb4_unicode_ci', }) mediumTextColumn: string; @Column({ type: ColumnType.LONGTEXT, characterSet: 'utf8mb4', collate: 'utf8mb4_unicode_ci', }) longTextColumn: string; @Column({ type: ColumnType.ENUM, enums: [ 'A', 'B' ], characterSet: 'utf8mb4', collate: 'utf8mb4_unicode_ci', }) enumColumn: string; @Column({ type: ColumnType.SET, enums: [ 'A', 'B' ], characterSet: 'utf8mb4', collate: 'utf8mb4_unicode_ci', }) setColumn: string; @Column({ type: ColumnType.GEOMETRY, // SRID: 4326, }) geometryColumn: Geometry; @Column({ type: ColumnType.POINT, // SRID: 4326, }) pointColumn: Point; @Column({ type: ColumnType.LINESTRING, // SRID: 4326, }) lineStringColumn: Line; @Column({ type: ColumnType.POLYGON, // SRID: 4326, }) polygonColumn: Polygon; @Column({ type: ColumnType.MULTIPOINT, // SRID: 4326, }) multipointColumn: MultiPoint; @Column({ type: ColumnType.MULTILINESTRING, // SRID: 4326, }) multiLineStringColumn: MultiLine; @Column({ type: ColumnType.MULTIPOLYGON, // SRID: 4326, }) multiPolygonColumn: MultiPolygon; @Column({ type: ColumnType.GEOMETRYCOLLECTION, // SRID: 4326, }) geometryCollectionColumn: GeometryCollection; @Column({ type: ColumnType.JSON, }) jsonColumn: object; } ================================================ FILE: core/dal-runtime/test/fixtures/modules/dal/FooIndexName.ts ================================================ import { Column, ColumnType, Index, IndexStoreType, IndexType, Table, } from '@eggjs/dal-decorator'; @Table({ name: 'egg_foo', comment: 'foo table', characterSet: 'utf8mb4', collate: 'utf8mb4_unicode_ci', }) @Index({ keys: [ 'name', 'col1', 'bitColumn' ], type: IndexType.UNIQUE, storeType: IndexStoreType.BTREE, comment: 'index comment\n', }) @Index({ keys: [ 'col1', 'boolColumn' ], type: IndexType.FULLTEXT, comment: 'index comment\n', }) export class FooIndexName { @Column({ type: ColumnType.INT, }, { primaryKey: true, autoIncrement: true, comment: 'the primary key', }) id: number; @Column({ type: ColumnType.VARCHAR, length: 100, }, { uniqueKey: true, }) name: string; @Column({ type: ColumnType.VARCHAR, length: 100, }, { name: 'col1', }) col1: string; @Column({ type: ColumnType.BIT, length: 10, }) bitColumn: Buffer; @Column({ type: ColumnType.BOOL, }) boolColumn: 0 | 1; } ================================================ FILE: core/dal-runtime/test/fixtures/modules/dal/package.json ================================================ { "name": "dal", "eggModule": { "name": "dal" } } ================================================ FILE: core/dal-runtime/test/fixtures/modules/generate_codes/Foo.ts ================================================ import { Column, ColumnType, Geometry, GeometryCollection, Index, IndexType, Line, IndexStoreType, MultiLine, MultiPoint, MultiPolygon, Point, Polygon, Table, } from '@eggjs/dal-decorator'; @Table({ name: 'egg_foo', comment: 'foo table', // autoExtendSize: 1024, // autoIncrement: 100, // avgRowLength: 1024, characterSet: 'utf8mb4', collate: 'utf8mb4_unicode_ci', // compression: CompressionType.ZLIB, // encryption: true, // engine: 'NDB', // engineAttribute: '{"key":"value"}', // secondaryEngineAttribute: '{"key2":"value2"}', // insertMethod: InsertMethod.FIRST, // keyBlockSize: 1024, // maxRows: 1000000, // minRows: 100, // rowFormat: RowFormat.COMPRESSED, }) @Index({ keys: [ 'name', 'col1' ], type: IndexType.UNIQUE, storeType: IndexStoreType.BTREE, comment: 'index comment\n', // engineAttribute: '{"key":"value"}', // secondaryEngineAttribute: '{"key2":"value2"}', }) @Index({ keys: [ 'col1' ], type: IndexType.FULLTEXT, comment: 'index comment\n', // engineAttribute: '{"key":"value"}', // secondaryEngineAttribute: '{"key2":"value2"}', // parser: 'foo', }) export class Foo { @Column({ type: ColumnType.INT, }, { primaryKey: true, autoIncrement: true, comment: 'the primary key', }) id: number; @Column({ type: ColumnType.VARCHAR, length: 100, }, { uniqueKey: true, }) name: string; @Column({ type: ColumnType.VARCHAR, length: 100, }, { name: 'col1', }) col1: string; @Column({ type: ColumnType.BIT, length: 10, }) bitColumn: Buffer; @Column({ type: ColumnType.BOOL, }) boolColumn: 0 | 1; @Column({ type: ColumnType.TINYINT, length: 5, unsigned: true, zeroFill: true, }) tinyIntColumn: number; @Column({ type: ColumnType.SMALLINT, length: 5, unsigned: true, zeroFill: true, }) smallIntColumn: number; @Column({ type: ColumnType.MEDIUMINT, length: 5, unsigned: true, zeroFill: true, }) mediumIntColumn: number; @Column({ type: ColumnType.INT, length: 5, unsigned: true, zeroFill: true, }) intColumn: number; @Column({ type: ColumnType.BIGINT, length: 5, unsigned: true, zeroFill: true, }) bigIntColumn: string; @Column({ type: ColumnType.DECIMAL, length: 10, fractionalLength: 5, unsigned: true, zeroFill: true, }) decimalColumn: string; @Column({ type: ColumnType.FLOAT, length: 10, fractionalLength: 5, unsigned: true, zeroFill: true, }) floatColumn: number; @Column({ type: ColumnType.DOUBLE, length: 10, fractionalLength: 5, unsigned: true, zeroFill: true, }) doubleColumn: number; @Column({ type: ColumnType.DATE, }) dateColumn: Date; @Column({ type: ColumnType.DATETIME, precision: 3, }) dateTimeColumn: Date; @Column({ type: ColumnType.TIMESTAMP, precision: 3, }) timestampColumn: Date; @Column({ type: ColumnType.TIME, precision: 3, }) timeColumn: string; @Column({ type: ColumnType.YEAR, }) yearColumn: number; @Column({ type: ColumnType.VARCHAR, length: 100, characterSet: 'utf8mb4', collate: 'utf8mb4_unicode_ci', }) varCharColumn: string; @Column({ type: ColumnType.BINARY, }) binaryColumn: Buffer; @Column({ type: ColumnType.VARBINARY, length: 100, }) varBinaryColumn: Buffer; @Column({ type: ColumnType.TINYBLOB, }) tinyBlobColumn: Buffer; @Column({ type: ColumnType.TINYTEXT, characterSet: 'utf8mb4', collate: 'utf8mb4_unicode_ci', }) tinyTextColumn: string; @Column({ type: ColumnType.BLOB, length: 100, }) blobColumn: Buffer; @Column({ type: ColumnType.TEXT, length: 100, characterSet: 'utf8mb4', collate: 'utf8mb4_unicode_ci', }) textColumn: string; @Column({ type: ColumnType.MEDIUMBLOB, }) mediumBlobColumn: Buffer; @Column({ type: ColumnType.LONGBLOB, }) longBlobColumn: Buffer; @Column({ type: ColumnType.MEDIUMTEXT, characterSet: 'utf8mb4', collate: 'utf8mb4_unicode_ci', }) mediumTextColumn: string; @Column({ type: ColumnType.LONGTEXT, characterSet: 'utf8mb4', collate: 'utf8mb4_unicode_ci', }) longTextColumn: string; @Column({ type: ColumnType.ENUM, enums: [ 'A', 'B' ], characterSet: 'utf8mb4', collate: 'utf8mb4_unicode_ci', }) enumColumn: string; @Column({ type: ColumnType.SET, enums: [ 'A', 'B' ], characterSet: 'utf8mb4', collate: 'utf8mb4_unicode_ci', }) setColumn: string; @Column({ type: ColumnType.GEOMETRY, // SRID: 4326, }) geometryColumn: Geometry; @Column({ type: ColumnType.POINT, // SRID: 4326, }) pointColumn: Point; @Column({ type: ColumnType.LINESTRING, // SRID: 4326, }) lineStringColumn: Line; @Column({ type: ColumnType.POLYGON, // SRID: 4326, }) polygonColumn: Polygon; @Column({ type: ColumnType.MULTIPOINT, // SRID: 4326, }) multipointColumn: MultiPoint; @Column({ type: ColumnType.MULTILINESTRING, // SRID: 4326, }) multiLineStringColumn: MultiLine; @Column({ type: ColumnType.MULTIPOLYGON, // SRID: 4326, }) multiPolygonColumn: MultiPolygon; @Column({ type: ColumnType.GEOMETRYCOLLECTION, // SRID: 4326, }) geometryCollectionColumn: GeometryCollection; @Column({ type: ColumnType.JSON, }) jsonColumn: object; } ================================================ FILE: core/dal-runtime/test/fixtures/modules/generate_codes/MultiPrimaryKey.ts ================================================ import { Column, ColumnType, Index, IndexType, Table } from '@eggjs/dal-decorator'; @Table({ name: 'multi_primary_key_table', comment: 'multi primary key table', }) @Index({ name: 'pk_id1_id2', type: IndexType.PRIMARY, keys: ['id1', 'id2'], }) export class MultiPrimaryKey { @Column({ type: ColumnType.INT, }, { autoIncrement: true, comment: 'the primary key', }) id1: number; @Column({ type: ColumnType.INT, }, { comment: 'the primary key', }) id2: number; @Column({ type: ColumnType.VARCHAR, length: 100, }, { uniqueKey: true, }) name: string; } ================================================ FILE: core/dal-runtime/test/fixtures/modules/generate_codes/package.json ================================================ { "name": "dal", "eggModule": { "name": "dal" } } ================================================ FILE: core/dal-runtime/test/fixtures/modules/generate_codes_not_overwrite_dao/Foo.ts ================================================ import { Column, ColumnType, Geometry, GeometryCollection, Index, IndexType, Line, IndexStoreType, MultiLine, MultiPoint, MultiPolygon, Point, Polygon, Table, } from '@eggjs/dal-decorator'; @Table({ name: 'egg_foo', comment: 'foo table', // autoExtendSize: 1024, // autoIncrement: 100, // avgRowLength: 1024, characterSet: 'utf8mb4', collate: 'utf8mb4_unicode_ci', // compression: CompressionType.ZLIB, // encryption: true, // engine: 'NDB', // engineAttribute: '{"key":"value"}', // secondaryEngineAttribute: '{"key2":"value2"}', // insertMethod: InsertMethod.FIRST, // keyBlockSize: 1024, // maxRows: 1000000, // minRows: 100, // rowFormat: RowFormat.COMPRESSED, }) @Index({ keys: [ 'name', 'col1' ], type: IndexType.UNIQUE, storeType: IndexStoreType.BTREE, comment: 'index comment\n', // engineAttribute: '{"key":"value"}', // secondaryEngineAttribute: '{"key2":"value2"}', }) @Index({ keys: [ 'col1' ], type: IndexType.FULLTEXT, comment: 'index comment\n', // engineAttribute: '{"key":"value"}', // secondaryEngineAttribute: '{"key2":"value2"}', // parser: 'foo', }) export class Foo { @Column({ type: ColumnType.INT, }, { primaryKey: true, autoIncrement: true, comment: 'the primary key', }) id: number; @Column({ type: ColumnType.VARCHAR, length: 100, }, { uniqueKey: true, }) name: string; @Column({ type: ColumnType.VARCHAR, length: 100, }, { name: 'col1', }) col1: string; @Column({ type: ColumnType.BIT, length: 10, }) bitColumn: Buffer; @Column({ type: ColumnType.BOOL, }) boolColumn: 0 | 1; @Column({ type: ColumnType.TINYINT, length: 5, unsigned: true, zeroFill: true, }) tinyIntColumn: number; @Column({ type: ColumnType.SMALLINT, length: 5, unsigned: true, zeroFill: true, }) smallIntColumn: number; @Column({ type: ColumnType.MEDIUMINT, length: 5, unsigned: true, zeroFill: true, }) mediumIntColumn: number; @Column({ type: ColumnType.INT, length: 5, unsigned: true, zeroFill: true, }) intColumn: number; @Column({ type: ColumnType.BIGINT, length: 5, unsigned: true, zeroFill: true, }) bigIntColumn: string; @Column({ type: ColumnType.DECIMAL, length: 10, fractionalLength: 5, unsigned: true, zeroFill: true, }) decimalColumn: string; @Column({ type: ColumnType.FLOAT, length: 10, fractionalLength: 5, unsigned: true, zeroFill: true, }) floatColumn: number; @Column({ type: ColumnType.DOUBLE, length: 10, fractionalLength: 5, unsigned: true, zeroFill: true, }) doubleColumn: number; @Column({ type: ColumnType.DATE, }) dateColumn: Date; @Column({ type: ColumnType.DATETIME, precision: 3, }) dateTimeColumn: Date; @Column({ type: ColumnType.TIMESTAMP, precision: 3, }) timestampColumn: Date; @Column({ type: ColumnType.TIME, precision: 3, }) timeColumn: string; @Column({ type: ColumnType.YEAR, }) yearColumn: number; @Column({ type: ColumnType.VARCHAR, length: 100, characterSet: 'utf8mb4', collate: 'utf8mb4_unicode_ci', }) varCharColumn: string; @Column({ type: ColumnType.BINARY, }) binaryColumn: Buffer; @Column({ type: ColumnType.VARBINARY, length: 100, }) varBinaryColumn: Buffer; @Column({ type: ColumnType.TINYBLOB, }) tinyBlobColumn: Buffer; @Column({ type: ColumnType.TINYTEXT, characterSet: 'utf8mb4', collate: 'utf8mb4_unicode_ci', }) tinyTextColumn: string; @Column({ type: ColumnType.BLOB, length: 100, }) blobColumn: Buffer; @Column({ type: ColumnType.TEXT, length: 100, characterSet: 'utf8mb4', collate: 'utf8mb4_unicode_ci', }) textColumn: string; @Column({ type: ColumnType.MEDIUMBLOB, }) mediumBlobColumn: Buffer; @Column({ type: ColumnType.LONGBLOB, }) longBlobColumn: Buffer; @Column({ type: ColumnType.MEDIUMTEXT, characterSet: 'utf8mb4', collate: 'utf8mb4_unicode_ci', }) mediumTextColumn: string; @Column({ type: ColumnType.LONGTEXT, characterSet: 'utf8mb4', collate: 'utf8mb4_unicode_ci', }) longTextColumn: string; @Column({ type: ColumnType.ENUM, enums: [ 'A', 'B' ], characterSet: 'utf8mb4', collate: 'utf8mb4_unicode_ci', }) enumColumn: string; @Column({ type: ColumnType.SET, enums: [ 'A', 'B' ], characterSet: 'utf8mb4', collate: 'utf8mb4_unicode_ci', }) setColumn: string; @Column({ type: ColumnType.GEOMETRY, // SRID: 4326, }) geometryColumn: Geometry; @Column({ type: ColumnType.POINT, // SRID: 4326, }) pointColumn: Point; @Column({ type: ColumnType.LINESTRING, // SRID: 4326, }) lineStringColumn: Line; @Column({ type: ColumnType.POLYGON, // SRID: 4326, }) polygonColumn: Polygon; @Column({ type: ColumnType.MULTIPOINT, // SRID: 4326, }) multipointColumn: MultiPoint; @Column({ type: ColumnType.MULTILINESTRING, // SRID: 4326, }) multiLineStringColumn: MultiLine; @Column({ type: ColumnType.MULTIPOLYGON, // SRID: 4326, }) multiPolygonColumn: MultiPolygon; @Column({ type: ColumnType.GEOMETRYCOLLECTION, // SRID: 4326, }) geometryCollectionColumn: GeometryCollection; @Column({ type: ColumnType.JSON, }) jsonColumn: object; } ================================================ FILE: core/dal-runtime/test/fixtures/modules/generate_codes_not_overwrite_dao/package.json ================================================ { "name": "dal", "eggModule": { "name": "dal" } } ================================================ FILE: core/dal-runtime/test/fixtures/modules/generate_codes_to_src/package.json ================================================ { "name": "dal", "eggModule": { "name": "dal" } } ================================================ FILE: core/dal-runtime/test/fixtures/modules/generate_codes_to_src/src/Foo.ts ================================================ import { Column, ColumnType, Geometry, GeometryCollection, Index, IndexType, Line, IndexStoreType, MultiLine, MultiPoint, MultiPolygon, Point, Polygon, Table, } from '@eggjs/dal-decorator'; @Table({ name: 'egg_foo', comment: 'foo table', // autoExtendSize: 1024, // autoIncrement: 100, // avgRowLength: 1024, characterSet: 'utf8mb4', collate: 'utf8mb4_unicode_ci', // compression: CompressionType.ZLIB, // encryption: true, // engine: 'NDB', // engineAttribute: '{"key":"value"}', // secondaryEngineAttribute: '{"key2":"value2"}', // insertMethod: InsertMethod.FIRST, // keyBlockSize: 1024, // maxRows: 1000000, // minRows: 100, // rowFormat: RowFormat.COMPRESSED, }) @Index({ keys: [ 'name', 'col1' ], type: IndexType.UNIQUE, storeType: IndexStoreType.BTREE, comment: 'index comment\n', // engineAttribute: '{"key":"value"}', // secondaryEngineAttribute: '{"key2":"value2"}', }) @Index({ keys: [ 'col1' ], type: IndexType.FULLTEXT, comment: 'index comment\n', // engineAttribute: '{"key":"value"}', // secondaryEngineAttribute: '{"key2":"value2"}', // parser: 'foo', }) export class Foo { @Column({ type: ColumnType.INT, }, { primaryKey: true, autoIncrement: true, comment: 'the primary key', }) id: number; @Column({ type: ColumnType.VARCHAR, length: 100, }, { uniqueKey: true, }) name: string; @Column({ type: ColumnType.VARCHAR, length: 100, }, { name: 'col1', }) col1: string; @Column({ type: ColumnType.BIT, length: 10, }) bitColumn: Buffer; @Column({ type: ColumnType.BOOL, }) boolColumn: 0 | 1; @Column({ type: ColumnType.TINYINT, length: 5, unsigned: true, zeroFill: true, }) tinyIntColumn: number; @Column({ type: ColumnType.SMALLINT, length: 5, unsigned: true, zeroFill: true, }) smallIntColumn: number; @Column({ type: ColumnType.MEDIUMINT, length: 5, unsigned: true, zeroFill: true, }) mediumIntColumn: number; @Column({ type: ColumnType.INT, length: 5, unsigned: true, zeroFill: true, }) intColumn: number; @Column({ type: ColumnType.BIGINT, length: 5, unsigned: true, zeroFill: true, }) bigIntColumn: string; @Column({ type: ColumnType.DECIMAL, length: 10, fractionalLength: 5, unsigned: true, zeroFill: true, }) decimalColumn: string; @Column({ type: ColumnType.FLOAT, length: 10, fractionalLength: 5, unsigned: true, zeroFill: true, }) floatColumn: number; @Column({ type: ColumnType.DOUBLE, length: 10, fractionalLength: 5, unsigned: true, zeroFill: true, }) doubleColumn: number; @Column({ type: ColumnType.DATE, }) dateColumn: Date; @Column({ type: ColumnType.DATETIME, precision: 3, }) dateTimeColumn: Date; @Column({ type: ColumnType.TIMESTAMP, precision: 3, }) timestampColumn: Date; @Column({ type: ColumnType.TIME, precision: 3, }) timeColumn: string; @Column({ type: ColumnType.YEAR, }) yearColumn: number; @Column({ type: ColumnType.VARCHAR, length: 100, characterSet: 'utf8mb4', collate: 'utf8mb4_unicode_ci', }) varCharColumn: string; @Column({ type: ColumnType.BINARY, }) binaryColumn: Buffer; @Column({ type: ColumnType.VARBINARY, length: 100, }) varBinaryColumn: Buffer; @Column({ type: ColumnType.TINYBLOB, }) tinyBlobColumn: Buffer; @Column({ type: ColumnType.TINYTEXT, characterSet: 'utf8mb4', collate: 'utf8mb4_unicode_ci', }) tinyTextColumn: string; @Column({ type: ColumnType.BLOB, length: 100, }) blobColumn: Buffer; @Column({ type: ColumnType.TEXT, length: 100, characterSet: 'utf8mb4', collate: 'utf8mb4_unicode_ci', }) textColumn: string; @Column({ type: ColumnType.MEDIUMBLOB, }) mediumBlobColumn: Buffer; @Column({ type: ColumnType.LONGBLOB, }) longBlobColumn: Buffer; @Column({ type: ColumnType.MEDIUMTEXT, characterSet: 'utf8mb4', collate: 'utf8mb4_unicode_ci', }) mediumTextColumn: string; @Column({ type: ColumnType.LONGTEXT, characterSet: 'utf8mb4', collate: 'utf8mb4_unicode_ci', }) longTextColumn: string; @Column({ type: ColumnType.ENUM, enums: [ 'A', 'B' ], characterSet: 'utf8mb4', collate: 'utf8mb4_unicode_ci', }) enumColumn: string; @Column({ type: ColumnType.SET, enums: [ 'A', 'B' ], characterSet: 'utf8mb4', collate: 'utf8mb4_unicode_ci', }) setColumn: string; @Column({ type: ColumnType.GEOMETRY, // SRID: 4326, }) geometryColumn: Geometry; @Column({ type: ColumnType.POINT, // SRID: 4326, }) pointColumn: Point; @Column({ type: ColumnType.LINESTRING, // SRID: 4326, }) lineStringColumn: Line; @Column({ type: ColumnType.POLYGON, // SRID: 4326, }) polygonColumn: Polygon; @Column({ type: ColumnType.MULTIPOINT, // SRID: 4326, }) multipointColumn: MultiPoint; @Column({ type: ColumnType.MULTILINESTRING, // SRID: 4326, }) multiLineStringColumn: MultiLine; @Column({ type: ColumnType.MULTIPOLYGON, // SRID: 4326, }) multiPolygonColumn: MultiPolygon; @Column({ type: ColumnType.GEOMETRYCOLLECTION, // SRID: 4326, }) geometryCollectionColumn: GeometryCollection; @Column({ type: ColumnType.JSON, }) jsonColumn: object; } ================================================ FILE: core/dal-runtime/tsconfig.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "dist", "baseUrl": "./", "target": "ES2020" }, "exclude": [ "dist", "node_modules", "test" ] } ================================================ FILE: core/dal-runtime/tsconfig.pub.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "dist", "baseUrl": "./", "target": "ES2020" }, "exclude": [ "dist", "node_modules", "test" ] } ================================================ FILE: core/dynamic-inject/CHANGELOG.md ================================================ # Change Log All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. ## [3.78.15](https://github.com/eggjs/tegg/compare/v3.78.14...v3.78.15) (2026-04-23) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.78.14](https://github.com/eggjs/tegg/compare/v3.78.13...v3.78.14) (2026-04-21) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.78.13](https://github.com/eggjs/tegg/compare/v3.78.12...v3.78.13) (2026-04-21) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.78.12](https://github.com/eggjs/tegg/compare/v3.78.11...v3.78.12) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.78.11](https://github.com/eggjs/tegg/compare/v3.78.10...v3.78.11) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.78.10](https://github.com/eggjs/tegg/compare/v3.78.9...v3.78.10) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.78.9](https://github.com/eggjs/tegg/compare/v3.78.8...v3.78.9) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.78.8](https://github.com/eggjs/tegg/compare/v3.78.7...v3.78.8) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.78.7](https://github.com/eggjs/tegg/compare/v3.78.6...v3.78.7) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.78.6](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.6) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.78.5](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.5) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.78.4](https://github.com/eggjs/tegg/compare/v3.78.3...v3.78.4) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.78.3](https://github.com/eggjs/tegg/compare/v3.78.2...v3.78.3) (2026-04-15) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.78.2](https://github.com/eggjs/tegg/compare/v3.78.1...v3.78.2) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.78.1](https://github.com/eggjs/tegg/compare/v3.78.0...v3.78.1) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject # [3.78.0](https://github.com/eggjs/tegg/compare/v3.77.2...v3.78.0) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.77.2](https://github.com/eggjs/tegg/compare/v3.77.1...v3.77.2) (2026-04-07) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.77.1](https://github.com/eggjs/tegg/compare/v3.77.0...v3.77.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject # [3.77.0](https://github.com/eggjs/tegg/compare/v3.76.1...v3.77.0) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.76.1](https://github.com/eggjs/tegg/compare/v3.76.0...v3.76.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject # [3.76.0](https://github.com/eggjs/tegg/compare/v3.75.1...v3.76.0) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.75.1](https://github.com/eggjs/tegg/compare/v3.75.0...v3.75.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject # [3.75.0](https://github.com/eggjs/tegg/compare/v3.74.0...v3.75.0) (2026-03-30) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject # [3.74.0](https://github.com/eggjs/tegg/compare/v3.73.0...v3.74.0) (2026-03-30) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject # [3.73.0](https://github.com/eggjs/tegg/compare/v3.72.0...v3.73.0) (2026-03-25) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject # [3.72.0](https://github.com/eggjs/tegg/compare/v3.71.2...v3.72.0) (2026-02-04) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.71.2](https://github.com/eggjs/tegg/compare/v3.71.1...v3.71.2) (2026-01-30) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.71.1](https://github.com/eggjs/tegg/compare/v3.71.0...v3.71.1) (2026-01-28) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject # [3.71.0](https://github.com/eggjs/tegg/compare/v3.70.1...v3.71.0) (2026-01-26) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.70.1](https://github.com/eggjs/tegg/compare/v3.70.0...v3.70.1) (2026-01-14) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject # [3.70.0](https://github.com/eggjs/tegg/compare/v3.69.0...v3.70.0) (2026-01-07) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject # [3.69.0](https://github.com/eggjs/tegg/compare/v3.68.0...v3.69.0) (2026-01-06) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject # [3.68.0](https://github.com/eggjs/tegg/compare/v3.67.2...v3.68.0) (2025-12-29) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.67.2](https://github.com/eggjs/tegg/compare/v3.67.1...v3.67.2) (2025-12-24) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.67.1](https://github.com/eggjs/tegg/compare/v3.67.0...v3.67.1) (2025-12-23) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject # [3.67.0](https://github.com/eggjs/tegg/compare/v3.66.0...v3.67.0) (2025-12-18) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject # [3.66.0](https://github.com/eggjs/tegg/compare/v3.65.3...v3.66.0) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.65.3](https://github.com/eggjs/tegg/compare/v3.65.2...v3.65.3) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.65.2](https://github.com/eggjs/tegg/compare/v3.65.1...v3.65.2) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.65.1](https://github.com/eggjs/tegg/compare/v3.65.0...v3.65.1) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject # [3.65.0](https://github.com/eggjs/tegg/compare/v3.64.5...v3.65.0) (2025-12-10) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.64.5](https://github.com/eggjs/tegg/compare/v3.64.4...v3.64.5) (2025-12-08) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.64.4](https://github.com/eggjs/tegg/compare/v3.64.3...v3.64.4) (2025-12-06) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.64.3](https://github.com/eggjs/tegg/compare/v3.64.2...v3.64.3) (2025-12-06) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.64.2](https://github.com/eggjs/tegg/compare/v3.64.1...v3.64.2) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.64.1](https://github.com/eggjs/tegg/compare/v3.64.0...v3.64.1) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject # [3.64.0](https://github.com/eggjs/tegg/compare/v3.63.2...v3.64.0) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.63.2](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.2) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.63.1](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.1) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject # [3.63.0](https://github.com/eggjs/tegg/compare/v3.62.3...v3.63.0) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.62.3](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.3) (2025-11-05) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.62.2](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.2) (2025-11-05) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.62.1](https://github.com/eggjs/tegg/compare/v3.62.0...v3.62.1) (2025-11-03) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject # [3.62.0](https://github.com/eggjs/tegg/compare/v3.61.0...v3.62.0) (2025-09-24) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject # [3.61.0](https://github.com/eggjs/tegg/compare/v3.60.3...v3.61.0) (2025-08-15) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.60.3](https://github.com/eggjs/tegg/compare/v3.60.2...v3.60.3) (2025-08-06) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.60.2](https://github.com/eggjs/tegg/compare/v3.60.1...v3.60.2) (2025-08-06) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.60.1](https://github.com/eggjs/tegg/compare/v3.60.0...v3.60.1) (2025-07-28) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject # [3.60.0](https://github.com/eggjs/tegg/compare/v3.59.1...v3.60.0) (2025-07-28) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.59.1](https://github.com/eggjs/tegg/compare/v3.59.0...v3.59.1) (2025-07-16) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject # [3.59.0](https://github.com/eggjs/tegg/compare/v3.58.0...v3.59.0) (2025-07-07) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject # [3.58.0](https://github.com/eggjs/tegg/compare/v3.57.14...v3.58.0) (2025-07-01) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.57.14](https://github.com/eggjs/tegg/compare/v3.57.13...v3.57.14) (2025-06-18) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.57.13](https://github.com/eggjs/tegg/compare/v3.57.12...v3.57.13) (2025-06-18) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.57.12](https://github.com/eggjs/tegg/compare/v3.57.11...v3.57.12) (2025-06-16) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.57.11](https://github.com/eggjs/tegg/compare/v3.57.10...v3.57.11) (2025-06-16) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.57.10](https://github.com/eggjs/tegg/compare/v3.57.9...v3.57.10) (2025-06-12) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.57.9](https://github.com/eggjs/tegg/compare/v3.57.8...v3.57.9) (2025-05-29) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.57.8](https://github.com/eggjs/tegg/compare/v3.57.7...v3.57.8) (2025-05-29) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.57.7](https://github.com/eggjs/tegg/compare/v3.57.6...v3.57.7) (2025-05-28) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.57.6](https://github.com/eggjs/tegg/compare/v3.57.5...v3.57.6) (2025-05-27) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.57.5](https://github.com/eggjs/tegg/compare/v3.57.4...v3.57.5) (2025-05-15) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.57.4](https://github.com/eggjs/tegg/compare/v3.57.3...v3.57.4) (2025-05-14) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.57.3](https://github.com/eggjs/tegg/compare/v3.57.2...v3.57.3) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.57.2](https://github.com/eggjs/tegg/compare/v3.57.1...v3.57.2) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.57.1](https://github.com/eggjs/tegg/compare/v3.57.0...v3.57.1) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject # [3.57.0](https://github.com/eggjs/tegg/compare/v3.56.3...v3.57.0) (2025-05-09) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.56.3](https://github.com/eggjs/tegg/compare/v3.56.2...v3.56.3) (2025-05-07) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.56.2](https://github.com/eggjs/tegg/compare/v3.56.1...v3.56.2) (2025-05-01) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.56.1](https://github.com/eggjs/tegg/compare/v3.56.0...v3.56.1) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject # [3.56.0](https://github.com/eggjs/tegg/compare/v3.55.0...v3.56.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject # [3.55.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.55.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject # [3.54.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.54.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject # [3.53.0](https://github.com/eggjs/tegg/compare/v3.52.1...v3.53.0) (2025-04-21) ### Features * add timeout metadata for http controller ([#301](https://github.com/eggjs/tegg/issues/301)) ([68980c2](https://github.com/eggjs/tegg/commit/68980c23de81dbc9bd86c1d8df7b3952f52aa5ce)) ## [3.52.1](https://github.com/eggjs/tegg/compare/v3.52.0...v3.52.1) (2025-03-14) ### Bug Fixes * add qualifier check ([#295](https://github.com/eggjs/tegg/issues/295)) ([6744088](https://github.com/eggjs/tegg/commit/674408810d77fe0f4b95b25790bcb3975e543e26)) # [3.52.0](https://github.com/eggjs/tegg/compare/v3.51.2...v3.52.0) (2024-12-30) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.51.2](https://github.com/eggjs/tegg/compare/v3.51.1...v3.51.2) (2024-12-09) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.51.1](https://github.com/eggjs/tegg/compare/v3.51.0...v3.51.1) (2024-11-06) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject # [3.51.0](https://github.com/eggjs/tegg/compare/v3.50.1...v3.51.0) (2024-10-30) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.50.1](https://github.com/eggjs/tegg/compare/v3.50.0...v3.50.1) (2024-10-23) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject # [3.50.0](https://github.com/eggjs/tegg/compare/v3.49.0...v3.50.0) (2024-10-22) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject # [3.49.0](https://github.com/eggjs/tegg/compare/v3.48.1...v3.49.0) (2024-10-21) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.48.1](https://github.com/eggjs/tegg/compare/v3.48.0...v3.48.1) (2024-10-14) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject # [3.48.0](https://github.com/eggjs/tegg/compare/v3.47.2...v3.48.0) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.47.2](https://github.com/eggjs/tegg/compare/v3.47.1...v3.47.2) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.47.1](https://github.com/eggjs/tegg/compare/v3.47.0...v3.47.1) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject # [3.47.0](https://github.com/eggjs/tegg/compare/v3.46.4...v3.47.0) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.46.4](https://github.com/eggjs/tegg/compare/v3.46.3...v3.46.4) (2024-10-09) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.46.3](https://github.com/eggjs/tegg/compare/v3.46.2...v3.46.3) (2024-10-08) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.46.2](https://github.com/eggjs/tegg/compare/v3.46.1...v3.46.2) (2024-10-07) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.46.1](https://github.com/eggjs/tegg/compare/v3.46.0...v3.46.1) (2024-09-30) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject # [3.46.0](https://github.com/eggjs/tegg/compare/v3.45.0...v3.46.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject # [3.45.0](https://github.com/eggjs/tegg/compare/v3.44.1...v3.45.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.44.1](https://github.com/eggjs/tegg/compare/v3.44.0...v3.44.1) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject # [3.44.0](https://github.com/eggjs/tegg/compare/v3.43.2...v3.44.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.43.2](https://github.com/eggjs/tegg/compare/v3.43.1...v3.43.2) (2024-09-14) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.43.1](https://github.com/eggjs/tegg/compare/v3.43.0...v3.43.1) (2024-09-14) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject # [3.43.0](https://github.com/eggjs/tegg/compare/v3.42.0...v3.43.0) (2024-09-13) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject # [3.42.0](https://github.com/eggjs/tegg/compare/v3.41.0...v3.42.0) (2024-09-10) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject # [3.41.0](https://github.com/eggjs/tegg/compare/v3.40.1...v3.41.0) (2024-08-26) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.40.1](https://github.com/eggjs/tegg/compare/v3.40.0...v3.40.1) (2024-08-23) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject # [3.40.0](https://github.com/eggjs/tegg/compare/v3.39.5...v3.40.0) (2024-08-22) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.39.5](https://github.com/eggjs/tegg/compare/v3.39.4...v3.39.5) (2024-08-09) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.39.4](https://github.com/eggjs/tegg/compare/v3.39.3...v3.39.4) (2024-07-09) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.39.3](https://github.com/eggjs/tegg/compare/v3.39.2...v3.39.3) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.39.2](https://github.com/eggjs/tegg/compare/v3.39.1...v3.39.2) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.39.1](https://github.com/eggjs/tegg/compare/v3.39.0...v3.39.1) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject # [3.39.0](https://github.com/eggjs/tegg/compare/v3.38.0...v3.39.0) (2024-04-19) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject # [3.38.0](https://github.com/eggjs/tegg/compare/v3.37.3...v3.38.0) (2024-04-18) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.37.3](https://github.com/eggjs/tegg/compare/v3.37.2...v3.37.3) (2024-04-17) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.37.2](https://github.com/eggjs/tegg/compare/v3.37.1...v3.37.2) (2024-04-17) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.37.1](https://github.com/eggjs/tegg/compare/v3.37.0...v3.37.1) (2024-04-16) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject # [3.37.0](https://github.com/eggjs/tegg/compare/v3.36.3...v3.37.0) (2024-04-16) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.36.3](https://github.com/eggjs/tegg/compare/v3.36.2...v3.36.3) (2024-04-10) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.36.2](https://github.com/eggjs/tegg/compare/v3.36.1...v3.36.2) (2024-04-08) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.36.1](https://github.com/eggjs/tegg/compare/v3.36.0...v3.36.1) (2024-04-07) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject # [3.36.0](https://github.com/eggjs/tegg/compare/v3.35.1...v3.36.0) (2024-04-02) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.35.1](https://github.com/eggjs/tegg/compare/v3.35.0...v3.35.1) (2024-03-26) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject # [3.35.0](https://github.com/eggjs/tegg/compare/v3.34.0...v3.35.0) (2024-03-26) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject # [3.34.0](https://github.com/eggjs/tegg/compare/v3.33.1...v3.34.0) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.33.1](https://github.com/eggjs/tegg/compare/v3.33.0...v3.33.1) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject # [3.33.0](https://github.com/eggjs/tegg/compare/v3.32.0...v3.33.0) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject # [3.32.0](https://github.com/eggjs/tegg/compare/v3.31.0...v3.32.0) (2024-02-19) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject # [3.31.0](https://github.com/eggjs/tegg/compare/v3.30.1...v3.31.0) (2024-01-31) ### Features * add getEggObjects API to fetch all instances ([#189](https://github.com/eggjs/tegg/issues/189)) ([f8592c2](https://github.com/eggjs/tegg/commit/f8592c2cd141d01b4f1730b1e3d66e35c3e1ce05)) ## [3.30.1](https://github.com/eggjs/tegg/compare/v3.30.0...v3.30.1) (2024-01-25) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject # [3.30.0](https://github.com/eggjs/tegg/compare/v3.29.0...v3.30.0) (2024-01-17) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject # [3.29.0](https://github.com/eggjs/tegg/compare/v3.28.2...v3.29.0) (2023-12-26) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.28.2](https://github.com/eggjs/tegg/compare/v3.28.1...v3.28.2) (2023-12-12) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.28.1](https://github.com/eggjs/tegg/compare/v3.28.0...v3.28.1) (2023-12-11) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject # [3.28.0](https://github.com/eggjs/tegg/compare/v3.27.0...v3.28.0) (2023-12-10) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject # [3.27.0](https://github.com/eggjs/tegg/compare/v3.26.0...v3.27.0) (2023-11-23) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.25.2](https://github.com/eggjs/tegg/compare/v3.25.1...v3.25.2) (2023-11-06) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.25.1](https://github.com/eggjs/tegg/compare/v3.25.0...v3.25.1) (2023-11-03) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject # [3.25.0](https://github.com/eggjs/tegg/compare/v3.24.0...v3.25.0) (2023-11-03) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject # [3.24.0](https://github.com/eggjs/tegg/compare/v3.23.0...v3.24.0) (2023-10-26) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject # [3.23.0](https://github.com/eggjs/tegg/compare/v3.22.0...v3.23.0) (2023-09-20) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject # [3.20.0](https://github.com/eggjs/tegg/compare/v3.19.0...v3.20.0) (2023-09-07) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject # [3.19.0](https://github.com/eggjs/tegg/compare/v3.18.1...v3.19.0) (2023-08-31) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.18.1](https://github.com/eggjs/tegg/compare/v3.18.0...v3.18.1) (2023-08-29) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject # [3.18.0](https://github.com/eggjs/tegg/compare/v3.17.0...v3.18.0) (2023-08-29) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject # [3.17.0](https://github.com/eggjs/tegg/compare/v3.16.0...v3.17.0) (2023-08-24) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject # [3.16.0](https://github.com/eggjs/tegg/compare/v3.15.0...v3.16.0) (2023-08-24) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.14.3](https://github.com/eggjs/tegg/compare/v3.14.2...v3.14.3) (2023-08-14) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.14.2](https://github.com/eggjs/tegg/compare/v3.14.1...v3.14.2) (2023-08-14) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.14.1](https://github.com/eggjs/tegg/compare/v3.14.0...v3.14.1) (2023-08-11) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject # [3.14.0](https://github.com/eggjs/tegg/compare/v3.13.0...v3.14.0) (2023-07-31) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject # [3.13.0](https://github.com/eggjs/tegg/compare/v3.12.0...v3.13.0) (2023-07-25) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject # [3.12.0](https://github.com/eggjs/tegg/compare/v3.11.1...v3.12.0) (2023-07-13) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.11.1](https://github.com/eggjs/tegg/compare/v3.11.0...v3.11.1) (2023-06-29) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject # [3.11.0](https://github.com/eggjs/tegg/compare/v3.10.0...v3.11.0) (2023-06-29) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject # [3.10.0](https://github.com/eggjs/tegg/compare/v3.9.0...v3.10.0) (2023-06-28) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject # [3.8.0](https://github.com/eggjs/tegg/compare/v3.7.0...v3.8.0) (2023-05-30) ### Features * impl EggObjectLifecycle hook in decorator ([#119](https://github.com/eggjs/tegg/issues/119)) ([cced8a2](https://github.com/eggjs/tegg/commit/cced8a2e009c33d5040fa21d00409fddef471b0e)) # [3.7.0](https://github.com/eggjs/tegg/compare/v3.6.3...v3.7.0) (2023-04-03) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject # [3.5.0](https://github.com/eggjs/tegg/compare/v3.4.1...v3.5.0) (2023-02-10) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.2.3](https://github.com/eggjs/tegg/compare/v3.2.2...v3.2.3) (2023-01-16) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ## [3.2.2](https://github.com/eggjs/tegg/compare/v3.2.1...v3.2.2) (2023-01-06) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject # [3.1.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.1.0) (2022-12-27) ### Features * **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) # [3.0.0](https://github.com/eggjs/tegg/compare/v3.0.0-alpha.0...v3.0.0) (2022-12-26) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject # [3.0.0-alpha.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.0.0-alpha.0) (2022-12-22) ### Features * **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) # [1.3.0](https://github.com/eggjs/tegg/compare/v1.2.0...v1.3.0) (2022-07-01) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject # [0.2.0](https://github.com/eggjs/tegg/compare/v0.1.19...v0.2.0) (2022-01-20) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject ================================================ FILE: core/dynamic-inject/README.md ================================================ # `dynamic-inject` ## Usage This is an internal tegg library, you probably shouldn't use it directly. ================================================ FILE: core/dynamic-inject/index.ts ================================================ export * from '@eggjs/tegg-types/dynamic-inject'; export * from './src/QualifierImplUtil'; export * from './src/QualifierImplDecoratorUtil'; ================================================ FILE: core/dynamic-inject/package.json ================================================ { "name": "@eggjs/tegg-dynamic-inject", "version": "3.78.15", "description": "tegg dyniamic inject", "main": "dist/index.js", "files": [ "dist/**/*.js", "dist/**/*.d.ts" ], "typings": "dist/index.d.ts", "keywords": [ "egg", "typescript", "runtime", "tegg" ], "scripts": { "test": "cross-env NODE_ENV=test NODE_OPTIONS='--no-deprecation' mocha", "clean": "tsc -b --clean", "tsc": "ut run clean && tsc -p ./tsconfig.json", "tsc:pub": "ut run clean && tsc -p ./tsconfig.pub.json", "prepublishOnly": "ut tsc:pub" }, "author": "killagu ", "license": "MIT", "homepage": "https://github.com/eggjs/tegg", "bugs": { "url": "https://github.com/eggjs/tegg/issues" }, "repository": { "type": "git", "url": "git@github.com:eggjs/tegg.git", "directory": "core/dynamic-inject" }, "engines": { "node": ">=10.0.0" }, "publishConfig": { "access": "public" }, "dependencies": { "@eggjs/core-decorator": "^3.78.15", "@eggjs/tegg-types": "^3.78.15" }, "devDependencies": { "@types/mocha": "^10.0.1", "@types/node": "^20.2.4", "coffee": "^5.4.0", "cross-env": "^7.0.3", "mocha": "^10.2.0", "ts-node": "^10.9.1", "typescript": "^5.0.4" }, "gitHead": "240a3e9f40fda65ebb7589727d197db5ce17916c" } ================================================ FILE: core/dynamic-inject/src/QualifierImplDecoratorUtil.ts ================================================ import type { EggAbstractClazz, EggProtoImplClass, ImplDecorator, ImplTypeEnum, QualifierAttribute } from '@eggjs/tegg-types'; import { QualifierUtil } from '@eggjs/core-decorator'; import { QualifierImplUtil } from './QualifierImplUtil'; export class QualifierImplDecoratorUtil { static generatorDecorator(abstractClazz: EggAbstractClazz, attribute: QualifierAttribute): ImplDecorator { return function(type: Enum[keyof Enum]) { return function(clazz: EggProtoImplClass) { QualifierImplUtil.addQualifierImpl(abstractClazz, type, clazz); QualifierUtil.addProtoQualifier(clazz, attribute, type); }; }; } } ================================================ FILE: core/dynamic-inject/src/QualifierImplUtil.ts ================================================ import { MetadataUtil } from '@eggjs/core-decorator'; import { QUALIFIER_IMPL_MAP } from '@eggjs/tegg-types'; import type { EggAbstractClazz, EggProtoImplClass, QualifierValue } from '@eggjs/tegg-types'; export class QualifierImplUtil { static addQualifierImpl(abstractClazz: EggAbstractClazz, qualifierValue: QualifierValue, implClazz: EggProtoImplClass) { const implMap = MetadataUtil.initOwnMapMetaData(QUALIFIER_IMPL_MAP, abstractClazz as unknown as EggProtoImplClass, new Map()); implMap.set(qualifierValue, implClazz); } static getQualifierImp(abstractClazz: EggAbstractClazz, qualifierValue: QualifierValue): EggProtoImplClass | undefined { const implMap: Map | undefined = MetadataUtil.getMetaData(QUALIFIER_IMPL_MAP, abstractClazz as unknown as EggProtoImplClass); return implMap?.get(qualifierValue); } static getQualifierImpMap(abstractClazz: EggAbstractClazz): Map { const implMap: Map | undefined = MetadataUtil.getMetaData(QUALIFIER_IMPL_MAP, abstractClazz as unknown as EggProtoImplClass); return implMap || new Map(); } } ================================================ FILE: core/dynamic-inject/test/fixtures/modules/base/AbstractContextHello.ts ================================================ export abstract class AbstractContextHello { abstract hello(): string; } ================================================ FILE: core/dynamic-inject/test/fixtures/modules/base/ContextHello.ts ================================================ import { ContextHelloType } from './FooType'; import { ImplDecorator, QualifierImplDecoratorUtil } from '../../../../index'; import { AbstractContextHello } from './AbstractContextHello'; export const CONTEXT_HELLO_ATTRIBUTE = 'CONTEXT_HELLO_ATTRIBUTE'; export const ContextHello: ImplDecorator = QualifierImplDecoratorUtil.generatorDecorator(AbstractContextHello, CONTEXT_HELLO_ATTRIBUTE); ================================================ FILE: core/dynamic-inject/test/fixtures/modules/base/FooType.ts ================================================ export enum ContextHelloType { FOO = 'FOO', BAR = 'BAR', } ================================================ FILE: core/dynamic-inject/test/fixtures/modules/wrong-enum-module/WrongEnumCase.ts ================================================ import { ContextProto } from '@eggjs/core-decorator'; import { ContextHello } from '../base/ContextHello'; import { AbstractContextHello } from '../base/AbstractContextHello'; @ContextProto() @ContextHello('WRONG_ENUM') export class BarContextHello extends AbstractContextHello { id = 0; hello(): string { return `hello, bar(context:${this.id++})`; } } ================================================ FILE: core/dynamic-inject/test/fixtures/modules/wrong-enum-module/tsconfig.json ================================================ { "extends": "../../../../tsconfig.json", "compilerOptions": { "outDir": "./dist", "baseUrl": "./" }, "exclude": [ "./dist", "node_modules" ] } ================================================ FILE: core/dynamic-inject/test/fixtures/modules/wrong-extends-module/WrongExtendsCase.ts ================================================ import { ContextProto } from '@eggjs/core-decorator'; import { ContextHello } from '../base/ContextHello'; import { ContextHelloType } from '../base/FooType'; @ContextProto() @ContextHello(ContextHelloType.FOO) export class BarContextHello { id = 0; helloWrong(): string { return `hello, bar(context:${this.id++})`; } } ================================================ FILE: core/dynamic-inject/test/fixtures/modules/wrong-extends-module/tsconfig.json ================================================ { "extends": "../../../../tsconfig.json", "compilerOptions": { "outDir": "./dist", "baseUrl": "./" }, "exclude": [ "./dist", "node_modules" ] } ================================================ FILE: core/dynamic-inject/test/typing.test.ts ================================================ import path from 'path'; import coffee from 'coffee'; describe('test/typing.test.ts', () => { it('should check enum value', async () => { const tsc = require.resolve('typescript/bin/tsc'); await coffee.fork(tsc, [ '--noEmit', '-p', './tsconfig.json' ], { cwd: path.join(__dirname, 'fixtures/modules/wrong-enum-module'), }) .debug() .expect('stdout', /Argument of type '"WRONG_ENUM"' is not assignable to parameter of type 'ContextHelloType'/) .notExpect('code', 0) .end(); }); it('should check extends', async () => { const tsc = require.resolve('typescript/bin/tsc'); await coffee.fork(tsc, [ '--noEmit', '-p', './tsconfig.json' ], { cwd: path.join(__dirname, 'fixtures/modules/wrong-extends-module'), }) .debug() .expect('stdout', / Property 'hello' is missing in type 'BarContextHello' but required in type 'AbstractContextHello'/) .notExpect('code', 0) .end(); }); }); ================================================ FILE: core/dynamic-inject/tsconfig.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "dist", "baseUrl": "./" }, "exclude": [ "dist", "node_modules", "test" ] } ================================================ FILE: core/dynamic-inject/tsconfig.pub.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "dist", "baseUrl": "./" }, "exclude": [ "dist", "node_modules", "test" ] } ================================================ FILE: core/dynamic-inject-runtime/CHANGELOG.md ================================================ # Change Log All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. ## [3.78.15](https://github.com/eggjs/tegg/compare/v3.78.14...v3.78.15) (2026-04-23) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.78.14](https://github.com/eggjs/tegg/compare/v3.78.13...v3.78.14) (2026-04-21) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.78.13](https://github.com/eggjs/tegg/compare/v3.78.12...v3.78.13) (2026-04-21) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.78.12](https://github.com/eggjs/tegg/compare/v3.78.11...v3.78.12) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.78.11](https://github.com/eggjs/tegg/compare/v3.78.10...v3.78.11) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.78.10](https://github.com/eggjs/tegg/compare/v3.78.9...v3.78.10) (2026-04-18) ### Bug Fixes * import ([c7ed1b7](https://github.com/eggjs/tegg/commit/c7ed1b78f9c0ee308c85029e79d5187fd7fd1bd4)) ## [3.78.9](https://github.com/eggjs/tegg/compare/v3.78.8...v3.78.9) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.78.8](https://github.com/eggjs/tegg/compare/v3.78.7...v3.78.8) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.78.7](https://github.com/eggjs/tegg/compare/v3.78.6...v3.78.7) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.78.6](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.6) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.78.5](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.5) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.78.4](https://github.com/eggjs/tegg/compare/v3.78.3...v3.78.4) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.78.3](https://github.com/eggjs/tegg/compare/v3.78.2...v3.78.3) (2026-04-15) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.78.2](https://github.com/eggjs/tegg/compare/v3.78.1...v3.78.2) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.78.1](https://github.com/eggjs/tegg/compare/v3.78.0...v3.78.1) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime # [3.78.0](https://github.com/eggjs/tegg/compare/v3.77.2...v3.78.0) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.77.2](https://github.com/eggjs/tegg/compare/v3.77.1...v3.77.2) (2026-04-07) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.77.1](https://github.com/eggjs/tegg/compare/v3.77.0...v3.77.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime # [3.77.0](https://github.com/eggjs/tegg/compare/v3.76.1...v3.77.0) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.76.1](https://github.com/eggjs/tegg/compare/v3.76.0...v3.76.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime # [3.76.0](https://github.com/eggjs/tegg/compare/v3.75.1...v3.76.0) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.75.1](https://github.com/eggjs/tegg/compare/v3.75.0...v3.75.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime # [3.75.0](https://github.com/eggjs/tegg/compare/v3.74.0...v3.75.0) (2026-03-30) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime # [3.74.0](https://github.com/eggjs/tegg/compare/v3.73.0...v3.74.0) (2026-03-30) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime # [3.73.0](https://github.com/eggjs/tegg/compare/v3.72.0...v3.73.0) (2026-03-25) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime # [3.72.0](https://github.com/eggjs/tegg/compare/v3.71.2...v3.72.0) (2026-02-04) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.71.2](https://github.com/eggjs/tegg/compare/v3.71.1...v3.71.2) (2026-01-30) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.71.1](https://github.com/eggjs/tegg/compare/v3.71.0...v3.71.1) (2026-01-28) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime # [3.71.0](https://github.com/eggjs/tegg/compare/v3.70.1...v3.71.0) (2026-01-26) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.70.1](https://github.com/eggjs/tegg/compare/v3.70.0...v3.70.1) (2026-01-14) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime # [3.70.0](https://github.com/eggjs/tegg/compare/v3.69.0...v3.70.0) (2026-01-07) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime # [3.69.0](https://github.com/eggjs/tegg/compare/v3.68.0...v3.69.0) (2026-01-06) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime # [3.68.0](https://github.com/eggjs/tegg/compare/v3.67.2...v3.68.0) (2025-12-29) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.67.2](https://github.com/eggjs/tegg/compare/v3.67.1...v3.67.2) (2025-12-24) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.67.1](https://github.com/eggjs/tegg/compare/v3.67.0...v3.67.1) (2025-12-23) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime # [3.67.0](https://github.com/eggjs/tegg/compare/v3.66.0...v3.67.0) (2025-12-18) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime # [3.66.0](https://github.com/eggjs/tegg/compare/v3.65.3...v3.66.0) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.65.3](https://github.com/eggjs/tegg/compare/v3.65.2...v3.65.3) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.65.2](https://github.com/eggjs/tegg/compare/v3.65.1...v3.65.2) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.65.1](https://github.com/eggjs/tegg/compare/v3.65.0...v3.65.1) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime # [3.65.0](https://github.com/eggjs/tegg/compare/v3.64.5...v3.65.0) (2025-12-10) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.64.5](https://github.com/eggjs/tegg/compare/v3.64.4...v3.64.5) (2025-12-08) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.64.4](https://github.com/eggjs/tegg/compare/v3.64.3...v3.64.4) (2025-12-06) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.64.3](https://github.com/eggjs/tegg/compare/v3.64.2...v3.64.3) (2025-12-06) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.64.2](https://github.com/eggjs/tegg/compare/v3.64.1...v3.64.2) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.64.1](https://github.com/eggjs/tegg/compare/v3.64.0...v3.64.1) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime # [3.64.0](https://github.com/eggjs/tegg/compare/v3.63.2...v3.64.0) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.63.2](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.2) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.63.1](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.1) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime # [3.63.0](https://github.com/eggjs/tegg/compare/v3.62.3...v3.63.0) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.62.3](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.3) (2025-11-05) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.62.2](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.2) (2025-11-05) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.62.1](https://github.com/eggjs/tegg/compare/v3.62.0...v3.62.1) (2025-11-03) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime # [3.62.0](https://github.com/eggjs/tegg/compare/v3.61.0...v3.62.0) (2025-09-24) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime # [3.61.0](https://github.com/eggjs/tegg/compare/v3.60.3...v3.61.0) (2025-08-15) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.60.3](https://github.com/eggjs/tegg/compare/v3.60.2...v3.60.3) (2025-08-06) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.60.2](https://github.com/eggjs/tegg/compare/v3.60.1...v3.60.2) (2025-08-06) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.60.1](https://github.com/eggjs/tegg/compare/v3.60.0...v3.60.1) (2025-07-28) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime # [3.60.0](https://github.com/eggjs/tegg/compare/v3.59.1...v3.60.0) (2025-07-28) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.59.1](https://github.com/eggjs/tegg/compare/v3.59.0...v3.59.1) (2025-07-16) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime # [3.59.0](https://github.com/eggjs/tegg/compare/v3.58.0...v3.59.0) (2025-07-07) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime # [3.58.0](https://github.com/eggjs/tegg/compare/v3.57.14...v3.58.0) (2025-07-01) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.57.14](https://github.com/eggjs/tegg/compare/v3.57.13...v3.57.14) (2025-06-18) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.57.13](https://github.com/eggjs/tegg/compare/v3.57.12...v3.57.13) (2025-06-18) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.57.12](https://github.com/eggjs/tegg/compare/v3.57.11...v3.57.12) (2025-06-16) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.57.11](https://github.com/eggjs/tegg/compare/v3.57.10...v3.57.11) (2025-06-16) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.57.10](https://github.com/eggjs/tegg/compare/v3.57.9...v3.57.10) (2025-06-12) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.57.9](https://github.com/eggjs/tegg/compare/v3.57.8...v3.57.9) (2025-05-29) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.57.8](https://github.com/eggjs/tegg/compare/v3.57.7...v3.57.8) (2025-05-29) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.57.7](https://github.com/eggjs/tegg/compare/v3.57.6...v3.57.7) (2025-05-28) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.57.6](https://github.com/eggjs/tegg/compare/v3.57.5...v3.57.6) (2025-05-27) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.57.5](https://github.com/eggjs/tegg/compare/v3.57.4...v3.57.5) (2025-05-15) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.57.4](https://github.com/eggjs/tegg/compare/v3.57.3...v3.57.4) (2025-05-14) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.57.3](https://github.com/eggjs/tegg/compare/v3.57.2...v3.57.3) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.57.2](https://github.com/eggjs/tegg/compare/v3.57.1...v3.57.2) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.57.1](https://github.com/eggjs/tegg/compare/v3.57.0...v3.57.1) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime # [3.57.0](https://github.com/eggjs/tegg/compare/v3.56.3...v3.57.0) (2025-05-09) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.56.3](https://github.com/eggjs/tegg/compare/v3.56.2...v3.56.3) (2025-05-07) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.56.2](https://github.com/eggjs/tegg/compare/v3.56.1...v3.56.2) (2025-05-01) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.56.1](https://github.com/eggjs/tegg/compare/v3.56.0...v3.56.1) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime # [3.56.0](https://github.com/eggjs/tegg/compare/v3.55.0...v3.56.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime # [3.55.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.55.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime # [3.54.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.54.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime # [3.53.0](https://github.com/eggjs/tegg/compare/v3.52.1...v3.53.0) (2025-04-21) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.52.1](https://github.com/eggjs/tegg/compare/v3.52.0...v3.52.1) (2025-03-14) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime # [3.52.0](https://github.com/eggjs/tegg/compare/v3.51.2...v3.52.0) (2024-12-30) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.51.2](https://github.com/eggjs/tegg/compare/v3.51.1...v3.51.2) (2024-12-09) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.51.1](https://github.com/eggjs/tegg/compare/v3.51.0...v3.51.1) (2024-11-06) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime # [3.51.0](https://github.com/eggjs/tegg/compare/v3.50.1...v3.51.0) (2024-10-30) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.50.1](https://github.com/eggjs/tegg/compare/v3.50.0...v3.50.1) (2024-10-23) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime # [3.50.0](https://github.com/eggjs/tegg/compare/v3.49.0...v3.50.0) (2024-10-22) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime # [3.49.0](https://github.com/eggjs/tegg/compare/v3.48.1...v3.49.0) (2024-10-21) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.48.1](https://github.com/eggjs/tegg/compare/v3.48.0...v3.48.1) (2024-10-14) ### Bug Fixes * fix merge qualifier ([#250](https://github.com/eggjs/tegg/issues/250)) ([d5a8a93](https://github.com/eggjs/tegg/commit/d5a8a93abad570f69881f9fa42f39d7b5cd436be)) # [3.48.0](https://github.com/eggjs/tegg/compare/v3.47.2...v3.48.0) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.47.2](https://github.com/eggjs/tegg/compare/v3.47.1...v3.47.2) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.47.1](https://github.com/eggjs/tegg/compare/v3.47.0...v3.47.1) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime # [3.47.0](https://github.com/eggjs/tegg/compare/v3.46.4...v3.47.0) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.46.4](https://github.com/eggjs/tegg/compare/v3.46.3...v3.46.4) (2024-10-09) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.46.3](https://github.com/eggjs/tegg/compare/v3.46.2...v3.46.3) (2024-10-08) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.46.2](https://github.com/eggjs/tegg/compare/v3.46.1...v3.46.2) (2024-10-07) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.46.1](https://github.com/eggjs/tegg/compare/v3.46.0...v3.46.1) (2024-09-30) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime # [3.46.0](https://github.com/eggjs/tegg/compare/v3.45.0...v3.46.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime # [3.45.0](https://github.com/eggjs/tegg/compare/v3.44.1...v3.45.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.44.1](https://github.com/eggjs/tegg/compare/v3.44.0...v3.44.1) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime # [3.44.0](https://github.com/eggjs/tegg/compare/v3.43.2...v3.44.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.43.2](https://github.com/eggjs/tegg/compare/v3.43.1...v3.43.2) (2024-09-14) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.43.1](https://github.com/eggjs/tegg/compare/v3.43.0...v3.43.1) (2024-09-14) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime # [3.43.0](https://github.com/eggjs/tegg/compare/v3.42.0...v3.43.0) (2024-09-13) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime # [3.42.0](https://github.com/eggjs/tegg/compare/v3.41.0...v3.42.0) (2024-09-10) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime # [3.41.0](https://github.com/eggjs/tegg/compare/v3.40.1...v3.41.0) (2024-08-26) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.40.1](https://github.com/eggjs/tegg/compare/v3.40.0...v3.40.1) (2024-08-23) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime # [3.40.0](https://github.com/eggjs/tegg/compare/v3.39.5...v3.40.0) (2024-08-22) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.39.5](https://github.com/eggjs/tegg/compare/v3.39.4...v3.39.5) (2024-08-09) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.39.4](https://github.com/eggjs/tegg/compare/v3.39.3...v3.39.4) (2024-07-09) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.39.3](https://github.com/eggjs/tegg/compare/v3.39.2...v3.39.3) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.39.2](https://github.com/eggjs/tegg/compare/v3.39.1...v3.39.2) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.39.1](https://github.com/eggjs/tegg/compare/v3.39.0...v3.39.1) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime # [3.39.0](https://github.com/eggjs/tegg/compare/v3.38.0...v3.39.0) (2024-04-19) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime # [3.38.0](https://github.com/eggjs/tegg/compare/v3.37.3...v3.38.0) (2024-04-18) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.37.3](https://github.com/eggjs/tegg/compare/v3.37.2...v3.37.3) (2024-04-17) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.37.2](https://github.com/eggjs/tegg/compare/v3.37.1...v3.37.2) (2024-04-17) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.37.1](https://github.com/eggjs/tegg/compare/v3.37.0...v3.37.1) (2024-04-16) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime # [3.37.0](https://github.com/eggjs/tegg/compare/v3.36.3...v3.37.0) (2024-04-16) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.36.3](https://github.com/eggjs/tegg/compare/v3.36.2...v3.36.3) (2024-04-10) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.36.2](https://github.com/eggjs/tegg/compare/v3.36.1...v3.36.2) (2024-04-08) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.36.1](https://github.com/eggjs/tegg/compare/v3.36.0...v3.36.1) (2024-04-07) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime # [3.36.0](https://github.com/eggjs/tegg/compare/v3.35.1...v3.36.0) (2024-04-02) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.35.1](https://github.com/eggjs/tegg/compare/v3.35.0...v3.35.1) (2024-03-26) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime # [3.35.0](https://github.com/eggjs/tegg/compare/v3.34.0...v3.35.0) (2024-03-26) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime # [3.34.0](https://github.com/eggjs/tegg/compare/v3.33.1...v3.34.0) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.33.1](https://github.com/eggjs/tegg/compare/v3.33.0...v3.33.1) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime # [3.33.0](https://github.com/eggjs/tegg/compare/v3.32.0...v3.33.0) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime # [3.32.0](https://github.com/eggjs/tegg/compare/v3.31.0...v3.32.0) (2024-02-19) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime # [3.31.0](https://github.com/eggjs/tegg/compare/v3.30.1...v3.31.0) (2024-01-31) ### Features * add getEggObjects API to fetch all instances ([#189](https://github.com/eggjs/tegg/issues/189)) ([f8592c2](https://github.com/eggjs/tegg/commit/f8592c2cd141d01b4f1730b1e3d66e35c3e1ce05)) ## [3.30.1](https://github.com/eggjs/tegg/compare/v3.30.0...v3.30.1) (2024-01-25) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime # [3.30.0](https://github.com/eggjs/tegg/compare/v3.29.0...v3.30.0) (2024-01-17) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime # [3.29.0](https://github.com/eggjs/tegg/compare/v3.28.2...v3.29.0) (2023-12-26) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.28.2](https://github.com/eggjs/tegg/compare/v3.28.1...v3.28.2) (2023-12-12) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.28.1](https://github.com/eggjs/tegg/compare/v3.28.0...v3.28.1) (2023-12-11) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime # [3.28.0](https://github.com/eggjs/tegg/compare/v3.27.0...v3.28.0) (2023-12-10) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime # [3.27.0](https://github.com/eggjs/tegg/compare/v3.26.0...v3.27.0) (2023-11-23) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.25.2](https://github.com/eggjs/tegg/compare/v3.25.1...v3.25.2) (2023-11-06) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.25.1](https://github.com/eggjs/tegg/compare/v3.25.0...v3.25.1) (2023-11-03) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime # [3.25.0](https://github.com/eggjs/tegg/compare/v3.24.0...v3.25.0) (2023-11-03) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime # [3.24.0](https://github.com/eggjs/tegg/compare/v3.23.0...v3.24.0) (2023-10-26) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime # [3.23.0](https://github.com/eggjs/tegg/compare/v3.22.0...v3.23.0) (2023-09-20) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime # [3.20.0](https://github.com/eggjs/tegg/compare/v3.19.0...v3.20.0) (2023-09-07) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime # [3.19.0](https://github.com/eggjs/tegg/compare/v3.18.1...v3.19.0) (2023-08-31) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.18.1](https://github.com/eggjs/tegg/compare/v3.18.0...v3.18.1) (2023-08-29) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime # [3.18.0](https://github.com/eggjs/tegg/compare/v3.17.0...v3.18.0) (2023-08-29) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime # [3.17.0](https://github.com/eggjs/tegg/compare/v3.16.0...v3.17.0) (2023-08-24) ### Features * impl MultiInstanceProto ([#145](https://github.com/eggjs/tegg/issues/145)) ([12fd5cf](https://github.com/eggjs/tegg/commit/12fd5cff4004578bcc737dcdf4f7e9d1159f5633)) # [3.16.0](https://github.com/eggjs/tegg/compare/v3.15.0...v3.16.0) (2023-08-24) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.14.3](https://github.com/eggjs/tegg/compare/v3.14.2...v3.14.3) (2023-08-14) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.14.2](https://github.com/eggjs/tegg/compare/v3.14.1...v3.14.2) (2023-08-14) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.14.1](https://github.com/eggjs/tegg/compare/v3.14.0...v3.14.1) (2023-08-11) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime # [3.14.0](https://github.com/eggjs/tegg/compare/v3.13.0...v3.14.0) (2023-07-31) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime # [3.13.0](https://github.com/eggjs/tegg/compare/v3.12.0...v3.13.0) (2023-07-25) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime # [3.12.0](https://github.com/eggjs/tegg/compare/v3.11.1...v3.12.0) (2023-07-13) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.11.1](https://github.com/eggjs/tegg/compare/v3.11.0...v3.11.1) (2023-06-29) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime # [3.11.0](https://github.com/eggjs/tegg/compare/v3.10.0...v3.11.0) (2023-06-29) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime # [3.10.0](https://github.com/eggjs/tegg/compare/v3.9.0...v3.10.0) (2023-06-28) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime # [3.8.0](https://github.com/eggjs/tegg/compare/v3.7.0...v3.8.0) (2023-05-30) ### Features * impl EggObjectLifecycle hook in decorator ([#119](https://github.com/eggjs/tegg/issues/119)) ([cced8a2](https://github.com/eggjs/tegg/commit/cced8a2e009c33d5040fa21d00409fddef471b0e)) # [3.7.0](https://github.com/eggjs/tegg/compare/v3.6.3...v3.7.0) (2023-04-03) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.6.3](https://github.com/eggjs/tegg/compare/v3.6.2...v3.6.3) (2023-03-02) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime # [3.5.0](https://github.com/eggjs/tegg/compare/v3.4.1...v3.5.0) (2023-02-10) ### Features * remove context egg object factory ([#93](https://github.com/eggjs/tegg/issues/93)) ([e14bdb2](https://github.com/eggjs/tegg/commit/e14bdb257eaebc0b0a4c37c6073a5c3237718718)) ## [3.3.1](https://github.com/eggjs/tegg/compare/v3.3.0...v3.3.1) (2023-01-28) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.2.3](https://github.com/eggjs/tegg/compare/v3.2.2...v3.2.3) (2023-01-16) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.2.2](https://github.com/eggjs/tegg/compare/v3.2.1...v3.2.2) (2023-01-06) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ## [3.2.1](https://github.com/eggjs/tegg/compare/v3.2.0...v3.2.1) (2022-12-28) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime # [3.1.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.1.0) (2022-12-27) ### Features * **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) # [3.0.0](https://github.com/eggjs/tegg/compare/v3.0.0-alpha.0...v3.0.0) (2022-12-26) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime # [3.0.0-alpha.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.0.0-alpha.0) (2022-12-22) ### Features * **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) # [1.3.0](https://github.com/eggjs/tegg/compare/v1.2.0...v1.3.0) (2022-07-01) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime # [0.2.0](https://github.com/eggjs/tegg/compare/v0.1.19...v0.2.0) (2022-01-20) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime ================================================ FILE: core/dynamic-inject-runtime/README.md ================================================ # `dyniamic-inject-runtime` ## Usage This is an internal tegg library, you probably shouldn't use it directly. ================================================ FILE: core/dynamic-inject-runtime/index.ts ================================================ export * from './src/EggObjectFactory'; import './src/EggObjectFactoryPrototype'; import './src/EggObjectFactoryObject'; export * from './src/EggObjectFactoryPrototype'; ================================================ FILE: core/dynamic-inject-runtime/package.json ================================================ { "name": "@eggjs/tegg-dynamic-inject-runtime", "version": "3.78.15", "description": "tegg dyniamic inject", "main": "dist/index.js", "eggModule": { "name": "teggDyniamicInjectRuntime" }, "files": [ "dist/**/*.js", "dist/**/*.d.ts" ], "typings": "dist/index.d.ts", "keywords": [ "egg", "typescript", "runtime", "tegg" ], "scripts": { "test": "cross-env NODE_ENV=test NODE_OPTIONS='--no-deprecation' mocha", "clean": "tsc -b --clean && rm -rf dist", "tsc": "ut run clean && tsc -p ./tsconfig.json", "tsc:pub": "ut run clean && tsc -p ./tsconfig.pub.json", "prepublishOnly": "ut tsc:pub" }, "author": "killagu ", "license": "MIT", "homepage": "https://github.com/eggjs/tegg", "bugs": { "url": "https://github.com/eggjs/tegg/issues" }, "repository": { "type": "git", "url": "git@github.com:eggjs/tegg.git", "directory": "core/dynamic-inject" }, "engines": { "node": ">=10.0.0" }, "publishConfig": { "access": "public" }, "dependencies": { "@eggjs/core-decorator": "^3.78.15", "@eggjs/tegg-common-util": "^3.78.15", "@eggjs/tegg-dynamic-inject": "^3.78.15", "@eggjs/tegg-lifecycle": "^3.78.15", "@eggjs/tegg-metadata": "^3.78.15", "@eggjs/tegg-runtime": "^3.78.15", "@eggjs/tegg-types": "^3.78.15" }, "devDependencies": { "@eggjs/module-test-util": "^3.78.15", "@eggjs/tegg-loader": "^3.78.15", "@types/mocha": "^10.0.1", "@types/node": "^20.2.4", "cross-env": "^7.0.3", "mocha": "^10.2.0", "ts-node": "^10.9.1", "typescript": "^5.0.4" }, "gitHead": "240a3e9f40fda65ebb7589727d197db5ce17916c" } ================================================ FILE: core/dynamic-inject-runtime/src/EggObjectFactory.ts ================================================ import { AccessLevel } from '@eggjs/tegg-types'; import type { QualifierValue, EggAbstractClazz, EggObjectFactory as IEggObjectFactory, } from '@eggjs/tegg-types'; import type { EggContainerFactory } from '@eggjs/tegg-runtime'; import { PrototypeUtil, SingletonProto } from '@eggjs/core-decorator'; import { QualifierImplUtil } from '@eggjs/tegg-dynamic-inject'; import { EGG_OBJECT_FACTORY_PROTO_IMPLE_TYPE } from './EggObjectFactoryPrototype'; @SingletonProto({ protoImplType: EGG_OBJECT_FACTORY_PROTO_IMPLE_TYPE, name: 'eggObjectFactory', accessLevel: AccessLevel.PUBLIC, }) export class EggObjectFactory implements IEggObjectFactory { eggContainerFactory: typeof EggContainerFactory; async getEggObject(abstractClazz: EggAbstractClazz, qualifierValue: QualifierValue) { const implClazz = QualifierImplUtil.getQualifierImp(abstractClazz, qualifierValue); if (!implClazz) { throw new Error(`has no impl for ${abstractClazz.name} with qualifier ${qualifierValue}`); } const protoObj: any = PrototypeUtil.getClazzProto(implClazz); if (!protoObj) { throw new Error(`can not get proto for clazz ${implClazz.name}`); } const eggObject = await this.eggContainerFactory.getOrCreateEggObject(protoObj, protoObj.name); return eggObject.obj as T; } async getEggObjects(abstractClazz: EggAbstractClazz) { const implClazzMap = QualifierImplUtil.getQualifierImpMap(abstractClazz); const getEggObject = this.getEggObject.bind(this); const qualifierValues = Array.from(implClazzMap.keys()); return { [Symbol.asyncIterator]() { return { key: 0, async next() { if (this.key === qualifierValues.length) { return { done: true } as IteratorResult; } const value = await getEggObject(abstractClazz, qualifierValues[this.key++]); return { value, done: false } as IteratorResult; }, }; }, }; } } ================================================ FILE: core/dynamic-inject-runtime/src/EggObjectFactoryObject.ts ================================================ import { EggContainerFactory, EggObjectFactory as TEggObjectFactory, } from '@eggjs/tegg-runtime'; import { EggObjectFactoryPrototype } from './EggObjectFactoryPrototype'; import { IdenticalUtil } from '@eggjs/tegg-lifecycle'; import type { EggRuntimeContext, EggObject, EggObjectName, EggPrototype, } from '@eggjs/tegg-types'; import { EggObjectFactory } from './EggObjectFactory'; const OBJ = Symbol('EggObjectFactoryObject#obj'); export class EggObjectFactoryObject implements EggObject { readonly proto: EggObjectFactoryPrototype; readonly name: EggObjectName; readonly ctx?: EggRuntimeContext; readonly id: string; private [OBJ]: EggObjectFactory; constructor(name: EggObjectName, proto: EggObjectFactoryPrototype) { this.proto = proto; this.name = name; this.id = IdenticalUtil.createObjectId(this.proto.id, this.ctx?.id); } get obj() { if (!this[OBJ]) { this[OBJ] = this.proto.constructEggObject() as EggObjectFactory; this[OBJ].eggContainerFactory = EggContainerFactory; } return this[OBJ]; } static async createObject(name: EggObjectName, proto: EggPrototype): Promise { return new EggObjectFactoryObject(name, proto as EggObjectFactoryPrototype); } readonly isReady: true; injectProperty(): any { return; } } TEggObjectFactory.registerEggObjectCreateMethod(EggObjectFactoryPrototype, EggObjectFactoryObject.createObject); ================================================ FILE: core/dynamic-inject-runtime/src/EggObjectFactoryPrototype.ts ================================================ import { EggPrototypeCreatorFactory } from '@eggjs/tegg-metadata'; import { MetadataUtil, QualifierUtil, } from '@eggjs/core-decorator'; import { NameUtil } from '@eggjs/tegg-common-util'; import { IdenticalUtil } from '@eggjs/tegg-lifecycle'; import type { AccessLevel, EggObjectFactory, EggPrototype, EggProtoImplClass, EggPrototypeInfo, EggPrototypeLifecycleContext, EggPrototypeName, InjectObjectProto, LoadUnit, MetaDataKey, ObjectInitTypeLike, QualifierInfo, QualifierValue, Id, } from '@eggjs/tegg-types'; export const EGG_OBJECT_FACTORY_PROTO_IMPLE_TYPE = 'EGG_OBJECT_FACTORY_PROTOTYPE'; export class EggObjectFactoryPrototype implements EggPrototype { readonly clazz: EggProtoImplClass; readonly accessLevel: AccessLevel; readonly id: Id; readonly initType: ObjectInitTypeLike; readonly injectObjects: InjectObjectProto[]; readonly loadUnitId: string; readonly name: EggPrototypeName; readonly qualifiers: QualifierInfo[]; constructor(clazz: EggProtoImplClass, loadUnit: LoadUnit, prototypeInfo: EggPrototypeInfo) { this.clazz = clazz; this.qualifiers = QualifierUtil.mergeQualifiers( QualifierUtil.getProtoQualifiers(clazz), (prototypeInfo.qualifiers ?? []), ); this.id = IdenticalUtil.createProtoId(loadUnit.id, NameUtil.getClassName(this.clazz)); this.initType = prototypeInfo.initType; this.accessLevel = prototypeInfo.accessLevel; this.loadUnitId = loadUnit.id; this.name = prototypeInfo.name || NameUtil.getClassName(this.clazz); this.injectObjects = []; } constructEggObject(): EggObjectFactory { return Reflect.construct(this.clazz, []); } getMetaData(metadataKey: MetaDataKey): T | undefined { return MetadataUtil.getMetaData(metadataKey, this.clazz); } verifyQualifier(qualifier: QualifierInfo): boolean { const selfQualifiers = this.qualifiers.find(t => t.attribute === qualifier.attribute); return selfQualifiers?.value === qualifier.value; } getQualifier(attribute: string): QualifierValue | undefined { return this.qualifiers.find(t => t.attribute === attribute)?.value; } verifyQualifiers(qualifiers: QualifierInfo[]): boolean { for (const qualifier of qualifiers) { if (!this.verifyQualifier(qualifier)) { return false; } } return true; } static create(ctx: EggPrototypeLifecycleContext) { return new EggObjectFactoryPrototype(ctx.clazz as EggProtoImplClass, ctx.loadUnit, ctx.prototypeInfo); } } EggPrototypeCreatorFactory.registerPrototypeCreator(EGG_OBJECT_FACTORY_PROTO_IMPLE_TYPE, EggObjectFactoryPrototype.create); ================================================ FILE: core/dynamic-inject-runtime/test/fixtures/modules/dynamic-inject-module/AbstractContextHello.ts ================================================ export abstract class AbstractContextHello { abstract hello(): string; } ================================================ FILE: core/dynamic-inject-runtime/test/fixtures/modules/dynamic-inject-module/AbstractSingletonHello.ts ================================================ export abstract class AbstractSingletonHello { abstract hello(): string; } ================================================ FILE: core/dynamic-inject-runtime/test/fixtures/modules/dynamic-inject-module/FooType.ts ================================================ export enum ContextHelloType { FOO = 'FOO', BAR = 'BAR', } export enum SingletonHelloType { FOO = 'FOO', BAR = 'BAR', } ================================================ FILE: core/dynamic-inject-runtime/test/fixtures/modules/dynamic-inject-module/HelloService.ts ================================================ import { ContextProto, Inject } from '@eggjs/core-decorator'; import { EggObjectFactory } from '@eggjs/tegg-dynamic-inject'; import { AbstractContextHello } from './AbstractContextHello'; import { AbstractSingletonHello } from './AbstractSingletonHello'; import { ContextHelloType, SingletonHelloType } from './FooType'; @ContextProto() export class HelloService { @Inject() private readonly eggObjectFactory: EggObjectFactory; async hello(): Promise { const helloImpls = await Promise.all([ this.eggObjectFactory.getEggObject(AbstractContextHello, ContextHelloType.FOO), this.eggObjectFactory.getEggObject(AbstractContextHello, ContextHelloType.BAR), this.eggObjectFactory.getEggObject(AbstractSingletonHello, SingletonHelloType.FOO), this.eggObjectFactory.getEggObject(AbstractSingletonHello, SingletonHelloType.BAR), ]); const msgs = helloImpls.map(helloImpl => helloImpl.hello()); return msgs; } async sayHelloToAll(): Promise { const singletonHellos = await this.eggObjectFactory.getEggObjects(AbstractSingletonHello); const contextHellos = await this.eggObjectFactory.getEggObjects(AbstractContextHello); const msgs: string[] = []; for await (const helloImpl of singletonHellos) { msgs.push(helloImpl.hello()); } for await (const helloImpl of contextHellos) { msgs.push(helloImpl.hello()); } return msgs; } } ================================================ FILE: core/dynamic-inject-runtime/test/fixtures/modules/dynamic-inject-module/decorator/ContextHello.ts ================================================ import { ContextHelloType } from '../FooType'; import { ImplDecorator, QualifierImplDecoratorUtil } from '@eggjs/tegg-dynamic-inject'; import { AbstractContextHello } from '../AbstractContextHello'; export const CONTEXT_HELLO_ATTRIBUTE = 'CONTEXT_HELLO_ATTRIBUTE'; export const ContextHello: ImplDecorator = QualifierImplDecoratorUtil.generatorDecorator(AbstractContextHello, CONTEXT_HELLO_ATTRIBUTE); ================================================ FILE: core/dynamic-inject-runtime/test/fixtures/modules/dynamic-inject-module/decorator/SingletonHello.ts ================================================ import { SingletonHelloType } from '../FooType'; import { ImplDecorator, QualifierImplDecoratorUtil } from '@eggjs/tegg-dynamic-inject'; import { AbstractSingletonHello } from '../AbstractSingletonHello'; export const SINGLETON_HELLO_ATTRIBUTE = 'SINGLETON_HELLO_ATTRIBUTE'; export const SingletonHello: ImplDecorator = QualifierImplDecoratorUtil.generatorDecorator(AbstractSingletonHello, SINGLETON_HELLO_ATTRIBUTE); ================================================ FILE: core/dynamic-inject-runtime/test/fixtures/modules/dynamic-inject-module/impl/BarContextHello.ts ================================================ import { ContextProto } from '@eggjs/core-decorator'; import { ContextHello } from '../decorator/ContextHello'; import { ContextHelloType } from '../FooType'; import { AbstractContextHello } from '../AbstractContextHello'; @ContextProto() @ContextHello(ContextHelloType.BAR) export class BarContextHello extends AbstractContextHello { id = 0; hello(): string { return `hello, bar(context:${this.id++})`; } } ================================================ FILE: core/dynamic-inject-runtime/test/fixtures/modules/dynamic-inject-module/impl/BarSingletonHello.ts ================================================ import { SingletonProto } from '@eggjs/core-decorator'; import { SingletonHelloType } from '../FooType'; import { SingletonHello } from '../decorator/SingletonHello'; import { AbstractContextHello } from '../AbstractContextHello'; @SingletonProto() @SingletonHello(SingletonHelloType.BAR) export class BarSingletonHello extends AbstractContextHello { id = 0; hello(): string { return `hello, bar(singleton:${this.id++})`; } } ================================================ FILE: core/dynamic-inject-runtime/test/fixtures/modules/dynamic-inject-module/impl/FooContextHello.ts ================================================ import { ContextProto } from '@eggjs/core-decorator'; import { ContextHello } from '../decorator/ContextHello'; import { ContextHelloType } from '../FooType'; import { AbstractContextHello } from '../AbstractContextHello'; @ContextProto() @ContextHello(ContextHelloType.FOO) export class FooContextHello extends AbstractContextHello { id = 0; hello(): string { return `hello, foo(context:${this.id++})`; } } ================================================ FILE: core/dynamic-inject-runtime/test/fixtures/modules/dynamic-inject-module/impl/FooSingletonHello.ts ================================================ import { SingletonProto } from '@eggjs/core-decorator'; import { SingletonHelloType } from '../FooType'; import { SingletonHello } from '../decorator/SingletonHello'; import { AbstractContextHello } from '../AbstractContextHello'; @SingletonProto() @SingletonHello(SingletonHelloType.FOO) export class FooSingletonHello extends AbstractContextHello { id = 0; hello(): string { return `hello, foo(singleton:${this.id++})`; } } ================================================ FILE: core/dynamic-inject-runtime/test/fixtures/modules/dynamic-inject-module/package.json ================================================ { "name": "dynamic-inject-module", "eggModule": { "name": "dynamicInjectModule" } } ================================================ FILE: core/dynamic-inject-runtime/test/index.test.ts ================================================ import assert from 'assert'; import path from 'path'; import { LoadUnitFactory } from '@eggjs/tegg-metadata'; import { LoadUnitInstance, LoadUnitInstanceFactory } from '@eggjs/tegg-runtime'; import { EggTestContext } from '../../test-util'; import { CoreTestHelper } from '../../test-util/CoreTestHelper'; import { HelloService } from './fixtures/modules/dynamic-inject-module/HelloService'; describe('test/dynamic-inject-runtime.test.ts', () => { let modules: Array; beforeEach(async () => { modules = await CoreTestHelper.prepareModules([ path.join(__dirname, '..'), path.join(__dirname, 'fixtures/modules/dynamic-inject-module'), ]); }); afterEach(async () => { for (const module of modules) { await LoadUnitFactory.destroyLoadUnit(module.loadUnit); await LoadUnitInstanceFactory.destroyLoadUnitInstance(module); } }); it('should work', async () => { await EggTestContext.mockContext(async () => { const helloService = await CoreTestHelper.getObject(HelloService); const msgs = await helloService.hello(); assert.deepStrictEqual(msgs, [ 'hello, foo(context:0)', 'hello, bar(context:0)', 'hello, foo(singleton:0)', 'hello, bar(singleton:0)', ]); }); await EggTestContext.mockContext(async () => { const helloService = await CoreTestHelper.getObject(HelloService); const msgs = await helloService.hello(); assert.deepStrictEqual(msgs, [ 'hello, foo(context:0)', 'hello, bar(context:0)', // singleton use the same object // counter should has cache 'hello, foo(singleton:1)', 'hello, bar(singleton:1)', ]); }); }); it('should work with getAllEggObjects', async () => { await EggTestContext.mockContext(async () => { const helloService = await CoreTestHelper.getObject(HelloService); const msgs = await helloService.sayHelloToAll(); assert.deepStrictEqual(msgs, [ 'hello, bar(singleton:0)', 'hello, foo(singleton:0)', 'hello, bar(context:0)', 'hello, foo(context:0)', ]); }); }); }); ================================================ FILE: core/dynamic-inject-runtime/tsconfig.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "dist", "baseUrl": "./" }, "exclude": [ "dist", "node_modules", "test" ] } ================================================ FILE: core/dynamic-inject-runtime/tsconfig.pub.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "dist", "baseUrl": "./" }, "exclude": [ "dist", "node_modules", "test" ] } ================================================ FILE: core/eventbus-decorator/CHANGELOG.md ================================================ # Change Log All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. ## [3.78.15](https://github.com/eggjs/tegg/compare/v3.78.14...v3.78.15) (2026-04-23) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.78.14](https://github.com/eggjs/tegg/compare/v3.78.13...v3.78.14) (2026-04-21) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.78.13](https://github.com/eggjs/tegg/compare/v3.78.12...v3.78.13) (2026-04-21) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.78.12](https://github.com/eggjs/tegg/compare/v3.78.11...v3.78.12) (2026-04-18) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.78.11](https://github.com/eggjs/tegg/compare/v3.78.10...v3.78.11) (2026-04-18) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.78.10](https://github.com/eggjs/tegg/compare/v3.78.9...v3.78.10) (2026-04-18) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.78.9](https://github.com/eggjs/tegg/compare/v3.78.8...v3.78.9) (2026-04-18) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.78.8](https://github.com/eggjs/tegg/compare/v3.78.7...v3.78.8) (2026-04-18) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.78.7](https://github.com/eggjs/tegg/compare/v3.78.6...v3.78.7) (2026-04-18) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.78.6](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.6) (2026-04-18) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.78.5](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.5) (2026-04-18) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.78.4](https://github.com/eggjs/tegg/compare/v3.78.3...v3.78.4) (2026-04-18) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.78.3](https://github.com/eggjs/tegg/compare/v3.78.2...v3.78.3) (2026-04-15) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.78.2](https://github.com/eggjs/tegg/compare/v3.78.1...v3.78.2) (2026-04-09) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.78.1](https://github.com/eggjs/tegg/compare/v3.78.0...v3.78.1) (2026-04-09) **Note:** Version bump only for package @eggjs/eventbus-decorator # [3.78.0](https://github.com/eggjs/tegg/compare/v3.77.2...v3.78.0) (2026-04-09) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.77.2](https://github.com/eggjs/tegg/compare/v3.77.1...v3.77.2) (2026-04-07) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.77.1](https://github.com/eggjs/tegg/compare/v3.77.0...v3.77.1) (2026-04-01) **Note:** Version bump only for package @eggjs/eventbus-decorator # [3.77.0](https://github.com/eggjs/tegg/compare/v3.76.1...v3.77.0) (2026-04-01) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.76.1](https://github.com/eggjs/tegg/compare/v3.76.0...v3.76.1) (2026-04-01) **Note:** Version bump only for package @eggjs/eventbus-decorator # [3.76.0](https://github.com/eggjs/tegg/compare/v3.75.1...v3.76.0) (2026-04-01) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.75.1](https://github.com/eggjs/tegg/compare/v3.75.0...v3.75.1) (2026-04-01) **Note:** Version bump only for package @eggjs/eventbus-decorator # [3.75.0](https://github.com/eggjs/tegg/compare/v3.74.0...v3.75.0) (2026-03-30) **Note:** Version bump only for package @eggjs/eventbus-decorator # [3.74.0](https://github.com/eggjs/tegg/compare/v3.73.0...v3.74.0) (2026-03-30) **Note:** Version bump only for package @eggjs/eventbus-decorator # [3.73.0](https://github.com/eggjs/tegg/compare/v3.72.0...v3.73.0) (2026-03-25) **Note:** Version bump only for package @eggjs/eventbus-decorator # [3.72.0](https://github.com/eggjs/tegg/compare/v3.71.2...v3.72.0) (2026-02-04) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.71.2](https://github.com/eggjs/tegg/compare/v3.71.1...v3.71.2) (2026-01-30) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.71.1](https://github.com/eggjs/tegg/compare/v3.71.0...v3.71.1) (2026-01-28) **Note:** Version bump only for package @eggjs/eventbus-decorator # [3.71.0](https://github.com/eggjs/tegg/compare/v3.70.1...v3.71.0) (2026-01-26) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.70.1](https://github.com/eggjs/tegg/compare/v3.70.0...v3.70.1) (2026-01-14) **Note:** Version bump only for package @eggjs/eventbus-decorator # [3.70.0](https://github.com/eggjs/tegg/compare/v3.69.0...v3.70.0) (2026-01-07) **Note:** Version bump only for package @eggjs/eventbus-decorator # [3.69.0](https://github.com/eggjs/tegg/compare/v3.68.0...v3.69.0) (2026-01-06) **Note:** Version bump only for package @eggjs/eventbus-decorator # [3.68.0](https://github.com/eggjs/tegg/compare/v3.67.2...v3.68.0) (2025-12-29) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.67.2](https://github.com/eggjs/tegg/compare/v3.67.1...v3.67.2) (2025-12-24) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.67.1](https://github.com/eggjs/tegg/compare/v3.67.0...v3.67.1) (2025-12-23) **Note:** Version bump only for package @eggjs/eventbus-decorator # [3.67.0](https://github.com/eggjs/tegg/compare/v3.66.0...v3.67.0) (2025-12-18) **Note:** Version bump only for package @eggjs/eventbus-decorator # [3.66.0](https://github.com/eggjs/tegg/compare/v3.65.3...v3.66.0) (2025-12-16) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.65.3](https://github.com/eggjs/tegg/compare/v3.65.2...v3.65.3) (2025-12-16) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.65.2](https://github.com/eggjs/tegg/compare/v3.65.1...v3.65.2) (2025-12-16) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.65.1](https://github.com/eggjs/tegg/compare/v3.65.0...v3.65.1) (2025-12-16) **Note:** Version bump only for package @eggjs/eventbus-decorator # [3.65.0](https://github.com/eggjs/tegg/compare/v3.64.5...v3.65.0) (2025-12-10) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.64.5](https://github.com/eggjs/tegg/compare/v3.64.4...v3.64.5) (2025-12-08) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.64.4](https://github.com/eggjs/tegg/compare/v3.64.3...v3.64.4) (2025-12-06) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.64.3](https://github.com/eggjs/tegg/compare/v3.64.2...v3.64.3) (2025-12-06) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.64.2](https://github.com/eggjs/tegg/compare/v3.64.1...v3.64.2) (2025-11-27) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.64.1](https://github.com/eggjs/tegg/compare/v3.64.0...v3.64.1) (2025-11-27) **Note:** Version bump only for package @eggjs/eventbus-decorator # [3.64.0](https://github.com/eggjs/tegg/compare/v3.63.2...v3.64.0) (2025-11-27) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.63.2](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.2) (2025-11-18) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.63.1](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.1) (2025-11-18) **Note:** Version bump only for package @eggjs/eventbus-decorator # [3.63.0](https://github.com/eggjs/tegg/compare/v3.62.3...v3.63.0) (2025-11-18) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.62.3](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.3) (2025-11-05) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.62.2](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.2) (2025-11-05) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.62.1](https://github.com/eggjs/tegg/compare/v3.62.0...v3.62.1) (2025-11-03) **Note:** Version bump only for package @eggjs/eventbus-decorator # [3.62.0](https://github.com/eggjs/tegg/compare/v3.61.0...v3.62.0) (2025-09-24) **Note:** Version bump only for package @eggjs/eventbus-decorator # [3.61.0](https://github.com/eggjs/tegg/compare/v3.60.3...v3.61.0) (2025-08-15) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.60.3](https://github.com/eggjs/tegg/compare/v3.60.2...v3.60.3) (2025-08-06) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.60.2](https://github.com/eggjs/tegg/compare/v3.60.1...v3.60.2) (2025-08-06) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.60.1](https://github.com/eggjs/tegg/compare/v3.60.0...v3.60.1) (2025-07-28) **Note:** Version bump only for package @eggjs/eventbus-decorator # [3.60.0](https://github.com/eggjs/tegg/compare/v3.59.1...v3.60.0) (2025-07-28) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.59.1](https://github.com/eggjs/tegg/compare/v3.59.0...v3.59.1) (2025-07-16) **Note:** Version bump only for package @eggjs/eventbus-decorator # [3.59.0](https://github.com/eggjs/tegg/compare/v3.58.0...v3.59.0) (2025-07-07) **Note:** Version bump only for package @eggjs/eventbus-decorator # [3.58.0](https://github.com/eggjs/tegg/compare/v3.57.14...v3.58.0) (2025-07-01) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.57.14](https://github.com/eggjs/tegg/compare/v3.57.13...v3.57.14) (2025-06-18) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.57.13](https://github.com/eggjs/tegg/compare/v3.57.12...v3.57.13) (2025-06-18) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.57.12](https://github.com/eggjs/tegg/compare/v3.57.11...v3.57.12) (2025-06-16) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.57.11](https://github.com/eggjs/tegg/compare/v3.57.10...v3.57.11) (2025-06-16) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.57.10](https://github.com/eggjs/tegg/compare/v3.57.9...v3.57.10) (2025-06-12) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.57.9](https://github.com/eggjs/tegg/compare/v3.57.8...v3.57.9) (2025-05-29) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.57.8](https://github.com/eggjs/tegg/compare/v3.57.7...v3.57.8) (2025-05-29) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.57.7](https://github.com/eggjs/tegg/compare/v3.57.6...v3.57.7) (2025-05-28) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.57.6](https://github.com/eggjs/tegg/compare/v3.57.5...v3.57.6) (2025-05-27) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.57.5](https://github.com/eggjs/tegg/compare/v3.57.4...v3.57.5) (2025-05-15) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.57.4](https://github.com/eggjs/tegg/compare/v3.57.3...v3.57.4) (2025-05-14) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.57.3](https://github.com/eggjs/tegg/compare/v3.57.2...v3.57.3) (2025-05-13) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.57.2](https://github.com/eggjs/tegg/compare/v3.57.1...v3.57.2) (2025-05-13) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.57.1](https://github.com/eggjs/tegg/compare/v3.57.0...v3.57.1) (2025-05-13) **Note:** Version bump only for package @eggjs/eventbus-decorator # [3.57.0](https://github.com/eggjs/tegg/compare/v3.56.3...v3.57.0) (2025-05-09) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.56.3](https://github.com/eggjs/tegg/compare/v3.56.2...v3.56.3) (2025-05-07) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.56.2](https://github.com/eggjs/tegg/compare/v3.56.1...v3.56.2) (2025-05-01) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.56.1](https://github.com/eggjs/tegg/compare/v3.56.0...v3.56.1) (2025-04-29) **Note:** Version bump only for package @eggjs/eventbus-decorator # [3.56.0](https://github.com/eggjs/tegg/compare/v3.55.0...v3.56.0) (2025-04-29) **Note:** Version bump only for package @eggjs/eventbus-decorator # [3.55.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.55.0) (2025-04-29) **Note:** Version bump only for package @eggjs/eventbus-decorator # [3.54.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.54.0) (2025-04-29) **Note:** Version bump only for package @eggjs/eventbus-decorator # [3.53.0](https://github.com/eggjs/tegg/compare/v3.52.1...v3.53.0) (2025-04-21) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.52.1](https://github.com/eggjs/tegg/compare/v3.52.0...v3.52.1) (2025-03-14) **Note:** Version bump only for package @eggjs/eventbus-decorator # [3.52.0](https://github.com/eggjs/tegg/compare/v3.51.2...v3.52.0) (2024-12-30) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.51.2](https://github.com/eggjs/tegg/compare/v3.51.1...v3.51.2) (2024-12-09) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.51.1](https://github.com/eggjs/tegg/compare/v3.51.0...v3.51.1) (2024-11-06) **Note:** Version bump only for package @eggjs/eventbus-decorator # [3.51.0](https://github.com/eggjs/tegg/compare/v3.50.1...v3.51.0) (2024-10-30) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.50.1](https://github.com/eggjs/tegg/compare/v3.50.0...v3.50.1) (2024-10-23) **Note:** Version bump only for package @eggjs/eventbus-decorator # [3.50.0](https://github.com/eggjs/tegg/compare/v3.49.0...v3.50.0) (2024-10-22) **Note:** Version bump only for package @eggjs/eventbus-decorator # [3.49.0](https://github.com/eggjs/tegg/compare/v3.48.1...v3.49.0) (2024-10-21) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.48.1](https://github.com/eggjs/tegg/compare/v3.48.0...v3.48.1) (2024-10-14) **Note:** Version bump only for package @eggjs/eventbus-decorator # [3.48.0](https://github.com/eggjs/tegg/compare/v3.47.2...v3.48.0) (2024-10-10) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.47.2](https://github.com/eggjs/tegg/compare/v3.47.1...v3.47.2) (2024-10-10) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.47.1](https://github.com/eggjs/tegg/compare/v3.47.0...v3.47.1) (2024-10-10) **Note:** Version bump only for package @eggjs/eventbus-decorator # [3.47.0](https://github.com/eggjs/tegg/compare/v3.46.4...v3.47.0) (2024-10-10) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.46.4](https://github.com/eggjs/tegg/compare/v3.46.3...v3.46.4) (2024-10-09) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.46.3](https://github.com/eggjs/tegg/compare/v3.46.2...v3.46.3) (2024-10-08) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.46.2](https://github.com/eggjs/tegg/compare/v3.46.1...v3.46.2) (2024-10-07) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.46.1](https://github.com/eggjs/tegg/compare/v3.46.0...v3.46.1) (2024-09-30) **Note:** Version bump only for package @eggjs/eventbus-decorator # [3.46.0](https://github.com/eggjs/tegg/compare/v3.45.0...v3.46.0) (2024-09-29) **Note:** Version bump only for package @eggjs/eventbus-decorator # [3.45.0](https://github.com/eggjs/tegg/compare/v3.44.1...v3.45.0) (2024-09-29) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.44.1](https://github.com/eggjs/tegg/compare/v3.44.0...v3.44.1) (2024-09-29) **Note:** Version bump only for package @eggjs/eventbus-decorator # [3.44.0](https://github.com/eggjs/tegg/compare/v3.43.2...v3.44.0) (2024-09-29) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.43.2](https://github.com/eggjs/tegg/compare/v3.43.1...v3.43.2) (2024-09-14) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.43.1](https://github.com/eggjs/tegg/compare/v3.43.0...v3.43.1) (2024-09-14) **Note:** Version bump only for package @eggjs/eventbus-decorator # [3.43.0](https://github.com/eggjs/tegg/compare/v3.42.0...v3.43.0) (2024-09-13) **Note:** Version bump only for package @eggjs/eventbus-decorator # [3.42.0](https://github.com/eggjs/tegg/compare/v3.41.0...v3.42.0) (2024-09-10) **Note:** Version bump only for package @eggjs/eventbus-decorator # [3.41.0](https://github.com/eggjs/tegg/compare/v3.40.1...v3.41.0) (2024-08-26) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.40.1](https://github.com/eggjs/tegg/compare/v3.40.0...v3.40.1) (2024-08-23) **Note:** Version bump only for package @eggjs/eventbus-decorator # [3.40.0](https://github.com/eggjs/tegg/compare/v3.39.5...v3.40.0) (2024-08-22) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.39.5](https://github.com/eggjs/tegg/compare/v3.39.4...v3.39.5) (2024-08-09) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.39.4](https://github.com/eggjs/tegg/compare/v3.39.3...v3.39.4) (2024-07-09) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.39.3](https://github.com/eggjs/tegg/compare/v3.39.2...v3.39.3) (2024-04-28) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.39.2](https://github.com/eggjs/tegg/compare/v3.39.1...v3.39.2) (2024-04-28) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.39.1](https://github.com/eggjs/tegg/compare/v3.39.0...v3.39.1) (2024-04-28) **Note:** Version bump only for package @eggjs/eventbus-decorator # [3.39.0](https://github.com/eggjs/tegg/compare/v3.38.0...v3.39.0) (2024-04-19) **Note:** Version bump only for package @eggjs/eventbus-decorator # [3.38.0](https://github.com/eggjs/tegg/compare/v3.37.3...v3.38.0) (2024-04-18) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.37.3](https://github.com/eggjs/tegg/compare/v3.37.2...v3.37.3) (2024-04-17) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.37.2](https://github.com/eggjs/tegg/compare/v3.37.1...v3.37.2) (2024-04-17) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.37.1](https://github.com/eggjs/tegg/compare/v3.37.0...v3.37.1) (2024-04-16) **Note:** Version bump only for package @eggjs/eventbus-decorator # [3.37.0](https://github.com/eggjs/tegg/compare/v3.36.3...v3.37.0) (2024-04-16) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.36.3](https://github.com/eggjs/tegg/compare/v3.36.2...v3.36.3) (2024-04-10) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.36.2](https://github.com/eggjs/tegg/compare/v3.36.1...v3.36.2) (2024-04-08) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.36.1](https://github.com/eggjs/tegg/compare/v3.36.0...v3.36.1) (2024-04-07) **Note:** Version bump only for package @eggjs/eventbus-decorator # [3.36.0](https://github.com/eggjs/tegg/compare/v3.35.1...v3.36.0) (2024-04-02) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.35.1](https://github.com/eggjs/tegg/compare/v3.35.0...v3.35.1) (2024-03-26) **Note:** Version bump only for package @eggjs/eventbus-decorator # [3.35.0](https://github.com/eggjs/tegg/compare/v3.34.0...v3.35.0) (2024-03-26) **Note:** Version bump only for package @eggjs/eventbus-decorator # [3.34.0](https://github.com/eggjs/tegg/compare/v3.33.1...v3.34.0) (2024-03-22) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.33.1](https://github.com/eggjs/tegg/compare/v3.33.0...v3.33.1) (2024-03-22) **Note:** Version bump only for package @eggjs/eventbus-decorator # [3.33.0](https://github.com/eggjs/tegg/compare/v3.32.0...v3.33.0) (2024-03-22) **Note:** Version bump only for package @eggjs/eventbus-decorator # [3.32.0](https://github.com/eggjs/tegg/compare/v3.31.0...v3.32.0) (2024-02-19) **Note:** Version bump only for package @eggjs/eventbus-decorator # [3.31.0](https://github.com/eggjs/tegg/compare/v3.30.1...v3.31.0) (2024-01-31) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.30.1](https://github.com/eggjs/tegg/compare/v3.30.0...v3.30.1) (2024-01-25) **Note:** Version bump only for package @eggjs/eventbus-decorator # [3.30.0](https://github.com/eggjs/tegg/compare/v3.29.0...v3.30.0) (2024-01-17) **Note:** Version bump only for package @eggjs/eventbus-decorator # [3.29.0](https://github.com/eggjs/tegg/compare/v3.28.2...v3.29.0) (2023-12-26) ### Features * allow a handler to subscribe to multiple events ([#179](https://github.com/eggjs/tegg/issues/179)) ([1d460a5](https://github.com/eggjs/tegg/commit/1d460a5a6bdcf9a3d61b13d3527633c8b990a38c)) ## [3.28.2](https://github.com/eggjs/tegg/compare/v3.28.1...v3.28.2) (2023-12-12) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.28.1](https://github.com/eggjs/tegg/compare/v3.28.0...v3.28.1) (2023-12-11) **Note:** Version bump only for package @eggjs/eventbus-decorator # [3.28.0](https://github.com/eggjs/tegg/compare/v3.27.0...v3.28.0) (2023-12-10) **Note:** Version bump only for package @eggjs/eventbus-decorator # [3.27.0](https://github.com/eggjs/tegg/compare/v3.26.0...v3.27.0) (2023-11-23) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.25.2](https://github.com/eggjs/tegg/compare/v3.25.1...v3.25.2) (2023-11-06) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.25.1](https://github.com/eggjs/tegg/compare/v3.25.0...v3.25.1) (2023-11-03) **Note:** Version bump only for package @eggjs/eventbus-decorator # [3.25.0](https://github.com/eggjs/tegg/compare/v3.24.0...v3.25.0) (2023-11-03) **Note:** Version bump only for package @eggjs/eventbus-decorator # [3.24.0](https://github.com/eggjs/tegg/compare/v3.23.0...v3.24.0) (2023-10-26) **Note:** Version bump only for package @eggjs/eventbus-decorator # [3.23.0](https://github.com/eggjs/tegg/compare/v3.22.0...v3.23.0) (2023-09-20) **Note:** Version bump only for package @eggjs/eventbus-decorator # [3.20.0](https://github.com/eggjs/tegg/compare/v3.19.0...v3.20.0) (2023-09-07) **Note:** Version bump only for package @eggjs/eventbus-decorator # [3.19.0](https://github.com/eggjs/tegg/compare/v3.18.1...v3.19.0) (2023-08-31) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.18.1](https://github.com/eggjs/tegg/compare/v3.18.0...v3.18.1) (2023-08-29) **Note:** Version bump only for package @eggjs/eventbus-decorator # [3.18.0](https://github.com/eggjs/tegg/compare/v3.17.0...v3.18.0) (2023-08-29) **Note:** Version bump only for package @eggjs/eventbus-decorator # [3.17.0](https://github.com/eggjs/tegg/compare/v3.16.0...v3.17.0) (2023-08-24) **Note:** Version bump only for package @eggjs/eventbus-decorator # [3.16.0](https://github.com/eggjs/tegg/compare/v3.15.0...v3.16.0) (2023-08-24) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.14.3](https://github.com/eggjs/tegg/compare/v3.14.2...v3.14.3) (2023-08-14) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.14.2](https://github.com/eggjs/tegg/compare/v3.14.1...v3.14.2) (2023-08-14) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.14.1](https://github.com/eggjs/tegg/compare/v3.14.0...v3.14.1) (2023-08-11) **Note:** Version bump only for package @eggjs/eventbus-decorator # [3.14.0](https://github.com/eggjs/tegg/compare/v3.13.0...v3.14.0) (2023-07-31) **Note:** Version bump only for package @eggjs/eventbus-decorator # [3.13.0](https://github.com/eggjs/tegg/compare/v3.12.0...v3.13.0) (2023-07-25) **Note:** Version bump only for package @eggjs/eventbus-decorator # [3.12.0](https://github.com/eggjs/tegg/compare/v3.11.1...v3.12.0) (2023-07-13) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.11.1](https://github.com/eggjs/tegg/compare/v3.11.0...v3.11.1) (2023-06-29) **Note:** Version bump only for package @eggjs/eventbus-decorator # [3.11.0](https://github.com/eggjs/tegg/compare/v3.10.0...v3.11.0) (2023-06-29) **Note:** Version bump only for package @eggjs/eventbus-decorator # [3.10.0](https://github.com/eggjs/tegg/compare/v3.9.0...v3.10.0) (2023-06-28) **Note:** Version bump only for package @eggjs/eventbus-decorator # [3.8.0](https://github.com/eggjs/tegg/compare/v3.7.0...v3.8.0) (2023-05-30) ### Features * impl EggObjectLifecycle hook in decorator ([#119](https://github.com/eggjs/tegg/issues/119)) ([cced8a2](https://github.com/eggjs/tegg/commit/cced8a2e009c33d5040fa21d00409fddef471b0e)) # [3.7.0](https://github.com/eggjs/tegg/compare/v3.6.3...v3.7.0) (2023-04-03) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.5.2](https://github.com/eggjs/tegg/compare/v3.5.1...v3.5.2) (2023-02-10) ### Bug Fixes * eventbus cork should support reentry ([#98](https://github.com/eggjs/tegg/issues/98)) ([077044c](https://github.com/eggjs/tegg/commit/077044c040f8423572605eb2980e3cc6da8c038e)) # [3.5.0](https://github.com/eggjs/tegg/compare/v3.4.1...v3.5.0) (2023-02-10) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.2.3](https://github.com/eggjs/tegg/compare/v3.2.2...v3.2.3) (2023-01-16) **Note:** Version bump only for package @eggjs/eventbus-decorator ## [3.2.2](https://github.com/eggjs/tegg/compare/v3.2.1...v3.2.2) (2023-01-06) **Note:** Version bump only for package @eggjs/eventbus-decorator # [3.1.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.1.0) (2022-12-27) ### Features * **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) * implement cork/uncork for eventbus ([#60](https://github.com/eggjs/tegg/issues/60)) ([38114bd](https://github.com/eggjs/tegg/commit/38114bd7ea3b46cc4a79556a005ef18b2ae11ec2)) # [3.0.0](https://github.com/eggjs/tegg/compare/v3.0.0-alpha.0...v3.0.0) (2022-12-26) **Note:** Version bump only for package @eggjs/eventbus-decorator # [3.0.0-alpha.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.0.0-alpha.0) (2022-12-22) ### Features * **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) * implement cork/uncork for eventbus ([#60](https://github.com/eggjs/tegg/issues/60)) ([38114bd](https://github.com/eggjs/tegg/commit/38114bd7ea3b46cc4a79556a005ef18b2ae11ec2)) # [1.3.0](https://github.com/eggjs/tegg/compare/v1.2.0...v1.3.0) (2022-07-01) **Note:** Version bump only for package @eggjs/eventbus-decorator # [0.2.0](https://github.com/eggjs/tegg/compare/v0.1.19...v0.2.0) (2022-01-20) **Note:** Version bump only for package @eggjs/eventbus-decorator ================================================ FILE: core/eventbus-decorator/README.md ================================================ # `@eggjs/eventbus-decorator` ## Usage ### emit event ```ts import { EventBus } from '@eggjs/eventbus-decorator' // Define event first. // Ts can check event and args type for you. declare module '@eggjs/eventbus-decorator' { interface Events { hello: (msg: string) => Promise; } } class Foo { @Inject() private readonly eventBus: EventBus; bar() { this.eventBus.emit('hello', '01'); } } ``` ### cork events Cache events in memory until uncork. ```ts class Foo { @Inject() private readonly eventBus: ContextEventBus; bar() { this.eventBus.cork(); // ...do something this.eventBus.emit('hello', '01'); // ...do other things // emit all cached events this.eventBus.uncork(); } } ``` ### handle event ```ts @Event('hello') export class Foo { async handle(msg: string): Promise { console.log('msg: ', msg); } } ``` ### handle multiple event ```ts @Event('hello') @Event('hi') export class Foo { async handle(msg: string): Promise { console.log('msg: ', msg); } } ``` ### inject event context inject event context if you want to know which event is being handled. The context param must be the first param ```ts @Event('hello') @Event('hi') export class Foo { async handle(@EventContext() ctx: IEventContext, msg: string):Promise { console.log('eventName: ', ctx.eventName); console.log('msg: ', msg); } } ``` ================================================ FILE: core/eventbus-decorator/index.ts ================================================ export * from './src/EventBus'; export * from './src/Event'; export * from './src/EventInfoUtil'; export * from './src/EventContext'; // trick for use declaration export interface Events { } ================================================ FILE: core/eventbus-decorator/package.json ================================================ { "name": "@eggjs/eventbus-decorator", "version": "3.78.15", "description": "tegg eventbus decorator", "keywords": [ "egg", "typescript", "eventbus", "eventemitter", "tegg" ], "main": "dist/index.js", "files": [ "dist/**/*.js", "dist/**/*.d.ts" ], "typings": "dist/index.d.ts", "scripts": { "test": "cross-env NODE_ENV=test NODE_OPTIONS='--no-deprecation' mocha", "clean": "tsc -b --clean", "tsc": "ut run clean && tsc -p ./tsconfig.json", "tsc:pub": "ut run clean && tsc -p ./tsconfig.pub.json", "prepublishOnly": "ut tsc:pub" }, "author": "killagu ", "license": "MIT", "homepage": "https://github.com/eggjs/tegg", "bugs": { "url": "https://github.com/eggjs/tegg/issues" }, "repository": { "type": "git", "url": "git@github.com:eggjs/tegg.git", "directory": "core/eventbus-decorator" }, "dependencies": { "@eggjs/core-decorator": "^3.78.15", "@eggjs/tegg-common-util": "^3.78.15", "@eggjs/tegg-types": "^3.78.15", "typed-emitter": "^1.3.1" }, "engines": { "node": ">=14.0.0" }, "devDependencies": { "@types/mocha": "^10.0.1", "@types/node": "^20.2.4", "coffee": "^5.4.0", "cross-env": "^7.0.3", "mocha": "^10.2.0", "ts-node": "^10.9.1", "typescript": "^5.0.4" }, "publishConfig": { "access": "public" }, "gitHead": "240a3e9f40fda65ebb7589727d197db5ce17916c" } ================================================ FILE: core/eventbus-decorator/src/Event.ts ================================================ import { AccessLevel, PrototypeUtil, SingletonProto } from '@eggjs/core-decorator'; import { StackUtil } from '@eggjs/tegg-common-util'; import { EventHandler } from '../index'; import { EventInfoUtil } from './EventInfoUtil'; // use @eggjs/tegg as namespace // eslint-disable-next-line import/no-unresolved import type { Events } from '@eggjs/tegg'; export function Event(eventName: E) { return function(clazz: new () => EventHandler) { EventInfoUtil.addEventName(eventName, clazz); const func = SingletonProto({ accessLevel: AccessLevel.PUBLIC, }); func(clazz); PrototypeUtil.setFilePath(clazz, StackUtil.getCalleeFromStack(false, 5)); }; } ================================================ FILE: core/eventbus-decorator/src/EventBus.ts ================================================ import TypedEventEmitter from 'typed-emitter'; import type { Arguments } from 'typed-emitter'; // use @eggjs/tegg as namespace // eslint-disable-next-line import/no-unresolved import type { Events } from '@eggjs/tegg'; import { IEventContext } from './EventContext'; export type EventName = string | symbol; export type { Arguments } from 'typed-emitter'; /** * use `emit` to emit a event */ export interface EventBus extends Pick, 'emit'> { cork(corkId: string); /** * @return true if uncorked */ uncork(corkId: string): boolean; } export const CORK_ID = Symbol.for('eventBus#corkId'); export interface ContextEventBus extends EventBus { cork(); uncork(); } export type EventKeys = keyof Events; /** * use to ensure event will happen * Can not inject for now, only use for unittest */ export interface EventWaiter { await(event: E): Promise> awaitFirst(e1: E1, e2: E2): Promise<{ event: E1 | E2, args: Arguments }> awaitFirst(e1: E1, e2: E2, e3: E3): Promise<{ event: E1 | E2 | E3, args: Arguments }> awaitFirst(e1: E1, e2: E2, e3: E3, e4: E4): Promise<{ event: E1 | E2 | E3 | E4, args: Arguments }> } type EventHandlerWithContext = { handle: (ctx: IEventContext, ...args: Arguments) => ReturnType }; export type EventHandler = { handle: Events[E]; } | EventHandlerWithContext; ================================================ FILE: core/eventbus-decorator/src/EventContext.ts ================================================ // use @eggjs/tegg as namespace // eslint-disable-next-line import/no-unresolved import type { Events } from '@eggjs/tegg'; import assert from 'node:assert'; import type { EggProtoImplClass } from '@eggjs/tegg-types'; import { EventInfoUtil } from './EventInfoUtil'; export interface IEventContext { eventName: keyof Events } export function EventContext() { return function(target: any, propertyKey: PropertyKey, parameterIndex: number) { assert(propertyKey === 'handle', `[eventHandler ${target.name}] expect method name be handle, but now is ${String(propertyKey)}`); assert(parameterIndex === 0, `[eventHandler ${target.name}] expect param EventContext be the first param`); const clazz = target.constructor as EggProtoImplClass; EventInfoUtil.setEventHandlerContextInject(true, clazz); }; } ================================================ FILE: core/eventbus-decorator/src/EventInfoUtil.ts ================================================ import { MetadataUtil } from '@eggjs/core-decorator'; import type { EggProtoImplClass } from '@eggjs/tegg-types'; import { EventName } from './EventBus'; export const EVENT_NAME = Symbol.for('EggPrototype#eventName'); export const EVENT_CONTEXT_INJECT = Symbol.for('EggPrototype#event#handler#context#inject'); export class EventInfoUtil { /** * @deprecated */ static setEventName(eventName: EventName, clazz: EggProtoImplClass) { EventInfoUtil.addEventName(eventName, clazz); } static addEventName(eventName: EventName, clazz: EggProtoImplClass) { const eventNameList = MetadataUtil.initOwnArrayMetaData(EVENT_NAME, clazz, []); eventNameList.push(eventName); } static getEventNameList(clazz: EggProtoImplClass): EventName[] { return MetadataUtil.getArrayMetaData(EVENT_NAME, clazz); } /** * @deprecated * return the last eventName which is subscribed firstly by Event decorator */ static getEventName(clazz: EggProtoImplClass): EventName | undefined { const eventNameList = MetadataUtil.initOwnArrayMetaData(EVENT_NAME, clazz, []); if (eventNameList.length !== 0) { return eventNameList[eventNameList.length - 1]; } return undefined; } static setEventHandlerContextInject(enable: boolean, clazz: EggProtoImplClass): void { MetadataUtil.defineMetaData(EVENT_CONTEXT_INJECT, enable, clazz); } static getEventHandlerContextInject(clazz: EggProtoImplClass): boolean { return MetadataUtil.getMetaData(EVENT_CONTEXT_INJECT, clazz) ?? false; } } ================================================ FILE: core/eventbus-decorator/src/type.d.ts ================================================ // https://www.typescriptlang.org/docs/handbook/declaration-merging.html // use declaration merging to allow user define self events declare module '@eggjs/tegg' { export interface Events { } } ================================================ FILE: core/eventbus-decorator/test/Event.test.ts ================================================ import assert from 'assert'; import path from 'path'; import coffee from 'coffee'; import { FooHandler } from './fixtures/right-event-handle'; import { MultiHandler } from './fixtures/multiple-events-handle'; import { EventContextHandler } from './fixtures/event-handle-with-context'; import { EmptyHandler } from './fixtures/empty-handle'; import { EventInfoUtil } from '../src/EventInfoUtil'; describe('test/Event.test.ts', () => { it('getEventName should work', () => { assert.equal(EventInfoUtil.getEventName(FooHandler), 'foo'); assert.equal(EventInfoUtil.getEventName(EmptyHandler), undefined); }); it('getEventNameList should work', function() { const event = EventInfoUtil.getEventName(MultiHandler); assert.deepStrictEqual(event, 'hello'); const eventList = EventInfoUtil.getEventNameList(MultiHandler); assert.deepStrictEqual(eventList, [ 'hi', 'hello' ]); }); it('setEventName should work', function() { EventInfoUtil.setEventName('foo', EmptyHandler); assert.equal(EventInfoUtil.getEventName(EmptyHandler), 'foo'); EventInfoUtil.setEventName('bar', EmptyHandler); assert.equal(EventInfoUtil.getEventName(EmptyHandler), 'bar'); }); it('getEventHandlerContextInject', function() { assert.equal(EventInfoUtil.getEventHandlerContextInject(EventContextHandler), true); assert.equal(EventInfoUtil.getEventHandlerContextInject(FooHandler), false); }); it('event type check should work', async () => { const tsc = require.resolve('typescript/bin/tsc'); const files = [ path.join(__dirname, 'fixtures/wrong-event-handle.ts'), path.join(__dirname, '../src/type.d.ts'), ]; await coffee.fork( tsc, [ '--noEmit', '--experimentalDecorators', ...files ], ) // .debug() .expect('stdout', /Type 'number' is not assignable to type 'string'/) .notExpect('code', 0) .end(); }); }); ================================================ FILE: core/eventbus-decorator/test/fixtures/empty-handle.ts ================================================ export class EmptyHandler{} ================================================ FILE: core/eventbus-decorator/test/fixtures/event-handle-with-context.ts ================================================ import { Inject, SingletonProto } from '@eggjs/core-decorator'; import {EventBus, Event, IEventContext, EventContext} from '../..'; declare module '@eggjs/tegg' { interface Events { ctxEvent: (msg: string) => void; } } @SingletonProto() export class EventContextProducer { @Inject() private readonly eventBus: EventBus; trigger() { this.eventBus.emit('ctxEvent', 'hello'); } } @Event('ctxEvent') export class EventContextHandler { handle(@EventContext() ctx: IEventContext, msg: string): void { console.log('ctx: ', ctx); console.log('msg: ', msg); } } ================================================ FILE: core/eventbus-decorator/test/fixtures/multiple-events-handle.ts ================================================ import { Inject, SingletonProto } from '@eggjs/core-decorator'; import { EventBus, Event } from '../..'; declare module '@eggjs/tegg' { interface Events { hello: (msg: string) => void; hi: (msg: string) => void; } } @SingletonProto() export class MultiProducer { @Inject() private readonly eventBus: EventBus; trigger() { this.eventBus.emit('hello', 'Ydream'); } } @Event('hello') @Event('hi') export class MultiHandler { handle(msg: string): void { console.log('msg: ', msg); } } ================================================ FILE: core/eventbus-decorator/test/fixtures/right-event-handle.ts ================================================ import { Inject, SingletonProto } from '@eggjs/core-decorator'; import { EventBus, Event } from '../..'; declare module '@eggjs/tegg' { interface Events { foo: (msg: string) => void; } } @SingletonProto() export class FooProducer { @Inject() private readonly eventBus: EventBus; trigger() { this.eventBus.emit('foo', 'hello'); } } @Event('foo') export class FooHandler { handle(msg: string): void { console.log('msg: ', msg); } } ================================================ FILE: core/eventbus-decorator/test/fixtures/wrong-event-handle.ts ================================================ import { Inject, SingletonProto } from '@eggjs/core-decorator'; import { EventBus, Event } from '../..'; declare module '@eggjs/tegg' { interface Events { bar: (msg: number) => void; } } @SingletonProto() export class BarProducer { @Inject() private readonly eventBus: EventBus; trigger() { this.eventBus.emit('bar', 'hello'); } } @Event('bar') export class BarHandler { handle(msg: string): void { console.log('msg: ', msg); } } ================================================ FILE: core/eventbus-decorator/tsconfig.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "dist", "baseUrl": "./" }, "exclude": [ "dist", "node_modules", "test" ] } ================================================ FILE: core/eventbus-decorator/tsconfig.pub.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "dist", "baseUrl": "./" }, "exclude": [ "dist", "node_modules", "test" ] } ================================================ FILE: core/eventbus-runtime/CHANGELOG.md ================================================ # Change Log All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. ## [3.78.15](https://github.com/eggjs/tegg/compare/v3.78.14...v3.78.15) (2026-04-23) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.78.14](https://github.com/eggjs/tegg/compare/v3.78.13...v3.78.14) (2026-04-21) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.78.13](https://github.com/eggjs/tegg/compare/v3.78.12...v3.78.13) (2026-04-21) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.78.12](https://github.com/eggjs/tegg/compare/v3.78.11...v3.78.12) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.78.11](https://github.com/eggjs/tegg/compare/v3.78.10...v3.78.11) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.78.10](https://github.com/eggjs/tegg/compare/v3.78.9...v3.78.10) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.78.9](https://github.com/eggjs/tegg/compare/v3.78.8...v3.78.9) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.78.8](https://github.com/eggjs/tegg/compare/v3.78.7...v3.78.8) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.78.7](https://github.com/eggjs/tegg/compare/v3.78.6...v3.78.7) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.78.6](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.6) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.78.5](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.5) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.78.4](https://github.com/eggjs/tegg/compare/v3.78.3...v3.78.4) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.78.3](https://github.com/eggjs/tegg/compare/v3.78.2...v3.78.3) (2026-04-15) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.78.2](https://github.com/eggjs/tegg/compare/v3.78.1...v3.78.2) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.78.1](https://github.com/eggjs/tegg/compare/v3.78.0...v3.78.1) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime # [3.78.0](https://github.com/eggjs/tegg/compare/v3.77.2...v3.78.0) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.77.2](https://github.com/eggjs/tegg/compare/v3.77.1...v3.77.2) (2026-04-07) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.77.1](https://github.com/eggjs/tegg/compare/v3.77.0...v3.77.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime # [3.77.0](https://github.com/eggjs/tegg/compare/v3.76.1...v3.77.0) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.76.1](https://github.com/eggjs/tegg/compare/v3.76.0...v3.76.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime # [3.76.0](https://github.com/eggjs/tegg/compare/v3.75.1...v3.76.0) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.75.1](https://github.com/eggjs/tegg/compare/v3.75.0...v3.75.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime # [3.75.0](https://github.com/eggjs/tegg/compare/v3.74.0...v3.75.0) (2026-03-30) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime # [3.74.0](https://github.com/eggjs/tegg/compare/v3.73.0...v3.74.0) (2026-03-30) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime # [3.73.0](https://github.com/eggjs/tegg/compare/v3.72.0...v3.73.0) (2026-03-25) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime # [3.72.0](https://github.com/eggjs/tegg/compare/v3.71.2...v3.72.0) (2026-02-04) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.71.2](https://github.com/eggjs/tegg/compare/v3.71.1...v3.71.2) (2026-01-30) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.71.1](https://github.com/eggjs/tegg/compare/v3.71.0...v3.71.1) (2026-01-28) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime # [3.71.0](https://github.com/eggjs/tegg/compare/v3.70.1...v3.71.0) (2026-01-26) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.70.1](https://github.com/eggjs/tegg/compare/v3.70.0...v3.70.1) (2026-01-14) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime # [3.70.0](https://github.com/eggjs/tegg/compare/v3.69.0...v3.70.0) (2026-01-07) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime # [3.69.0](https://github.com/eggjs/tegg/compare/v3.68.0...v3.69.0) (2026-01-06) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime # [3.68.0](https://github.com/eggjs/tegg/compare/v3.67.2...v3.68.0) (2025-12-29) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.67.2](https://github.com/eggjs/tegg/compare/v3.67.1...v3.67.2) (2025-12-24) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.67.1](https://github.com/eggjs/tegg/compare/v3.67.0...v3.67.1) (2025-12-23) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime # [3.67.0](https://github.com/eggjs/tegg/compare/v3.66.0...v3.67.0) (2025-12-18) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime # [3.66.0](https://github.com/eggjs/tegg/compare/v3.65.3...v3.66.0) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.65.3](https://github.com/eggjs/tegg/compare/v3.65.2...v3.65.3) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.65.2](https://github.com/eggjs/tegg/compare/v3.65.1...v3.65.2) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.65.1](https://github.com/eggjs/tegg/compare/v3.65.0...v3.65.1) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime # [3.65.0](https://github.com/eggjs/tegg/compare/v3.64.5...v3.65.0) (2025-12-10) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.64.5](https://github.com/eggjs/tegg/compare/v3.64.4...v3.64.5) (2025-12-08) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.64.4](https://github.com/eggjs/tegg/compare/v3.64.3...v3.64.4) (2025-12-06) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.64.3](https://github.com/eggjs/tegg/compare/v3.64.2...v3.64.3) (2025-12-06) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.64.2](https://github.com/eggjs/tegg/compare/v3.64.1...v3.64.2) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.64.1](https://github.com/eggjs/tegg/compare/v3.64.0...v3.64.1) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime # [3.64.0](https://github.com/eggjs/tegg/compare/v3.63.2...v3.64.0) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.63.2](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.2) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.63.1](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.1) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime # [3.63.0](https://github.com/eggjs/tegg/compare/v3.62.3...v3.63.0) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.62.3](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.3) (2025-11-05) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.62.2](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.2) (2025-11-05) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.62.1](https://github.com/eggjs/tegg/compare/v3.62.0...v3.62.1) (2025-11-03) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime # [3.62.0](https://github.com/eggjs/tegg/compare/v3.61.0...v3.62.0) (2025-09-24) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime # [3.61.0](https://github.com/eggjs/tegg/compare/v3.60.3...v3.61.0) (2025-08-15) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.60.3](https://github.com/eggjs/tegg/compare/v3.60.2...v3.60.3) (2025-08-06) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.60.2](https://github.com/eggjs/tegg/compare/v3.60.1...v3.60.2) (2025-08-06) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.60.1](https://github.com/eggjs/tegg/compare/v3.60.0...v3.60.1) (2025-07-28) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime # [3.60.0](https://github.com/eggjs/tegg/compare/v3.59.1...v3.60.0) (2025-07-28) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.59.1](https://github.com/eggjs/tegg/compare/v3.59.0...v3.59.1) (2025-07-16) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime # [3.59.0](https://github.com/eggjs/tegg/compare/v3.58.0...v3.59.0) (2025-07-07) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime # [3.58.0](https://github.com/eggjs/tegg/compare/v3.57.14...v3.58.0) (2025-07-01) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.57.14](https://github.com/eggjs/tegg/compare/v3.57.13...v3.57.14) (2025-06-18) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.57.13](https://github.com/eggjs/tegg/compare/v3.57.12...v3.57.13) (2025-06-18) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.57.12](https://github.com/eggjs/tegg/compare/v3.57.11...v3.57.12) (2025-06-16) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.57.11](https://github.com/eggjs/tegg/compare/v3.57.10...v3.57.11) (2025-06-16) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.57.10](https://github.com/eggjs/tegg/compare/v3.57.9...v3.57.10) (2025-06-12) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.57.9](https://github.com/eggjs/tegg/compare/v3.57.8...v3.57.9) (2025-05-29) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.57.8](https://github.com/eggjs/tegg/compare/v3.57.7...v3.57.8) (2025-05-29) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.57.7](https://github.com/eggjs/tegg/compare/v3.57.6...v3.57.7) (2025-05-28) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.57.6](https://github.com/eggjs/tegg/compare/v3.57.5...v3.57.6) (2025-05-27) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.57.5](https://github.com/eggjs/tegg/compare/v3.57.4...v3.57.5) (2025-05-15) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.57.4](https://github.com/eggjs/tegg/compare/v3.57.3...v3.57.4) (2025-05-14) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.57.3](https://github.com/eggjs/tegg/compare/v3.57.2...v3.57.3) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.57.2](https://github.com/eggjs/tegg/compare/v3.57.1...v3.57.2) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.57.1](https://github.com/eggjs/tegg/compare/v3.57.0...v3.57.1) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime # [3.57.0](https://github.com/eggjs/tegg/compare/v3.56.3...v3.57.0) (2025-05-09) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.56.3](https://github.com/eggjs/tegg/compare/v3.56.2...v3.56.3) (2025-05-07) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.56.2](https://github.com/eggjs/tegg/compare/v3.56.1...v3.56.2) (2025-05-01) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.56.1](https://github.com/eggjs/tegg/compare/v3.56.0...v3.56.1) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime # [3.56.0](https://github.com/eggjs/tegg/compare/v3.55.0...v3.56.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime # [3.55.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.55.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime # [3.54.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.54.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime # [3.53.0](https://github.com/eggjs/tegg/compare/v3.52.1...v3.53.0) (2025-04-21) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.52.1](https://github.com/eggjs/tegg/compare/v3.52.0...v3.52.1) (2025-03-14) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime # [3.52.0](https://github.com/eggjs/tegg/compare/v3.51.2...v3.52.0) (2024-12-30) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.51.2](https://github.com/eggjs/tegg/compare/v3.51.1...v3.51.2) (2024-12-09) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.51.1](https://github.com/eggjs/tegg/compare/v3.51.0...v3.51.1) (2024-11-06) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime # [3.51.0](https://github.com/eggjs/tegg/compare/v3.50.1...v3.51.0) (2024-10-30) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.50.1](https://github.com/eggjs/tegg/compare/v3.50.0...v3.50.1) (2024-10-23) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime # [3.50.0](https://github.com/eggjs/tegg/compare/v3.49.0...v3.50.0) (2024-10-22) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime # [3.49.0](https://github.com/eggjs/tegg/compare/v3.48.1...v3.49.0) (2024-10-21) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.48.1](https://github.com/eggjs/tegg/compare/v3.48.0...v3.48.1) (2024-10-14) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime # [3.48.0](https://github.com/eggjs/tegg/compare/v3.47.2...v3.48.0) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.47.2](https://github.com/eggjs/tegg/compare/v3.47.1...v3.47.2) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.47.1](https://github.com/eggjs/tegg/compare/v3.47.0...v3.47.1) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime # [3.47.0](https://github.com/eggjs/tegg/compare/v3.46.4...v3.47.0) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.46.4](https://github.com/eggjs/tegg/compare/v3.46.3...v3.46.4) (2024-10-09) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.46.3](https://github.com/eggjs/tegg/compare/v3.46.2...v3.46.3) (2024-10-08) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.46.2](https://github.com/eggjs/tegg/compare/v3.46.1...v3.46.2) (2024-10-07) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.46.1](https://github.com/eggjs/tegg/compare/v3.46.0...v3.46.1) (2024-09-30) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime # [3.46.0](https://github.com/eggjs/tegg/compare/v3.45.0...v3.46.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime # [3.45.0](https://github.com/eggjs/tegg/compare/v3.44.1...v3.45.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.44.1](https://github.com/eggjs/tegg/compare/v3.44.0...v3.44.1) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime # [3.44.0](https://github.com/eggjs/tegg/compare/v3.43.2...v3.44.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.43.2](https://github.com/eggjs/tegg/compare/v3.43.1...v3.43.2) (2024-09-14) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.43.1](https://github.com/eggjs/tegg/compare/v3.43.0...v3.43.1) (2024-09-14) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime # [3.43.0](https://github.com/eggjs/tegg/compare/v3.42.0...v3.43.0) (2024-09-13) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime # [3.42.0](https://github.com/eggjs/tegg/compare/v3.41.0...v3.42.0) (2024-09-10) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime # [3.41.0](https://github.com/eggjs/tegg/compare/v3.40.1...v3.41.0) (2024-08-26) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.40.1](https://github.com/eggjs/tegg/compare/v3.40.0...v3.40.1) (2024-08-23) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime # [3.40.0](https://github.com/eggjs/tegg/compare/v3.39.5...v3.40.0) (2024-08-22) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.39.5](https://github.com/eggjs/tegg/compare/v3.39.4...v3.39.5) (2024-08-09) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.39.4](https://github.com/eggjs/tegg/compare/v3.39.3...v3.39.4) (2024-07-09) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.39.3](https://github.com/eggjs/tegg/compare/v3.39.2...v3.39.3) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.39.2](https://github.com/eggjs/tegg/compare/v3.39.1...v3.39.2) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.39.1](https://github.com/eggjs/tegg/compare/v3.39.0...v3.39.1) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime # [3.39.0](https://github.com/eggjs/tegg/compare/v3.38.0...v3.39.0) (2024-04-19) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime # [3.38.0](https://github.com/eggjs/tegg/compare/v3.37.3...v3.38.0) (2024-04-18) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.37.3](https://github.com/eggjs/tegg/compare/v3.37.2...v3.37.3) (2024-04-17) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.37.2](https://github.com/eggjs/tegg/compare/v3.37.1...v3.37.2) (2024-04-17) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.37.1](https://github.com/eggjs/tegg/compare/v3.37.0...v3.37.1) (2024-04-16) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime # [3.37.0](https://github.com/eggjs/tegg/compare/v3.36.3...v3.37.0) (2024-04-16) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.36.3](https://github.com/eggjs/tegg/compare/v3.36.2...v3.36.3) (2024-04-10) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.36.2](https://github.com/eggjs/tegg/compare/v3.36.1...v3.36.2) (2024-04-08) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.36.1](https://github.com/eggjs/tegg/compare/v3.36.0...v3.36.1) (2024-04-07) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime # [3.36.0](https://github.com/eggjs/tegg/compare/v3.35.1...v3.36.0) (2024-04-02) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.35.1](https://github.com/eggjs/tegg/compare/v3.35.0...v3.35.1) (2024-03-26) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime # [3.35.0](https://github.com/eggjs/tegg/compare/v3.34.0...v3.35.0) (2024-03-26) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime # [3.34.0](https://github.com/eggjs/tegg/compare/v3.33.1...v3.34.0) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.33.1](https://github.com/eggjs/tegg/compare/v3.33.0...v3.33.1) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime # [3.33.0](https://github.com/eggjs/tegg/compare/v3.32.0...v3.33.0) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime # [3.32.0](https://github.com/eggjs/tegg/compare/v3.31.0...v3.32.0) (2024-02-19) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime # [3.31.0](https://github.com/eggjs/tegg/compare/v3.30.1...v3.31.0) (2024-01-31) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.30.1](https://github.com/eggjs/tegg/compare/v3.30.0...v3.30.1) (2024-01-25) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime # [3.30.0](https://github.com/eggjs/tegg/compare/v3.29.0...v3.30.0) (2024-01-17) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime # [3.29.0](https://github.com/eggjs/tegg/compare/v3.28.2...v3.29.0) (2023-12-26) ### Features * allow a handler to subscribe to multiple events ([#179](https://github.com/eggjs/tegg/issues/179)) ([1d460a5](https://github.com/eggjs/tegg/commit/1d460a5a6bdcf9a3d61b13d3527633c8b990a38c)) ## [3.28.2](https://github.com/eggjs/tegg/compare/v3.28.1...v3.28.2) (2023-12-12) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.28.1](https://github.com/eggjs/tegg/compare/v3.28.0...v3.28.1) (2023-12-11) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime # [3.28.0](https://github.com/eggjs/tegg/compare/v3.27.0...v3.28.0) (2023-12-10) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime # [3.27.0](https://github.com/eggjs/tegg/compare/v3.26.0...v3.27.0) (2023-11-23) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.25.2](https://github.com/eggjs/tegg/compare/v3.25.1...v3.25.2) (2023-11-06) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.25.1](https://github.com/eggjs/tegg/compare/v3.25.0...v3.25.1) (2023-11-03) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime # [3.25.0](https://github.com/eggjs/tegg/compare/v3.24.0...v3.25.0) (2023-11-03) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime # [3.24.0](https://github.com/eggjs/tegg/compare/v3.23.0...v3.24.0) (2023-10-26) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime # [3.23.0](https://github.com/eggjs/tegg/compare/v3.22.0...v3.23.0) (2023-09-20) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime # [3.20.0](https://github.com/eggjs/tegg/compare/v3.19.0...v3.20.0) (2023-09-07) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime # [3.19.0](https://github.com/eggjs/tegg/compare/v3.18.1...v3.19.0) (2023-08-31) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.18.1](https://github.com/eggjs/tegg/compare/v3.18.0...v3.18.1) (2023-08-29) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime # [3.18.0](https://github.com/eggjs/tegg/compare/v3.17.0...v3.18.0) (2023-08-29) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime # [3.17.0](https://github.com/eggjs/tegg/compare/v3.16.0...v3.17.0) (2023-08-24) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime # [3.16.0](https://github.com/eggjs/tegg/compare/v3.15.0...v3.16.0) (2023-08-24) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.14.3](https://github.com/eggjs/tegg/compare/v3.14.2...v3.14.3) (2023-08-14) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.14.2](https://github.com/eggjs/tegg/compare/v3.14.1...v3.14.2) (2023-08-14) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.14.1](https://github.com/eggjs/tegg/compare/v3.14.0...v3.14.1) (2023-08-11) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime # [3.14.0](https://github.com/eggjs/tegg/compare/v3.13.0...v3.14.0) (2023-07-31) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime # [3.13.0](https://github.com/eggjs/tegg/compare/v3.12.0...v3.13.0) (2023-07-25) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime # [3.12.0](https://github.com/eggjs/tegg/compare/v3.11.1...v3.12.0) (2023-07-13) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.11.1](https://github.com/eggjs/tegg/compare/v3.11.0...v3.11.1) (2023-06-29) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime # [3.11.0](https://github.com/eggjs/tegg/compare/v3.10.0...v3.11.0) (2023-06-29) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime # [3.10.0](https://github.com/eggjs/tegg/compare/v3.9.0...v3.10.0) (2023-06-28) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime # [3.8.0](https://github.com/eggjs/tegg/compare/v3.7.0...v3.8.0) (2023-05-30) ### Features * impl EggObjectLifecycle hook in decorator ([#119](https://github.com/eggjs/tegg/issues/119)) ([cced8a2](https://github.com/eggjs/tegg/commit/cced8a2e009c33d5040fa21d00409fddef471b0e)) # [3.7.0](https://github.com/eggjs/tegg/compare/v3.6.3...v3.7.0) (2023-04-03) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.6.3](https://github.com/eggjs/tegg/compare/v3.6.2...v3.6.3) (2023-03-02) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.5.2](https://github.com/eggjs/tegg/compare/v3.5.1...v3.5.2) (2023-02-10) ### Bug Fixes * eventbus cork should support reentry ([#98](https://github.com/eggjs/tegg/issues/98)) ([077044c](https://github.com/eggjs/tegg/commit/077044c040f8423572605eb2980e3cc6da8c038e)) # [3.5.0](https://github.com/eggjs/tegg/compare/v3.4.1...v3.5.0) (2023-02-10) ### Features * use SingletonProto for egg ctx object ([#92](https://github.com/eggjs/tegg/issues/92)) ([3385d57](https://github.com/eggjs/tegg/commit/3385d571b076d3148978f252188f29d9cf2c6781)) ## [3.3.1](https://github.com/eggjs/tegg/compare/v3.3.0...v3.3.1) (2023-01-28) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.2.3](https://github.com/eggjs/tegg/compare/v3.2.2...v3.2.3) (2023-01-16) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.2.2](https://github.com/eggjs/tegg/compare/v3.2.1...v3.2.2) (2023-01-06) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ## [3.2.1](https://github.com/eggjs/tegg/compare/v3.2.0...v3.2.1) (2022-12-28) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime # [3.1.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.1.0) (2022-12-27) ### Bug Fixes * eventbus runtime should wait all handlers done ([#51](https://github.com/eggjs/tegg/issues/51)) ([0651d30](https://github.com/eggjs/tegg/commit/0651d300f9a18bd97299548f3ebccad1d0382d28)) * fix events type from any to keyof Events ([#54](https://github.com/eggjs/tegg/issues/54)) ([a2551b2](https://github.com/eggjs/tegg/commit/a2551b2d9f9eabf9ed5c87f83489615eefa3e6d1)) * inject context proto to singleton proto ([#72](https://github.com/eggjs/tegg/issues/72)) ([fcc0b2b](https://github.com/eggjs/tegg/commit/fcc0b2b48fc9bce580c1f2bcfcc38039ae909951)) ### Features * **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) * implement cork/uncork for eventbus ([#60](https://github.com/eggjs/tegg/issues/60)) ([38114bd](https://github.com/eggjs/tegg/commit/38114bd7ea3b46cc4a79556a005ef18b2ae11ec2)) # [3.0.0](https://github.com/eggjs/tegg/compare/v3.0.0-alpha.0...v3.0.0) (2022-12-26) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime # [3.0.0-alpha.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.0.0-alpha.0) (2022-12-22) ### Bug Fixes * eventbus runtime should wait all handlers done ([#51](https://github.com/eggjs/tegg/issues/51)) ([0651d30](https://github.com/eggjs/tegg/commit/0651d300f9a18bd97299548f3ebccad1d0382d28)) * fix events type from any to keyof Events ([#54](https://github.com/eggjs/tegg/issues/54)) ([a2551b2](https://github.com/eggjs/tegg/commit/a2551b2d9f9eabf9ed5c87f83489615eefa3e6d1)) ### Features * **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) * implement cork/uncork for eventbus ([#60](https://github.com/eggjs/tegg/issues/60)) ([38114bd](https://github.com/eggjs/tegg/commit/38114bd7ea3b46cc4a79556a005ef18b2ae11ec2)) ## [1.4.1](https://github.com/eggjs/tegg/compare/@eggjs/tegg-eventbus-runtime@1.4.0...@eggjs/tegg-eventbus-runtime@1.4.1) (2022-09-04) ### Bug Fixes * fix events type from any to keyof Events ([#54](https://github.com/eggjs/tegg/issues/54)) ([a2551b2](https://github.com/eggjs/tegg/commit/a2551b2d9f9eabf9ed5c87f83489615eefa3e6d1)) # 1.4.0 (2022-08-24) ### Bug Fixes * eventbus runtime should wait all handlers done ([#51](https://github.com/eggjs/tegg/issues/51)) ([0651d30](https://github.com/eggjs/tegg/commit/0651d300f9a18bd97299548f3ebccad1d0382d28)) # 1.3.0 (2022-07-01) # 1.2.0 (2022-06-29) ## 1.1.1 (2022-06-21) # 1.1.0 (2022-06-15) ## 1.0.5 (2022-04-24) ## 1.0.1 (2022-02-08) # 1.0.0 (2022-02-08) # 0.2.0 (2022-01-20) ## 0.1.19 (2022-01-05) ## 0.1.18 (2021-12-31) ## 0.1.16 (2021-12-15) ## 0.1.13 (2021-11-14) ### Features * impl standalone tegg ([af9f682](https://github.com/eggjs/tegg/commit/af9f6826ef882ef7206e80ee25433a2b19012995)) ## 0.1.10 (2021-10-26) ## 0.1.7 (2021-09-13) ## 0.1.6 (2021-09-09) ## 0.1.5 (2021-09-08) ## 0.1.2 (2021-09-01) ### Features * impl dynamic inject ([bdafd5a](https://github.com/eggjs/tegg/commit/bdafd5a445b815515fc9e872fcfefc67a53ea562)) # 0.1.0 (2021-07-22) ### Bug Fixes * set publishConfig.access to public ([527f1fa](https://github.com/eggjs/tegg/commit/527f1fa8e3bcaf45ff5b3a63d90473d4a6a2e2b0)) # [1.3.0](https://github.com/eggjs/tegg/compare/v1.2.0...v1.3.0) (2022-07-01) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime # [0.2.0](https://github.com/eggjs/tegg/compare/v0.1.19...v0.2.0) (2022-01-20) **Note:** Version bump only for package @eggjs/tegg-eventbus-runtime ================================================ FILE: core/eventbus-runtime/README.md ================================================ # `@eggjs/eventbus-runtime` # Usage This is an internal tegg library, you probably shouldn't use it directly. ================================================ FILE: core/eventbus-runtime/index.ts ================================================ export * from './src/SingletonEventBus'; export * from './src/EventHandlerFactory'; export * from './src/EventContextFactory'; ================================================ FILE: core/eventbus-runtime/package.json ================================================ { "name": "@eggjs/tegg-eventbus-runtime", "version": "3.78.15", "description": "tegg eventbus runtime", "keywords": [ "egg", "typescript", "eventbus", "eventemitter", "tegg" ], "eggModule": { "name": "eventbusRuntime" }, "main": "dist/index.js", "files": [ "dist/**/*.js", "dist/**/*.d.ts" ], "typings": "dist/index.d.ts", "scripts": { "test": "cross-env NODE_ENV=test NODE_OPTIONS='--no-deprecation' mocha", "clean": "tsc -b --clean && rm -rf dist", "tsc": "ut run clean && tsc -p ./tsconfig.json", "tsc:pub": "ut run clean && tsc -p ./tsconfig.pub.json", "prepublishOnly": "ut tsc:pub" }, "author": "killagu ", "license": "MIT", "homepage": "https://github.com/eggjs/tegg", "bugs": { "url": "https://github.com/eggjs/tegg/issues" }, "repository": { "type": "git", "url": "git@github.com:eggjs/tegg.git", "directory": "core/eventbus-runtime" }, "dependencies": { "@eggjs/core-decorator": "^3.78.15", "@eggjs/eventbus-decorator": "^3.78.15", "@eggjs/tegg-common-util": "^3.78.15", "@eggjs/tegg-runtime": "^3.78.15", "@eggjs/tegg-types": "^3.78.15", "await-event": "^2.1.0", "await-first": "^1.0.0" }, "engines": { "node": ">=14.0.0" }, "devDependencies": { "@eggjs/tegg-loader": "^3.78.15", "@eggjs/tegg-metadata": "^3.78.15", "@types/mocha": "^10.0.1", "@types/node": "^20.2.4", "coffee": "^5.4.0", "cross-env": "^7.0.3", "egg": "^3.9.1", "mm": "^3.2.0", "mocha": "^10.2.0", "ts-node": "^10.9.1", "typescript": "^5.0.4" }, "publishConfig": { "access": "public" }, "gitHead": "240a3e9f40fda65ebb7589727d197db5ce17916c" } ================================================ FILE: core/eventbus-runtime/src/EventContextFactory.ts ================================================ import { SingletonProto } from '@eggjs/core-decorator'; import { AccessLevel } from '@eggjs/tegg-types'; import type { EggRuntimeContext } from '@eggjs/tegg-types'; export type ContextCreator = (parentContext?: EggRuntimeContext) => EggRuntimeContext; @SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) export class EventContextFactory { private creator: ContextCreator; createContext(parentContext?: EggRuntimeContext): EggRuntimeContext { return this.creator(parentContext); } registerContextCreator(creator: ContextCreator) { this.creator = creator; } } ================================================ FILE: core/eventbus-runtime/src/EventHandlerFactory.ts ================================================ import { EventHandler, EventName, Events, Arguments, EVENT_CONTEXT_INJECT } from '@eggjs/eventbus-decorator'; import { EggContainerFactory } from '@eggjs/tegg-runtime'; import { AccessLevel } from '@eggjs/tegg-types'; import type { EggPrototype } from '@eggjs/tegg-types'; import { MapUtil } from '@eggjs/tegg-common-util'; import { SingletonProto } from '@eggjs/core-decorator'; @SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) export class EventHandlerFactory { private handlerProtoMap: Map> = new Map(); registerHandler(event: EventName, proto: EggPrototype) { const protos = MapUtil.getOrStore(this.handlerProtoMap, event, []); protos.push(proto); } hasListeners(event: EventName) { return this.handlerProtoMap.has(event); } getHandlerProtos(event: EventName): Array { const handlerProtos = this.handlerProtoMap.get(event) || []; return handlerProtos; } async getHandler(proto: EggPrototype): Promise> { const eggObj = await EggContainerFactory.getOrCreateEggObject(proto, proto.name); return eggObj.obj as EventHandler; } async getHandlers(event: EventName): Promise>> { const handlerProtos = this.getHandlerProtos(event); return await Promise.all(handlerProtos.map(proto => { return this.getHandler(proto); })); } async handle(eventName: EventName, proto: EggPrototype, args: Arguments): Promise { const handler = await this.getHandler(proto); const enableInjectCtx = proto.getMetaData(EVENT_CONTEXT_INJECT) ?? false; if (enableInjectCtx) { const ctx = { eventName, }; await Reflect.apply(handler.handle, handler, [ ctx, ...args ]); } else { await Reflect.apply(handler.handle, handler, args); } } } ================================================ FILE: core/eventbus-runtime/src/SingletonEventBus.ts ================================================ import { Inject, SingletonProto } from '@eggjs/core-decorator'; import { EventBus, Events, EventWaiter, EventName, CORK_ID } from '@eggjs/eventbus-decorator'; import type { Arguments } from '@eggjs/eventbus-decorator'; import { ContextHandler } from '@eggjs/tegg-runtime'; import type { EggLogger } from 'egg'; import { AccessLevel } from '@eggjs/tegg-types'; import type { EggRuntimeContext } from '@eggjs/tegg-types'; import { EventContextFactory } from './EventContextFactory'; import { EventHandlerFactory } from './EventHandlerFactory'; import { EventEmitter } from 'events'; import awaitEvent from 'await-event'; import awaitFirst from 'await-first'; export interface Event { name: EventName; args: Array; context?: EggRuntimeContext; } export interface CorkEvents { times: number; events: Array; } @SingletonProto({ // TODO 需要考虑支持别名 // SingletonEventBus 同时实现了两个接口 name: 'eventBus', accessLevel: AccessLevel.PUBLIC, }) export class SingletonEventBus implements EventBus, EventWaiter { private readonly emitter = new EventEmitter(); @Inject() private readonly eventContextFactory: EventContextFactory; @Inject() private readonly eventHandlerFactory: EventHandlerFactory; @Inject({ name: 'logger', }) private readonly logger: EggLogger; private corkIdSequence = 0; private readonly corkedEvents = new Map(); /** * only use for ensure event will happen */ once(event: E, listener: Events[E]): this { this.emitter.once(event, listener); return this; } async await(event: E): Promise> { return awaitEvent(this.emitter, event); } awaitFirst(...e: Array): Promise<{ event: EventName, args: Arguments }> { return awaitFirst(this.emitter, e); } emit(event: E, ...args: Arguments): boolean { const ctx = this.eventContextFactory.createContext(); const hasListener = this.eventHandlerFactory.hasListeners(event); this.doEmit(ctx, event, args); return hasListener; } generateCorkId(): string { return String(++this.corkIdSequence); } cork(corkId: string) { let corkEvents = this.corkedEvents.get(corkId); if (!corkEvents) { corkEvents = { times: 0, events: [], } as unknown as CorkEvents; this.corkedEvents.set(corkId, corkEvents); } corkEvents!.times++; } uncork(corkId: string) { const corkEvents = this.corkedEvents.get(corkId); if (!corkEvents) { throw new Error(`eventbus corkId ${corkId} not found`); } if (--corkEvents.times !== 0) { return false; } this.corkedEvents.delete(corkId); for (const event of corkEvents.events) { if (event.context) { this.doEmitWithContext(event.context, event.name, event.args); } } return true; } queueEvent(corkId: string, event: Event) { const corkdEvents = this.corkedEvents.get(corkId); if (!corkdEvents) { throw new Error(`eventbus corkId ${corkId} not found`); } corkdEvents.events.push(event); } emitWithContext(parentContext: EggRuntimeContext, event: E, args: Arguments): boolean { const corkId = parentContext.get(CORK_ID); const hasListener = this.eventHandlerFactory.hasListeners(event); if (corkId) { this.queueEvent(corkId, { name: event, args, context: parentContext }); return hasListener; } return this.doEmitWithContext(parentContext, event, args); } private doEmitWithContext(parentContext: EggRuntimeContext, event: EventName, args: Array): boolean { const hasListener = this.eventHandlerFactory.hasListeners(event); const ctx = this.eventContextFactory.createContext(parentContext); this.doEmit(ctx, event, args); return hasListener; } private doOnceEmit(event: EventName, args: Array) { try { this.emitter.emit(event, ...args); } catch (e) { e.message = `[EventBus] process once event ${String(event)} failed: ${e.message}`; this.logger.error(e); } } private async doEmit(ctx: EggRuntimeContext, event: EventName, args: Array) { await ContextHandler.run(ctx, async () => { const lifecycle = {}; if (ctx.init) { await ctx.init(lifecycle); } try { const handlerProtos = this.eventHandlerFactory.getHandlerProtos(event); await Promise.all(handlerProtos.map(async proto => { try { await this.eventHandlerFactory.handle(event, proto, args); } catch (e) { // should wait all handlers done then destroy ctx e.message = `[EventBus] process event ${String(event)} for handler ${String(proto.name)} failed: ${e.message}`; this.logger.error(e); } })); } catch (e) { e.message = `[EventBus] process event ${String(event)} failed: ${e.message}`; this.logger.error(e); } finally { if (ctx.destroy) { ctx.destroy(lifecycle).catch(e => { e.message = '[tegg/SingletonEventBus] destroy tegg ctx failed:' + e.message; this.logger.error(e); }); } } this.doOnceEmit(event, args); }); } } ================================================ FILE: core/eventbus-runtime/test/EventBus.test.ts ================================================ import path from 'path'; import mm from 'mm'; import assert from 'assert'; import { LoadUnitInstance, LoadUnitInstanceFactory } from '@eggjs/tegg-runtime'; import { EggPrototype, LoadUnitFactory } from '@eggjs/tegg-metadata'; import { TimerUtil } from '@eggjs/tegg-common-util'; import { HelloHandler, HelloProducer } from './fixtures/modules/event/HelloEvent'; import { PrototypeUtil } from '@eggjs/core-decorator'; import { EventInfoUtil, CORK_ID } from '@eggjs/eventbus-decorator'; import { CoreTestHelper, EggTestContext } from '@eggjs/module-test-util'; import { EventContextFactory, EventHandlerFactory, SingletonEventBus } from '..'; import { Timeout0Handler, Timeout100Handler, TimeoutProducer } from './fixtures/modules/event/MultiEvent'; import { MultiWithContextHandler, MultiWithContextProducer } from './fixtures/modules/event/MultiEventWithContext'; describe('test/EventBus.test.ts', () => { let modules: Array; beforeEach(async () => { modules = await CoreTestHelper.prepareModules([ path.join(__dirname, 'fixtures/modules/mock-module'), path.join(__dirname, '..'), path.join(__dirname, 'fixtures/modules/event'), ]); }); afterEach(async () => { for (const module of modules) { await LoadUnitFactory.destroyLoadUnit(module.loadUnit); await LoadUnitInstanceFactory.destroyLoadUnitInstance(module); } }); it('should work', async () => { await EggTestContext.mockContext(async (ctx: EggTestContext) => { const eventContextFactory = await CoreTestHelper.getObject(EventContextFactory); eventContextFactory.registerContextCreator(() => { return ctx; }); const eventHandlerFactory = await CoreTestHelper.getObject(EventHandlerFactory); eventHandlerFactory.registerHandler( EventInfoUtil.getEventName(HelloHandler)!, PrototypeUtil.getClazzProto(HelloHandler) as EggPrototype); const eventBus = await CoreTestHelper.getObject(SingletonEventBus); const helloProducer = await CoreTestHelper.getObject(HelloProducer); const helloHandler = await CoreTestHelper.getObject(HelloHandler); const helloEvent = eventBus.await('hello'); let msg: string | undefined; mm(helloHandler, 'handle', async (m: string) => { msg = m; }); helloProducer.trigger(); await helloEvent; assert(msg === '01'); }); }); it('should work with EventContext', async function() { await EggTestContext.mockContext(async (ctx: EggTestContext) => { const eventContextFactory = await CoreTestHelper.getObject(EventContextFactory); eventContextFactory.registerContextCreator(() => { return ctx; }); const eventHandlerFactory = await CoreTestHelper.getObject(EventHandlerFactory); EventInfoUtil.getEventNameList(MultiWithContextHandler) .forEach(eventName => eventHandlerFactory.registerHandler(eventName, PrototypeUtil.getClazzProto(MultiWithContextHandler) as EggPrototype)); const eventBus = await CoreTestHelper.getObject(SingletonEventBus); const producer = await CoreTestHelper.getObject(MultiWithContextProducer); const fooEvent = eventBus.await('foo'); producer.foo(); await fooEvent; assert.equal(MultiWithContextHandler.eventName, 'foo'); assert.equal(MultiWithContextHandler.msg, '123'); const barEvent = eventBus.await('bar'); producer.bar(); await barEvent; assert.equal(MultiWithContextHandler.eventName, 'bar'); assert.equal(MultiWithContextHandler.msg, '321'); }); }); it('EventBus.awaitFirst should work', async function() { await EggTestContext.mockContext(async (ctx: EggTestContext) => { const eventContextFactory = await CoreTestHelper.getObject(EventContextFactory); eventContextFactory.registerContextCreator(() => { return ctx; }); const eventHandlerFactory = await CoreTestHelper.getObject(EventHandlerFactory); EventInfoUtil.getEventNameList(MultiWithContextHandler) .forEach(eventName => eventHandlerFactory.registerHandler(eventName, PrototypeUtil.getClazzProto(MultiWithContextHandler) as EggPrototype)); const eventBus = await CoreTestHelper.getObject(SingletonEventBus); const producer = await CoreTestHelper.getObject(MultiWithContextProducer); const fooEvent = eventBus.awaitFirst('foo', 'bar'); producer.foo(); await fooEvent; assert.equal(MultiWithContextHandler.eventName, 'foo'); assert.equal(MultiWithContextHandler.msg, '123'); }); }); it('EventHandlerFactory.getHandlers should work', async function() { await EggTestContext.mockContext(async (ctx: EggTestContext) => { const eventContextFactory = await CoreTestHelper.getObject(EventContextFactory); eventContextFactory.registerContextCreator(() => { return ctx; }); const eventHandlerFactory = await CoreTestHelper.getObject(EventHandlerFactory); EventInfoUtil.getEventNameList(MultiWithContextHandler) .forEach(eventName => eventHandlerFactory.registerHandler(eventName, PrototypeUtil.getClazzProto(MultiWithContextHandler) as EggPrototype)); const handlers = await eventHandlerFactory.getHandlers('foo'); assert.equal(handlers.length, 1); const handler = handlers[0]; assert(handler instanceof MultiWithContextHandler); await Reflect.apply(handler.handle, handler, [{ eventName: 'foo' }, '123' ]); assert.equal(MultiWithContextHandler.eventName, 'foo'); assert.equal(MultiWithContextHandler.msg, '123'); }); }); it('destroy should be called', async () => { await EggTestContext.mockContext(async (ctx: EggTestContext) => { let destroyCalled = false; mm(ctx, 'destroy', async () => { destroyCalled = true; }); const eventContextFactory = await CoreTestHelper.getObject(EventContextFactory); eventContextFactory.registerContextCreator(() => { return ctx; }); const eventHandlerFactory = await CoreTestHelper.getObject(EventHandlerFactory); eventHandlerFactory.registerHandler( EventInfoUtil.getEventName(HelloHandler)!, PrototypeUtil.getClazzProto(HelloHandler) as EggPrototype); const eventBus = await CoreTestHelper.getObject(SingletonEventBus); const helloProducer = await CoreTestHelper.getObject(HelloProducer); const helloEvent = eventBus.await('hello'); helloProducer.trigger(); await helloEvent; assert(destroyCalled); }); }); it('should wait all handler done', async () => { await EggTestContext.mockContext(async (ctx: EggTestContext) => { const eventContextFactory = await CoreTestHelper.getObject(EventContextFactory); eventContextFactory.registerContextCreator(() => { return ctx; }); const eventHandlerFactory = await CoreTestHelper.getObject(EventHandlerFactory); eventHandlerFactory.registerHandler( EventInfoUtil.getEventName(Timeout0Handler)!, PrototypeUtil.getClazzProto(Timeout0Handler) as EggPrototype); eventHandlerFactory.registerHandler( EventInfoUtil.getEventName(Timeout100Handler)!, PrototypeUtil.getClazzProto(Timeout100Handler) as EggPrototype); const eventBus = await CoreTestHelper.getObject(SingletonEventBus); const timeoutProducer = await CoreTestHelper.getObject(TimeoutProducer); const timeoutEvent = eventBus.await('timeout'); timeoutProducer.trigger(); await timeoutEvent; assert(Timeout100Handler.called); }); }); it('cork should work', async () => { await EggTestContext.mockContext(async (ctx: EggTestContext) => { const eventContextFactory = await CoreTestHelper.getObject(EventContextFactory); eventContextFactory.registerContextCreator(() => { return ctx; }); const eventHandlerFactory = await CoreTestHelper.getObject(EventHandlerFactory); eventHandlerFactory.registerHandler( EventInfoUtil.getEventName(HelloHandler)!, PrototypeUtil.getClazzProto(HelloHandler) as EggPrototype); const eventBus = await CoreTestHelper.getObject(SingletonEventBus); const corkId = eventBus.generateCorkId(); ctx.set(CORK_ID, corkId); eventBus.cork(corkId); const helloHandler = await CoreTestHelper.getObject(HelloHandler); const helloEvent = eventBus.await('hello'); let eventTime = 0; mm(helloHandler, 'handle', async () => { eventTime = Date.now(); }); eventBus.emitWithContext(ctx, 'hello', [ '01' ]); const triggerTime = Date.now(); await TimerUtil.sleep(100); eventBus.uncork(corkId); await helloEvent; assert(eventTime >= triggerTime + 100); }); }); it('multi cork should work', async () => { await EggTestContext.mockContext(async (ctx: EggTestContext) => { const eventContextFactory = await CoreTestHelper.getObject(EventContextFactory); eventContextFactory.registerContextCreator(() => { return ctx; }); const eventHandlerFactory = await CoreTestHelper.getObject(EventHandlerFactory); eventHandlerFactory.registerHandler( EventInfoUtil.getEventName(HelloHandler)!, PrototypeUtil.getClazzProto(HelloHandler) as EggPrototype); const eventBus = await CoreTestHelper.getObject(SingletonEventBus); const corkId = eventBus.generateCorkId(); ctx.set(CORK_ID, corkId); eventBus.cork(corkId); eventBus.cork(corkId); const helloHandler = await CoreTestHelper.getObject(HelloHandler); const helloEvent = eventBus.await('hello'); let eventTime = 0; mm(helloHandler, 'handle', async () => { eventTime = Date.now(); }); eventBus.emitWithContext(ctx, 'hello', [ '01' ]); const triggerTime = Date.now(); await TimerUtil.sleep(100); eventBus.uncork(corkId); await TimerUtil.sleep(100); eventBus.uncork(corkId); await helloEvent; assert(eventTime >= triggerTime + 200); }); }); }); ================================================ FILE: core/eventbus-runtime/test/fixtures/modules/event/HelloEvent.ts ================================================ import { Event, EventBus } from '@eggjs/eventbus-decorator'; import { AccessLevel, Inject, SingletonProto } from '@eggjs/core-decorator'; declare module '@eggjs/eventbus-decorator' { interface Events { hello: (hello: string) => void; } } @SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) export class HelloProducer { @Inject() private readonly eventBus: EventBus; trigger() { this.eventBus.emit('hello', '01'); } } @Event('hello') export class HelloHandler { handle(hello: string) { console.log('hello, ', hello); } } ================================================ FILE: core/eventbus-runtime/test/fixtures/modules/event/MultiEvent.ts ================================================ import { Event, EventBus } from '@eggjs/eventbus-decorator'; import { AccessLevel, Inject, SingletonProto } from '@eggjs/core-decorator'; import { TimerUtil } from '@eggjs/tegg-common-util'; import type { EggLogger } from 'egg'; declare module '@eggjs/eventbus-decorator' { interface Events { timeout: () => void; } } @SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) export class TimeoutProducer { @Inject() private readonly eventBus: EventBus; trigger() { this.eventBus.emit('timeout'); } } @Event('timeout') export class Timeout0Handler { handle() { throw new Error('mock error'); } } @Event('timeout') export class Timeout100Handler { static called = false; @Inject() private readonly logger: EggLogger; async handle() { await TimerUtil.sleep(100); // access logger, ensure context still alive this.logger.info('timeout 100'); Timeout100Handler.called = true; } } ================================================ FILE: core/eventbus-runtime/test/fixtures/modules/event/MultiEventWithContext.ts ================================================ import {Event, EventBus, EventContext, IEventContext} from '@eggjs/eventbus-decorator'; import { AccessLevel, Inject, SingletonProto } from '@eggjs/core-decorator'; declare module '@eggjs/eventbus-decorator' { interface Events { foo: (msg: string) => void; bar: (msg: string) => void; } } @SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) export class MultiWithContextProducer { @Inject() private readonly eventBus: EventBus; foo() { this.eventBus.emit('foo', '123'); } bar() { this.eventBus.emit('bar', '321'); } } @Event('foo') @Event('bar') export class MultiWithContextHandler { static eventName: string; static msg: string; async handle(@EventContext() ctx: IEventContext, msg: string) { MultiWithContextHandler.eventName = ctx.eventName; MultiWithContextHandler.msg = msg } } ================================================ FILE: core/eventbus-runtime/test/fixtures/modules/event/package.json ================================================ { "name": "mock-event", "eggModule": { "name": "mockEvent" } } ================================================ FILE: core/eventbus-runtime/test/fixtures/modules/mock-module/MockLogger.ts ================================================ import { AccessLevel, ContextProto, SingletonProto } from '@eggjs/core-decorator'; @SingletonProto({ accessLevel: AccessLevel.PUBLIC, name: 'logger', }) export class MockLogger { constructor() { const methods = Object.keys(console); for (const method of methods) { this[method] = (...args) => { console[method](...args); } } } } @ContextProto({ accessLevel: AccessLevel.PUBLIC, name: 'logger', }) export class MockContextLogger { constructor() { const methods = Object.keys(console); for (const method of methods) { this[method] = (...args) => { console[method](...args); } } } } ================================================ FILE: core/eventbus-runtime/test/fixtures/modules/mock-module/package.json ================================================ { "name": "mock-module", "eggModule": { "name": "mock" } } ================================================ FILE: core/eventbus-runtime/tsconfig.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "dist", "baseUrl": "./" }, "exclude": [ "dist", "node_modules", "test" ] } ================================================ FILE: core/eventbus-runtime/tsconfig.pub.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "dist", "baseUrl": "./" }, "exclude": [ "dist", "node_modules", "test" ] } ================================================ FILE: core/langchain-decorator/CHANGELOG.md ================================================ # Change Log All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. ## [3.78.15](https://github.com/eggjs/tegg/compare/v3.78.14...v3.78.15) (2026-04-23) **Note:** Version bump only for package @eggjs/tegg-langchain-decorator ## [3.78.14](https://github.com/eggjs/tegg/compare/v3.78.13...v3.78.14) (2026-04-21) **Note:** Version bump only for package @eggjs/tegg-langchain-decorator ## [3.78.13](https://github.com/eggjs/tegg/compare/v3.78.12...v3.78.13) (2026-04-21) **Note:** Version bump only for package @eggjs/tegg-langchain-decorator ## [3.78.12](https://github.com/eggjs/tegg/compare/v3.78.11...v3.78.12) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-langchain-decorator ## [3.78.11](https://github.com/eggjs/tegg/compare/v3.78.10...v3.78.11) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-langchain-decorator ## [3.78.10](https://github.com/eggjs/tegg/compare/v3.78.9...v3.78.10) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-langchain-decorator ## [3.78.9](https://github.com/eggjs/tegg/compare/v3.78.8...v3.78.9) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-langchain-decorator ## [3.78.8](https://github.com/eggjs/tegg/compare/v3.78.7...v3.78.8) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-langchain-decorator ## [3.78.7](https://github.com/eggjs/tegg/compare/v3.78.6...v3.78.7) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-langchain-decorator ## [3.78.6](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.6) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-langchain-decorator ## [3.78.5](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.5) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-langchain-decorator ## [3.78.4](https://github.com/eggjs/tegg/compare/v3.78.3...v3.78.4) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-langchain-decorator ## [3.78.3](https://github.com/eggjs/tegg/compare/v3.78.2...v3.78.3) (2026-04-15) **Note:** Version bump only for package @eggjs/tegg-langchain-decorator ## [3.78.2](https://github.com/eggjs/tegg/compare/v3.78.1...v3.78.2) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-langchain-decorator ## [3.78.1](https://github.com/eggjs/tegg/compare/v3.78.0...v3.78.1) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-langchain-decorator # [3.78.0](https://github.com/eggjs/tegg/compare/v3.77.2...v3.78.0) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-langchain-decorator ## [3.77.2](https://github.com/eggjs/tegg/compare/v3.77.1...v3.77.2) (2026-04-07) **Note:** Version bump only for package @eggjs/tegg-langchain-decorator ## [3.77.1](https://github.com/eggjs/tegg/compare/v3.77.0...v3.77.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-langchain-decorator # [3.77.0](https://github.com/eggjs/tegg/compare/v3.76.1...v3.77.0) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-langchain-decorator ## [3.76.1](https://github.com/eggjs/tegg/compare/v3.76.0...v3.76.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-langchain-decorator # [3.76.0](https://github.com/eggjs/tegg/compare/v3.75.1...v3.76.0) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-langchain-decorator ## [3.75.1](https://github.com/eggjs/tegg/compare/v3.75.0...v3.75.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-langchain-decorator # [3.75.0](https://github.com/eggjs/tegg/compare/v3.74.0...v3.75.0) (2026-03-30) **Note:** Version bump only for package @eggjs/tegg-langchain-decorator # [3.74.0](https://github.com/eggjs/tegg/compare/v3.73.0...v3.74.0) (2026-03-30) **Note:** Version bump only for package @eggjs/tegg-langchain-decorator # [3.73.0](https://github.com/eggjs/tegg/compare/v3.72.0...v3.73.0) (2026-03-25) **Note:** Version bump only for package @eggjs/tegg-langchain-decorator # [3.72.0](https://github.com/eggjs/tegg/compare/v3.71.2...v3.72.0) (2026-02-04) ### Features * add structured tool ([#387](https://github.com/eggjs/tegg/issues/387)) ([56c23ad](https://github.com/eggjs/tegg/commit/56c23adb0af25ce0fd3624491eaf7af3fb1570cf)) ## [3.71.2](https://github.com/eggjs/tegg/compare/v3.71.1...v3.71.2) (2026-01-30) **Note:** Version bump only for package @eggjs/tegg-langchain-decorator ## [3.71.1](https://github.com/eggjs/tegg/compare/v3.71.0...v3.71.1) (2026-01-28) **Note:** Version bump only for package @eggjs/tegg-langchain-decorator # [3.71.0](https://github.com/eggjs/tegg/compare/v3.70.1...v3.71.0) (2026-01-26) **Note:** Version bump only for package @eggjs/tegg-langchain-decorator ## [3.70.1](https://github.com/eggjs/tegg/compare/v3.70.0...v3.70.1) (2026-01-14) ### Bug Fixes * add stream log and fix add node options ([#394](https://github.com/eggjs/tegg/issues/394)) ([9fce038](https://github.com/eggjs/tegg/commit/9fce038b876100f22344ced707a8c8039594aa3b)) # [3.70.0](https://github.com/eggjs/tegg/compare/v3.69.0...v3.70.0) (2026-01-07) **Note:** Version bump only for package @eggjs/tegg-langchain-decorator # [3.69.0](https://github.com/eggjs/tegg/compare/v3.68.0...v3.69.0) (2026-01-06) **Note:** Version bump only for package @eggjs/tegg-langchain-decorator # [3.68.0](https://github.com/eggjs/tegg/compare/v3.67.2...v3.68.0) (2025-12-29) **Note:** Version bump only for package @eggjs/tegg-langchain-decorator ## [3.67.2](https://github.com/eggjs/tegg/compare/v3.67.1...v3.67.2) (2025-12-24) ### Bug Fixes * langchain version ([#384](https://github.com/eggjs/tegg/issues/384)) ([2bdb3b4](https://github.com/eggjs/tegg/commit/2bdb3b49a1891bdbd9bb24c30ca52295ef4833d4)) ## [3.67.1](https://github.com/eggjs/tegg/compare/v3.67.0...v3.67.1) (2025-12-23) **Note:** Version bump only for package @eggjs/tegg-langchain-decorator # [3.67.0](https://github.com/eggjs/tegg/compare/v3.66.0...v3.67.0) (2025-12-18) **Note:** Version bump only for package @eggjs/tegg-langchain-decorator # [3.66.0](https://github.com/eggjs/tegg/compare/v3.65.3...v3.66.0) (2025-12-16) ### Features * use langchain for deepagents ([#376](https://github.com/eggjs/tegg/issues/376)) ([0af84c7](https://github.com/eggjs/tegg/commit/0af84c7b143cba9234dceb6675ea14004b8b3c9c)) ## [3.65.3](https://github.com/eggjs/tegg/compare/v3.65.2...v3.65.3) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-langchain-decorator ## [3.65.2](https://github.com/eggjs/tegg/compare/v3.65.1...v3.65.2) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-langchain-decorator ## [3.65.1](https://github.com/eggjs/tegg/compare/v3.65.0...v3.65.1) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-langchain-decorator # [3.65.0](https://github.com/eggjs/tegg/compare/v3.64.5...v3.65.0) (2025-12-10) **Note:** Version bump only for package @eggjs/tegg-langchain-decorator ## [3.64.5](https://github.com/eggjs/tegg/compare/v3.64.4...v3.64.5) (2025-12-08) **Note:** Version bump only for package @eggjs/tegg-langchain-decorator ## [3.64.4](https://github.com/eggjs/tegg/compare/v3.64.3...v3.64.4) (2025-12-06) **Note:** Version bump only for package @eggjs/tegg-langchain-decorator ## [3.64.3](https://github.com/eggjs/tegg/compare/v3.64.2...v3.64.3) (2025-12-06) **Note:** Version bump only for package @eggjs/tegg-langchain-decorator ## [3.64.2](https://github.com/eggjs/tegg/compare/v3.64.1...v3.64.2) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-langchain-decorator ## [3.64.1](https://github.com/eggjs/tegg/compare/v3.64.0...v3.64.1) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-langchain-decorator # [3.64.0](https://github.com/eggjs/tegg/compare/v3.63.2...v3.64.0) (2025-11-27) ### Bug Fixes * mcp zod type and langchain test version ([#369](https://github.com/eggjs/tegg/issues/369)) ([8178168](https://github.com/eggjs/tegg/commit/81781685c392346d21c56b649bfe8bb7a99bc9fb)) ### Features * add langchain decorator ([#356](https://github.com/eggjs/tegg/issues/356)) ([b176c73](https://github.com/eggjs/tegg/commit/b176c7325009c372ce9d17f348b4fc1f1b6d7fb1)) # [3.62.0](https://github.com/eggjs/tegg/compare/v3.61.0...v3.62.0) (2025-09-24) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.61.0](https://github.com/eggjs/tegg/compare/v3.60.3...v3.61.0) (2025-08-15) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.60.3](https://github.com/eggjs/tegg/compare/v3.60.2...v3.60.3) (2025-08-06) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.60.2](https://github.com/eggjs/tegg/compare/v3.60.1...v3.60.2) (2025-08-06) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.60.1](https://github.com/eggjs/tegg/compare/v3.60.0...v3.60.1) (2025-07-28) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.60.0](https://github.com/eggjs/tegg/compare/v3.59.1...v3.60.0) (2025-07-28) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.59.1](https://github.com/eggjs/tegg/compare/v3.59.0...v3.59.1) (2025-07-16) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.59.0](https://github.com/eggjs/tegg/compare/v3.58.0...v3.59.0) (2025-07-07) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.58.0](https://github.com/eggjs/tegg/compare/v3.57.14...v3.58.0) (2025-07-01) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.57.14](https://github.com/eggjs/tegg/compare/v3.57.13...v3.57.14) (2025-06-18) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.57.13](https://github.com/eggjs/tegg/compare/v3.57.12...v3.57.13) (2025-06-18) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.57.12](https://github.com/eggjs/tegg/compare/v3.57.11...v3.57.12) (2025-06-16) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.57.11](https://github.com/eggjs/tegg/compare/v3.57.10...v3.57.11) (2025-06-16) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.57.10](https://github.com/eggjs/tegg/compare/v3.57.9...v3.57.10) (2025-06-12) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.57.9](https://github.com/eggjs/tegg/compare/v3.57.8...v3.57.9) (2025-05-29) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.57.8](https://github.com/eggjs/tegg/compare/v3.57.7...v3.57.8) (2025-05-29) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.57.7](https://github.com/eggjs/tegg/compare/v3.57.6...v3.57.7) (2025-05-28) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.57.6](https://github.com/eggjs/tegg/compare/v3.57.5...v3.57.6) (2025-05-27) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.57.5](https://github.com/eggjs/tegg/compare/v3.57.4...v3.57.5) (2025-05-15) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.57.4](https://github.com/eggjs/tegg/compare/v3.57.3...v3.57.4) (2025-05-14) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.57.3](https://github.com/eggjs/tegg/compare/v3.57.2...v3.57.3) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.57.2](https://github.com/eggjs/tegg/compare/v3.57.1...v3.57.2) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.57.1](https://github.com/eggjs/tegg/compare/v3.57.0...v3.57.1) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.57.0](https://github.com/eggjs/tegg/compare/v3.56.3...v3.57.0) (2025-05-09) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.56.3](https://github.com/eggjs/tegg/compare/v3.56.2...v3.56.3) (2025-05-07) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.56.2](https://github.com/eggjs/tegg/compare/v3.56.1...v3.56.2) (2025-05-01) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.56.1](https://github.com/eggjs/tegg/compare/v3.56.0...v3.56.1) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.56.0](https://github.com/eggjs/tegg/compare/v3.55.0...v3.56.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.55.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.55.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.54.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.54.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.53.0](https://github.com/eggjs/tegg/compare/v3.52.1...v3.53.0) (2025-04-21) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.52.1](https://github.com/eggjs/tegg/compare/v3.52.0...v3.52.1) (2025-03-14) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.52.0](https://github.com/eggjs/tegg/compare/v3.51.2...v3.52.0) (2024-12-30) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.51.2](https://github.com/eggjs/tegg/compare/v3.51.1...v3.51.2) (2024-12-09) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.51.1](https://github.com/eggjs/tegg/compare/v3.51.0...v3.51.1) (2024-11-06) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.51.0](https://github.com/eggjs/tegg/compare/v3.50.1...v3.51.0) (2024-10-30) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.50.1](https://github.com/eggjs/tegg/compare/v3.50.0...v3.50.1) (2024-10-23) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.50.0](https://github.com/eggjs/tegg/compare/v3.49.0...v3.50.0) (2024-10-22) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.49.0](https://github.com/eggjs/tegg/compare/v3.48.1...v3.49.0) (2024-10-21) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.48.1](https://github.com/eggjs/tegg/compare/v3.48.0...v3.48.1) (2024-10-14) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.48.0](https://github.com/eggjs/tegg/compare/v3.47.2...v3.48.0) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.47.2](https://github.com/eggjs/tegg/compare/v3.47.1...v3.47.2) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.47.1](https://github.com/eggjs/tegg/compare/v3.47.0...v3.47.1) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.47.0](https://github.com/eggjs/tegg/compare/v3.46.4...v3.47.0) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.46.4](https://github.com/eggjs/tegg/compare/v3.46.3...v3.46.4) (2024-10-09) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.46.3](https://github.com/eggjs/tegg/compare/v3.46.2...v3.46.3) (2024-10-08) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.46.2](https://github.com/eggjs/tegg/compare/v3.46.1...v3.46.2) (2024-10-07) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.46.1](https://github.com/eggjs/tegg/compare/v3.46.0...v3.46.1) (2024-09-30) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.46.0](https://github.com/eggjs/tegg/compare/v3.45.0...v3.46.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.45.0](https://github.com/eggjs/tegg/compare/v3.44.1...v3.45.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.44.1](https://github.com/eggjs/tegg/compare/v3.44.0...v3.44.1) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.44.0](https://github.com/eggjs/tegg/compare/v3.43.2...v3.44.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.43.2](https://github.com/eggjs/tegg/compare/v3.43.1...v3.43.2) (2024-09-14) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.43.1](https://github.com/eggjs/tegg/compare/v3.43.0...v3.43.1) (2024-09-14) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.43.0](https://github.com/eggjs/tegg/compare/v3.42.0...v3.43.0) (2024-09-13) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.42.0](https://github.com/eggjs/tegg/compare/v3.41.0...v3.42.0) (2024-09-10) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.41.0](https://github.com/eggjs/tegg/compare/v3.40.1...v3.41.0) (2024-08-26) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.40.1](https://github.com/eggjs/tegg/compare/v3.40.0...v3.40.1) (2024-08-23) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.40.0](https://github.com/eggjs/tegg/compare/v3.39.5...v3.40.0) (2024-08-22) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.39.5](https://github.com/eggjs/tegg/compare/v3.39.4...v3.39.5) (2024-08-09) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.39.4](https://github.com/eggjs/tegg/compare/v3.39.3...v3.39.4) (2024-07-09) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.39.3](https://github.com/eggjs/tegg/compare/v3.39.2...v3.39.3) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.39.2](https://github.com/eggjs/tegg/compare/v3.39.1...v3.39.2) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.39.1](https://github.com/eggjs/tegg/compare/v3.39.0...v3.39.1) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.39.0](https://github.com/eggjs/tegg/compare/v3.38.0...v3.39.0) (2024-04-19) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.38.0](https://github.com/eggjs/tegg/compare/v3.37.3...v3.38.0) (2024-04-18) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.37.3](https://github.com/eggjs/tegg/compare/v3.37.2...v3.37.3) (2024-04-17) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.37.2](https://github.com/eggjs/tegg/compare/v3.37.1...v3.37.2) (2024-04-17) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.37.1](https://github.com/eggjs/tegg/compare/v3.37.0...v3.37.1) (2024-04-16) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.37.0](https://github.com/eggjs/tegg/compare/v3.36.3...v3.37.0) (2024-04-16) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.36.3](https://github.com/eggjs/tegg/compare/v3.36.2...v3.36.3) (2024-04-10) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.36.2](https://github.com/eggjs/tegg/compare/v3.36.1...v3.36.2) (2024-04-08) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.36.1](https://github.com/eggjs/tegg/compare/v3.36.0...v3.36.1) (2024-04-07) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.36.0](https://github.com/eggjs/tegg/compare/v3.35.1...v3.36.0) (2024-04-02) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.35.1](https://github.com/eggjs/tegg/compare/v3.35.0...v3.35.1) (2024-03-26) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.35.0](https://github.com/eggjs/tegg/compare/v3.34.0...v3.35.0) (2024-03-26) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.34.0](https://github.com/eggjs/tegg/compare/v3.33.1...v3.34.0) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.33.1](https://github.com/eggjs/tegg/compare/v3.33.0...v3.33.1) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.33.0](https://github.com/eggjs/tegg/compare/v3.32.0...v3.33.0) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.32.0](https://github.com/eggjs/tegg/compare/v3.31.0...v3.32.0) (2024-02-19) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.31.0](https://github.com/eggjs/tegg/compare/v3.30.1...v3.31.0) (2024-01-31) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.30.1](https://github.com/eggjs/tegg/compare/v3.30.0...v3.30.1) (2024-01-25) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.30.0](https://github.com/eggjs/tegg/compare/v3.29.0...v3.30.0) (2024-01-17) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.29.0](https://github.com/eggjs/tegg/compare/v3.28.2...v3.29.0) (2023-12-26) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.28.2](https://github.com/eggjs/tegg/compare/v3.28.1...v3.28.2) (2023-12-12) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.28.1](https://github.com/eggjs/tegg/compare/v3.28.0...v3.28.1) (2023-12-11) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.28.0](https://github.com/eggjs/tegg/compare/v3.27.0...v3.28.0) (2023-12-10) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.27.0](https://github.com/eggjs/tegg/compare/v3.26.0...v3.27.0) (2023-11-23) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.25.2](https://github.com/eggjs/tegg/compare/v3.25.1...v3.25.2) (2023-11-06) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.25.1](https://github.com/eggjs/tegg/compare/v3.25.0...v3.25.1) (2023-11-03) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.25.0](https://github.com/eggjs/tegg/compare/v3.24.0...v3.25.0) (2023-11-03) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.24.0](https://github.com/eggjs/tegg/compare/v3.23.0...v3.24.0) (2023-10-26) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.23.0](https://github.com/eggjs/tegg/compare/v3.22.0...v3.23.0) (2023-09-20) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.20.0](https://github.com/eggjs/tegg/compare/v3.19.0...v3.20.0) (2023-09-07) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.19.0](https://github.com/eggjs/tegg/compare/v3.18.1...v3.19.0) (2023-08-31) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.18.1](https://github.com/eggjs/tegg/compare/v3.18.0...v3.18.1) (2023-08-29) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.18.0](https://github.com/eggjs/tegg/compare/v3.17.0...v3.18.0) (2023-08-29) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.17.0](https://github.com/eggjs/tegg/compare/v3.16.0...v3.17.0) (2023-08-24) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.16.0](https://github.com/eggjs/tegg/compare/v3.15.0...v3.16.0) (2023-08-24) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.14.3](https://github.com/eggjs/tegg/compare/v3.14.2...v3.14.3) (2023-08-14) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.14.2](https://github.com/eggjs/tegg/compare/v3.14.1...v3.14.2) (2023-08-14) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.14.1](https://github.com/eggjs/tegg/compare/v3.14.0...v3.14.1) (2023-08-11) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.14.0](https://github.com/eggjs/tegg/compare/v3.13.0...v3.14.0) (2023-07-31) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.13.0](https://github.com/eggjs/tegg/compare/v3.12.0...v3.13.0) (2023-07-25) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.12.0](https://github.com/eggjs/tegg/compare/v3.11.1...v3.12.0) (2023-07-13) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.11.1](https://github.com/eggjs/tegg/compare/v3.11.0...v3.11.1) (2023-06-29) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.11.0](https://github.com/eggjs/tegg/compare/v3.10.0...v3.11.0) (2023-06-29) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.10.0](https://github.com/eggjs/tegg/compare/v3.9.0...v3.10.0) (2023-06-28) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.8.0](https://github.com/eggjs/tegg/compare/v3.7.0...v3.8.0) (2023-05-30) ### Features * impl EggObjectLifecycle hook in decorator ([#119](https://github.com/eggjs/tegg/issues/119)) ([cced8a2](https://github.com/eggjs/tegg/commit/cced8a2e009c33d5040fa21d00409fddef471b0e)) # [3.7.0](https://github.com/eggjs/tegg/compare/v3.6.3...v3.7.0) (2023-04-03) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.5.0](https://github.com/eggjs/tegg/compare/v3.4.1...v3.5.0) (2023-02-10) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.4.0](https://github.com/eggjs/tegg/compare/v3.3.4...v3.4.0) (2023-02-01) ### Features * use singleton model insteadof context ([#89](https://github.com/eggjs/tegg/issues/89)) ([cfdfc05](https://github.com/eggjs/tegg/commit/cfdfc05f13048806274de1a35b1207c073a8519d)) # [3.3.0](https://github.com/eggjs/tegg/compare/v3.2.4...v3.3.0) (2023-01-28) ### Features * export singleton orm client ([#82](https://github.com/eggjs/tegg/issues/82)) ([5320af7](https://github.com/eggjs/tegg/commit/5320af77d7e7c5c73b80560a576f2ce01fc21fff)) ## [3.2.3](https://github.com/eggjs/tegg/compare/v3.2.2...v3.2.3) (2023-01-16) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.2.2](https://github.com/eggjs/tegg/compare/v3.2.1...v3.2.2) (2023-01-06) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.1.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.1.0) (2022-12-27) ### Bug Fixes * use getMetaData for ModelMetadataUtil ([#44](https://github.com/eggjs/tegg/issues/44)) ([87a306c](https://github.com/eggjs/tegg/commit/87a306c4fba51fd519a47c0caaa79442643ea107)) ### Features * **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) * impl Inject Model ([#43](https://github.com/eggjs/tegg/issues/43)) ([ced2ce2](https://github.com/eggjs/tegg/commit/ced2ce2134964dcb410410c0192a34f77507c42d)) # [3.0.0](https://github.com/eggjs/tegg/compare/v3.0.0-alpha.0...v3.0.0) (2022-12-26) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.0.0-alpha.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.0.0-alpha.0) (2022-12-22) ### Bug Fixes * use getMetaData for ModelMetadataUtil ([#44](https://github.com/eggjs/tegg/issues/44)) ([87a306c](https://github.com/eggjs/tegg/commit/87a306c4fba51fd519a47c0caaa79442643ea107)) ### Features * **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) * impl Inject Model ([#43](https://github.com/eggjs/tegg/issues/43)) ([ced2ce2](https://github.com/eggjs/tegg/commit/ced2ce2134964dcb410410c0192a34f77507c42d)) ## [1.4.1](https://github.com/eggjs/tegg/compare/@eggjs/tegg-orm-decorator@1.4.0...@eggjs/tegg-orm-decorator@1.4.1) (2022-07-20) ### Bug Fixes * use getMetaData for ModelMetadataUtil ([#44](https://github.com/eggjs/tegg/issues/44)) ([87a306c](https://github.com/eggjs/tegg/commit/87a306c4fba51fd519a47c0caaa79442643ea107)) # [1.4.0](https://github.com/eggjs/tegg/compare/@eggjs/tegg-orm-decorator@1.3.1...@eggjs/tegg-orm-decorator@1.4.0) (2022-07-20) ### Features * impl Inject Model ([#43](https://github.com/eggjs/tegg/issues/43)) ([ced2ce2](https://github.com/eggjs/tegg/commit/ced2ce2134964dcb410410c0192a34f77507c42d)) # [1.3.0](https://github.com/eggjs/tegg/compare/v1.2.0...v1.3.0) (2022-07-01) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [0.2.0](https://github.com/eggjs/tegg/compare/v0.1.19...v0.2.0) (2022-01-20) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ================================================ FILE: core/langchain-decorator/README.md ================================================ # `@eggjs/tegg-langchain-decorator` ## Install ```shell npm i --save @eggjs/tegg-langchain-decorator ``` ================================================ FILE: core/langchain-decorator/index.ts ================================================ export * from './src/builder/GraphEdgeMetaBuilder'; export * from './src/builder/GraphMetaBuilder'; export * from './src/builder/GraphNodeMetaBuilder'; export * from './src/builder/GraphToolMetaBuilder'; export * from './src/builder/BoundModelMetaBuilder'; export * from './src/decorator/Graph'; export * from './src/decorator/GraphEdge'; export * from './src/decorator/GraphNode'; export * from './src/decorator/GraphTool'; export * from './src/decorator/BoundModel'; export * from './src/model/GraphToolMetadata'; export * from './src/model/GraphEdgeMetadata'; export * from './src/model/GraphNodeMetadata'; export * from './src/model/GraphMetadata'; export * from './src/model/BoundModelMetadata'; export * from './src/util/GraphEdgeInfoUtil'; export * from './src/util/GraphInfoUtil'; export * from './src/util/GraphNodeInfoUtil'; export * from './src/util/GraphToolInfoUtil'; export * from './src/util/BoundModelInfoUtil'; export * from './src/qualifier/ChatModelQualifier'; export * from './src/qualifier/ChatCheckpointSaverQualifier'; export * from './src/type/metadataKey'; ================================================ FILE: core/langchain-decorator/package.json ================================================ { "name": "@eggjs/tegg-langchain-decorator", "version": "3.78.15", "description": "tegg langchain decorator", "main": "dist/index.js", "files": [ "dist/**/*.js", "dist/**/*.d.ts" ], "typings": "dist/index.d.ts", "keywords": [ "egg", "typescript", "runtime", "tegg" ], "scripts": { "test": "cross-env NODE_ENV=test NODE_OPTIONS='--no-deprecation' mocha", "clean": "tsc -b --clean", "tsc": "npm run clean && tsc -p ./tsconfig.json", "tsc:pub": "npm run clean && tsc -p ./tsconfig.pub.json", "prepublishOnly": "npm run tsc:pub" }, "author": "akitaSummer ", "license": "MIT", "homepage": "https://github.com/eggjs/tegg", "bugs": { "url": "https://github.com/eggjs/tegg/issues" }, "repository": { "type": "git", "url": "git@github.com:eggjs/tegg.git", "directory": "core/langchain-decorator" }, "engines": { "node": ">=14.0.0" }, "dependencies": { "@eggjs/core-decorator": "^3.78.15", "@eggjs/tegg-common-util": "^3.78.15", "@eggjs/tegg-metadata": "^3.78.15", "@eggjs/tegg-types": "^3.78.15", "@langchain/community": "^1.0.5", "@langchain/core": "^1.1.1", "@langchain/langgraph": "^1.0.2", "@langchain/openai": "^1.1.0", "langchain": "^1.1.2", "lodash": "^4.17.21", "pluralize": "^8.0.0" }, "publishConfig": { "access": "public" }, "devDependencies": { "@types/mocha": "^10.0.1", "@types/node": "^20.2.4", "cross-env": "^7.0.3", "mocha": "^10.2.0", "ts-node": "^10.9.1", "typescript": "^5.0.4" }, "gitHead": "240a3e9f40fda65ebb7589727d197db5ce17916c" } ================================================ FILE: core/langchain-decorator/src/builder/BoundModelMetaBuilder.ts ================================================ import { EggProtoImplClass } from '@eggjs/tegg'; import { BoundModelInfoUtil } from '../util/BoundModelInfoUtil'; import { BoundModelMetadata } from '../model/BoundModelMetadata'; export class BoundModelMetaBuilder { private readonly clazz: EggProtoImplClass; constructor(clazz: EggProtoImplClass) { this.clazz = clazz; } build(): BoundModelMetadata | undefined { const metadata = BoundModelInfoUtil.getBoundModelMetadata(this.clazz); if (metadata) { return new BoundModelMetadata(metadata); } } static create(clazz: EggProtoImplClass): BoundModelMetaBuilder { return new BoundModelMetaBuilder(clazz); } } ================================================ FILE: core/langchain-decorator/src/builder/GraphEdgeMetaBuilder.ts ================================================ import { EggProtoImplClass } from '@eggjs/tegg'; import { GraphEdgeInfoUtil } from '../util/GraphEdgeInfoUtil'; import { GraphEdgeMetadata } from '../model/GraphEdgeMetadata'; export class GraphEdgeMetaBuilder { private readonly clazz: EggProtoImplClass; constructor(clazz: EggProtoImplClass) { this.clazz = clazz; } build(): GraphEdgeMetadata | undefined { const metadata = GraphEdgeInfoUtil.getGraphEdgeMetadata(this.clazz); if (metadata) { return new GraphEdgeMetadata(metadata); } } static create(clazz: EggProtoImplClass): GraphEdgeMetaBuilder { return new GraphEdgeMetaBuilder(clazz); } } ================================================ FILE: core/langchain-decorator/src/builder/GraphMetaBuilder.ts ================================================ import { EggProtoImplClass } from '@eggjs/tegg'; import { GraphInfoUtil } from '../util/GraphInfoUtil'; import { GraphMetadata } from '../model/GraphMetadata'; export class GraphMetaBuilder { private readonly clazz: EggProtoImplClass; constructor(clazz: EggProtoImplClass) { this.clazz = clazz; } build(): GraphMetadata | undefined { const metadata = GraphInfoUtil.getGraphMetadata(this.clazz); if (metadata) { return new GraphMetadata(metadata); } } static create(clazz: EggProtoImplClass): GraphMetaBuilder { return new GraphMetaBuilder(clazz); } } ================================================ FILE: core/langchain-decorator/src/builder/GraphNodeMetaBuilder.ts ================================================ import { EggProtoImplClass } from '@eggjs/tegg'; import { GraphNodeInfoUtil } from '../util/GraphNodeInfoUtil'; import { GraphNodeMetadata } from '../model/GraphNodeMetadata'; export class GraphNodeMetaBuilder { private readonly clazz: EggProtoImplClass; constructor(clazz: EggProtoImplClass) { this.clazz = clazz; } build(): GraphNodeMetadata | undefined { const metadata = GraphNodeInfoUtil.getGraphNodeMetadata(this.clazz); if (metadata) { return new GraphNodeMetadata(metadata); } } static create(clazz: EggProtoImplClass): GraphNodeMetaBuilder { return new GraphNodeMetaBuilder(clazz); } } ================================================ FILE: core/langchain-decorator/src/builder/GraphToolMetaBuilder.ts ================================================ import { EggProtoImplClass } from '@eggjs/tegg'; import { GraphToolInfoUtil } from '../util/GraphToolInfoUtil'; import { GraphToolMetadata } from '../model/GraphToolMetadata'; export class GraphToolMetaBuilder { private readonly clazz: EggProtoImplClass; constructor(clazz: EggProtoImplClass) { this.clazz = clazz; } build(): GraphToolMetadata | undefined { const metadata = GraphToolInfoUtil.getGraphToolMetadata(this.clazz); if (metadata) { return new GraphToolMetadata(metadata); } } static create(clazz: EggProtoImplClass): GraphToolMetaBuilder { return new GraphToolMetaBuilder(clazz); } } ================================================ FILE: core/langchain-decorator/src/decorator/BoundModel.ts ================================================ import { StackUtil } from '@eggjs/tegg-common-util'; import { AccessLevel, SingletonProto, PrototypeUtil, EggProtoImplClass, } from '@eggjs/tegg'; import { IBoundModelMetadata } from '../model/BoundModelMetadata'; import { BoundModelInfoUtil } from '../util/BoundModelInfoUtil'; import { BaseChatOpenAI, ChatOpenAICallOptions } from '@langchain/openai'; export function BoundModel(params: IBoundModelMetadata) { return (constructor: EggProtoImplClass) => { const func = SingletonProto({ accessLevel: params?.accessLevel ?? AccessLevel.PUBLIC, name: params?.name, }); func(constructor); PrototypeUtil.setFilePath(constructor, StackUtil.getCalleeFromStack(false, 5)); BoundModelInfoUtil.setBoundModelMetadata(params, constructor); }; } type BaseChatModel = InstanceType> extends infer C ? C : never; export type TeggBoundModel = S & ReturnType['bindTools']>>; ================================================ FILE: core/langchain-decorator/src/decorator/Graph.ts ================================================ import { AnnotationRoot, CompiledStateGraph, StateDefinition, StateGraph, StateType, UpdateType } from '@langchain/langgraph'; import { AccessLevel, SingletonProto, PrototypeUtil, EggProtoImplClass, } from '@eggjs/tegg'; import { IGraphMetadata } from '../model/GraphMetadata'; import { GraphInfoUtil } from '../util/GraphInfoUtil'; import { StackUtil } from '@eggjs/tegg-common-util'; export function Graph(params: IGraphMetadata) { return (constructor: EggProtoImplClass>) => { const func = SingletonProto({ accessLevel: params?.accessLevel ?? AccessLevel.PUBLIC, name: params?.name, }); func(constructor); PrototypeUtil.setFilePath(constructor, StackUtil.getCalleeFromStack(false, 5)); GraphInfoUtil.setGraphMetadata(params, constructor); }; } export interface IGraph extends StateGraph['State'], UpdateType, N> { build?(): Promise, UpdateType, string, StateDefinition, StateDefinition, StateDefinition> | undefined>; } export abstract class AbstractStateGraph extends StateGraph['State'], UpdateType, N> implements IGraph { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore private _names: N; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore private _state: S; } export type TeggCompiledStateGraph = G extends AbstractStateGraph ? CompiledStateGraph, UpdateType, N, S, S> : never; ================================================ FILE: core/langchain-decorator/src/decorator/GraphEdge.ts ================================================ import { AccessLevel, SingletonProto, PrototypeUtil, EggProtoImplClass, } from '@eggjs/tegg'; import { StackUtil } from '@eggjs/tegg-common-util'; import { IGraphEdgeMetadata } from '../model/GraphEdgeMetadata'; import { GraphEdgeInfoUtil } from '../util/GraphEdgeInfoUtil'; import { AnnotationRoot, StateDefinition, UpdateType } from '@langchain/langgraph'; /** * @description GraphEdge decorator * @param {IGraphEdgeMetadata} params * @example * ```ts * @GraphEdge({ * fromNodeName: 'start', // 标记启动点,如果只有 fromNodeName 和 toNodeNames,那么就是单向边 * toNodeNames: ['end'], // 标记结束点,可以是多个,多个的时候就必须要实现 execute * }) * ``` * @return {Function} */ export function GraphEdge(params: IGraphEdgeMetadata) { return (constructor: EggProtoImplClass>) => { const func = SingletonProto({ accessLevel: params?.accessLevel ?? AccessLevel.PUBLIC, name: params?.name, }); func(constructor); PrototypeUtil.setFilePath(constructor, StackUtil.getCalleeFromStack(false, 5)); GraphEdgeInfoUtil.setGraphEdgeMetadata(params, constructor); }; } export type GraphStateType = AnnotationRoot['State']; export type GraphUpdateType = UpdateType; export interface IGraphEdge { execute?(state: AnnotationRoot['State']): Promise; } ================================================ FILE: core/langchain-decorator/src/decorator/GraphNode.ts ================================================ import { StackUtil } from '@eggjs/tegg-common-util'; import { AccessLevel, SingletonProto, PrototypeUtil, EggProtoImplClass, } from '@eggjs/tegg'; import { IGraphNodeMetadata } from '../model/GraphNodeMetadata'; import { GraphNodeInfoUtil } from '../util/GraphNodeInfoUtil'; import { AnnotationRoot, Runtime, StateDefinition, StateGraph, UpdateType } from '@langchain/langgraph'; import { ConfigurableModel } from 'langchain/chat_models/universal'; import { ToolNode } from '@langchain/langgraph/prebuilt'; import { BaseChatOpenAI } from '@langchain/openai'; export function GraphNode(params: IGraphNodeMetadata) { return (constructor: EggProtoImplClass | TeggToolNode>) => { const func = SingletonProto({ accessLevel: params?.accessLevel ?? AccessLevel.PUBLIC, name: params?.name, }); func(constructor); PrototypeUtil.setFilePath(constructor, StackUtil.getCalleeFromStack(false, 5)); GraphNodeInfoUtil.setGraphNodeMetadata(params, constructor); }; } export type StateGraphAddNodeOptions = Parameters[2]; export type GraphRuntime, InterruptType = any, WriterType = any> = Runtime; export interface IGraphNode { options?: StateGraphAddNodeOptions; execute(state: AnnotationRoot['State'], options?: GraphRuntime): Promise & Record> | Promise>; build?: (tools: Parameters['0']) => Promise | ReturnType['bindTools']>>; } export class TeggToolNode implements IGraphNode { toolNode: ToolNode; async execute() { return this.toolNode; } } ================================================ FILE: core/langchain-decorator/src/decorator/GraphTool.ts ================================================ import { StackUtil } from '@eggjs/tegg-common-util'; import { AccessLevel, SingletonProto, PrototypeUtil, EggProtoImplClass, } from '@eggjs/tegg'; import { IGraphToolMetadata } from '../model/GraphToolMetadata'; import { GraphToolInfoUtil } from '../util/GraphToolInfoUtil'; import { DynamicStructuredTool, ToolSchemaBase } from '@langchain/core/tools'; export function GraphTool(params: IGraphToolMetadata) { return (constructor: EggProtoImplClass>) => { const func = SingletonProto({ accessLevel: params?.accessLevel ?? AccessLevel.PUBLIC, name: params?.name, }); func(constructor); PrototypeUtil.setFilePath(constructor, StackUtil.getCalleeFromStack(false, 5)); GraphToolInfoUtil.setGraphToolMetadata(params, constructor); }; } export interface IGraphTool { execute: DynamicStructuredTool['func']; } export type IGraphStructuredTool = DynamicStructuredTool[0]>; ================================================ FILE: core/langchain-decorator/src/model/BoundModelMetadata.ts ================================================ import type { EggProtoImplClass, SingletonProtoParams } from '@eggjs/tegg-types'; export interface IBoundModelMetadata extends SingletonProtoParams { modelName: string; tools?: EggProtoImplClass[]; mcpServers?: string[]; } export class BoundModelMetadata { modelName: string; tools?: EggProtoImplClass[]; mcpServers?: string[]; constructor(params: IBoundModelMetadata) { this.modelName = params.modelName; this.tools = params.tools; this.mcpServers = params.mcpServers; } } ================================================ FILE: core/langchain-decorator/src/model/GraphEdgeMetadata.ts ================================================ import type { SingletonProtoParams } from '@eggjs/tegg-types'; export interface IGraphEdgeMetadata extends SingletonProtoParams { fromNodeName: string; toNodeNames: string[]; } export class GraphEdgeMetadata { fromNodeName: string; toNodeNames: string[]; constructor(params: IGraphEdgeMetadata) { this.fromNodeName = params.fromNodeName; this.toNodeNames = params.toNodeNames; } } ================================================ FILE: core/langchain-decorator/src/model/GraphMetadata.ts ================================================ import type { SingletonProtoParams, EggProtoImplClass } from '@eggjs/tegg-types'; import { BaseCheckpointSaver } from '@langchain/langgraph'; export interface IGraphMetadata extends SingletonProtoParams { nodes?: EggProtoImplClass[]; edges?: EggProtoImplClass[]; checkpoint?: EggProtoImplClass | string; } export class GraphMetadata implements IGraphMetadata { nodes?: EggProtoImplClass[]; edges?: EggProtoImplClass[]; checkpoint?: EggProtoImplClass | string; constructor(params: IGraphMetadata) { this.nodes = params.nodes; this.edges = params.edges; this.checkpoint = params.checkpoint; } } ================================================ FILE: core/langchain-decorator/src/model/GraphNodeMetadata.ts ================================================ import type { SingletonProtoParams, EggProtoImplClass } from '@eggjs/tegg-types'; export interface IGraphNodeMetadata extends SingletonProtoParams { nodeName: string; tools?: EggProtoImplClass[]; mcpServers?: string[]; } export class GraphNodeMetadata { nodeName: string; tools?: EggProtoImplClass[]; mcpServers?: string[]; constructor(params: IGraphNodeMetadata) { this.nodeName = params.nodeName; this.tools = params.tools; this.mcpServers = params.mcpServers; } } ================================================ FILE: core/langchain-decorator/src/model/GraphToolMetadata.ts ================================================ import type { SingletonProtoParams } from '@eggjs/tegg-types'; export interface IGraphToolMetadata extends SingletonProtoParams { toolName: string; description: string; // schema: Parameters['2']; } export class GraphToolMetadata implements IGraphToolMetadata { toolName = ''; description = ''; constructor(params: IGraphToolMetadata) { Object.assign(this, params); } } ================================================ FILE: core/langchain-decorator/src/qualifier/ChatCheckpointSaverQualifier.ts ================================================ import { QualifierUtil } from '@eggjs/tegg'; export const ChatCheckpointSaverQualifierAttribute = Symbol.for('Qualifier.ChatCheckpointSaver'); export const ChatCheckpointSaverInjectName = 'chatCheckpointSaver'; export function ChatCheckpointSaverQualifier(chatCheckpointSaverName: string) { return function(target: any, propertyKey?: PropertyKey, parameterIndex?: number) { QualifierUtil.addInjectQualifier(target, propertyKey, parameterIndex, ChatCheckpointSaverQualifierAttribute, chatCheckpointSaverName); }; } ================================================ FILE: core/langchain-decorator/src/qualifier/ChatModelQualifier.ts ================================================ import { QualifierUtil } from '@eggjs/tegg'; export const ChatModelQualifierAttribute = Symbol.for('Qualifier.ChatModel'); export const ChatModelInjectName = 'chatModel'; export function ChatModelQualifier(chatModelName: string) { return function(target: any, propertyKey?: PropertyKey, parameterIndex?: number) { QualifierUtil.addInjectQualifier(target, propertyKey, parameterIndex, ChatModelQualifierAttribute, chatModelName); }; } ================================================ FILE: core/langchain-decorator/src/type/metadataKey.ts ================================================ export const GRAPH_TOOL_METADATA = Symbol.for('EggPrototype#graph#tool#metadata'); export const GRAPH_EDGE_METADATA = Symbol.for('EggPrototype#graph#edge#metadata'); export const GRAPH_NODE_METADATA = Symbol.for('EggPrototype#graph#node#metadata'); export const GRAPH_GRAPH_METADATA = Symbol.for('EggPrototype#graph#graph#metadata'); export const PROMPT_KEY_METADATA = Symbol.for('EggPrototype#prompt#key#metadata'); export const BOUND_MODEL_METADATA = Symbol.for('EggPrototype#bound#model#metadata'); ================================================ FILE: core/langchain-decorator/src/util/BoundModelInfoUtil.ts ================================================ import { EggProtoImplClass, MetadataUtil } from '@eggjs/tegg'; import { BOUND_MODEL_METADATA } from '../type/metadataKey'; import type { IBoundModelMetadata } from '../model/BoundModelMetadata'; export class BoundModelInfoUtil { static setBoundModelMetadata(metadata: IBoundModelMetadata, clazz: EggProtoImplClass) { MetadataUtil.defineMetaData(BOUND_MODEL_METADATA, metadata, clazz); } static getBoundModelMetadata(clazz: EggProtoImplClass): IBoundModelMetadata | undefined { return MetadataUtil.getMetaData(BOUND_MODEL_METADATA, clazz); } } ================================================ FILE: core/langchain-decorator/src/util/GraphEdgeInfoUtil.ts ================================================ import { EggProtoImplClass, MetadataUtil } from '@eggjs/tegg'; import { GRAPH_EDGE_METADATA } from '../type/metadataKey'; import type { IGraphEdgeMetadata } from '../model/GraphEdgeMetadata'; export class GraphEdgeInfoUtil { static setGraphEdgeMetadata(metadata: IGraphEdgeMetadata, clazz: EggProtoImplClass) { MetadataUtil.defineMetaData(GRAPH_EDGE_METADATA, metadata, clazz); } static getGraphEdgeMetadata(clazz: EggProtoImplClass): IGraphEdgeMetadata | undefined { return MetadataUtil.getMetaData(GRAPH_EDGE_METADATA, clazz); } } ================================================ FILE: core/langchain-decorator/src/util/GraphInfoUtil.ts ================================================ import { EggProtoImplClass, MetadataUtil } from '@eggjs/tegg'; import { GRAPH_GRAPH_METADATA } from '../type/metadataKey'; import type { IGraphMetadata } from '../model/GraphMetadata'; export class GraphInfoUtil { static graphMap = new Map(); static setGraphMetadata(metadata: IGraphMetadata, clazz: EggProtoImplClass) { MetadataUtil.defineMetaData(GRAPH_GRAPH_METADATA, metadata, clazz); GraphInfoUtil.graphMap.set(clazz, metadata); } static getGraphMetadata(clazz: EggProtoImplClass): IGraphMetadata | undefined { return MetadataUtil.getMetaData(GRAPH_GRAPH_METADATA, clazz); } static getAllGraphMetadata(): Map { return GraphInfoUtil.graphMap; } } ================================================ FILE: core/langchain-decorator/src/util/GraphNodeInfoUtil.ts ================================================ import { EggProtoImplClass, MetadataUtil } from '@eggjs/tegg'; import { GRAPH_NODE_METADATA } from '../type/metadataKey'; import type { IGraphNodeMetadata } from '../model/GraphNodeMetadata'; export class GraphNodeInfoUtil { static setGraphNodeMetadata(metadata: IGraphNodeMetadata, clazz: EggProtoImplClass) { MetadataUtil.defineMetaData(GRAPH_NODE_METADATA, metadata, clazz); } static getGraphNodeMetadata(clazz: EggProtoImplClass): IGraphNodeMetadata | undefined { return MetadataUtil.getMetaData(GRAPH_NODE_METADATA, clazz); } } ================================================ FILE: core/langchain-decorator/src/util/GraphToolInfoUtil.ts ================================================ import { EggProtoImplClass, MetadataUtil } from '@eggjs/tegg'; import { GRAPH_TOOL_METADATA } from '../type/metadataKey'; import type { IGraphToolMetadata } from '../model/GraphToolMetadata'; export class GraphToolInfoUtil { static graphToolMap = new Map(); static setGraphToolMetadata(metadata: IGraphToolMetadata, clazz: EggProtoImplClass) { MetadataUtil.defineMetaData(GRAPH_TOOL_METADATA, metadata, clazz); GraphToolInfoUtil.graphToolMap.set(clazz, metadata); } static getGraphToolMetadata(clazz: EggProtoImplClass): IGraphToolMetadata | undefined { return MetadataUtil.getMetaData(GRAPH_TOOL_METADATA, clazz); } static getAllGraphToolMetadata(): Map { return GraphToolInfoUtil.graphToolMap; } } ================================================ FILE: core/langchain-decorator/src/util/index.ts ================================================ export * from './GraphEdgeInfoUtil.js'; export * from './GraphInfoUtil.js'; export * from './GraphNodeInfoUtil.js'; export * from './GraphToolInfoUtil.js'; export * from './BoundModelInfoUtil.js'; ================================================ FILE: core/langchain-decorator/test/fixtures/modules/langchain/index.ts ================================================ import { ChatOpenAI } from '@langchain/openai'; import { ChatModelQualifier, } from '../../../..'; import { AccessLevel, Inject, SingletonProto } from '@eggjs/core-decorator'; @SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) export class Foo { @Inject() @ChatModelQualifier('chat') chatModel: ChatOpenAI; } ================================================ FILE: core/langchain-decorator/test/fixtures/modules/langchain/package.json ================================================ { "name": "langchain", "eggModule": { "name": "langchain" } } ================================================ FILE: core/langchain-decorator/test/fixtures/modules/langgraph/Graph.ts ================================================ import { Graph, GraphEdge, IGraphEdge, AbstractStateGraph, GraphNode, IGraphNode, GraphStateType, GraphTool, IGraphTool, ChatModelQualifier, TeggCompiledStateGraph, TeggToolNode, BoundModel, TeggBoundModel } from '../../../..'; import { Annotation, MemorySaver } from '@langchain/langgraph'; import { ChatOpenAI } from '@langchain/openai'; import * as z from 'zod/v4'; import { Inject, SingletonProto } from '@eggjs/core-decorator'; import { BaseMessage } from '@langchain/core/messages'; import { AccessLevel, ToolArgs } from '@eggjs/tegg-types'; import { ToolArgsSchema } from '@eggjs/controller-decorator'; import { AIMessage } from '@langchain/core/messages'; export enum FooGraphNodeName { START = '__start__', END = '__end__', ACTION = 'action', TOOLS = 'tools', AGENT = 'agent', NODE_A = 'a', NODE_B = 'b', NODE_C = 'c', NODE_D = 'd', } @SingletonProto() export class FooSaver extends MemorySaver {} // state export const fooAnnotationStateDefinition = { messages: Annotation({ reducer: (x, y) => x.concat(y), }), aggregate: Annotation({ reducer: (x, y) => x.concat(y), }), }; export type FooAnnotationStateDefinition = typeof fooAnnotationStateDefinition; export const ToolType = { query: z.string().describe('npm package name'), }; @GraphTool({ toolName: 'foo', description: 'Call the foo tool', }) export class FooTool implements IGraphTool { async execute(@ToolArgsSchema(ToolType) args: ToolArgs) { console.log('query: ', args.query); return 'It\'s sunny in San Francisco, but you better look out if you\'re a Gemini 😈.'; } } @BoundModel({ modelName: 'chat', tools: [ FooTool ], mcpServers: [ 'fooMcpServer' ], }) export class FooChatModel {} @GraphNode({ nodeName: FooGraphNodeName.TOOLS, tools: [ FooTool ], mcpServers: [ 'fooMcpServer' ], }) export class ToolNode extends TeggToolNode {} @GraphNode({ nodeName: FooGraphNodeName.ACTION, tools: [ FooTool ], mcpServers: [ 'fooMcpServer' ], }) export class FooNode implements IGraphNode { @Inject() @ChatModelQualifier('chat') chatModel: ChatOpenAI; async execute( state: GraphStateType, ) { console.log('start call model'); const response = await this.chatModel.invoke(state.messages); console.log('response: ', response); return { messages: [ response ] }; } } @GraphEdge({ fromNodeName: FooGraphNodeName.ACTION, toNodeNames: [ FooGraphNodeName.END, FooGraphNodeName.ACTION ], }) export class FooContinueEdge implements IGraphEdge { async execute( state: GraphStateType, ): Promise { const lastMessage = state.messages[state.messages.length - 1]; if (lastMessage && !(lastMessage as AIMessage).tool_calls?.length) { return FooGraphNodeName.END; } return FooGraphNodeName.ACTION; } } @Graph({ accessLevel: AccessLevel.PUBLIC, nodes: [ FooNode ], edges: [ FooContinueEdge ], checkpoint: FooSaver, }) export class FooGraph extends AbstractStateGraph { constructor() { super(fooAnnotationStateDefinition); } } // 手动挡 @GraphNode({ nodeName: FooGraphNodeName.ACTION, }) export class BarNode implements IGraphNode { @Inject() @ChatModelQualifier('chat') chatModel: ChatOpenAI; @Inject() fooTool: FooTool; async execute( state: GraphStateType, ) { console.log('start call model'); const response = await this.chatModel.invoke(state.messages); console.log('response: ', response); return { messages: [ response ] }; } async build() { return this.chatModel.bindTools([ this.fooTool ]); } } @Graph({ accessLevel: AccessLevel.PUBLIC, }) export class BarGraph extends AbstractStateGraph { @Inject() fooSaver: FooSaver; @Inject() fooContinueEdge: FooContinueEdge; @Inject() barNode: BarNode; @Inject() fooChatModel: TeggBoundModel; constructor() { super(fooAnnotationStateDefinition); } async build() { this.addNode(FooGraphNodeName.ACTION, this.barNode.execute); this.addConditionalEdges(FooGraphNodeName.ACTION, this.fooContinueEdge.execute); return this.compile({ checkpointer: this.fooSaver }); } } @SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) export class FooService { @Inject() fooGraph: TeggCompiledStateGraph; async blablabla() { await this.fooGraph.invoke({ messages: [], aggregate: [], }); } } ================================================ FILE: core/langchain-decorator/test/graph.test.ts ================================================ import { strict as assert } from 'node:assert'; import { MCPInfoUtil } from '@eggjs/controller-decorator'; describe('Graph', () => { // https://github.com/langchain-ai/langchainjs/blob/main/libs/langchain/package.json#L9 if (parseInt(process.version.slice(1, 3)) > 19) { // eslint-disable-next-line @typescript-eslint/no-var-requires const { GraphMetaBuilder, GraphEdgeMetaBuilder, GraphNodeMetaBuilder, GraphToolMetaBuilder, GraphToolMetadata, GraphMetadata, TeggToolNode, BoundModelMetaBuilder } = require('../'); // eslint-disable-next-line @typescript-eslint/no-var-requires const { FooContinueEdge, FooGraph, FooNode, FooSaver, FooTool, BarGraph, BarNode, ToolNode, ToolType, FooChatModel } = require('./fixtures/modules/langgraph/Graph'); it('graph should work', () => { const meta = new GraphMetaBuilder(FooGraph).build(); assert.deepEqual(meta?.checkpoint, FooSaver); assert.deepEqual(meta?.nodes, [ FooNode ]); assert.deepEqual(meta?.edges, [ FooContinueEdge ]); }); it('edge should work', () => { const meta = new GraphEdgeMetaBuilder(FooContinueEdge).build(); assert.deepEqual(meta?.fromNodeName, 'action'); assert.deepEqual(meta?.toNodeNames, [ '__end__', 'action' ]); }); it('node should work', () => { const meta = new GraphNodeMetaBuilder(FooNode).build(); assert.deepEqual(meta?.nodeName, 'action'); assert.deepEqual(meta?.tools, [ FooTool ]); assert.deepEqual(meta?.mcpServers, [ 'fooMcpServer' ]); }); it('bound model should work', () => { const meta = new BoundModelMetaBuilder(FooChatModel).build(); assert.deepEqual(meta?.modelName, 'chat'); assert.deepEqual(meta?.tools, [ FooTool ]); assert.deepEqual(meta?.mcpServers, [ 'fooMcpServer' ]); }); it('tool should work', () => { const meta = new GraphToolMetaBuilder(FooTool).build(); assert.deepEqual(meta instanceof GraphToolMetadata, true); const MCPToolParams = MCPInfoUtil.getMCPToolArgsIndex(FooTool, 'execute'); assert.equal(MCPToolParams?.argsSchema, ToolType); }); it('node build should work', () => { const meta = new GraphNodeMetaBuilder(BarNode).build(); assert.deepEqual(meta?.nodeName, 'action'); }); it('graph build should work', () => { const meta = new GraphMetaBuilder(BarGraph).build(); assert.deepEqual(meta instanceof GraphMetadata, true); }); it('tool node should extend TeggToolNode', () => { assert.equal(TeggToolNode.prototype.isPrototypeOf(ToolNode.prototype), true); }); } }); ================================================ FILE: core/langchain-decorator/test/index.test.ts ================================================ import { QualifierUtil } from '@eggjs/core-decorator'; import assert from 'node:assert'; describe('index.test.ts', () => { // https://github.com/langchain-ai/langchainjs/blob/main/libs/langchain/package.json#L9 if (parseInt(process.version.slice(1, 3)) > 19) { it('should success', () => { // eslint-disable-next-line @typescript-eslint/no-var-requires const { Foo } = require('./fixtures/modules/langchain'); // eslint-disable-next-line @typescript-eslint/no-var-requires const { ChatModelQualifierAttribute } = require('../'); const chatModelQualifier = QualifierUtil.getProperQualifier(Foo, 'chatModel', ChatModelQualifierAttribute); assert.equal(chatModelQualifier, 'chat'); }); } }); ================================================ FILE: core/langchain-decorator/test/package.json ================================================ { "type": "commonjs" } ================================================ FILE: core/langchain-decorator/test/tsconfig.json ================================================ { "compilerOptions": { "outDir": "dist", "module": "nodenext", "moduleResolution": "nodenext", "esModuleInterop": true, "baseUrl": "./" }, "exclude": [ "dist", "node_modules" ] } ================================================ FILE: core/langchain-decorator/tsconfig.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "dist", "module": "nodenext", "moduleResolution": "nodenext", "baseUrl": "./" }, "exclude": [ "dist", "node_modules", "test" ] } ================================================ FILE: core/langchain-decorator/tsconfig.pub.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "dist", "baseUrl": "./", "module": "nodenext", "moduleResolution": "nodenext" }, "exclude": [ "dist", "node_modules", "test" ] } ================================================ FILE: core/lifecycle/CHANGELOG.md ================================================ # Change Log All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. ## [3.78.15](https://github.com/eggjs/tegg/compare/v3.78.14...v3.78.15) (2026-04-23) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.78.14](https://github.com/eggjs/tegg/compare/v3.78.13...v3.78.14) (2026-04-21) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.78.13](https://github.com/eggjs/tegg/compare/v3.78.12...v3.78.13) (2026-04-21) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.78.12](https://github.com/eggjs/tegg/compare/v3.78.11...v3.78.12) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.78.11](https://github.com/eggjs/tegg/compare/v3.78.10...v3.78.11) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.78.10](https://github.com/eggjs/tegg/compare/v3.78.9...v3.78.10) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.78.9](https://github.com/eggjs/tegg/compare/v3.78.8...v3.78.9) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.78.8](https://github.com/eggjs/tegg/compare/v3.78.7...v3.78.8) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.78.7](https://github.com/eggjs/tegg/compare/v3.78.6...v3.78.7) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.78.6](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.6) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.78.5](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.5) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.78.4](https://github.com/eggjs/tegg/compare/v3.78.3...v3.78.4) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.78.3](https://github.com/eggjs/tegg/compare/v3.78.2...v3.78.3) (2026-04-15) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.78.2](https://github.com/eggjs/tegg/compare/v3.78.1...v3.78.2) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.78.1](https://github.com/eggjs/tegg/compare/v3.78.0...v3.78.1) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-lifecycle # [3.78.0](https://github.com/eggjs/tegg/compare/v3.77.2...v3.78.0) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.77.2](https://github.com/eggjs/tegg/compare/v3.77.1...v3.77.2) (2026-04-07) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.77.1](https://github.com/eggjs/tegg/compare/v3.77.0...v3.77.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-lifecycle # [3.77.0](https://github.com/eggjs/tegg/compare/v3.76.1...v3.77.0) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.76.1](https://github.com/eggjs/tegg/compare/v3.76.0...v3.76.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-lifecycle # [3.76.0](https://github.com/eggjs/tegg/compare/v3.75.1...v3.76.0) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.75.1](https://github.com/eggjs/tegg/compare/v3.75.0...v3.75.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-lifecycle # [3.75.0](https://github.com/eggjs/tegg/compare/v3.74.0...v3.75.0) (2026-03-30) **Note:** Version bump only for package @eggjs/tegg-lifecycle # [3.74.0](https://github.com/eggjs/tegg/compare/v3.73.0...v3.74.0) (2026-03-30) **Note:** Version bump only for package @eggjs/tegg-lifecycle # [3.73.0](https://github.com/eggjs/tegg/compare/v3.72.0...v3.73.0) (2026-03-25) **Note:** Version bump only for package @eggjs/tegg-lifecycle # [3.72.0](https://github.com/eggjs/tegg/compare/v3.71.2...v3.72.0) (2026-02-04) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.71.2](https://github.com/eggjs/tegg/compare/v3.71.1...v3.71.2) (2026-01-30) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.71.1](https://github.com/eggjs/tegg/compare/v3.71.0...v3.71.1) (2026-01-28) **Note:** Version bump only for package @eggjs/tegg-lifecycle # [3.71.0](https://github.com/eggjs/tegg/compare/v3.70.1...v3.71.0) (2026-01-26) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.70.1](https://github.com/eggjs/tegg/compare/v3.70.0...v3.70.1) (2026-01-14) **Note:** Version bump only for package @eggjs/tegg-lifecycle # [3.70.0](https://github.com/eggjs/tegg/compare/v3.69.0...v3.70.0) (2026-01-07) **Note:** Version bump only for package @eggjs/tegg-lifecycle # [3.69.0](https://github.com/eggjs/tegg/compare/v3.68.0...v3.69.0) (2026-01-06) **Note:** Version bump only for package @eggjs/tegg-lifecycle # [3.68.0](https://github.com/eggjs/tegg/compare/v3.67.2...v3.68.0) (2025-12-29) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.67.2](https://github.com/eggjs/tegg/compare/v3.67.1...v3.67.2) (2025-12-24) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.67.1](https://github.com/eggjs/tegg/compare/v3.67.0...v3.67.1) (2025-12-23) **Note:** Version bump only for package @eggjs/tegg-lifecycle # [3.67.0](https://github.com/eggjs/tegg/compare/v3.66.0...v3.67.0) (2025-12-18) **Note:** Version bump only for package @eggjs/tegg-lifecycle # [3.66.0](https://github.com/eggjs/tegg/compare/v3.65.3...v3.66.0) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.65.3](https://github.com/eggjs/tegg/compare/v3.65.2...v3.65.3) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.65.2](https://github.com/eggjs/tegg/compare/v3.65.1...v3.65.2) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.65.1](https://github.com/eggjs/tegg/compare/v3.65.0...v3.65.1) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-lifecycle # [3.65.0](https://github.com/eggjs/tegg/compare/v3.64.5...v3.65.0) (2025-12-10) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.64.5](https://github.com/eggjs/tegg/compare/v3.64.4...v3.64.5) (2025-12-08) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.64.4](https://github.com/eggjs/tegg/compare/v3.64.3...v3.64.4) (2025-12-06) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.64.3](https://github.com/eggjs/tegg/compare/v3.64.2...v3.64.3) (2025-12-06) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.64.2](https://github.com/eggjs/tegg/compare/v3.64.1...v3.64.2) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.64.1](https://github.com/eggjs/tegg/compare/v3.64.0...v3.64.1) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-lifecycle # [3.64.0](https://github.com/eggjs/tegg/compare/v3.63.2...v3.64.0) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.63.2](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.2) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.63.1](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.1) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg-lifecycle # [3.63.0](https://github.com/eggjs/tegg/compare/v3.62.3...v3.63.0) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.62.3](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.3) (2025-11-05) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.62.2](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.2) (2025-11-05) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.62.1](https://github.com/eggjs/tegg/compare/v3.62.0...v3.62.1) (2025-11-03) **Note:** Version bump only for package @eggjs/tegg-lifecycle # [3.62.0](https://github.com/eggjs/tegg/compare/v3.61.0...v3.62.0) (2025-09-24) **Note:** Version bump only for package @eggjs/tegg-lifecycle # [3.61.0](https://github.com/eggjs/tegg/compare/v3.60.3...v3.61.0) (2025-08-15) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.60.3](https://github.com/eggjs/tegg/compare/v3.60.2...v3.60.3) (2025-08-06) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.60.2](https://github.com/eggjs/tegg/compare/v3.60.1...v3.60.2) (2025-08-06) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.60.1](https://github.com/eggjs/tegg/compare/v3.60.0...v3.60.1) (2025-07-28) **Note:** Version bump only for package @eggjs/tegg-lifecycle # [3.60.0](https://github.com/eggjs/tegg/compare/v3.59.1...v3.60.0) (2025-07-28) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.59.1](https://github.com/eggjs/tegg/compare/v3.59.0...v3.59.1) (2025-07-16) **Note:** Version bump only for package @eggjs/tegg-lifecycle # [3.59.0](https://github.com/eggjs/tegg/compare/v3.58.0...v3.59.0) (2025-07-07) **Note:** Version bump only for package @eggjs/tegg-lifecycle # [3.58.0](https://github.com/eggjs/tegg/compare/v3.57.14...v3.58.0) (2025-07-01) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.57.14](https://github.com/eggjs/tegg/compare/v3.57.13...v3.57.14) (2025-06-18) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.57.13](https://github.com/eggjs/tegg/compare/v3.57.12...v3.57.13) (2025-06-18) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.57.12](https://github.com/eggjs/tegg/compare/v3.57.11...v3.57.12) (2025-06-16) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.57.11](https://github.com/eggjs/tegg/compare/v3.57.10...v3.57.11) (2025-06-16) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.57.10](https://github.com/eggjs/tegg/compare/v3.57.9...v3.57.10) (2025-06-12) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.57.9](https://github.com/eggjs/tegg/compare/v3.57.8...v3.57.9) (2025-05-29) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.57.8](https://github.com/eggjs/tegg/compare/v3.57.7...v3.57.8) (2025-05-29) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.57.7](https://github.com/eggjs/tegg/compare/v3.57.6...v3.57.7) (2025-05-28) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.57.6](https://github.com/eggjs/tegg/compare/v3.57.5...v3.57.6) (2025-05-27) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.57.5](https://github.com/eggjs/tegg/compare/v3.57.4...v3.57.5) (2025-05-15) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.57.4](https://github.com/eggjs/tegg/compare/v3.57.3...v3.57.4) (2025-05-14) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.57.3](https://github.com/eggjs/tegg/compare/v3.57.2...v3.57.3) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.57.2](https://github.com/eggjs/tegg/compare/v3.57.1...v3.57.2) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.57.1](https://github.com/eggjs/tegg/compare/v3.57.0...v3.57.1) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-lifecycle # [3.57.0](https://github.com/eggjs/tegg/compare/v3.56.3...v3.57.0) (2025-05-09) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.56.3](https://github.com/eggjs/tegg/compare/v3.56.2...v3.56.3) (2025-05-07) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.56.2](https://github.com/eggjs/tegg/compare/v3.56.1...v3.56.2) (2025-05-01) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.56.1](https://github.com/eggjs/tegg/compare/v3.56.0...v3.56.1) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-lifecycle # [3.56.0](https://github.com/eggjs/tegg/compare/v3.55.0...v3.56.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-lifecycle # [3.55.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.55.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-lifecycle # [3.54.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.54.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-lifecycle # [3.53.0](https://github.com/eggjs/tegg/compare/v3.52.1...v3.53.0) (2025-04-21) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.52.1](https://github.com/eggjs/tegg/compare/v3.52.0...v3.52.1) (2025-03-14) **Note:** Version bump only for package @eggjs/tegg-lifecycle # [3.52.0](https://github.com/eggjs/tegg/compare/v3.51.2...v3.52.0) (2024-12-30) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.51.2](https://github.com/eggjs/tegg/compare/v3.51.1...v3.51.2) (2024-12-09) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.51.1](https://github.com/eggjs/tegg/compare/v3.51.0...v3.51.1) (2024-11-06) **Note:** Version bump only for package @eggjs/tegg-lifecycle # [3.51.0](https://github.com/eggjs/tegg/compare/v3.50.1...v3.51.0) (2024-10-30) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.50.1](https://github.com/eggjs/tegg/compare/v3.50.0...v3.50.1) (2024-10-23) **Note:** Version bump only for package @eggjs/tegg-lifecycle # [3.50.0](https://github.com/eggjs/tegg/compare/v3.49.0...v3.50.0) (2024-10-22) **Note:** Version bump only for package @eggjs/tegg-lifecycle # [3.49.0](https://github.com/eggjs/tegg/compare/v3.48.1...v3.49.0) (2024-10-21) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.48.1](https://github.com/eggjs/tegg/compare/v3.48.0...v3.48.1) (2024-10-14) **Note:** Version bump only for package @eggjs/tegg-lifecycle # [3.48.0](https://github.com/eggjs/tegg/compare/v3.47.2...v3.48.0) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.47.2](https://github.com/eggjs/tegg/compare/v3.47.1...v3.47.2) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.47.1](https://github.com/eggjs/tegg/compare/v3.47.0...v3.47.1) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-lifecycle # [3.47.0](https://github.com/eggjs/tegg/compare/v3.46.4...v3.47.0) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.46.4](https://github.com/eggjs/tegg/compare/v3.46.3...v3.46.4) (2024-10-09) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.46.3](https://github.com/eggjs/tegg/compare/v3.46.2...v3.46.3) (2024-10-08) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.46.2](https://github.com/eggjs/tegg/compare/v3.46.1...v3.46.2) (2024-10-07) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.46.1](https://github.com/eggjs/tegg/compare/v3.46.0...v3.46.1) (2024-09-30) **Note:** Version bump only for package @eggjs/tegg-lifecycle # [3.46.0](https://github.com/eggjs/tegg/compare/v3.45.0...v3.46.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-lifecycle # [3.45.0](https://github.com/eggjs/tegg/compare/v3.44.1...v3.45.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.44.1](https://github.com/eggjs/tegg/compare/v3.44.0...v3.44.1) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-lifecycle # [3.44.0](https://github.com/eggjs/tegg/compare/v3.43.2...v3.44.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.43.2](https://github.com/eggjs/tegg/compare/v3.43.1...v3.43.2) (2024-09-14) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.43.1](https://github.com/eggjs/tegg/compare/v3.43.0...v3.43.1) (2024-09-14) **Note:** Version bump only for package @eggjs/tegg-lifecycle # [3.43.0](https://github.com/eggjs/tegg/compare/v3.42.0...v3.43.0) (2024-09-13) **Note:** Version bump only for package @eggjs/tegg-lifecycle # [3.42.0](https://github.com/eggjs/tegg/compare/v3.41.0...v3.42.0) (2024-09-10) ### Features * add LifecyclePreLoad ([#234](https://github.com/eggjs/tegg/issues/234)) ([2b72163](https://github.com/eggjs/tegg/commit/2b7216387f02cd045952447eaa21baa3a7ee04a3)) # [3.41.0](https://github.com/eggjs/tegg/compare/v3.40.1...v3.41.0) (2024-08-26) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.40.1](https://github.com/eggjs/tegg/compare/v3.40.0...v3.40.1) (2024-08-23) **Note:** Version bump only for package @eggjs/tegg-lifecycle # [3.40.0](https://github.com/eggjs/tegg/compare/v3.39.5...v3.40.0) (2024-08-22) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.39.5](https://github.com/eggjs/tegg/compare/v3.39.4...v3.39.5) (2024-08-09) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.39.4](https://github.com/eggjs/tegg/compare/v3.39.3...v3.39.4) (2024-07-09) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.39.3](https://github.com/eggjs/tegg/compare/v3.39.2...v3.39.3) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.39.2](https://github.com/eggjs/tegg/compare/v3.39.1...v3.39.2) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.39.1](https://github.com/eggjs/tegg/compare/v3.39.0...v3.39.1) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-lifecycle # [3.39.0](https://github.com/eggjs/tegg/compare/v3.38.0...v3.39.0) (2024-04-19) **Note:** Version bump only for package @eggjs/tegg-lifecycle # [3.38.0](https://github.com/eggjs/tegg/compare/v3.37.3...v3.38.0) (2024-04-18) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.37.3](https://github.com/eggjs/tegg/compare/v3.37.2...v3.37.3) (2024-04-17) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.37.2](https://github.com/eggjs/tegg/compare/v3.37.1...v3.37.2) (2024-04-17) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.37.1](https://github.com/eggjs/tegg/compare/v3.37.0...v3.37.1) (2024-04-16) **Note:** Version bump only for package @eggjs/tegg-lifecycle # [3.37.0](https://github.com/eggjs/tegg/compare/v3.36.3...v3.37.0) (2024-04-16) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.36.3](https://github.com/eggjs/tegg/compare/v3.36.2...v3.36.3) (2024-04-10) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.36.2](https://github.com/eggjs/tegg/compare/v3.36.1...v3.36.2) (2024-04-08) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.36.1](https://github.com/eggjs/tegg/compare/v3.36.0...v3.36.1) (2024-04-07) **Note:** Version bump only for package @eggjs/tegg-lifecycle # [3.36.0](https://github.com/eggjs/tegg/compare/v3.35.1...v3.36.0) (2024-04-02) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.35.1](https://github.com/eggjs/tegg/compare/v3.35.0...v3.35.1) (2024-03-26) **Note:** Version bump only for package @eggjs/tegg-lifecycle # [3.35.0](https://github.com/eggjs/tegg/compare/v3.34.0...v3.35.0) (2024-03-26) **Note:** Version bump only for package @eggjs/tegg-lifecycle # [3.34.0](https://github.com/eggjs/tegg/compare/v3.33.1...v3.34.0) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.33.1](https://github.com/eggjs/tegg/compare/v3.33.0...v3.33.1) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-lifecycle # [3.33.0](https://github.com/eggjs/tegg/compare/v3.32.0...v3.33.0) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-lifecycle # [3.32.0](https://github.com/eggjs/tegg/compare/v3.31.0...v3.32.0) (2024-02-19) **Note:** Version bump only for package @eggjs/tegg-lifecycle # [3.31.0](https://github.com/eggjs/tegg/compare/v3.30.1...v3.31.0) (2024-01-31) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.30.1](https://github.com/eggjs/tegg/compare/v3.30.0...v3.30.1) (2024-01-25) **Note:** Version bump only for package @eggjs/tegg-lifecycle # [3.30.0](https://github.com/eggjs/tegg/compare/v3.29.0...v3.30.0) (2024-01-17) **Note:** Version bump only for package @eggjs/tegg-lifecycle # [3.29.0](https://github.com/eggjs/tegg/compare/v3.28.2...v3.29.0) (2023-12-26) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.28.2](https://github.com/eggjs/tegg/compare/v3.28.1...v3.28.2) (2023-12-12) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.28.1](https://github.com/eggjs/tegg/compare/v3.28.0...v3.28.1) (2023-12-11) **Note:** Version bump only for package @eggjs/tegg-lifecycle # [3.28.0](https://github.com/eggjs/tegg/compare/v3.27.0...v3.28.0) (2023-12-10) **Note:** Version bump only for package @eggjs/tegg-lifecycle # [3.27.0](https://github.com/eggjs/tegg/compare/v3.26.0...v3.27.0) (2023-11-23) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.25.2](https://github.com/eggjs/tegg/compare/v3.25.1...v3.25.2) (2023-11-06) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.25.1](https://github.com/eggjs/tegg/compare/v3.25.0...v3.25.1) (2023-11-03) **Note:** Version bump only for package @eggjs/tegg-lifecycle # [3.25.0](https://github.com/eggjs/tegg/compare/v3.24.0...v3.25.0) (2023-11-03) **Note:** Version bump only for package @eggjs/tegg-lifecycle # [3.24.0](https://github.com/eggjs/tegg/compare/v3.23.0...v3.24.0) (2023-10-26) **Note:** Version bump only for package @eggjs/tegg-lifecycle # [3.23.0](https://github.com/eggjs/tegg/compare/v3.22.0...v3.23.0) (2023-09-20) **Note:** Version bump only for package @eggjs/tegg-lifecycle # [3.20.0](https://github.com/eggjs/tegg/compare/v3.19.0...v3.20.0) (2023-09-07) **Note:** Version bump only for package @eggjs/tegg-lifecycle # [3.19.0](https://github.com/eggjs/tegg/compare/v3.18.1...v3.19.0) (2023-08-31) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.18.1](https://github.com/eggjs/tegg/compare/v3.18.0...v3.18.1) (2023-08-29) **Note:** Version bump only for package @eggjs/tegg-lifecycle # [3.18.0](https://github.com/eggjs/tegg/compare/v3.17.0...v3.18.0) (2023-08-29) **Note:** Version bump only for package @eggjs/tegg-lifecycle # [3.17.0](https://github.com/eggjs/tegg/compare/v3.16.0...v3.17.0) (2023-08-24) ### Features * impl MultiInstanceProto ([#145](https://github.com/eggjs/tegg/issues/145)) ([12fd5cf](https://github.com/eggjs/tegg/commit/12fd5cff4004578bcc737dcdf4f7e9d1159f5633)) # [3.16.0](https://github.com/eggjs/tegg/compare/v3.15.0...v3.16.0) (2023-08-24) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.14.3](https://github.com/eggjs/tegg/compare/v3.14.2...v3.14.3) (2023-08-14) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.14.2](https://github.com/eggjs/tegg/compare/v3.14.1...v3.14.2) (2023-08-14) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.14.1](https://github.com/eggjs/tegg/compare/v3.14.0...v3.14.1) (2023-08-11) **Note:** Version bump only for package @eggjs/tegg-lifecycle # [3.14.0](https://github.com/eggjs/tegg/compare/v3.13.0...v3.14.0) (2023-07-31) **Note:** Version bump only for package @eggjs/tegg-lifecycle # [3.13.0](https://github.com/eggjs/tegg/compare/v3.12.0...v3.13.0) (2023-07-25) **Note:** Version bump only for package @eggjs/tegg-lifecycle # [3.12.0](https://github.com/eggjs/tegg/compare/v3.11.1...v3.12.0) (2023-07-13) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.11.1](https://github.com/eggjs/tegg/compare/v3.11.0...v3.11.1) (2023-06-29) **Note:** Version bump only for package @eggjs/tegg-lifecycle # [3.11.0](https://github.com/eggjs/tegg/compare/v3.10.0...v3.11.0) (2023-06-29) **Note:** Version bump only for package @eggjs/tegg-lifecycle # [3.10.0](https://github.com/eggjs/tegg/compare/v3.9.0...v3.10.0) (2023-06-28) **Note:** Version bump only for package @eggjs/tegg-lifecycle # [3.8.0](https://github.com/eggjs/tegg/compare/v3.7.0...v3.8.0) (2023-05-30) ### Features * impl EggObjectLifecycle hook in decorator ([#119](https://github.com/eggjs/tegg/issues/119)) ([cced8a2](https://github.com/eggjs/tegg/commit/cced8a2e009c33d5040fa21d00409fddef471b0e)) ## [3.2.3](https://github.com/eggjs/tegg/compare/v3.2.2...v3.2.3) (2023-01-16) **Note:** Version bump only for package @eggjs/tegg-lifecycle ## [3.2.2](https://github.com/eggjs/tegg/compare/v3.2.1...v3.2.2) (2023-01-06) **Note:** Version bump only for package @eggjs/tegg-lifecycle # [0.2.0](https://github.com/eggjs/tegg/compare/v0.1.19...v0.2.0) (2022-01-20) **Note:** Version bump only for package @eggjs/tegg-lifecycle ================================================ FILE: core/lifecycle/README.md ================================================ # `@eggjs/tegg-lifecycle` # Usage This is an internal tegg library, you probably shouldn't use it directly. ================================================ FILE: core/lifecycle/index.ts ================================================ export * from '@eggjs/tegg-types/lifecycle'; export * from './src/LifycycleUtil'; export * from './src/IdenticalObject'; export * from './src/decorator'; ================================================ FILE: core/lifecycle/package.json ================================================ { "name": "@eggjs/tegg-lifecycle", "version": "3.78.15", "description": "tegg lifecycle definition", "keywords": [ "egg", "typescript", "lifecycle", "tegg" ], "main": "dist/index.js", "files": [ "dist/**/*.js", "dist/**/*.d.ts" ], "typings": "dist/index.d.ts", "scripts": { "test": "cross-env NODE_ENV=test NODE_OPTIONS='--no-deprecation' mocha", "clean": "tsc -b --clean", "tsc": "ut run clean && tsc -p ./tsconfig.json", "tsc:pub": "ut run clean && tsc -p ./tsconfig.pub.json", "prepublishOnly": "ut tsc:pub" }, "author": "killagu ", "license": "MIT", "homepage": "https://github.com/eggjs/tegg", "bugs": { "url": "https://github.com/eggjs/tegg/issues" }, "repository": { "type": "git", "url": "git@github.com:eggjs/tegg.git", "directory": "core/lifecycle" }, "engines": { "node": ">=14.0.0" }, "publishConfig": { "access": "public" }, "dependencies": { "@eggjs/core-decorator": "^3.78.15", "@eggjs/tegg-types": "^3.78.15" }, "devDependencies": { "@types/mocha": "^10.0.1", "@types/node": "^20.2.4", "cross-env": "^7.0.3", "mocha": "^10.2.0", "ts-node": "^10.9.1", "typescript": "^5.0.4" }, "gitHead": "240a3e9f40fda65ebb7589727d197db5ce17916c" } ================================================ FILE: core/lifecycle/src/IdenticalObject.ts ================================================ import type { Id } from '@eggjs/tegg-types'; export class IdenticalUtil { private static objIndex = 0; private static protoIndex = 0; private static ctxIndex = 0; static createLoadUnitId(loadUnitName: string): Id { // LOAD_UNIT:xxx return `LOAD_UNIT:${loadUnitName}`; } static createProtoId(loadUnitId: Id, name: PropertyKey): Id { // LOAD_UNIT:xxx:PROTO:CONTEXT:xxx return `${loadUnitId}:PROTO:${this.protoIndex++}:${String(name)}`; } static createLoadUnitInstanceId(loadUnitId: Id): Id { // LOAD_UNIT:xxx:INSTANCE return `${loadUnitId}:INSTANCE`; } static createContextId(traceId?: string) { // CONTEXT:0 if (traceId) { return `CONTEXT:${traceId}:${this.ctxIndex++}`; } return `CONTEXT:${this.ctxIndex++}`; } static createObjectId(protoId: Id, ctxId?: Id) { if (ctxId) { // LOAD_UNIT:xxx:PROTO:CONTEXT:xxx:INSTANCE:CONTEXT:0 return `${protoId}:INSTANCE:${ctxId}`; } // LOAD_UNIT:xxx:PROTO:CONTEXT:xxx:INSTANCE:0 return `${protoId}:INSTANCE:${this.objIndex++}`; } } ================================================ FILE: core/lifecycle/src/LifycycleUtil.ts ================================================ import { MetadataUtil } from '@eggjs/core-decorator'; import type { EggPrototype, EggProtoImplClass, LifecycleContext, LifecycleHook, LifecycleHookName, LifecycleObject, } from '@eggjs/tegg-types'; export class LifecycleUtil> { private lifecycleSet: Set> = new Set(); private objLifecycleSet: Map>> = new Map(); registerLifecycle(lifecycle: LifecycleHook) { this.lifecycleSet.add(lifecycle); } deleteLifecycle(lifecycle: LifecycleHook) { this.lifecycleSet.delete(lifecycle); } getLifecycleList(): LifecycleHook[] { return Array.from(this.lifecycleSet); } registerObjectLifecycle(obj: R, lifecycle: LifecycleHook) { if (!this.objLifecycleSet.has(obj.id)) { this.objLifecycleSet.set(obj.id, new Set()); } this.objLifecycleSet.get(obj.id)!.add(lifecycle); } deleteObjectLifecycle(obj: R, lifecycle: LifecycleHook) { this.objLifecycleSet.get(obj.id)?.delete(lifecycle); } clearObjectLifecycle(obj: R) { this.objLifecycleSet.delete(obj.id); } getObjectLifecycleList(obj: R): LifecycleHook[] { if (this.objLifecycleSet.has(obj.id)) { return Array.from(this.objLifecycleSet.get(obj.id)!); } return []; } async objectPreCreate(ctx: T, obj: R) { const globalLifecycleList = this.getLifecycleList(); const objLifecycleList = this.getObjectLifecycleList(obj); await Promise.all(globalLifecycleList.map(lifecycle => LifecycleUtil.callPreCreate(lifecycle, ctx, obj))); await Promise.all(objLifecycleList.map(lifecycle => LifecycleUtil.callPreCreate(lifecycle, ctx, obj))); } async objectPostCreate(ctx: T, obj: R) { const lifecycleList = this.getLifecycleList(); const objLifecycleList = this.getObjectLifecycleList(obj); await Promise.all(lifecycleList.map(lifecycle => LifecycleUtil.callPostCreate(lifecycle, ctx, obj))); await Promise.all(objLifecycleList.map(lifecycle => LifecycleUtil.callPostCreate(lifecycle, ctx, obj))); } async objectPreDestroy(ctx: T, obj: R) { const lifecycleList = this.getLifecycleList(); const objLifecycleList = this.getObjectLifecycleList(obj); await Promise.all(lifecycleList.map(lifecycle => LifecycleUtil.callPreDestroy(lifecycle, ctx, obj))); await Promise.all(objLifecycleList.map(lifecycle => LifecycleUtil.callPreDestroy(lifecycle, ctx, obj))); } static async callPreCreate>(lifecycle: LifecycleHook | undefined, ctx: T, obj: R) { if (!lifecycle || !lifecycle.preCreate) { return; } await lifecycle.preCreate(ctx, obj); } static async callPostCreate>(lifecycle: LifecycleHook | undefined, ctx: T, obj: R) { if (!lifecycle || !lifecycle.postCreate) { return; } await lifecycle.postCreate(ctx, obj); } static async callPreDestroy>(lifecycle: LifecycleHook | undefined, ctx: T, obj: R) { if (!lifecycle || !lifecycle.preDestroy) { return; } await lifecycle.preDestroy(ctx, obj); } static setLifecycleHook(method: string, hookName: LifecycleHookName, clazz: EggProtoImplClass) { const LIFECYCLE_HOOK = Symbol.for(`EggPrototype#Lifecycle${hookName}`); MetadataUtil.defineMetaData(LIFECYCLE_HOOK, method, clazz); } getLifecycleHook(hookName: LifecycleHookName, proto: EggPrototype) { const LIFECYCLE_HOOK = Symbol.for(`EggPrototype#Lifecycle${hookName}`); return proto.getMetaData(LIFECYCLE_HOOK); } static getStaticLifecycleHook(hookName: LifecycleHookName, clazz: EggProtoImplClass) { const LIFECYCLE_HOOK = Symbol.for(`EggPrototype#Lifecycle${hookName}`); return MetadataUtil.getMetaData(LIFECYCLE_HOOK, clazz); } } ================================================ FILE: core/lifecycle/src/decorator/index.ts ================================================ import type { EggProtoImplClass, LifecycleHookName } from '@eggjs/tegg-types'; import { LifecycleUtil } from '../LifycycleUtil'; function createLifecycle(hookName: LifecycleHookName) { return () => { return function(target: object, methodName: string) { const clazz = target.constructor as EggProtoImplClass; LifecycleUtil.setLifecycleHook(methodName, hookName, clazz); }; }; } function createStaticLifecycle(hookName: LifecycleHookName) { return () => { return function(target: EggProtoImplClass, methodName: string) { if (typeof target !== 'function') { throw new Error(`${hookName} must be a static function`); } LifecycleUtil.setLifecycleHook(methodName, hookName, target); }; }; } export const LifecyclePostConstruct = createLifecycle('postConstruct'); export const LifecyclePreInject = createLifecycle('preInject'); export const LifecyclePostInject = createLifecycle('postInject'); export const LifecycleInit = createLifecycle('init'); export const LifecyclePreDestroy = createLifecycle('preDestroy'); export const LifecycleDestroy = createLifecycle('destroy'); export const LifecyclePreLoad = createStaticLifecycle('preLoad'); ================================================ FILE: core/lifecycle/test/IdenticalObject.test.ts ================================================ import assert from 'assert'; import { IdenticalUtil } from '../src/IdenticalObject'; describe('test/IdenticalObject.test.ts', () => { it('should generate unique ctx id', () => { const traceId = 'mock_trace_id'; const id1 = IdenticalUtil.createContextId(traceId); const id2 = IdenticalUtil.createContextId(traceId); assert(id1 !== id2); }); it('should generate unique ctx id', () => { const id1 = IdenticalUtil.createContextId(); const id2 = IdenticalUtil.createContextId(); assert(id1 !== id2); }); }); ================================================ FILE: core/lifecycle/tsconfig.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "dist", "baseUrl": "./" }, "exclude": [ "dist", "node_modules" ] } ================================================ FILE: core/lifecycle/tsconfig.pub.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "dist", "baseUrl": "./" }, "exclude": [ "dist", "node_modules", "test" ] } ================================================ FILE: core/loader/CHANGELOG.md ================================================ # Change Log All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. ## [3.78.15](https://github.com/eggjs/tegg/compare/v3.78.14...v3.78.15) (2026-04-23) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.78.14](https://github.com/eggjs/tegg/compare/v3.78.13...v3.78.14) (2026-04-21) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.78.13](https://github.com/eggjs/tegg/compare/v3.78.12...v3.78.13) (2026-04-21) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.78.12](https://github.com/eggjs/tegg/compare/v3.78.11...v3.78.12) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.78.11](https://github.com/eggjs/tegg/compare/v3.78.10...v3.78.11) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.78.10](https://github.com/eggjs/tegg/compare/v3.78.9...v3.78.10) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.78.9](https://github.com/eggjs/tegg/compare/v3.78.8...v3.78.9) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.78.8](https://github.com/eggjs/tegg/compare/v3.78.7...v3.78.8) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.78.7](https://github.com/eggjs/tegg/compare/v3.78.6...v3.78.7) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.78.6](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.6) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.78.5](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.5) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.78.4](https://github.com/eggjs/tegg/compare/v3.78.3...v3.78.4) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.78.3](https://github.com/eggjs/tegg/compare/v3.78.2...v3.78.3) (2026-04-15) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.78.2](https://github.com/eggjs/tegg/compare/v3.78.1...v3.78.2) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.78.1](https://github.com/eggjs/tegg/compare/v3.78.0...v3.78.1) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-loader # [3.78.0](https://github.com/eggjs/tegg/compare/v3.77.2...v3.78.0) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.77.2](https://github.com/eggjs/tegg/compare/v3.77.1...v3.77.2) (2026-04-07) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.77.1](https://github.com/eggjs/tegg/compare/v3.77.0...v3.77.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-loader # [3.77.0](https://github.com/eggjs/tegg/compare/v3.76.1...v3.77.0) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.76.1](https://github.com/eggjs/tegg/compare/v3.76.0...v3.76.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-loader # [3.76.0](https://github.com/eggjs/tegg/compare/v3.75.1...v3.76.0) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.75.1](https://github.com/eggjs/tegg/compare/v3.75.0...v3.75.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-loader # [3.75.0](https://github.com/eggjs/tegg/compare/v3.74.0...v3.75.0) (2026-03-30) **Note:** Version bump only for package @eggjs/tegg-loader # [3.74.0](https://github.com/eggjs/tegg/compare/v3.73.0...v3.74.0) (2026-03-30) **Note:** Version bump only for package @eggjs/tegg-loader # [3.73.0](https://github.com/eggjs/tegg/compare/v3.72.0...v3.73.0) (2026-03-25) **Note:** Version bump only for package @eggjs/tegg-loader # [3.72.0](https://github.com/eggjs/tegg/compare/v3.71.2...v3.72.0) (2026-02-04) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.71.2](https://github.com/eggjs/tegg/compare/v3.71.1...v3.71.2) (2026-01-30) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.71.1](https://github.com/eggjs/tegg/compare/v3.71.0...v3.71.1) (2026-01-28) **Note:** Version bump only for package @eggjs/tegg-loader # [3.71.0](https://github.com/eggjs/tegg/compare/v3.70.1...v3.71.0) (2026-01-26) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.70.1](https://github.com/eggjs/tegg/compare/v3.70.0...v3.70.1) (2026-01-14) **Note:** Version bump only for package @eggjs/tegg-loader # [3.70.0](https://github.com/eggjs/tegg/compare/v3.69.0...v3.70.0) (2026-01-07) **Note:** Version bump only for package @eggjs/tegg-loader # [3.69.0](https://github.com/eggjs/tegg/compare/v3.68.0...v3.69.0) (2026-01-06) **Note:** Version bump only for package @eggjs/tegg-loader # [3.68.0](https://github.com/eggjs/tegg/compare/v3.67.2...v3.68.0) (2025-12-29) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.67.2](https://github.com/eggjs/tegg/compare/v3.67.1...v3.67.2) (2025-12-24) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.67.1](https://github.com/eggjs/tegg/compare/v3.67.0...v3.67.1) (2025-12-23) **Note:** Version bump only for package @eggjs/tegg-loader # [3.67.0](https://github.com/eggjs/tegg/compare/v3.66.0...v3.67.0) (2025-12-18) **Note:** Version bump only for package @eggjs/tegg-loader # [3.66.0](https://github.com/eggjs/tegg/compare/v3.65.3...v3.66.0) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.65.3](https://github.com/eggjs/tegg/compare/v3.65.2...v3.65.3) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.65.2](https://github.com/eggjs/tegg/compare/v3.65.1...v3.65.2) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.65.1](https://github.com/eggjs/tegg/compare/v3.65.0...v3.65.1) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-loader # [3.65.0](https://github.com/eggjs/tegg/compare/v3.64.5...v3.65.0) (2025-12-10) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.64.5](https://github.com/eggjs/tegg/compare/v3.64.4...v3.64.5) (2025-12-08) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.64.4](https://github.com/eggjs/tegg/compare/v3.64.3...v3.64.4) (2025-12-06) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.64.3](https://github.com/eggjs/tegg/compare/v3.64.2...v3.64.3) (2025-12-06) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.64.2](https://github.com/eggjs/tegg/compare/v3.64.1...v3.64.2) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.64.1](https://github.com/eggjs/tegg/compare/v3.64.0...v3.64.1) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-loader # [3.64.0](https://github.com/eggjs/tegg/compare/v3.63.2...v3.64.0) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.63.2](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.2) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.63.1](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.1) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg-loader # [3.63.0](https://github.com/eggjs/tegg/compare/v3.62.3...v3.63.0) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.62.3](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.3) (2025-11-05) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.62.2](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.2) (2025-11-05) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.62.1](https://github.com/eggjs/tegg/compare/v3.62.0...v3.62.1) (2025-11-03) **Note:** Version bump only for package @eggjs/tegg-loader # [3.62.0](https://github.com/eggjs/tegg/compare/v3.61.0...v3.62.0) (2025-09-24) **Note:** Version bump only for package @eggjs/tegg-loader # [3.61.0](https://github.com/eggjs/tegg/compare/v3.60.3...v3.61.0) (2025-08-15) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.60.3](https://github.com/eggjs/tegg/compare/v3.60.2...v3.60.3) (2025-08-06) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.60.2](https://github.com/eggjs/tegg/compare/v3.60.1...v3.60.2) (2025-08-06) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.60.1](https://github.com/eggjs/tegg/compare/v3.60.0...v3.60.1) (2025-07-28) **Note:** Version bump only for package @eggjs/tegg-loader # [3.60.0](https://github.com/eggjs/tegg/compare/v3.59.1...v3.60.0) (2025-07-28) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.59.1](https://github.com/eggjs/tegg/compare/v3.59.0...v3.59.1) (2025-07-16) **Note:** Version bump only for package @eggjs/tegg-loader # [3.59.0](https://github.com/eggjs/tegg/compare/v3.58.0...v3.59.0) (2025-07-07) **Note:** Version bump only for package @eggjs/tegg-loader # [3.58.0](https://github.com/eggjs/tegg/compare/v3.57.14...v3.58.0) (2025-07-01) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.57.14](https://github.com/eggjs/tegg/compare/v3.57.13...v3.57.14) (2025-06-18) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.57.13](https://github.com/eggjs/tegg/compare/v3.57.12...v3.57.13) (2025-06-18) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.57.12](https://github.com/eggjs/tegg/compare/v3.57.11...v3.57.12) (2025-06-16) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.57.11](https://github.com/eggjs/tegg/compare/v3.57.10...v3.57.11) (2025-06-16) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.57.10](https://github.com/eggjs/tegg/compare/v3.57.9...v3.57.10) (2025-06-12) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.57.9](https://github.com/eggjs/tegg/compare/v3.57.8...v3.57.9) (2025-05-29) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.57.8](https://github.com/eggjs/tegg/compare/v3.57.7...v3.57.8) (2025-05-29) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.57.7](https://github.com/eggjs/tegg/compare/v3.57.6...v3.57.7) (2025-05-28) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.57.6](https://github.com/eggjs/tegg/compare/v3.57.5...v3.57.6) (2025-05-27) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.57.5](https://github.com/eggjs/tegg/compare/v3.57.4...v3.57.5) (2025-05-15) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.57.4](https://github.com/eggjs/tegg/compare/v3.57.3...v3.57.4) (2025-05-14) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.57.3](https://github.com/eggjs/tegg/compare/v3.57.2...v3.57.3) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.57.2](https://github.com/eggjs/tegg/compare/v3.57.1...v3.57.2) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.57.1](https://github.com/eggjs/tegg/compare/v3.57.0...v3.57.1) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-loader # [3.57.0](https://github.com/eggjs/tegg/compare/v3.56.3...v3.57.0) (2025-05-09) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.56.3](https://github.com/eggjs/tegg/compare/v3.56.2...v3.56.3) (2025-05-07) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.56.2](https://github.com/eggjs/tegg/compare/v3.56.1...v3.56.2) (2025-05-01) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.56.1](https://github.com/eggjs/tegg/compare/v3.56.0...v3.56.1) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-loader # [3.56.0](https://github.com/eggjs/tegg/compare/v3.55.0...v3.56.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-loader # [3.55.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.55.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-loader # [3.54.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.54.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-loader # [3.53.0](https://github.com/eggjs/tegg/compare/v3.52.1...v3.53.0) (2025-04-21) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.52.1](https://github.com/eggjs/tegg/compare/v3.52.0...v3.52.1) (2025-03-14) **Note:** Version bump only for package @eggjs/tegg-loader # [3.52.0](https://github.com/eggjs/tegg/compare/v3.51.2...v3.52.0) (2024-12-30) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.51.2](https://github.com/eggjs/tegg/compare/v3.51.1...v3.51.2) (2024-12-09) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.51.1](https://github.com/eggjs/tegg/compare/v3.51.0...v3.51.1) (2024-11-06) **Note:** Version bump only for package @eggjs/tegg-loader # [3.51.0](https://github.com/eggjs/tegg/compare/v3.50.1...v3.51.0) (2024-10-30) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.50.1](https://github.com/eggjs/tegg/compare/v3.50.0...v3.50.1) (2024-10-23) **Note:** Version bump only for package @eggjs/tegg-loader # [3.50.0](https://github.com/eggjs/tegg/compare/v3.49.0...v3.50.0) (2024-10-22) **Note:** Version bump only for package @eggjs/tegg-loader # [3.49.0](https://github.com/eggjs/tegg/compare/v3.48.1...v3.49.0) (2024-10-21) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.48.1](https://github.com/eggjs/tegg/compare/v3.48.0...v3.48.1) (2024-10-14) **Note:** Version bump only for package @eggjs/tegg-loader # [3.48.0](https://github.com/eggjs/tegg/compare/v3.47.2...v3.48.0) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.47.2](https://github.com/eggjs/tegg/compare/v3.47.1...v3.47.2) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.47.1](https://github.com/eggjs/tegg/compare/v3.47.0...v3.47.1) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-loader # [3.47.0](https://github.com/eggjs/tegg/compare/v3.46.4...v3.47.0) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.46.4](https://github.com/eggjs/tegg/compare/v3.46.3...v3.46.4) (2024-10-09) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.46.3](https://github.com/eggjs/tegg/compare/v3.46.2...v3.46.3) (2024-10-08) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.46.2](https://github.com/eggjs/tegg/compare/v3.46.1...v3.46.2) (2024-10-07) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.46.1](https://github.com/eggjs/tegg/compare/v3.46.0...v3.46.1) (2024-09-30) **Note:** Version bump only for package @eggjs/tegg-loader # [3.46.0](https://github.com/eggjs/tegg/compare/v3.45.0...v3.46.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-loader # [3.45.0](https://github.com/eggjs/tegg/compare/v3.44.1...v3.45.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.44.1](https://github.com/eggjs/tegg/compare/v3.44.0...v3.44.1) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-loader # [3.44.0](https://github.com/eggjs/tegg/compare/v3.43.2...v3.44.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.43.2](https://github.com/eggjs/tegg/compare/v3.43.1...v3.43.2) (2024-09-14) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.43.1](https://github.com/eggjs/tegg/compare/v3.43.0...v3.43.1) (2024-09-14) **Note:** Version bump only for package @eggjs/tegg-loader # [3.43.0](https://github.com/eggjs/tegg/compare/v3.42.0...v3.43.0) (2024-09-13) **Note:** Version bump only for package @eggjs/tegg-loader # [3.42.0](https://github.com/eggjs/tegg/compare/v3.41.0...v3.42.0) (2024-09-10) **Note:** Version bump only for package @eggjs/tegg-loader # [3.41.0](https://github.com/eggjs/tegg/compare/v3.40.1...v3.41.0) (2024-08-26) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.40.1](https://github.com/eggjs/tegg/compare/v3.40.0...v3.40.1) (2024-08-23) **Note:** Version bump only for package @eggjs/tegg-loader # [3.40.0](https://github.com/eggjs/tegg/compare/v3.39.5...v3.40.0) (2024-08-22) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.39.5](https://github.com/eggjs/tegg/compare/v3.39.4...v3.39.5) (2024-08-09) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.39.4](https://github.com/eggjs/tegg/compare/v3.39.3...v3.39.4) (2024-07-09) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.39.3](https://github.com/eggjs/tegg/compare/v3.39.2...v3.39.3) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.39.2](https://github.com/eggjs/tegg/compare/v3.39.1...v3.39.2) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.39.1](https://github.com/eggjs/tegg/compare/v3.39.0...v3.39.1) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-loader # [3.39.0](https://github.com/eggjs/tegg/compare/v3.38.0...v3.39.0) (2024-04-19) **Note:** Version bump only for package @eggjs/tegg-loader # [3.38.0](https://github.com/eggjs/tegg/compare/v3.37.3...v3.38.0) (2024-04-18) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.37.3](https://github.com/eggjs/tegg/compare/v3.37.2...v3.37.3) (2024-04-17) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.37.2](https://github.com/eggjs/tegg/compare/v3.37.1...v3.37.2) (2024-04-17) ### Bug Fixes * always get extension from Module._extensions ([#211](https://github.com/eggjs/tegg/issues/211)) ([62e9c06](https://github.com/eggjs/tegg/commit/62e9c06f3cbde28d17d0e43797d4080279d7b9fa)) ## [3.37.1](https://github.com/eggjs/tegg/compare/v3.37.0...v3.37.1) (2024-04-16) **Note:** Version bump only for package @eggjs/tegg-loader # [3.37.0](https://github.com/eggjs/tegg/compare/v3.36.3...v3.37.0) (2024-04-16) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.36.3](https://github.com/eggjs/tegg/compare/v3.36.2...v3.36.3) (2024-04-10) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.36.2](https://github.com/eggjs/tegg/compare/v3.36.1...v3.36.2) (2024-04-08) ### Bug Fixes * fix dao extension in prod ([#206](https://github.com/eggjs/tegg/issues/206)) ([0498e9d](https://github.com/eggjs/tegg/commit/0498e9d11bd9e4d186160e8b6af07e627dde6a20)) ## [3.36.1](https://github.com/eggjs/tegg/compare/v3.36.0...v3.36.1) (2024-04-07) **Note:** Version bump only for package @eggjs/tegg-loader # [3.36.0](https://github.com/eggjs/tegg/compare/v3.35.1...v3.36.0) (2024-04-02) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.35.1](https://github.com/eggjs/tegg/compare/v3.35.0...v3.35.1) (2024-03-26) **Note:** Version bump only for package @eggjs/tegg-loader # [3.35.0](https://github.com/eggjs/tegg/compare/v3.34.0...v3.35.0) (2024-03-26) **Note:** Version bump only for package @eggjs/tegg-loader # [3.34.0](https://github.com/eggjs/tegg/compare/v3.33.1...v3.34.0) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.33.1](https://github.com/eggjs/tegg/compare/v3.33.0...v3.33.1) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-loader # [3.33.0](https://github.com/eggjs/tegg/compare/v3.32.0...v3.33.0) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-loader # [3.32.0](https://github.com/eggjs/tegg/compare/v3.31.0...v3.32.0) (2024-02-19) **Note:** Version bump only for package @eggjs/tegg-loader # [3.31.0](https://github.com/eggjs/tegg/compare/v3.30.1...v3.31.0) (2024-01-31) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.30.1](https://github.com/eggjs/tegg/compare/v3.30.0...v3.30.1) (2024-01-25) **Note:** Version bump only for package @eggjs/tegg-loader # [3.30.0](https://github.com/eggjs/tegg/compare/v3.29.0...v3.30.0) (2024-01-17) **Note:** Version bump only for package @eggjs/tegg-loader # [3.29.0](https://github.com/eggjs/tegg/compare/v3.28.2...v3.29.0) (2023-12-26) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.28.2](https://github.com/eggjs/tegg/compare/v3.28.1...v3.28.2) (2023-12-12) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.28.1](https://github.com/eggjs/tegg/compare/v3.28.0...v3.28.1) (2023-12-11) **Note:** Version bump only for package @eggjs/tegg-loader # [3.28.0](https://github.com/eggjs/tegg/compare/v3.27.0...v3.28.0) (2023-12-10) **Note:** Version bump only for package @eggjs/tegg-loader # [3.27.0](https://github.com/eggjs/tegg/compare/v3.26.0...v3.27.0) (2023-11-23) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.25.2](https://github.com/eggjs/tegg/compare/v3.25.1...v3.25.2) (2023-11-06) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.25.1](https://github.com/eggjs/tegg/compare/v3.25.0...v3.25.1) (2023-11-03) **Note:** Version bump only for package @eggjs/tegg-loader # [3.25.0](https://github.com/eggjs/tegg/compare/v3.24.0...v3.25.0) (2023-11-03) **Note:** Version bump only for package @eggjs/tegg-loader # [3.24.0](https://github.com/eggjs/tegg/compare/v3.23.0...v3.24.0) (2023-10-26) **Note:** Version bump only for package @eggjs/tegg-loader # [3.23.0](https://github.com/eggjs/tegg/compare/v3.22.0...v3.23.0) (2023-09-20) **Note:** Version bump only for package @eggjs/tegg-loader # [3.20.0](https://github.com/eggjs/tegg/compare/v3.19.0...v3.20.0) (2023-09-07) **Note:** Version bump only for package @eggjs/tegg-loader # [3.19.0](https://github.com/eggjs/tegg/compare/v3.18.1...v3.19.0) (2023-08-31) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.18.1](https://github.com/eggjs/tegg/compare/v3.18.0...v3.18.1) (2023-08-29) **Note:** Version bump only for package @eggjs/tegg-loader # [3.18.0](https://github.com/eggjs/tegg/compare/v3.17.0...v3.18.0) (2023-08-29) **Note:** Version bump only for package @eggjs/tegg-loader # [3.17.0](https://github.com/eggjs/tegg/compare/v3.16.0...v3.17.0) (2023-08-24) ### Features * impl MultiInstanceProto ([#145](https://github.com/eggjs/tegg/issues/145)) ([12fd5cf](https://github.com/eggjs/tegg/commit/12fd5cff4004578bcc737dcdf4f7e9d1159f5633)) # [3.16.0](https://github.com/eggjs/tegg/compare/v3.15.0...v3.16.0) (2023-08-24) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.14.3](https://github.com/eggjs/tegg/compare/v3.14.2...v3.14.3) (2023-08-14) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.14.2](https://github.com/eggjs/tegg/compare/v3.14.1...v3.14.2) (2023-08-14) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.14.1](https://github.com/eggjs/tegg/compare/v3.14.0...v3.14.1) (2023-08-11) **Note:** Version bump only for package @eggjs/tegg-loader # [3.14.0](https://github.com/eggjs/tegg/compare/v3.13.0...v3.14.0) (2023-07-31) **Note:** Version bump only for package @eggjs/tegg-loader # [3.13.0](https://github.com/eggjs/tegg/compare/v3.12.0...v3.13.0) (2023-07-25) **Note:** Version bump only for package @eggjs/tegg-loader # [3.12.0](https://github.com/eggjs/tegg/compare/v3.11.1...v3.12.0) (2023-07-13) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.11.1](https://github.com/eggjs/tegg/compare/v3.11.0...v3.11.1) (2023-06-29) **Note:** Version bump only for package @eggjs/tegg-loader # [3.11.0](https://github.com/eggjs/tegg/compare/v3.10.0...v3.11.0) (2023-06-29) **Note:** Version bump only for package @eggjs/tegg-loader # [3.10.0](https://github.com/eggjs/tegg/compare/v3.9.0...v3.10.0) (2023-06-28) **Note:** Version bump only for package @eggjs/tegg-loader # [3.8.0](https://github.com/eggjs/tegg/compare/v3.7.0...v3.8.0) (2023-05-30) ### Features * impl EggObjectLifecycle hook in decorator ([#119](https://github.com/eggjs/tegg/issues/119)) ([cced8a2](https://github.com/eggjs/tegg/commit/cced8a2e009c33d5040fa21d00409fddef471b0e)) # [3.7.0](https://github.com/eggjs/tegg/compare/v3.6.3...v3.7.0) (2023-04-03) **Note:** Version bump only for package @eggjs/tegg-loader # [3.5.0](https://github.com/eggjs/tegg/compare/v3.4.1...v3.5.0) (2023-02-10) ### Bug Fixes * loader should not deps metadata ([#94](https://github.com/eggjs/tegg/issues/94)) ([ff57de4](https://github.com/eggjs/tegg/commit/ff57de4f3e0d0dc33d77d05a887242fcb4c32024)) ## [3.2.3](https://github.com/eggjs/tegg/compare/v3.2.2...v3.2.3) (2023-01-16) **Note:** Version bump only for package @eggjs/tegg-loader ## [3.2.2](https://github.com/eggjs/tegg/compare/v3.2.1...v3.2.2) (2023-01-06) **Note:** Version bump only for package @eggjs/tegg-loader # [3.1.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.1.0) (2022-12-27) **Note:** Version bump only for package @eggjs/tegg-loader # [3.0.0](https://github.com/eggjs/tegg/compare/v3.0.0-alpha.0...v3.0.0) (2022-12-26) **Note:** Version bump only for package @eggjs/tegg-loader # [3.0.0-alpha.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.0.0-alpha.0) (2022-12-22) **Note:** Version bump only for package @eggjs/tegg-loader # [1.3.0](https://github.com/eggjs/tegg/compare/v1.2.0...v1.3.0) (2022-07-01) **Note:** Version bump only for package @eggjs/tegg-loader # [0.2.0](https://github.com/eggjs/tegg/compare/v0.1.19...v0.2.0) (2022-01-20) **Note:** Version bump only for package @eggjs/tegg-loader ================================================ FILE: core/loader/README.md ================================================ # `@eggjs/tegg-loader` # Usage This is an internal tegg library, you probably shouldn't use it directly. ================================================ FILE: core/loader/index.ts ================================================ export * from './src/LoaderFactory'; export * from './src/LoaderUtil'; import './src/impl/ModuleLoader'; ================================================ FILE: core/loader/package.json ================================================ { "name": "@eggjs/tegg-loader", "version": "3.78.15", "description": "tegg default loader implement", "keywords": [ "egg", "typescript", "loader", "tegg" ], "main": "dist/index.js", "files": [ "dist/**/*.js", "dist/**/*.d.ts" ], "typings": "dist/index.d.ts", "scripts": { "test": "cross-env NODE_ENV=test NODE_OPTIONS='--no-deprecation' mocha", "clean": "tsc -b --clean", "tsc": "ut run clean && tsc -p ./tsconfig.json", "tsc:pub": "ut run clean && tsc -p ./tsconfig.pub.json", "prepublishOnly": "ut tsc:pub" }, "author": "killagu ", "license": "MIT", "homepage": "https://github.com/eggjs/tegg", "bugs": { "url": "https://github.com/eggjs/tegg/issues" }, "repository": { "type": "git", "url": "git@github.com:eggjs/tegg.git", "directory": "core/loader" }, "engines": { "node": ">=14.0.0" }, "dependencies": { "@eggjs/core-decorator": "^3.78.15", "@eggjs/tegg-common-util": "^3.78.15", "@eggjs/tegg-types": "^3.78.15", "globby": "^11.1.0", "is-type-of": "^1.2.1" }, "publishConfig": { "access": "public" }, "devDependencies": { "@types/mocha": "^10.0.1", "@types/node": "^20.2.4", "cross-env": "^7.0.3", "mocha": "^10.2.0", "ts-node": "^10.9.1", "typescript": "^5.0.4" }, "gitHead": "240a3e9f40fda65ebb7589727d197db5ce17916c" } ================================================ FILE: core/loader/src/LoaderFactory.ts ================================================ import { EggLoadUnitType, EggLoadUnitTypeLike, EggProtoImplClass, Loader, ModuleReference } from '@eggjs/tegg-types'; import { ModuleDescriptor } from '@eggjs/tegg-metadata'; import { PrototypeUtil } from '@eggjs/core-decorator'; export type LoaderCreator = (unitPath: string) => Loader; export class LoaderFactory { private static loaderCreatorMap: Map = new Map(); static createLoader(unitPath: string, type: EggLoadUnitTypeLike): Loader { const creator = this.loaderCreatorMap.get(type); if (!creator) { throw new Error(`not find creator for loader type ${type}`); } return creator(unitPath); } static registerLoader(type: EggLoadUnitTypeLike, creator: LoaderCreator) { this.loaderCreatorMap.set(type, creator); } static loadApp(moduleReferences: readonly ModuleReference[]): ModuleDescriptor[] { const result: ModuleDescriptor[] = []; const multiInstanceClazzList: EggProtoImplClass[] = []; for (const moduleReference of moduleReferences) { const loader = LoaderFactory.createLoader(moduleReference.path, moduleReference.loaderType || EggLoadUnitType.MODULE); const res: ModuleDescriptor = { name: moduleReference.name, unitPath: moduleReference.path, clazzList: [], protos: [], multiInstanceClazzList, optional: moduleReference.optional, }; result.push(res); const clazzList = loader.load(); for (const clazz of clazzList) { if (PrototypeUtil.isEggPrototype(clazz)) { res.clazzList.push(clazz); } else if (PrototypeUtil.isEggMultiInstancePrototype(clazz)) { res.multiInstanceClazzList.push(clazz); } } } return result; } } ================================================ FILE: core/loader/src/LoaderUtil.ts ================================================ import { PrototypeUtil } from '@eggjs/core-decorator'; import type { EggProtoImplClass } from '@eggjs/tegg-types'; import BuiltinModule from 'module'; import is from 'is-type-of'; // Guard against poorly mocked module constructors. const Module = module.constructor.length > 1 ? module.constructor /* istanbul ignore next */ : BuiltinModule; interface LoaderUtilConfig { extraFilePattern?: string[]; } export class LoaderUtil { static config: LoaderUtilConfig = {}; static setConfig(config: LoaderUtilConfig) { this.config = config; } static get extension() { return Object.keys((Module as any)._extensions).find(t => t === '.ts') ? '.ts' : '.js'; } static filePattern(): string[] { const extensions = Object.keys((Module as any)._extensions); const extensionPattern = extensions.map(t => t.substring(1)) // JSON file will not export class .filter(t => t !== 'json') .join('|'); const filePattern = [ // load file end with node module allow extensions `**/*.(${extensionPattern})`, // not load files in .xxx/ '!**/+(.*)/**', // not load node module '!**/node_modules', // node load type defintions '!**/*.d.ts', // not load test/coverage files '!**/test', '!**/coverage', // extra file pattern ...(this.config.extraFilePattern || []), ]; return filePattern; } static loadFile(filePath: string): EggProtoImplClass[] { let exports; try { exports = require(filePath); } catch (e) { e.message = '[tegg/loader] load ' + filePath + ' failed: ' + e.message; throw e; } const clazzList: EggProtoImplClass[] = []; const exportNames = Object.keys(exports); for (const exportName of exportNames) { const clazz = exports[exportName]; const isEggProto = is.class(clazz) && (PrototypeUtil.isEggPrototype(clazz) || PrototypeUtil.isEggMultiInstancePrototype(clazz)); if (!isEggProto) { continue; } clazzList.push(clazz); } return clazzList; } } ================================================ FILE: core/loader/src/impl/ModuleLoader.ts ================================================ import globby from 'globby'; import path from 'node:path'; import { LoaderUtil } from '../LoaderUtil'; import type { EggProtoImplClass, Loader } from '@eggjs/tegg-types'; import { LoaderFactory } from '../LoaderFactory'; export class ModuleLoader implements Loader { private readonly moduleDir: string; private protoClazzList: EggProtoImplClass[]; constructor(moduleDir: string) { this.moduleDir = moduleDir; } load(): EggProtoImplClass[] { // optimise for EggModuleLoader if (this.protoClazzList) { return this.protoClazzList; } const protoClassList: EggProtoImplClass[] = []; const filePattern = LoaderUtil.filePattern(); const files = globby.sync(filePattern, { cwd: this.moduleDir }); for (const file of files) { const realPath = path.join(this.moduleDir, file); const fileClazzList = LoaderUtil.loadFile(realPath); for (const clazz of fileClazzList) { protoClassList.push(clazz); } } this.protoClazzList = Array.from(new Set(protoClassList)); return this.protoClazzList; } static createModuleLoader(path: string): ModuleLoader { return new ModuleLoader(path); } } LoaderFactory.registerLoader('MODULE', ModuleLoader.createModuleLoader); ================================================ FILE: core/loader/test/Loader.test.ts ================================================ import assert from 'assert'; import path from 'path'; import { LoaderFactory, LoaderUtil } from '..'; import { EggLoadUnitType } from '@eggjs/tegg-metadata'; describe('test/loader/Loader.test.ts', () => { describe('module loader', () => { it('should load module', () => { const repoModulePath = path.join(__dirname, './fixtures/modules/module-for-loader'); const loader = LoaderFactory.createLoader(repoModulePath, EggLoadUnitType.MODULE); const prototypes = loader.load(); assert(prototypes.length === 4); const appRepoProto = prototypes.find(t => t.name === 'AppRepo'); const appRepo2Proto = prototypes.find(t => t.name === 'AppRepo2'); const sprintRepoProto = prototypes.find(t => t.name === 'SprintRepo'); const userRepoProto = prototypes.find(t => t.name === 'UserRepo'); assert(appRepoProto); assert(appRepo2Proto); assert(sprintRepoProto); assert(userRepoProto); }); it('should not load test/coverage files', () => { const repoModulePath = path.join(__dirname, './fixtures/modules/module-with-test'); const loader = LoaderFactory.createLoader(repoModulePath, EggLoadUnitType.MODULE); const prototypes = loader.load(); assert(prototypes.length === 1); }); it('should set extraFilePattern without error', () => { LoaderUtil.setConfig({ extraFilePattern: [ '!extra' ] }); const repoModulePath = path.join(__dirname, './fixtures/modules/module-with-extra'); const loader = LoaderFactory.createLoader(repoModulePath, EggLoadUnitType.MODULE); const prototypes = loader.load(); assert(prototypes.length === 1); }); }); if (process.env.TS_NODE_TRANSPILE_ONLY !== 'true') { // If use ts-node transpile no compile error will throw describe('file has tsc error', () => { it('should failed', () => { const repoModulePath = path.join(__dirname, './fixtures/modules/loader-failed'); const loader = LoaderFactory.createLoader(repoModulePath, EggLoadUnitType.MODULE); assert.throws(() => { loader.load(); }, /'name' is declared but its value is never read/); }); }); } }); ================================================ FILE: core/loader/test/fixtures/modules/loader-failed/AppRepo.ts ================================================ import { Prototype } from '@eggjs/core-decorator'; interface App { name: string; } @Prototype() export default class AppRepo { async findAppByName(): Promise { return { name: 'hello', }; } } @Prototype() export class AppRepo2 { // eslint-disable-next-line @typescript-eslint/no-unused-vars async findAppByName(name: string): Promise { return { name: 'hello', }; } } ================================================ FILE: core/loader/test/fixtures/modules/loader-failed/package.json ================================================ { "name": "demo-app-repo", "eggModule": { "name": "app-repo" }, "main": "index.js" } ================================================ FILE: core/loader/test/fixtures/modules/module-for-loader/AppRepo.ts ================================================ import { Prototype } from '@eggjs/core-decorator'; interface App { name: string; } @Prototype() export default class AppRepo { async findAppByName(): Promise { return { name: 'hello', }; } } @Prototype() export class AppRepo2 { async findAppByName(): Promise { return { name: 'hello', }; } } ================================================ FILE: core/loader/test/fixtures/modules/module-for-loader/SprintRepo.ts ================================================ import { Prototype } from '@eggjs/core-decorator'; @Prototype() export default class SprintRepo { async save() { return Promise.resolve(); } } ================================================ FILE: core/loader/test/fixtures/modules/module-for-loader/UserRepo.ts ================================================ import { Prototype } from '@eggjs/core-decorator'; @Prototype() export default class UserRepo { } ================================================ FILE: core/loader/test/fixtures/modules/module-for-loader/package.json ================================================ { "name": "demo-app-repo", "eggModule": { "name": "app-repo" }, "main": "index.js" } ================================================ FILE: core/loader/test/fixtures/modules/module-with-extra/.dist/ThrowError.ts ================================================ throw new Error('should not load me'); ================================================ FILE: core/loader/test/fixtures/modules/module-with-extra/AppRepo.ts ================================================ import { Prototype } from '@eggjs/core-decorator'; interface App { name: string; } @Prototype() export default class AppRepo { async findAppByName(): Promise { return { name: 'hello', }; } } ================================================ FILE: core/loader/test/fixtures/modules/module-with-extra/extra/UserRepo.ts ================================================ import { Prototype } from '@eggjs/core-decorator'; @Prototype() export default class UserRepo { } ================================================ FILE: core/loader/test/fixtures/modules/module-with-extra/package.json ================================================ { "name": "demo-app-repo", "eggModule": { "name": "app-repo" }, "main": "index.js" } ================================================ FILE: core/loader/test/fixtures/modules/module-with-test/.gitignore ================================================ !coverage ================================================ FILE: core/loader/test/fixtures/modules/module-with-test/AppRepo.ts ================================================ import { Prototype } from '@eggjs/core-decorator'; interface App { name: string; } @Prototype() export default class AppRepo { async findAppByName(): Promise { return { name: 'hello', }; } } ================================================ FILE: core/loader/test/fixtures/modules/module-with-test/coverage/fixtures/UserRepo.ts ================================================ import { Prototype } from '@eggjs/core-decorator'; @Prototype() export default class UserRepo { } ================================================ FILE: core/loader/test/fixtures/modules/module-with-test/package.json ================================================ { "name": "demo-app-repo", "eggModule": { "name": "app-repo" }, "main": "index.js" } ================================================ FILE: core/loader/test/fixtures/modules/module-with-test/test/fixtures/UserRepo.ts ================================================ import { Prototype } from '@eggjs/core-decorator'; @Prototype() export default class UserRepo { } ================================================ FILE: core/loader/tsconfig.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "dist", "baseUrl": "./" }, "exclude": [ "dist", "node_modules", "test" ] } ================================================ FILE: core/loader/tsconfig.pub.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "dist", "baseUrl": "./" }, "exclude": [ "dist", "node_modules", "test" ] } ================================================ FILE: core/mcp-client/CHANGELOG.md ================================================ # Change Log All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. ## [3.78.15](https://github.com/eggjs/tegg/compare/v3.78.14...v3.78.15) (2026-04-23) **Note:** Version bump only for package @eggjs/mcp-client ## [3.78.14](https://github.com/eggjs/tegg/compare/v3.78.13...v3.78.14) (2026-04-21) **Note:** Version bump only for package @eggjs/mcp-client ## [3.78.13](https://github.com/eggjs/tegg/compare/v3.78.12...v3.78.13) (2026-04-21) **Note:** Version bump only for package @eggjs/mcp-client ## [3.78.12](https://github.com/eggjs/tegg/compare/v3.78.11...v3.78.12) (2026-04-18) **Note:** Version bump only for package @eggjs/mcp-client ## [3.78.11](https://github.com/eggjs/tegg/compare/v3.78.10...v3.78.11) (2026-04-18) **Note:** Version bump only for package @eggjs/mcp-client ## [3.78.10](https://github.com/eggjs/tegg/compare/v3.78.9...v3.78.10) (2026-04-18) **Note:** Version bump only for package @eggjs/mcp-client ## [3.78.9](https://github.com/eggjs/tegg/compare/v3.78.8...v3.78.9) (2026-04-18) **Note:** Version bump only for package @eggjs/mcp-client ## [3.78.8](https://github.com/eggjs/tegg/compare/v3.78.7...v3.78.8) (2026-04-18) **Note:** Version bump only for package @eggjs/mcp-client ## [3.78.7](https://github.com/eggjs/tegg/compare/v3.78.6...v3.78.7) (2026-04-18) **Note:** Version bump only for package @eggjs/mcp-client ## [3.78.6](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.6) (2026-04-18) **Note:** Version bump only for package @eggjs/mcp-client ## [3.78.5](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.5) (2026-04-18) **Note:** Version bump only for package @eggjs/mcp-client ## [3.78.4](https://github.com/eggjs/tegg/compare/v3.78.3...v3.78.4) (2026-04-18) **Note:** Version bump only for package @eggjs/mcp-client ## [3.78.3](https://github.com/eggjs/tegg/compare/v3.78.2...v3.78.3) (2026-04-15) **Note:** Version bump only for package @eggjs/mcp-client ## [3.78.2](https://github.com/eggjs/tegg/compare/v3.78.1...v3.78.2) (2026-04-09) **Note:** Version bump only for package @eggjs/mcp-client ## [3.78.1](https://github.com/eggjs/tegg/compare/v3.78.0...v3.78.1) (2026-04-09) **Note:** Version bump only for package @eggjs/mcp-client # [3.78.0](https://github.com/eggjs/tegg/compare/v3.77.2...v3.78.0) (2026-04-09) **Note:** Version bump only for package @eggjs/mcp-client ## [3.77.2](https://github.com/eggjs/tegg/compare/v3.77.1...v3.77.2) (2026-04-07) **Note:** Version bump only for package @eggjs/mcp-client ## [3.77.1](https://github.com/eggjs/tegg/compare/v3.77.0...v3.77.1) (2026-04-01) **Note:** Version bump only for package @eggjs/mcp-client # [3.77.0](https://github.com/eggjs/tegg/compare/v3.76.1...v3.77.0) (2026-04-01) **Note:** Version bump only for package @eggjs/mcp-client ## [3.76.1](https://github.com/eggjs/tegg/compare/v3.76.0...v3.76.1) (2026-04-01) ### Bug Fixes * **agent-runtime:** merge content blocks and support accumulate control ([#428](https://github.com/eggjs/tegg/issues/428)) ([f4f904e](https://github.com/eggjs/tegg/commit/f4f904e357497fc5ad9a2c7d2ece4e9b305f5738)) # [3.76.0](https://github.com/eggjs/tegg/compare/v3.75.1...v3.76.0) (2026-04-01) **Note:** Version bump only for package @eggjs/mcp-client ## [3.75.1](https://github.com/eggjs/tegg/compare/v3.75.0...v3.75.1) (2026-04-01) **Note:** Version bump only for package @eggjs/mcp-client # [3.75.0](https://github.com/eggjs/tegg/compare/v3.74.0...v3.75.0) (2026-03-30) **Note:** Version bump only for package @eggjs/mcp-client # [3.74.0](https://github.com/eggjs/tegg/compare/v3.73.0...v3.74.0) (2026-03-30) **Note:** Version bump only for package @eggjs/mcp-client # [3.73.0](https://github.com/eggjs/tegg/compare/v3.72.0...v3.73.0) (2026-03-25) **Note:** Version bump only for package @eggjs/mcp-client # [3.72.0](https://github.com/eggjs/tegg/compare/v3.71.2...v3.72.0) (2026-02-04) ### Features * add structured tool ([#387](https://github.com/eggjs/tegg/issues/387)) ([56c23ad](https://github.com/eggjs/tegg/commit/56c23adb0af25ce0fd3624491eaf7af3fb1570cf)) ## [3.71.2](https://github.com/eggjs/tegg/compare/v3.71.1...v3.71.2) (2026-01-30) **Note:** Version bump only for package @eggjs/mcp-client ## [3.71.1](https://github.com/eggjs/tegg/compare/v3.71.0...v3.71.1) (2026-01-28) **Note:** Version bump only for package @eggjs/mcp-client # [3.71.0](https://github.com/eggjs/tegg/compare/v3.70.1...v3.71.0) (2026-01-26) **Note:** Version bump only for package @eggjs/mcp-client ## [3.70.1](https://github.com/eggjs/tegg/compare/v3.70.0...v3.70.1) (2026-01-14) **Note:** Version bump only for package @eggjs/mcp-client # [3.70.0](https://github.com/eggjs/tegg/compare/v3.69.0...v3.70.0) (2026-01-07) **Note:** Version bump only for package @eggjs/mcp-client # [3.69.0](https://github.com/eggjs/tegg/compare/v3.68.0...v3.69.0) (2026-01-06) **Note:** Version bump only for package @eggjs/mcp-client # [3.68.0](https://github.com/eggjs/tegg/compare/v3.67.2...v3.68.0) (2025-12-29) **Note:** Version bump only for package @eggjs/mcp-client ## [3.67.2](https://github.com/eggjs/tegg/compare/v3.67.1...v3.67.2) (2025-12-24) **Note:** Version bump only for package @eggjs/mcp-client ## [3.67.1](https://github.com/eggjs/tegg/compare/v3.67.0...v3.67.1) (2025-12-23) **Note:** Version bump only for package @eggjs/mcp-client # [3.67.0](https://github.com/eggjs/tegg/compare/v3.66.0...v3.67.0) (2025-12-18) **Note:** Version bump only for package @eggjs/mcp-client # [3.66.0](https://github.com/eggjs/tegg/compare/v3.65.3...v3.66.0) (2025-12-16) **Note:** Version bump only for package @eggjs/mcp-client ## [3.65.3](https://github.com/eggjs/tegg/compare/v3.65.2...v3.65.3) (2025-12-16) **Note:** Version bump only for package @eggjs/mcp-client ## [3.65.2](https://github.com/eggjs/tegg/compare/v3.65.1...v3.65.2) (2025-12-16) **Note:** Version bump only for package @eggjs/mcp-client ## [3.65.1](https://github.com/eggjs/tegg/compare/v3.65.0...v3.65.1) (2025-12-16) ### Bug Fixes * hono node v16 ([#374](https://github.com/eggjs/tegg/issues/374)) ([870b5e3](https://github.com/eggjs/tegg/commit/870b5e34f41399a44023756614b8bb5c59efc6ee)) # [3.65.0](https://github.com/eggjs/tegg/compare/v3.64.5...v3.65.0) (2025-12-10) **Note:** Version bump only for package @eggjs/mcp-client ## [3.64.5](https://github.com/eggjs/tegg/compare/v3.64.4...v3.64.5) (2025-12-08) **Note:** Version bump only for package @eggjs/mcp-client ## [3.64.4](https://github.com/eggjs/tegg/compare/v3.64.3...v3.64.4) (2025-12-06) **Note:** Version bump only for package @eggjs/mcp-client ## [3.64.3](https://github.com/eggjs/tegg/compare/v3.64.2...v3.64.3) (2025-12-06) **Note:** Version bump only for package @eggjs/mcp-client ## [3.64.2](https://github.com/eggjs/tegg/compare/v3.64.1...v3.64.2) (2025-11-27) **Note:** Version bump only for package @eggjs/mcp-client ## [3.64.1](https://github.com/eggjs/tegg/compare/v3.64.0...v3.64.1) (2025-11-27) **Note:** Version bump only for package @eggjs/mcp-client # [3.64.0](https://github.com/eggjs/tegg/compare/v3.63.2...v3.64.0) (2025-11-27) ### Bug Fixes * mcp zod type and langchain test version ([#369](https://github.com/eggjs/tegg/issues/369)) ([8178168](https://github.com/eggjs/tegg/commit/81781685c392346d21c56b649bfe8bb7a99bc9fb)) ### Features * add langchain decorator ([#356](https://github.com/eggjs/tegg/issues/356)) ([b176c73](https://github.com/eggjs/tegg/commit/b176c7325009c372ce9d17f348b4fc1f1b6d7fb1)) # [3.62.0](https://github.com/eggjs/tegg/compare/v3.61.0...v3.62.0) (2025-09-24) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.61.0](https://github.com/eggjs/tegg/compare/v3.60.3...v3.61.0) (2025-08-15) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.60.3](https://github.com/eggjs/tegg/compare/v3.60.2...v3.60.3) (2025-08-06) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.60.2](https://github.com/eggjs/tegg/compare/v3.60.1...v3.60.2) (2025-08-06) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.60.1](https://github.com/eggjs/tegg/compare/v3.60.0...v3.60.1) (2025-07-28) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.60.0](https://github.com/eggjs/tegg/compare/v3.59.1...v3.60.0) (2025-07-28) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.59.1](https://github.com/eggjs/tegg/compare/v3.59.0...v3.59.1) (2025-07-16) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.59.0](https://github.com/eggjs/tegg/compare/v3.58.0...v3.59.0) (2025-07-07) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.58.0](https://github.com/eggjs/tegg/compare/v3.57.14...v3.58.0) (2025-07-01) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.57.14](https://github.com/eggjs/tegg/compare/v3.57.13...v3.57.14) (2025-06-18) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.57.13](https://github.com/eggjs/tegg/compare/v3.57.12...v3.57.13) (2025-06-18) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.57.12](https://github.com/eggjs/tegg/compare/v3.57.11...v3.57.12) (2025-06-16) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.57.11](https://github.com/eggjs/tegg/compare/v3.57.10...v3.57.11) (2025-06-16) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.57.10](https://github.com/eggjs/tegg/compare/v3.57.9...v3.57.10) (2025-06-12) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.57.9](https://github.com/eggjs/tegg/compare/v3.57.8...v3.57.9) (2025-05-29) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.57.8](https://github.com/eggjs/tegg/compare/v3.57.7...v3.57.8) (2025-05-29) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.57.7](https://github.com/eggjs/tegg/compare/v3.57.6...v3.57.7) (2025-05-28) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.57.6](https://github.com/eggjs/tegg/compare/v3.57.5...v3.57.6) (2025-05-27) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.57.5](https://github.com/eggjs/tegg/compare/v3.57.4...v3.57.5) (2025-05-15) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.57.4](https://github.com/eggjs/tegg/compare/v3.57.3...v3.57.4) (2025-05-14) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.57.3](https://github.com/eggjs/tegg/compare/v3.57.2...v3.57.3) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.57.2](https://github.com/eggjs/tegg/compare/v3.57.1...v3.57.2) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.57.1](https://github.com/eggjs/tegg/compare/v3.57.0...v3.57.1) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.57.0](https://github.com/eggjs/tegg/compare/v3.56.3...v3.57.0) (2025-05-09) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.56.3](https://github.com/eggjs/tegg/compare/v3.56.2...v3.56.3) (2025-05-07) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.56.2](https://github.com/eggjs/tegg/compare/v3.56.1...v3.56.2) (2025-05-01) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.56.1](https://github.com/eggjs/tegg/compare/v3.56.0...v3.56.1) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.56.0](https://github.com/eggjs/tegg/compare/v3.55.0...v3.56.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.55.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.55.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.54.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.54.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.53.0](https://github.com/eggjs/tegg/compare/v3.52.1...v3.53.0) (2025-04-21) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.52.1](https://github.com/eggjs/tegg/compare/v3.52.0...v3.52.1) (2025-03-14) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.52.0](https://github.com/eggjs/tegg/compare/v3.51.2...v3.52.0) (2024-12-30) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.51.2](https://github.com/eggjs/tegg/compare/v3.51.1...v3.51.2) (2024-12-09) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.51.1](https://github.com/eggjs/tegg/compare/v3.51.0...v3.51.1) (2024-11-06) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.51.0](https://github.com/eggjs/tegg/compare/v3.50.1...v3.51.0) (2024-10-30) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.50.1](https://github.com/eggjs/tegg/compare/v3.50.0...v3.50.1) (2024-10-23) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.50.0](https://github.com/eggjs/tegg/compare/v3.49.0...v3.50.0) (2024-10-22) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.49.0](https://github.com/eggjs/tegg/compare/v3.48.1...v3.49.0) (2024-10-21) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.48.1](https://github.com/eggjs/tegg/compare/v3.48.0...v3.48.1) (2024-10-14) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.48.0](https://github.com/eggjs/tegg/compare/v3.47.2...v3.48.0) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.47.2](https://github.com/eggjs/tegg/compare/v3.47.1...v3.47.2) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.47.1](https://github.com/eggjs/tegg/compare/v3.47.0...v3.47.1) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.47.0](https://github.com/eggjs/tegg/compare/v3.46.4...v3.47.0) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.46.4](https://github.com/eggjs/tegg/compare/v3.46.3...v3.46.4) (2024-10-09) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.46.3](https://github.com/eggjs/tegg/compare/v3.46.2...v3.46.3) (2024-10-08) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.46.2](https://github.com/eggjs/tegg/compare/v3.46.1...v3.46.2) (2024-10-07) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.46.1](https://github.com/eggjs/tegg/compare/v3.46.0...v3.46.1) (2024-09-30) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.46.0](https://github.com/eggjs/tegg/compare/v3.45.0...v3.46.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.45.0](https://github.com/eggjs/tegg/compare/v3.44.1...v3.45.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.44.1](https://github.com/eggjs/tegg/compare/v3.44.0...v3.44.1) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.44.0](https://github.com/eggjs/tegg/compare/v3.43.2...v3.44.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.43.2](https://github.com/eggjs/tegg/compare/v3.43.1...v3.43.2) (2024-09-14) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.43.1](https://github.com/eggjs/tegg/compare/v3.43.0...v3.43.1) (2024-09-14) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.43.0](https://github.com/eggjs/tegg/compare/v3.42.0...v3.43.0) (2024-09-13) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.42.0](https://github.com/eggjs/tegg/compare/v3.41.0...v3.42.0) (2024-09-10) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.41.0](https://github.com/eggjs/tegg/compare/v3.40.1...v3.41.0) (2024-08-26) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.40.1](https://github.com/eggjs/tegg/compare/v3.40.0...v3.40.1) (2024-08-23) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.40.0](https://github.com/eggjs/tegg/compare/v3.39.5...v3.40.0) (2024-08-22) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.39.5](https://github.com/eggjs/tegg/compare/v3.39.4...v3.39.5) (2024-08-09) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.39.4](https://github.com/eggjs/tegg/compare/v3.39.3...v3.39.4) (2024-07-09) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.39.3](https://github.com/eggjs/tegg/compare/v3.39.2...v3.39.3) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.39.2](https://github.com/eggjs/tegg/compare/v3.39.1...v3.39.2) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.39.1](https://github.com/eggjs/tegg/compare/v3.39.0...v3.39.1) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.39.0](https://github.com/eggjs/tegg/compare/v3.38.0...v3.39.0) (2024-04-19) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.38.0](https://github.com/eggjs/tegg/compare/v3.37.3...v3.38.0) (2024-04-18) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.37.3](https://github.com/eggjs/tegg/compare/v3.37.2...v3.37.3) (2024-04-17) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.37.2](https://github.com/eggjs/tegg/compare/v3.37.1...v3.37.2) (2024-04-17) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.37.1](https://github.com/eggjs/tegg/compare/v3.37.0...v3.37.1) (2024-04-16) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.37.0](https://github.com/eggjs/tegg/compare/v3.36.3...v3.37.0) (2024-04-16) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.36.3](https://github.com/eggjs/tegg/compare/v3.36.2...v3.36.3) (2024-04-10) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.36.2](https://github.com/eggjs/tegg/compare/v3.36.1...v3.36.2) (2024-04-08) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.36.1](https://github.com/eggjs/tegg/compare/v3.36.0...v3.36.1) (2024-04-07) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.36.0](https://github.com/eggjs/tegg/compare/v3.35.1...v3.36.0) (2024-04-02) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.35.1](https://github.com/eggjs/tegg/compare/v3.35.0...v3.35.1) (2024-03-26) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.35.0](https://github.com/eggjs/tegg/compare/v3.34.0...v3.35.0) (2024-03-26) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.34.0](https://github.com/eggjs/tegg/compare/v3.33.1...v3.34.0) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.33.1](https://github.com/eggjs/tegg/compare/v3.33.0...v3.33.1) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.33.0](https://github.com/eggjs/tegg/compare/v3.32.0...v3.33.0) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.32.0](https://github.com/eggjs/tegg/compare/v3.31.0...v3.32.0) (2024-02-19) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.31.0](https://github.com/eggjs/tegg/compare/v3.30.1...v3.31.0) (2024-01-31) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.30.1](https://github.com/eggjs/tegg/compare/v3.30.0...v3.30.1) (2024-01-25) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.30.0](https://github.com/eggjs/tegg/compare/v3.29.0...v3.30.0) (2024-01-17) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.29.0](https://github.com/eggjs/tegg/compare/v3.28.2...v3.29.0) (2023-12-26) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.28.2](https://github.com/eggjs/tegg/compare/v3.28.1...v3.28.2) (2023-12-12) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.28.1](https://github.com/eggjs/tegg/compare/v3.28.0...v3.28.1) (2023-12-11) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.28.0](https://github.com/eggjs/tegg/compare/v3.27.0...v3.28.0) (2023-12-10) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.27.0](https://github.com/eggjs/tegg/compare/v3.26.0...v3.27.0) (2023-11-23) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.25.2](https://github.com/eggjs/tegg/compare/v3.25.1...v3.25.2) (2023-11-06) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.25.1](https://github.com/eggjs/tegg/compare/v3.25.0...v3.25.1) (2023-11-03) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.25.0](https://github.com/eggjs/tegg/compare/v3.24.0...v3.25.0) (2023-11-03) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.24.0](https://github.com/eggjs/tegg/compare/v3.23.0...v3.24.0) (2023-10-26) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.23.0](https://github.com/eggjs/tegg/compare/v3.22.0...v3.23.0) (2023-09-20) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.20.0](https://github.com/eggjs/tegg/compare/v3.19.0...v3.20.0) (2023-09-07) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.19.0](https://github.com/eggjs/tegg/compare/v3.18.1...v3.19.0) (2023-08-31) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.18.1](https://github.com/eggjs/tegg/compare/v3.18.0...v3.18.1) (2023-08-29) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.18.0](https://github.com/eggjs/tegg/compare/v3.17.0...v3.18.0) (2023-08-29) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.17.0](https://github.com/eggjs/tegg/compare/v3.16.0...v3.17.0) (2023-08-24) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.16.0](https://github.com/eggjs/tegg/compare/v3.15.0...v3.16.0) (2023-08-24) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.14.3](https://github.com/eggjs/tegg/compare/v3.14.2...v3.14.3) (2023-08-14) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.14.2](https://github.com/eggjs/tegg/compare/v3.14.1...v3.14.2) (2023-08-14) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.14.1](https://github.com/eggjs/tegg/compare/v3.14.0...v3.14.1) (2023-08-11) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.14.0](https://github.com/eggjs/tegg/compare/v3.13.0...v3.14.0) (2023-07-31) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.13.0](https://github.com/eggjs/tegg/compare/v3.12.0...v3.13.0) (2023-07-25) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.12.0](https://github.com/eggjs/tegg/compare/v3.11.1...v3.12.0) (2023-07-13) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.11.1](https://github.com/eggjs/tegg/compare/v3.11.0...v3.11.1) (2023-06-29) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.11.0](https://github.com/eggjs/tegg/compare/v3.10.0...v3.11.0) (2023-06-29) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.10.0](https://github.com/eggjs/tegg/compare/v3.9.0...v3.10.0) (2023-06-28) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.8.0](https://github.com/eggjs/tegg/compare/v3.7.0...v3.8.0) (2023-05-30) ### Features * impl EggObjectLifecycle hook in decorator ([#119](https://github.com/eggjs/tegg/issues/119)) ([cced8a2](https://github.com/eggjs/tegg/commit/cced8a2e009c33d5040fa21d00409fddef471b0e)) # [3.7.0](https://github.com/eggjs/tegg/compare/v3.6.3...v3.7.0) (2023-04-03) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.5.0](https://github.com/eggjs/tegg/compare/v3.4.1...v3.5.0) (2023-02-10) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.4.0](https://github.com/eggjs/tegg/compare/v3.3.4...v3.4.0) (2023-02-01) ### Features * use singleton model insteadof context ([#89](https://github.com/eggjs/tegg/issues/89)) ([cfdfc05](https://github.com/eggjs/tegg/commit/cfdfc05f13048806274de1a35b1207c073a8519d)) # [3.3.0](https://github.com/eggjs/tegg/compare/v3.2.4...v3.3.0) (2023-01-28) ### Features * export singleton orm client ([#82](https://github.com/eggjs/tegg/issues/82)) ([5320af7](https://github.com/eggjs/tegg/commit/5320af77d7e7c5c73b80560a576f2ce01fc21fff)) ## [3.2.3](https://github.com/eggjs/tegg/compare/v3.2.2...v3.2.3) (2023-01-16) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.2.2](https://github.com/eggjs/tegg/compare/v3.2.1...v3.2.2) (2023-01-06) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.1.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.1.0) (2022-12-27) ### Bug Fixes * use getMetaData for ModelMetadataUtil ([#44](https://github.com/eggjs/tegg/issues/44)) ([87a306c](https://github.com/eggjs/tegg/commit/87a306c4fba51fd519a47c0caaa79442643ea107)) ### Features * **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) * impl Inject Model ([#43](https://github.com/eggjs/tegg/issues/43)) ([ced2ce2](https://github.com/eggjs/tegg/commit/ced2ce2134964dcb410410c0192a34f77507c42d)) # [3.0.0](https://github.com/eggjs/tegg/compare/v3.0.0-alpha.0...v3.0.0) (2022-12-26) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.0.0-alpha.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.0.0-alpha.0) (2022-12-22) ### Bug Fixes * use getMetaData for ModelMetadataUtil ([#44](https://github.com/eggjs/tegg/issues/44)) ([87a306c](https://github.com/eggjs/tegg/commit/87a306c4fba51fd519a47c0caaa79442643ea107)) ### Features * **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) * impl Inject Model ([#43](https://github.com/eggjs/tegg/issues/43)) ([ced2ce2](https://github.com/eggjs/tegg/commit/ced2ce2134964dcb410410c0192a34f77507c42d)) ## [1.4.1](https://github.com/eggjs/tegg/compare/@eggjs/tegg-orm-decorator@1.4.0...@eggjs/tegg-orm-decorator@1.4.1) (2022-07-20) ### Bug Fixes * use getMetaData for ModelMetadataUtil ([#44](https://github.com/eggjs/tegg/issues/44)) ([87a306c](https://github.com/eggjs/tegg/commit/87a306c4fba51fd519a47c0caaa79442643ea107)) # [1.4.0](https://github.com/eggjs/tegg/compare/@eggjs/tegg-orm-decorator@1.3.1...@eggjs/tegg-orm-decorator@1.4.0) (2022-07-20) ### Features * impl Inject Model ([#43](https://github.com/eggjs/tegg/issues/43)) ([ced2ce2](https://github.com/eggjs/tegg/commit/ced2ce2134964dcb410410c0192a34f77507c42d)) # [1.3.0](https://github.com/eggjs/tegg/compare/v1.2.0...v1.3.0) (2022-07-01) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [0.2.0](https://github.com/eggjs/tegg/compare/v0.1.19...v0.2.0) (2022-01-20) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ================================================ FILE: core/mcp-client/README.md ================================================ # `@eggjs/tegg-mcp-client` ## Install ```shell npm i --save @eggjs/tegg-mcp-client ``` ================================================ FILE: core/mcp-client/index.ts ================================================ export * from './src/HttpMCPClient'; export * from './src/MCPClientQualifier'; export * from './src/HeaderUtil'; ================================================ FILE: core/mcp-client/package.json ================================================ { "name": "@eggjs/mcp-client", "version": "3.78.15", "description": "tegg mcp client", "main": "dist/index.js", "files": [ "dist/**/*.js", "dist/**/*.d.ts" ], "typings": "dist/index.d.ts", "keywords": [ "egg", "typescript", "runtime", "tegg" ], "scripts": { "test": "cross-env NODE_ENV=test NODE_OPTIONS='--no-deprecation' mocha", "clean": "tsc -b --clean", "tsc": "npm run clean && tsc -p ./tsconfig.json", "tsc:pub": "npm run clean && tsc -p ./tsconfig.pub.json", "prepublishOnly": "npm run tsc:pub" }, "author": "akitaSummer ", "license": "MIT", "homepage": "https://github.com/eggjs/tegg", "bugs": { "url": "https://github.com/eggjs/tegg/issues" }, "repository": { "type": "git", "url": "git@github.com:eggjs/tegg.git", "directory": "core/langchain-decorator" }, "engines": { "node": ">=14.0.0" }, "dependencies": { "@langchain/mcp-adapters": "^1.0.0", "@modelcontextprotocol/sdk": "^1.23.0", "sdk-base": "^5.0.1", "urllib": "^4.6.11" }, "publishConfig": { "access": "public" }, "devDependencies": { "@types/mocha": "^10.0.1", "@types/node": "^20.2.4", "cross-env": "^7.0.3", "mocha": "^10.2.0", "ts-node": "^10.9.1", "typescript": "^5.0.4" }, "gitHead": "240a3e9f40fda65ebb7589727d197db5ce17916c" } ================================================ FILE: core/mcp-client/src/HeaderUtil.ts ================================================ import { Headers } from 'urllib'; export function mergeHeaders(...headersInits: Array): HeadersInit { const res = {}; for (const headersInit of headersInits) { if (!headersInit) continue; const headers = new Headers(headersInit as Record); for (const key of headers.keys()) { res[key] = headers.get(key); } } return res; } ================================================ FILE: core/mcp-client/src/HttpMCPClient.ts ================================================ import { Client, ClientOptions } from '@modelcontextprotocol/sdk/client/index.js'; import { SSEClientTransport, SSEClientTransportOptions } from '@modelcontextprotocol/sdk/client/sse.js'; import { StreamableHTTPClientTransport, StreamableHTTPClientTransportOptions } from '@modelcontextprotocol/sdk/client/streamableHttp.js'; import { Implementation } from '@modelcontextprotocol/sdk/types.js'; import { RequestOptions } from '@modelcontextprotocol/sdk/shared/protocol.js'; import { fetch } from 'urllib'; import { mergeHeaders } from './HeaderUtil'; import type { Logger } from '@eggjs/tegg'; import { loadMcpTools } from '@langchain/mcp-adapters'; export interface BaseHttpClientOptions extends ClientOptions { logger: Logger; fetch?: typeof fetch; url: string; transportType: 'SSE' | 'STREAMABLE_HTTP'; } export interface HttpSSEClientOptions extends BaseHttpClientOptions { transportOptions?: SSEClientTransportOptions; requestOptions?: RequestOptions; transportType: 'SSE'; } export interface HttpStreamableHTTPClientOptions extends BaseHttpClientOptions { transportOptions?: StreamableHTTPClientTransportOptions; requestOptions?: RequestOptions; transportType: 'STREAMABLE_HTTP'; } export type HttpClientOptions = HttpSSEClientOptions | HttpStreamableHTTPClientOptions; export class HttpMCPClient extends Client { protected logger: Logger; options: HttpClientOptions; #transport: SSEClientTransport | StreamableHTTPClientTransport; #fetch: typeof fetch; url: string; clientInfo: Implementation; constructor(clientInfo: Implementation, options: HttpClientOptions) { super(clientInfo, options); this.options = options; this.#fetch = options.fetch ?? fetch; this.logger = options.logger; this.url = options.url; this.clientInfo = clientInfo; } async #buildSSESTransport() { const self = this; this.logger.info('subscribe config %j use vip: %s', this.url); const url = new URL(this.url); const requestInit: { headers: Record } = { headers: {}, }; const fetchRequestInit = { get headers() { return mergeHeaders( self.options.transportOptions?.requestInit?.headers, requestInit.headers as HeadersInit, ); }, }; const transportRequestInit: SSEClientTransportOptions = { authProvider: this.options.transportOptions?.authProvider, fetch: this.#fetch as any, eventSourceInit: { async fetch(url, requestInit) { const headers = mergeHeaders( requestInit.headers, fetchRequestInit.headers, ); requestInit.headers = headers as any; return await self.#fetch(url, requestInit); }, }, get requestInit() { return { ...self.options.transportOptions?.requestInit, get headers() { return fetchRequestInit.headers; }, }; }, }; this.#transport = new SSEClientTransport(url, transportRequestInit); } async #buildStreamableHTTPTransport() { const self = this; this.logger.info('subscribe config %j use vip: %s', this.url); const url = new URL(this.url); const requestInit: { headers: Record } = { headers: {}, }; const fetchRequestInit = { get headers() { return mergeHeaders( self.options.transportOptions?.requestInit?.headers, requestInit.headers as HeadersInit, ); }, }; const transportRequestInit: StreamableHTTPClientTransportOptions = { authProvider: this.options.transportOptions?.authProvider, fetch: this.#fetch as any, get requestInit() { return { ...self.options.transportOptions?.requestInit, get headers() { return fetchRequestInit.headers; }, }; }, }; this.#transport = new StreamableHTTPClientTransport(url, transportRequestInit); } async init() { if (this.options.transportType === 'SSE') { await this.#buildSSESTransport(); } else { await this.#buildStreamableHTTPTransport(); } await this.connect(this.#transport, this.options.requestOptions); } async getLangChainTool() { return await loadMcpTools(this.clientInfo.name, this as any, { throwOnLoadError: true, prefixToolNameWithServerName: false, additionalToolNamePrefix: '', }); } } ================================================ FILE: core/mcp-client/src/MCPClientQualifier.ts ================================================ import { QualifierUtil, EggProtoImplClass, ModuleConfig, ObjectInfo } from '@eggjs/tegg'; import assert from 'node:assert'; export const MCPClientQualifierAttribute = Symbol.for('Qualifier.MCP_CLIENT'); export const MCPClientInjectName = 'mcpClient'; export function MCPClientQualifier(mcpClientName: string) { return function(target: any, propertyKey: PropertyKey) { QualifierUtil.addProperQualifier(target.constructor as EggProtoImplClass, propertyKey, MCPClientQualifierAttribute, mcpClientName); }; } export type MCPConfigType = Required['mcp']['clients'][keyof Required['mcp']['clients']]; export function getMCPClientName(objectInfo: ObjectInfo): string { const mcpClientName = objectInfo.qualifiers.find(t => t.attribute === MCPClientQualifierAttribute)?.value; assert(mcpClientName, 'not found mcpClientName name'); return mcpClientName as string; } export function getMCPClientConfig(config: ModuleConfig, objectInfo: ObjectInfo): MCPConfigType { const mcpClientName = getMCPClientName(objectInfo); const mcpClientConfig = config.mcp?.clients[mcpClientName]; if (!mcpClientConfig) { throw new Error(`not found ChatModel config for ${mcpClientName}`); } return mcpClientConfig!; } ================================================ FILE: core/mcp-client/test/HttpMCPClient.test.ts ================================================ import assert from 'node:assert/strict'; import { randomUUID } from 'node:crypto'; const majorVersion = parseInt(process.versions.node.split('.')[0], 10); describe('test/HttpMCPClient.test.ts', () => { if (majorVersion < 18) { return; } // eslint-disable-next-line @typescript-eslint/no-var-requires const { HttpMCPClient } = require('../src/HttpMCPClient'); // eslint-disable-next-line @typescript-eslint/no-var-requires const { startSSEServer, stopSSEServer } = require('./fixtures/sse-mcp-server/http'); // eslint-disable-next-line @typescript-eslint/no-var-requires const { startStreamableServer, stopStreamableServer } = require('./fixtures/streamable-mcp-server/http'); it('should work', async () => { await startStreamableServer(); const client = new HttpMCPClient({ name: 'test', version: '1.0.0', }, { transportType: 'STREAMABLE_HTTP', logger: console as any, url: 'http://127.0.0.1:17243', }); await client.init(); const tools = await client.listTools(); assert(tools); await stopStreamableServer(); }); it('should sse work', async () => { await startSSEServer(); const client = new HttpMCPClient({ name: 'test', version: '1.0.0', }, { transportType: 'SSE', logger: console as any, transportOptions: { requestInit: { headers: { 'SOFA-TraceId': randomUUID(), 'SOFA-RpcId': '0.1', }, }, }, url: 'http://127.0.0.1:17233/mcp/sse', }); await client.init(); const tools = await client.listTools(); assert(tools); await stopSSEServer(); }); }); ================================================ FILE: core/mcp-client/test/fixtures/sse-mcp-server/http.ts ================================================ import http from 'node:http'; import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js'; import * as z from 'zod/v4'; // Create an MCP server const server = new McpServer({ name: "Demo", version: "1.0.0" }); // Add an addition tool server.registerTool("add", { inputSchema: { a: z.number(), b: z.number() }, }, async ({ a, b }) => ({ content: [{ type: "text", text: String(a + b) }] }) ); // Add a dynamic greeting resource server.registerResource( "greeting", "greeting://{name}", {}, // @ts-ignore async (uri, { name }) => ({ contents: [{ uri: uri.href, text: `Hello, ${name}!` }] }) ); const transports = {}; export const headers = {}; export let httpServer; export async function startSSEServer(port = 17233) { const httpServer = http.createServer(async (req, res) => { const url = new URL(`http://127.0.0.1:${port}${req.url!}`); const headerKey = `${req.method}${url.pathname}`; const serverCode = req.headers['x-mcp-server-code'] as string; headers[serverCode] = headers[serverCode] || {}; headers[serverCode][headerKey] = headers[serverCode][headerKey] || []; headers[serverCode][headerKey].push(req.headers); if (req.method === 'GET') { const transport = new SSEServerTransport('/mcp', res); transports[transport.sessionId] = transport; // Connect the transport to the MCP server await server.connect(transport); } else if (req.method === 'POST') { const sessionId = url.searchParams.get('sessionId'); // const chunks: Buffer[] = []; // for await (const chunk of req) { // chunks.push(chunk); // } // const body = JSON.parse(Buffer.concat(chunks).toString('utf-8')); const transport = transports[sessionId!] as SSEServerTransport; await transport.handlePostMessage(req, res); res.statusCode = 201; res.end(); } }); return new Promise(resolve => { httpServer.listen(port, resolve); }); } export async function stopSSEServer() { server.close(); } ================================================ FILE: core/mcp-client/test/fixtures/streamable-mcp-server/http.ts ================================================ import http from 'node:http'; import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import * as z from 'zod/v4'; // Create an MCP server const server = new McpServer({ name: "Demo", version: "1.0.0" }); // Add an addition tool server.registerTool("add", { inputSchema: { a: z.number(), b: z.number() }, }, async ({ a, b }) => ({ content: [{ type: "text", text: String(a + b) }] }) ); // Add a dynamic greeting resource server.registerResource( "greeting", "greeting://{name}", {}, // @ts-ignore async (uri, { name }) => ({ contents: [{ uri: uri.href, text: `Hello, ${name}!` }] }) ); export const headers = {}; export let httpServer; export async function startStreamableServer(port = 17243){ const httpServer = http.createServer(async (req, res) => { const { StreamableHTTPServerTransport } = require('@modelcontextprotocol/sdk/server/streamableHttp.js'); const url = new URL(`http://127.0.0.1:${port}${req.url!}`); const headerKey = `${req.method}${url.pathname}`; const serverCode = req.headers['x-mcp-server-code'] as string; headers[serverCode] = headers[serverCode] || {}; headers[serverCode][headerKey] = headers[serverCode][headerKey] || []; headers[serverCode][headerKey].push(req.headers); if (req.method === 'POST') { try { const transport: typeof StreamableHTTPServerTransport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined, }); await server.connect(transport); await transport.handleRequest(req, res); res.on('close', () => { console.log('Request closed'); transport.close(); server.close(); }); } catch (error) { console.error('Error handling MCP request:', error); if (!res.headersSent) { res.statusCode = 500; res.setHeader('Content-Type', 'application/json'); res.end(JSON.stringify({ jsonrpc: '2.0', error: { code: -32603, message: 'Internal server error', }, id: null, })); } } } else { res.statusCode = 405; res.setHeader('Content-Type', 'application/json'); res.end(JSON.stringify({ jsonrpc: '2.0', error: { code: -32601, message: 'Method not found', }, id: null, })); } }); return new Promise(resolve => { httpServer.listen(port, resolve); }); } export async function stopStreamableServer() { server.close(); } ================================================ FILE: core/mcp-client/tsconfig.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "dist", "baseUrl": "./" }, "exclude": [ "dist", "node_modules", "test" ] } ================================================ FILE: core/mcp-client/tsconfig.pub.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "dist", "baseUrl": "./" }, "exclude": [ "dist", "node_modules", "test" ] } ================================================ FILE: core/mcp-client/typings/index.d.ts ================================================ import { EggContainerFactory } from '@eggjs/tegg-runtime'; import { Type, Static } from '@eggjs/tegg/ajv'; import 'egg'; import '@eggjs/tegg-plugin/typings'; import '@eggjs/tegg-controller-plugin/typings'; export const McpClientConfigSchema = Type.Object({ clients: Type.Record(Type.String(), Type.Object({ url: Type.Optional(Type.String({ description: 'mcp server url', })), clientName: Type.Optional(Type.String({ description: 'mcp client 名', })), version: Type.Optional(Type.String({ description: '客户端版本, 默认值是 1.0.0', })), transportType: Type.Optional(Type.String({ description: 'SSE 或者 STREAMABLE_HTTP', })), type: Type.Optional(Type.String({ description: '客户端类型', })), })), }, { title: 'MCP 设置', name: 'MCP', }); export type McpClientConfigSchemaType = Static; declare module '@eggjs/tegg' { export type McpClientConfig = { mcp?: McpClientConfigSchemaType; }; export interface ModuleConfig extends McpClientConfig { } } ================================================ FILE: core/metadata/CHANGELOG.md ================================================ # Change Log All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. ## [3.78.15](https://github.com/eggjs/tegg/compare/v3.78.14...v3.78.15) (2026-04-23) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.78.14](https://github.com/eggjs/tegg/compare/v3.78.13...v3.78.14) (2026-04-21) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.78.13](https://github.com/eggjs/tegg/compare/v3.78.12...v3.78.13) (2026-04-21) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.78.12](https://github.com/eggjs/tegg/compare/v3.78.11...v3.78.12) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.78.11](https://github.com/eggjs/tegg/compare/v3.78.10...v3.78.11) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.78.10](https://github.com/eggjs/tegg/compare/v3.78.9...v3.78.10) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.78.9](https://github.com/eggjs/tegg/compare/v3.78.8...v3.78.9) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.78.8](https://github.com/eggjs/tegg/compare/v3.78.7...v3.78.8) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.78.7](https://github.com/eggjs/tegg/compare/v3.78.6...v3.78.7) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.78.6](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.6) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.78.5](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.5) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.78.4](https://github.com/eggjs/tegg/compare/v3.78.3...v3.78.4) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.78.3](https://github.com/eggjs/tegg/compare/v3.78.2...v3.78.3) (2026-04-15) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.78.2](https://github.com/eggjs/tegg/compare/v3.78.1...v3.78.2) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.78.1](https://github.com/eggjs/tegg/compare/v3.78.0...v3.78.1) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-metadata # [3.78.0](https://github.com/eggjs/tegg/compare/v3.77.2...v3.78.0) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.77.2](https://github.com/eggjs/tegg/compare/v3.77.1...v3.77.2) (2026-04-07) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.77.1](https://github.com/eggjs/tegg/compare/v3.77.0...v3.77.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-metadata # [3.77.0](https://github.com/eggjs/tegg/compare/v3.76.1...v3.77.0) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.76.1](https://github.com/eggjs/tegg/compare/v3.76.0...v3.76.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-metadata # [3.76.0](https://github.com/eggjs/tegg/compare/v3.75.1...v3.76.0) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.75.1](https://github.com/eggjs/tegg/compare/v3.75.0...v3.75.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-metadata # [3.75.0](https://github.com/eggjs/tegg/compare/v3.74.0...v3.75.0) (2026-03-30) **Note:** Version bump only for package @eggjs/tegg-metadata # [3.74.0](https://github.com/eggjs/tegg/compare/v3.73.0...v3.74.0) (2026-03-30) **Note:** Version bump only for package @eggjs/tegg-metadata # [3.73.0](https://github.com/eggjs/tegg/compare/v3.72.0...v3.73.0) (2026-03-25) **Note:** Version bump only for package @eggjs/tegg-metadata # [3.72.0](https://github.com/eggjs/tegg/compare/v3.71.2...v3.72.0) (2026-02-04) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.71.2](https://github.com/eggjs/tegg/compare/v3.71.1...v3.71.2) (2026-01-30) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.71.1](https://github.com/eggjs/tegg/compare/v3.71.0...v3.71.1) (2026-01-28) **Note:** Version bump only for package @eggjs/tegg-metadata # [3.71.0](https://github.com/eggjs/tegg/compare/v3.70.1...v3.71.0) (2026-01-26) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.70.1](https://github.com/eggjs/tegg/compare/v3.70.0...v3.70.1) (2026-01-14) **Note:** Version bump only for package @eggjs/tegg-metadata # [3.70.0](https://github.com/eggjs/tegg/compare/v3.69.0...v3.70.0) (2026-01-07) **Note:** Version bump only for package @eggjs/tegg-metadata # [3.69.0](https://github.com/eggjs/tegg/compare/v3.68.0...v3.69.0) (2026-01-06) **Note:** Version bump only for package @eggjs/tegg-metadata # [3.68.0](https://github.com/eggjs/tegg/compare/v3.67.2...v3.68.0) (2025-12-29) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.67.2](https://github.com/eggjs/tegg/compare/v3.67.1...v3.67.2) (2025-12-24) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.67.1](https://github.com/eggjs/tegg/compare/v3.67.0...v3.67.1) (2025-12-23) **Note:** Version bump only for package @eggjs/tegg-metadata # [3.67.0](https://github.com/eggjs/tegg/compare/v3.66.0...v3.67.0) (2025-12-18) **Note:** Version bump only for package @eggjs/tegg-metadata # [3.66.0](https://github.com/eggjs/tegg/compare/v3.65.3...v3.66.0) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.65.3](https://github.com/eggjs/tegg/compare/v3.65.2...v3.65.3) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.65.2](https://github.com/eggjs/tegg/compare/v3.65.1...v3.65.2) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.65.1](https://github.com/eggjs/tegg/compare/v3.65.0...v3.65.1) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-metadata # [3.65.0](https://github.com/eggjs/tegg/compare/v3.64.5...v3.65.0) (2025-12-10) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.64.5](https://github.com/eggjs/tegg/compare/v3.64.4...v3.64.5) (2025-12-08) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.64.4](https://github.com/eggjs/tegg/compare/v3.64.3...v3.64.4) (2025-12-06) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.64.3](https://github.com/eggjs/tegg/compare/v3.64.2...v3.64.3) (2025-12-06) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.64.2](https://github.com/eggjs/tegg/compare/v3.64.1...v3.64.2) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.64.1](https://github.com/eggjs/tegg/compare/v3.64.0...v3.64.1) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-metadata # [3.64.0](https://github.com/eggjs/tegg/compare/v3.63.2...v3.64.0) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.63.2](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.2) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.63.1](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.1) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg-metadata # [3.63.0](https://github.com/eggjs/tegg/compare/v3.62.3...v3.63.0) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.62.3](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.3) (2025-11-05) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.62.2](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.2) (2025-11-05) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.62.1](https://github.com/eggjs/tegg/compare/v3.62.0...v3.62.1) (2025-11-03) **Note:** Version bump only for package @eggjs/tegg-metadata # [3.62.0](https://github.com/eggjs/tegg/compare/v3.61.0...v3.62.0) (2025-09-24) **Note:** Version bump only for package @eggjs/tegg-metadata # [3.61.0](https://github.com/eggjs/tegg/compare/v3.60.3...v3.61.0) (2025-08-15) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.60.3](https://github.com/eggjs/tegg/compare/v3.60.2...v3.60.3) (2025-08-06) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.60.2](https://github.com/eggjs/tegg/compare/v3.60.1...v3.60.2) (2025-08-06) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.60.1](https://github.com/eggjs/tegg/compare/v3.60.0...v3.60.1) (2025-07-28) **Note:** Version bump only for package @eggjs/tegg-metadata # [3.60.0](https://github.com/eggjs/tegg/compare/v3.59.1...v3.60.0) (2025-07-28) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.59.1](https://github.com/eggjs/tegg/compare/v3.59.0...v3.59.1) (2025-07-16) **Note:** Version bump only for package @eggjs/tegg-metadata # [3.59.0](https://github.com/eggjs/tegg/compare/v3.58.0...v3.59.0) (2025-07-07) **Note:** Version bump only for package @eggjs/tegg-metadata # [3.58.0](https://github.com/eggjs/tegg/compare/v3.57.14...v3.58.0) (2025-07-01) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.57.14](https://github.com/eggjs/tegg/compare/v3.57.13...v3.57.14) (2025-06-18) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.57.13](https://github.com/eggjs/tegg/compare/v3.57.12...v3.57.13) (2025-06-18) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.57.12](https://github.com/eggjs/tegg/compare/v3.57.11...v3.57.12) (2025-06-16) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.57.11](https://github.com/eggjs/tegg/compare/v3.57.10...v3.57.11) (2025-06-16) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.57.10](https://github.com/eggjs/tegg/compare/v3.57.9...v3.57.10) (2025-06-12) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.57.9](https://github.com/eggjs/tegg/compare/v3.57.8...v3.57.9) (2025-05-29) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.57.8](https://github.com/eggjs/tegg/compare/v3.57.7...v3.57.8) (2025-05-29) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.57.7](https://github.com/eggjs/tegg/compare/v3.57.6...v3.57.7) (2025-05-28) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.57.6](https://github.com/eggjs/tegg/compare/v3.57.5...v3.57.6) (2025-05-27) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.57.5](https://github.com/eggjs/tegg/compare/v3.57.4...v3.57.5) (2025-05-15) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.57.4](https://github.com/eggjs/tegg/compare/v3.57.3...v3.57.4) (2025-05-14) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.57.3](https://github.com/eggjs/tegg/compare/v3.57.2...v3.57.3) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.57.2](https://github.com/eggjs/tegg/compare/v3.57.1...v3.57.2) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.57.1](https://github.com/eggjs/tegg/compare/v3.57.0...v3.57.1) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-metadata # [3.57.0](https://github.com/eggjs/tegg/compare/v3.56.3...v3.57.0) (2025-05-09) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.56.3](https://github.com/eggjs/tegg/compare/v3.56.2...v3.56.3) (2025-05-07) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.56.2](https://github.com/eggjs/tegg/compare/v3.56.1...v3.56.2) (2025-05-01) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.56.1](https://github.com/eggjs/tegg/compare/v3.56.0...v3.56.1) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-metadata # [3.56.0](https://github.com/eggjs/tegg/compare/v3.55.0...v3.56.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-metadata # [3.55.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.55.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-metadata # [3.54.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.54.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-metadata # [3.53.0](https://github.com/eggjs/tegg/compare/v3.52.1...v3.53.0) (2025-04-21) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.52.1](https://github.com/eggjs/tegg/compare/v3.52.0...v3.52.1) (2025-03-14) **Note:** Version bump only for package @eggjs/tegg-metadata # [3.52.0](https://github.com/eggjs/tegg/compare/v3.51.2...v3.52.0) (2024-12-30) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.51.2](https://github.com/eggjs/tegg/compare/v3.51.1...v3.51.2) (2024-12-09) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.51.1](https://github.com/eggjs/tegg/compare/v3.51.0...v3.51.1) (2024-11-06) **Note:** Version bump only for package @eggjs/tegg-metadata # [3.51.0](https://github.com/eggjs/tegg/compare/v3.50.1...v3.51.0) (2024-10-30) ### Features * add default inject init type qualifier ([#255](https://github.com/eggjs/tegg/issues/255)) ([538ae80](https://github.com/eggjs/tegg/commit/538ae8033ff102ac0b1d141c6495058a800e46f1)) * support optional inject ([#254](https://github.com/eggjs/tegg/issues/254)) ([260470b](https://github.com/eggjs/tegg/commit/260470b766d5fdb323c1bd72cc6260a90468a161)) ## [3.50.1](https://github.com/eggjs/tegg/compare/v3.50.0...v3.50.1) (2024-10-23) **Note:** Version bump only for package @eggjs/tegg-metadata # [3.50.0](https://github.com/eggjs/tegg/compare/v3.49.0...v3.50.0) (2024-10-22) **Note:** Version bump only for package @eggjs/tegg-metadata # [3.49.0](https://github.com/eggjs/tegg/compare/v3.48.1...v3.49.0) (2024-10-21) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.48.1](https://github.com/eggjs/tegg/compare/v3.48.0...v3.48.1) (2024-10-14) ### Bug Fixes * fix merge qualifier ([#250](https://github.com/eggjs/tegg/issues/250)) ([d5a8a93](https://github.com/eggjs/tegg/commit/d5a8a93abad570f69881f9fa42f39d7b5cd436be)) # [3.48.0](https://github.com/eggjs/tegg/compare/v3.47.2...v3.48.0) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.47.2](https://github.com/eggjs/tegg/compare/v3.47.1...v3.47.2) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.47.1](https://github.com/eggjs/tegg/compare/v3.47.0...v3.47.1) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-metadata # [3.47.0](https://github.com/eggjs/tegg/compare/v3.46.4...v3.47.0) (2024-10-10) ### Features * export ProtoDescriptorHelper ([#245](https://github.com/eggjs/tegg/issues/245)) ([f01fb63](https://github.com/eggjs/tegg/commit/f01fb639b153a907fd9c951d4b1e40ba101b43d0)) * impl GlobalGraph build hook ([#246](https://github.com/eggjs/tegg/issues/246)) ([48fce45](https://github.com/eggjs/tegg/commit/48fce4512e99259ec26a9b032bfcc9f4046ad235)) ## [3.46.4](https://github.com/eggjs/tegg/compare/v3.46.3...v3.46.4) (2024-10-09) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.46.3](https://github.com/eggjs/tegg/compare/v3.46.2...v3.46.3) (2024-10-08) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.46.2](https://github.com/eggjs/tegg/compare/v3.46.1...v3.46.2) (2024-10-07) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.46.1](https://github.com/eggjs/tegg/compare/v3.46.0...v3.46.1) (2024-09-30) ### Bug Fixes * fix miss MultiInstance proper qualifiers ([#241](https://github.com/eggjs/tegg/issues/241)) ([15666d3](https://github.com/eggjs/tegg/commit/15666d36c18b99eccc4f1a11d8e7702503694ee1)) # [3.46.0](https://github.com/eggjs/tegg/compare/v3.45.0...v3.46.0) (2024-09-29) ### Features * impl MultiInstance inject MultiInstance ([#240](https://github.com/eggjs/tegg/issues/240)) ([08e3b0c](https://github.com/eggjs/tegg/commit/08e3b0cc02f3d2dbba767298a6aec6c00147f9ed)) # [3.45.0](https://github.com/eggjs/tegg/compare/v3.44.1...v3.45.0) (2024-09-29) ### Features * impl MultiInstanceInfo decorator ([#239](https://github.com/eggjs/tegg/issues/239)) ([70d4d95](https://github.com/eggjs/tegg/commit/70d4d95bca4a0c3e11d0d7cc4f292b1315e49e81)) ## [3.44.1](https://github.com/eggjs/tegg/compare/v3.44.0...v3.44.1) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-metadata # [3.44.0](https://github.com/eggjs/tegg/compare/v3.43.2...v3.44.0) (2024-09-29) ### Features * support inject in constructor ([#237](https://github.com/eggjs/tegg/issues/237)) ([e68b1ed](https://github.com/eggjs/tegg/commit/e68b1ed6a90432f1cb35a6f562914b7b04cb5114)) ## [3.43.2](https://github.com/eggjs/tegg/compare/v3.43.1...v3.43.2) (2024-09-14) ### Bug Fixes * add preload loadunit ([#236](https://github.com/eggjs/tegg/issues/236)) ([0e28972](https://github.com/eggjs/tegg/commit/0e2897200a9bc3bc6aa1028c8549bdbf45bbaaa3)) ## [3.43.1](https://github.com/eggjs/tegg/compare/v3.43.0...v3.43.1) (2024-09-14) **Note:** Version bump only for package @eggjs/tegg-metadata # [3.43.0](https://github.com/eggjs/tegg/compare/v3.42.0...v3.43.0) (2024-09-13) **Note:** Version bump only for package @eggjs/tegg-metadata # [3.42.0](https://github.com/eggjs/tegg/compare/v3.41.0...v3.42.0) (2024-09-10) ### Features * add LifecyclePreLoad ([#234](https://github.com/eggjs/tegg/issues/234)) ([2b72163](https://github.com/eggjs/tegg/commit/2b7216387f02cd045952447eaa21baa3a7ee04a3)) # [3.41.0](https://github.com/eggjs/tegg/compare/v3.40.1...v3.41.0) (2024-08-26) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.40.1](https://github.com/eggjs/tegg/compare/v3.40.0...v3.40.1) (2024-08-23) **Note:** Version bump only for package @eggjs/tegg-metadata # [3.40.0](https://github.com/eggjs/tegg/compare/v3.39.5...v3.40.0) (2024-08-22) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.39.5](https://github.com/eggjs/tegg/compare/v3.39.4...v3.39.5) (2024-08-09) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.39.4](https://github.com/eggjs/tegg/compare/v3.39.3...v3.39.4) (2024-07-09) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.39.3](https://github.com/eggjs/tegg/compare/v3.39.2...v3.39.3) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.39.2](https://github.com/eggjs/tegg/compare/v3.39.1...v3.39.2) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.39.1](https://github.com/eggjs/tegg/compare/v3.39.0...v3.39.1) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-metadata # [3.39.0](https://github.com/eggjs/tegg/compare/v3.38.0...v3.39.0) (2024-04-19) **Note:** Version bump only for package @eggjs/tegg-metadata # [3.38.0](https://github.com/eggjs/tegg/compare/v3.37.3...v3.38.0) (2024-04-18) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.37.3](https://github.com/eggjs/tegg/compare/v3.37.2...v3.37.3) (2024-04-17) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.37.2](https://github.com/eggjs/tegg/compare/v3.37.1...v3.37.2) (2024-04-17) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.37.1](https://github.com/eggjs/tegg/compare/v3.37.0...v3.37.1) (2024-04-16) **Note:** Version bump only for package @eggjs/tegg-metadata # [3.37.0](https://github.com/eggjs/tegg/compare/v3.36.3...v3.37.0) (2024-04-16) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.36.3](https://github.com/eggjs/tegg/compare/v3.36.2...v3.36.3) (2024-04-10) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.36.2](https://github.com/eggjs/tegg/compare/v3.36.1...v3.36.2) (2024-04-08) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.36.1](https://github.com/eggjs/tegg/compare/v3.36.0...v3.36.1) (2024-04-07) **Note:** Version bump only for package @eggjs/tegg-metadata # [3.36.0](https://github.com/eggjs/tegg/compare/v3.35.1...v3.36.0) (2024-04-02) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.35.1](https://github.com/eggjs/tegg/compare/v3.35.0...v3.35.1) (2024-03-26) **Note:** Version bump only for package @eggjs/tegg-metadata # [3.35.0](https://github.com/eggjs/tegg/compare/v3.34.0...v3.35.0) (2024-03-26) **Note:** Version bump only for package @eggjs/tegg-metadata # [3.34.0](https://github.com/eggjs/tegg/compare/v3.33.1...v3.34.0) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.33.1](https://github.com/eggjs/tegg/compare/v3.33.0...v3.33.1) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-metadata # [3.33.0](https://github.com/eggjs/tegg/compare/v3.32.0...v3.33.0) (2024-03-22) ### Features * impl dal ([#192](https://github.com/eggjs/tegg/issues/192)) ([1c7d145](https://github.com/eggjs/tegg/commit/1c7d1454bc8c600cd58c3ec7b9cda4e8a98c7287)) # [3.32.0](https://github.com/eggjs/tegg/compare/v3.31.0...v3.32.0) (2024-02-19) **Note:** Version bump only for package @eggjs/tegg-metadata # [3.31.0](https://github.com/eggjs/tegg/compare/v3.30.1...v3.31.0) (2024-01-31) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.30.1](https://github.com/eggjs/tegg/compare/v3.30.0...v3.30.1) (2024-01-25) **Note:** Version bump only for package @eggjs/tegg-metadata # [3.30.0](https://github.com/eggjs/tegg/compare/v3.29.0...v3.30.0) (2024-01-17) ### Features * scan framework dependencies as optional module ([#184](https://github.com/eggjs/tegg/issues/184)) ([a4908c6](https://github.com/eggjs/tegg/commit/a4908c6c640000c7068def57d32052cca15adf47)) # [3.29.0](https://github.com/eggjs/tegg/compare/v3.28.2...v3.29.0) (2023-12-26) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.28.2](https://github.com/eggjs/tegg/compare/v3.28.1...v3.28.2) (2023-12-12) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.28.1](https://github.com/eggjs/tegg/compare/v3.28.0...v3.28.1) (2023-12-11) **Note:** Version bump only for package @eggjs/tegg-metadata # [3.28.0](https://github.com/eggjs/tegg/compare/v3.27.0...v3.28.0) (2023-12-10) **Note:** Version bump only for package @eggjs/tegg-metadata # [3.27.0](https://github.com/eggjs/tegg/compare/v3.26.0...v3.27.0) (2023-11-23) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.25.2](https://github.com/eggjs/tegg/compare/v3.25.1...v3.25.2) (2023-11-06) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.25.1](https://github.com/eggjs/tegg/compare/v3.25.0...v3.25.1) (2023-11-03) **Note:** Version bump only for package @eggjs/tegg-metadata # [3.25.0](https://github.com/eggjs/tegg/compare/v3.24.0...v3.25.0) (2023-11-03) **Note:** Version bump only for package @eggjs/tegg-metadata # [3.24.0](https://github.com/eggjs/tegg/compare/v3.23.0...v3.24.0) (2023-10-26) **Note:** Version bump only for package @eggjs/tegg-metadata # [3.23.0](https://github.com/eggjs/tegg/compare/v3.22.0...v3.23.0) (2023-09-20) ### Features * add className property to EggPrototypeInfo ([#158](https://github.com/eggjs/tegg/issues/158)) ([bddac97](https://github.com/eggjs/tegg/commit/bddac97a9f575c9f13b794246a7e8346c58d1a09)) # [3.20.0](https://github.com/eggjs/tegg/compare/v3.19.0...v3.20.0) (2023-09-07) **Note:** Version bump only for package @eggjs/tegg-metadata # [3.19.0](https://github.com/eggjs/tegg/compare/v3.18.1...v3.19.0) (2023-08-31) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.18.1](https://github.com/eggjs/tegg/compare/v3.18.0...v3.18.1) (2023-08-29) ### Bug Fixes * fix use MultiInstanceProto from other modules ([#147](https://github.com/eggjs/tegg/issues/147)) ([b71af60](https://github.com/eggjs/tegg/commit/b71af60ce6d1da0d778f5e712633b8c15052bd70)) # [3.18.0](https://github.com/eggjs/tegg/compare/v3.17.0...v3.18.0) (2023-08-29) **Note:** Version bump only for package @eggjs/tegg-metadata # [3.17.0](https://github.com/eggjs/tegg/compare/v3.16.0...v3.17.0) (2023-08-24) ### Features * impl MultiInstanceProto ([#145](https://github.com/eggjs/tegg/issues/145)) ([12fd5cf](https://github.com/eggjs/tegg/commit/12fd5cff4004578bcc737dcdf4f7e9d1159f5633)) # [3.16.0](https://github.com/eggjs/tegg/compare/v3.15.0...v3.16.0) (2023-08-24) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.14.3](https://github.com/eggjs/tegg/compare/v3.14.2...v3.14.3) (2023-08-14) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.14.2](https://github.com/eggjs/tegg/compare/v3.14.1...v3.14.2) (2023-08-14) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.14.1](https://github.com/eggjs/tegg/compare/v3.14.0...v3.14.1) (2023-08-11) **Note:** Version bump only for package @eggjs/tegg-metadata # [3.14.0](https://github.com/eggjs/tegg/compare/v3.13.0...v3.14.0) (2023-07-31) **Note:** Version bump only for package @eggjs/tegg-metadata # [3.13.0](https://github.com/eggjs/tegg/compare/v3.12.0...v3.13.0) (2023-07-25) **Note:** Version bump only for package @eggjs/tegg-metadata # [3.12.0](https://github.com/eggjs/tegg/compare/v3.11.1...v3.12.0) (2023-07-13) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.11.1](https://github.com/eggjs/tegg/compare/v3.11.0...v3.11.1) (2023-06-29) **Note:** Version bump only for package @eggjs/tegg-metadata # [3.11.0](https://github.com/eggjs/tegg/compare/v3.10.0...v3.11.0) (2023-06-29) **Note:** Version bump only for package @eggjs/tegg-metadata # [3.10.0](https://github.com/eggjs/tegg/compare/v3.9.0...v3.10.0) (2023-06-28) **Note:** Version bump only for package @eggjs/tegg-metadata # [3.8.0](https://github.com/eggjs/tegg/compare/v3.7.0...v3.8.0) (2023-05-30) ### Features * impl EggObjectLifecycle hook in decorator ([#119](https://github.com/eggjs/tegg/issues/119)) ([cced8a2](https://github.com/eggjs/tegg/commit/cced8a2e009c33d5040fa21d00409fddef471b0e)) # [3.7.0](https://github.com/eggjs/tegg/compare/v3.6.3...v3.7.0) (2023-04-03) **Note:** Version bump only for package @eggjs/tegg-metadata # [3.5.0](https://github.com/eggjs/tegg/compare/v3.4.1...v3.5.0) (2023-02-10) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.2.3](https://github.com/eggjs/tegg/compare/v3.2.2...v3.2.3) (2023-01-16) **Note:** Version bump only for package @eggjs/tegg-metadata ## [3.2.2](https://github.com/eggjs/tegg/compare/v3.2.1...v3.2.2) (2023-01-06) **Note:** Version bump only for package @eggjs/tegg-metadata # [3.1.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.1.0) (2022-12-27) ### Bug Fixes * inject context proto to singleton proto ([#72](https://github.com/eggjs/tegg/issues/72)) ([fcc0b2b](https://github.com/eggjs/tegg/commit/fcc0b2b48fc9bce580c1f2bcfcc38039ae909951)) ### Features * **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) # [3.0.0](https://github.com/eggjs/tegg/compare/v3.0.0-alpha.0...v3.0.0) (2022-12-26) **Note:** Version bump only for package @eggjs/tegg-metadata # [3.0.0-alpha.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.0.0-alpha.0) (2022-12-22) ### Features * **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) # [1.3.0](https://github.com/eggjs/tegg/compare/v1.2.0...v1.3.0) (2022-07-01) ### Features * allow inject proto and name ([#40](https://github.com/eggjs/tegg/issues/40)) ([abd1766](https://github.com/eggjs/tegg/commit/abd17665af2528c4c2e33f4c6b0fceddd8a4e76b)) # [0.2.0](https://github.com/eggjs/tegg/compare/v0.1.19...v0.2.0) (2022-01-20) **Note:** Version bump only for package @eggjs/tegg-metadata ================================================ FILE: core/metadata/README.md ================================================ # `@eggjs/tegg-metadata` # Usage This is an internal tegg library, you probably shouldn't use it directly. ================================================ FILE: core/metadata/index.ts ================================================ export * from '@eggjs/tegg-types/metadata'; export * from './src/factory/EggPrototypeFactory'; export * from './src/factory/EggPrototypeCreatorFactory'; export * from './src/factory/LoadUnitFactory'; export * from './src/model/EggPrototype'; export * from './src/model/LoadUnit'; export * from './src/errors'; export * from './src/util/ClassUtil'; export * from './src/impl/LoadUnitMultiInstanceProtoHook'; export * from './src/model/AppGraph'; export * from './src/model/graph/GlobalGraph'; export * from './src/model/graph/GlobalModuleNode'; export * from './src/model/graph/GlobalModuleNodeBuilder'; export * from './src/model/graph/ProtoNode'; export * from './src/model/graph/ProtoSelector'; export * from './src/model/ProtoDescriptor/AbstractProtoDescriptor'; export * from './src/model/ProtoDescriptor/ClassProtoDescriptor'; export * from './src/model/ModuleDescriptor'; export * from './src/model/ProtoDescriptorHelper'; import './src/impl/ModuleLoadUnit'; import './src/impl/EggPrototypeBuilder'; ================================================ FILE: core/metadata/package.json ================================================ { "name": "@eggjs/tegg-metadata", "version": "3.78.15", "description": "tegg metadata", "keywords": [ "egg", "typescript", "metadata", "tegg" ], "main": "dist/index.js", "files": [ "dist/**/*.js", "dist/**/*.d.ts" ], "typings": "dist/index.d.ts", "scripts": { "test": "cross-env NODE_ENV=test NODE_OPTIONS='--no-deprecation' mocha", "clean": "tsc -b --clean", "tsc:pub": "ut run clean && tsc -p ./tsconfig.pub.json", "prepublishOnly": "ut tsc:pub" }, "author": "killagu ", "license": "MIT", "homepage": "https://github.com/eggjs/tegg", "bugs": { "url": "https://github.com/eggjs/tegg/issues" }, "repository": { "type": "git", "url": "git@github.com:eggjs/tegg.git", "directory": "core/metadata" }, "engines": { "node": ">=14.0.0" }, "dependencies": { "@eggjs/core-decorator": "^3.78.15", "@eggjs/tegg-common-util": "^3.78.15", "@eggjs/tegg-lifecycle": "^3.78.15", "@eggjs/tegg-types": "^3.78.15", "egg-errors": "^2.2.3" }, "devDependencies": { "@types/mocha": "^10.0.1", "@types/node": "^20.2.4", "cross-env": "^7.0.3", "globby": "^11.1.0", "is-type-of": "^1.2.1", "mocha": "^10.2.0", "ts-node": "^10.9.1", "typescript": "^5.0.4" }, "publishConfig": { "access": "public" }, "gitHead": "240a3e9f40fda65ebb7589727d197db5ce17916c" } ================================================ FILE: core/metadata/src/errors.ts ================================================ import { FrameworkBaseError } from 'egg-errors'; import { ErrorCodes } from '@eggjs/tegg-types'; import type { EggPrototypeName, QualifierInfo } from '@eggjs/tegg-types'; export class TeggError extends FrameworkBaseError { get module() { return 'TEGG'; } } export class EggPrototypeNotFound extends TeggError { constructor(protoName: EggPrototypeName, loadUnitId: string | undefined) { const msg = loadUnitId ? `Object ${String(protoName)} not found in ${loadUnitId}` : `Object ${String(protoName)} not found`; super(msg, ErrorCodes.EGG_PROTO_NOT_FOUND); } } export class MultiPrototypeFound extends TeggError { constructor(name: EggPrototypeName, qualifier: QualifierInfo[], result?: string) { const msg = `multi proto found for name:${String(name)} and qualifiers ${JSON.stringify(qualifier)}${result ? `, result is ${result}` : ''}`; super(msg, ErrorCodes.MULTI_PROTO_FOUND); } } export class IncompatibleProtoInject extends TeggError { constructor(msg: string) { super(msg, ErrorCodes.INCOMPATIBLE_PROTO_INJECT); } } ================================================ FILE: core/metadata/src/factory/EggPrototypeCreatorFactory.ts ================================================ import { InitTypeQualifierAttribute, LoadUnitNameQualifierAttribute, PrototypeUtil } from '@eggjs/core-decorator'; import type { EggProtoImplClass, EggPrototypeInfo, EggPrototypeCreator, LoadUnit, EggPrototype, EggPrototypeLifecycleContext, } from '@eggjs/tegg-types'; import { EggPrototypeLifecycleUtil } from '../model/EggPrototype'; import { ClassProtoDescriptor } from '../model/ProtoDescriptor/ClassProtoDescriptor'; export class EggPrototypeCreatorFactory { private static creatorMap = new Map(); static registerPrototypeCreator(type: string, creator: EggPrototypeCreator) { this.creatorMap.set(type, creator); } static getPrototypeCreator(type: string): EggPrototypeCreator | undefined { return this.creatorMap.get(type); } static async createProto(clazz: EggProtoImplClass, loadUnit: LoadUnit): Promise { let properties: EggPrototypeInfo[] = []; const defaultQualifier = [{ attribute: InitTypeQualifierAttribute, value: PrototypeUtil.getInitType(clazz, { unitPath: loadUnit.unitPath, moduleName: loadUnit.name, })!, }, { attribute: LoadUnitNameQualifierAttribute, value: loadUnit.name, }]; if (PrototypeUtil.isEggMultiInstancePrototype(clazz)) { const multiInstanceProtoInfo = PrototypeUtil.getMultiInstanceProperty(clazz, { unitPath: loadUnit.unitPath, moduleName: loadUnit.name, })!; for (const obj of multiInstanceProtoInfo.objects) { defaultQualifier.forEach(qualifier => { if (!obj.qualifiers.find(t => t.attribute === qualifier.attribute)) { obj.qualifiers.push(qualifier); } }); properties.push({ name: obj.name, protoImplType: multiInstanceProtoInfo.protoImplType, initType: multiInstanceProtoInfo.initType, accessLevel: multiInstanceProtoInfo.accessLevel, qualifiers: obj.qualifiers, properQualifiers: obj.properQualifiers, className: multiInstanceProtoInfo.className, }); } } else { const property = PrototypeUtil.getProperty(clazz)!; if (!property.qualifiers) { property.qualifiers = []; } defaultQualifier.forEach(qualifier => { if (!property.qualifiers!.find(t => t.attribute === qualifier.attribute)) { property.qualifiers!.push(qualifier); } }); properties = [ property ]; } const protos: EggPrototype[] = []; for (const property of properties) { const creator = this.getPrototypeCreator(property.protoImplType); if (!creator) { throw new Error(`not found proto creator for type: ${property.protoImplType}`); } const ctx: EggPrototypeLifecycleContext = { clazz, loadUnit, prototypeInfo: property, }; const proto = creator(ctx); // TODO release egg prototype await EggPrototypeLifecycleUtil.objectPreCreate(ctx, proto); if (proto.init) { await proto.init(ctx); } await EggPrototypeLifecycleUtil.objectPostCreate(ctx, proto); PrototypeUtil.setClazzProto(clazz, proto); protos.push(proto); } return protos; } static async createProtoByDescriptor(protoDescriptor: ClassProtoDescriptor, loadUnit: LoadUnit): Promise { const creator = this.getPrototypeCreator(protoDescriptor.protoImplType); if (!creator) { throw new Error(`not found proto creator for type: ${protoDescriptor.protoImplType}`); } const ctx: EggPrototypeLifecycleContext = { clazz: protoDescriptor.clazz, loadUnit, prototypeInfo: protoDescriptor, }; const proto = creator(ctx); await EggPrototypeLifecycleUtil.objectPreCreate(ctx, proto); if (proto.init) { await proto.init(ctx); } await EggPrototypeLifecycleUtil.objectPostCreate(ctx, proto); PrototypeUtil.setClazzProto(protoDescriptor.clazz, proto); return proto; } } ================================================ FILE: core/metadata/src/factory/EggPrototypeFactory.ts ================================================ import { MapUtil } from '@eggjs/tegg-common-util'; import { AccessLevel } from '@eggjs/tegg-types'; import type { EggPrototypeName, EggPrototype, LoadUnit, QualifierInfo } from '@eggjs/tegg-types'; import { FrameworkErrorFormater } from 'egg-errors'; import { EggPrototypeNotFound, MultiPrototypeFound } from '../errors'; export class EggPrototypeFactory { public static instance = new EggPrototypeFactory(); // Map> private publicProtoMap: Map = new Map(); public registerPrototype(proto: EggPrototype, loadUnit: LoadUnit) { if (proto.accessLevel === AccessLevel.PUBLIC) { const protoList = MapUtil.getOrStore(this.publicProtoMap, proto.name, []); protoList.push(proto); } loadUnit.registerEggPrototype(proto); } public deletePrototype(proto: EggPrototype, loadUnit: LoadUnit) { if (proto.accessLevel === AccessLevel.PUBLIC) { const protos = this.publicProtoMap.get(proto.name); if (protos) { const index = protos.indexOf(proto); if (index !== -1) { protos.splice(index, 1); } } } loadUnit.deletePrototype(proto); } public getPrototype(name: PropertyKey, loadUnit?: LoadUnit, qualifiers?: QualifierInfo[]): EggPrototype { qualifiers = qualifiers || []; const protos = this.doGetPrototype(name, qualifiers, loadUnit); if (!protos.length) { throw FrameworkErrorFormater.formatError(new EggPrototypeNotFound(name, loadUnit?.id)); } if (protos.length === 1) { return protos[0]; } throw FrameworkErrorFormater.formatError(new MultiPrototypeFound(name, qualifiers)); } private doGetPrototype(name: EggPrototypeName, qualifiers: QualifierInfo[], loadUnit?: LoadUnit): EggPrototype[] { if (loadUnit) { // 1. find private proto in load unit const protos = loadUnit.getEggPrototype(name, qualifiers); if (protos.length) { return protos; } } // 2. find public proto in global const protos = this.publicProtoMap.get(name); return protos?.filter(proto => proto.verifyQualifiers(qualifiers)) || []; } } ================================================ FILE: core/metadata/src/factory/LoadUnitFactory.ts ================================================ import type { EggLoadUnitTypeLike, Id, LoadUnit, LoadUnitLifecycleContext, Loader, LoadUnitCreator, LoadUnitPair, } from '@eggjs/tegg-types'; import { LoadUnitLifecycleUtil } from '../model/LoadUnit'; export class LoadUnitFactory { private static loadUnitCreatorMap: Map = new Map(); private static loadUnitMap: Map = new Map(); private static loadUnitIdMap: Map = new Map(); protected static async getLoanUnit(ctx: LoadUnitLifecycleContext, type: EggLoadUnitTypeLike) { const creator = LoadUnitFactory.loadUnitCreatorMap.get(type); if (!creator) { throw new Error(`not find creator for load unit type ${type}`); } return await creator(ctx); } static async createLoadUnit(unitPath: string, type: EggLoadUnitTypeLike, loader: Loader): Promise { if (LoadUnitFactory.loadUnitMap.has(unitPath)) { return LoadUnitFactory.loadUnitMap.get(unitPath)!.loadUnit; } const ctx: LoadUnitLifecycleContext = { unitPath, loader, }; const loadUnit = await LoadUnitFactory.getLoanUnit(ctx, type); await LoadUnitLifecycleUtil.objectPreCreate(ctx, loadUnit); if (loadUnit.init) { await loadUnit.init(ctx); } await LoadUnitLifecycleUtil.objectPostCreate(ctx, loadUnit); LoadUnitFactory.loadUnitMap.set(unitPath, { loadUnit, ctx }); LoadUnitFactory.loadUnitIdMap.set(loadUnit.id, loadUnit); return loadUnit; } static async createPreloadLoadUnit(unitPath: string, type: EggLoadUnitTypeLike, loader: Loader): Promise { const ctx: LoadUnitLifecycleContext = { unitPath, loader, }; return await LoadUnitFactory.getLoanUnit(ctx, type); } static async destroyLoadUnit(loadUnit: LoadUnit) { const { ctx } = LoadUnitFactory.loadUnitMap.get(loadUnit.unitPath)!; try { await LoadUnitLifecycleUtil.objectPreDestroy(ctx, loadUnit); if (loadUnit.destroy) { await loadUnit.destroy(ctx); } } finally { LoadUnitFactory.loadUnitMap.delete(loadUnit.unitPath); LoadUnitFactory.loadUnitIdMap.delete(loadUnit.id); LoadUnitLifecycleUtil.clearObjectLifecycle(loadUnit); } } static getLoadUnitById(id: Id): LoadUnit | undefined { return LoadUnitFactory.loadUnitIdMap.get(id); } static registerLoadUnitCreator(type: EggLoadUnitTypeLike, creator: LoadUnitCreator) { LoadUnitFactory.loadUnitCreatorMap.set(type, creator); } } ================================================ FILE: core/metadata/src/impl/EggPrototypeBuilder.ts ================================================ import assert from 'node:assert'; import { InjectType, PrototypeUtil, QualifierAttribute, QualifierUtil } from '@eggjs/core-decorator'; import type { AccessLevel, EggProtoImplClass, EggPrototype, EggPrototypeLifecycleContext, EggPrototypeName, InjectConstructor, InjectObject, InjectObjectProto, LoadUnit, ObjectInitTypeLike, QualifierInfo, } from '@eggjs/tegg-types'; import { DEFAULT_PROTO_IMPL_TYPE, InitTypeQualifierAttribute, InjectConstructorProto, ObjectInitType, } from '@eggjs/tegg-types'; import { EggPrototypeFactory } from '../factory/EggPrototypeFactory'; import { IdenticalUtil } from '@eggjs/tegg-lifecycle'; import { EggPrototypeImpl } from './EggPrototypeImpl'; import { EggPrototypeCreatorFactory } from '../factory/EggPrototypeCreatorFactory'; import { EggPrototypeNotFound, MultiPrototypeFound } from '../errors'; export class EggPrototypeBuilder { private clazz: EggProtoImplClass; private name: EggPrototypeName; private initType: ObjectInitTypeLike; private accessLevel: AccessLevel; private filepath: string; private injectType: InjectType | undefined; private injectObjects: Array = []; private loadUnit: LoadUnit; private qualifiers: QualifierInfo[] = []; private properQualifiers: Record = {}; private className?: string; private multiInstanceConstructorIndex?: number; private multiInstanceConstructorAttributes?: QualifierAttribute[]; static create(ctx: EggPrototypeLifecycleContext): EggPrototype { const { clazz, loadUnit } = ctx; const filepath = PrototypeUtil.getFilePath(clazz); assert(filepath, 'not find filepath'); const builder = new EggPrototypeBuilder(); builder.clazz = clazz; builder.name = ctx.prototypeInfo.name; builder.className = ctx.prototypeInfo.className; builder.initType = ctx.prototypeInfo.initType; builder.accessLevel = ctx.prototypeInfo.accessLevel; builder.filepath = filepath!; builder.injectType = PrototypeUtil.getInjectType(clazz); builder.injectObjects = PrototypeUtil.getInjectObjects(clazz) || []; builder.loadUnit = loadUnit; builder.qualifiers = QualifierUtil.mergeQualifiers( QualifierUtil.getProtoQualifiers(clazz), (ctx.prototypeInfo.qualifiers ?? []), ); builder.properQualifiers = ctx.prototypeInfo.properQualifiers ?? {}; builder.multiInstanceConstructorIndex = PrototypeUtil.getMultiInstanceConstructorIndex(clazz); builder.multiInstanceConstructorAttributes = PrototypeUtil.getMultiInstanceConstructorAttributes(clazz); return builder.build(); } private tryFindDefaultPrototype(injectObject: InjectObject | InjectConstructor): EggPrototype { const propertyQualifiers = QualifierUtil.getProperQualifiers(this.clazz, injectObject.refName); const multiInstancePropertyQualifiers = this.properQualifiers[injectObject.refName as string] ?? []; return EggPrototypeFactory.instance.getPrototype(injectObject.objName, this.loadUnit, QualifierUtil.mergeQualifiers( propertyQualifiers, multiInstancePropertyQualifiers, )); } private tryFindContextPrototype(injectObject: InjectObject | InjectConstructor): EggPrototype { const propertyQualifiers = QualifierUtil.getProperQualifiers(this.clazz, injectObject.refName); const multiInstancePropertyQualifiers = this.properQualifiers[injectObject.refName as string] ?? []; return EggPrototypeFactory.instance.getPrototype(injectObject.objName, this.loadUnit, QualifierUtil.mergeQualifiers( propertyQualifiers, multiInstancePropertyQualifiers, [{ attribute: InitTypeQualifierAttribute, value: ObjectInitType.CONTEXT, }], )); } private tryFindSelfInitTypePrototype(injectObject: InjectObject | InjectConstructor): EggPrototype { const propertyQualifiers = QualifierUtil.getProperQualifiers(this.clazz, injectObject.refName); const multiInstancePropertyQualifiers = this.properQualifiers[injectObject.refName as string] ?? []; return EggPrototypeFactory.instance.getPrototype(injectObject.objName, this.loadUnit, QualifierUtil.mergeQualifiers( propertyQualifiers, multiInstancePropertyQualifiers, [{ attribute: InitTypeQualifierAttribute, value: this.initType, }], )); } private findInjectObjectPrototype(injectObject: InjectObject | InjectConstructor): EggPrototype { const propertyQualifiers = QualifierUtil.getProperQualifiers(this.clazz, injectObject.refName); try { return this.tryFindDefaultPrototype(injectObject); } catch (e) { if (!(e instanceof MultiPrototypeFound && !propertyQualifiers.find(t => t.attribute === InitTypeQualifierAttribute))) { throw e; } } try { return this.tryFindContextPrototype(injectObject); } catch (e) { if (!(e instanceof EggPrototypeNotFound)) { throw e; } } return this.tryFindSelfInitTypePrototype(injectObject); } public build(): EggPrototype { const injectObjectProtos: Array = []; for (const injectObject of this.injectObjects) { const propertyQualifiers = QualifierUtil.getProperQualifiers(this.clazz, injectObject.refName); try { const proto = this.findInjectObjectPrototype(injectObject); let injectObjectProto: InjectObjectProto | InjectConstructorProto; if (this.injectType === InjectType.PROPERTY) { injectObjectProto = { refName: injectObject.refName, objName: injectObject.objName, qualifiers: propertyQualifiers, proto, }; } else { injectObjectProto = { refIndex: (injectObject as InjectConstructor).refIndex, refName: injectObject.refName, objName: injectObject.objName, qualifiers: propertyQualifiers, proto, }; } if (injectObject.optional) { injectObject.optional = true; } injectObjectProtos.push(injectObjectProto); } catch (e) { if (e instanceof EggPrototypeNotFound && injectObject.optional) { continue; } throw e; } } const id = IdenticalUtil.createProtoId(this.loadUnit.id, this.name); return new EggPrototypeImpl( id, this.name, this.clazz, this.filepath, this.initType, this.accessLevel, injectObjectProtos, this.loadUnit.id, this.qualifiers, this.className, this.injectType, this.multiInstanceConstructorIndex, this.multiInstanceConstructorAttributes, ); } } EggPrototypeCreatorFactory.registerPrototypeCreator(DEFAULT_PROTO_IMPL_TYPE, EggPrototypeBuilder.create); ================================================ FILE: core/metadata/src/impl/EggPrototypeImpl.ts ================================================ import { InjectType, MetadataUtil, QualifierAttribute } from '@eggjs/core-decorator'; import type { AccessLevel, EggProtoImplClass, EggPrototype, EggPrototypeName, Id, InjectConstructorProto, InjectObjectProto, MetaDataKey, ObjectInitTypeLike, QualifierInfo, QualifierValue, } from '@eggjs/tegg-types'; export class EggPrototypeImpl implements EggPrototype { private readonly clazz: EggProtoImplClass; private readonly qualifiers: QualifierInfo[]; readonly filepath: string; readonly id: string; readonly name: EggPrototypeName; readonly initType: ObjectInitTypeLike; readonly accessLevel: AccessLevel; readonly injectObjects: Array; readonly injectType: InjectType; readonly loadUnitId: Id; readonly className?: string; readonly multiInstanceConstructorIndex?: number; readonly multiInstanceConstructorAttributes?: QualifierAttribute[]; constructor( id: string, name: EggPrototypeName, clazz: EggProtoImplClass, filepath: string, initType: ObjectInitTypeLike, accessLevel: AccessLevel, injectObjectMap: Array, loadUnitId: Id, qualifiers: QualifierInfo[], className?: string, injectType?: InjectType, multiInstanceConstructorIndex?: number, multiInstanceConstructorAttributes?: QualifierAttribute[], ) { this.id = id; this.clazz = clazz; this.name = name; this.filepath = filepath; this.initType = initType; this.accessLevel = accessLevel; this.injectObjects = injectObjectMap; this.loadUnitId = loadUnitId; this.qualifiers = qualifiers; this.className = className; this.injectType = injectType || InjectType.PROPERTY; this.multiInstanceConstructorIndex = multiInstanceConstructorIndex; this.multiInstanceConstructorAttributes = multiInstanceConstructorAttributes; } verifyQualifiers(qualifiers: QualifierInfo[]): boolean { for (const qualifier of qualifiers) { if (!this.verifyQualifier(qualifier)) { return false; } } return true; } verifyQualifier(qualifier: QualifierInfo): boolean { const selfQualifiers = this.qualifiers.find(t => t.attribute === qualifier.attribute); return selfQualifiers?.value === qualifier.value; } getQualifier(attribute: string): QualifierValue | undefined { return this.qualifiers.find(t => t.attribute === attribute)?.value; } constructEggObject(...args: any): object { return Reflect.construct(this.clazz, args); } getMetaData(metadataKey: MetaDataKey): T | undefined { return MetadataUtil.getMetaData(metadataKey, this.clazz); } } ================================================ FILE: core/metadata/src/impl/LoadUnitMultiInstanceProtoHook.ts ================================================ import { PrototypeUtil } from '@eggjs/core-decorator'; import type { EggProtoImplClass, LifecycleHook, LoadUnit, LoadUnitLifecycleContext } from '@eggjs/tegg-types'; // import { EggPrototypeCreatorFactory } from '../factory/EggPrototypeCreatorFactory'; // import { EggPrototypeFactory } from '../factory/EggPrototypeFactory'; export class LoadUnitMultiInstanceProtoHook implements LifecycleHook { static multiInstanceClazzSet: Set = new Set(); static setAllClassList(clazzList: readonly EggProtoImplClass[]) { for (const clazz of clazzList) { if (PrototypeUtil.isEggMultiInstancePrototype(clazz)) { this.multiInstanceClazzSet.add(clazz); } } } static clear() { this.multiInstanceClazzSet.clear(); } async preCreate(): Promise { // ... } } ================================================ FILE: core/metadata/src/impl/ModuleLoadUnit.ts ================================================ import assert from 'node:assert'; import path from 'node:path'; import { EggLoadUnitType, GraphNodeObj, InitTypeQualifierAttribute, ObjectInitTypeLike, } from '@eggjs/tegg-types'; import type { EggProtoImplClass, EggPrototype, EggPrototypeName, LoadUnit, LoadUnitLifecycleContext, QualifierInfo, } from '@eggjs/tegg-types'; import { Graph, GraphNode, MapUtil } from '@eggjs/tegg-common-util'; import { IdenticalUtil, LifecycleUtil } from '@eggjs/tegg-lifecycle'; import { EggPrototypeFactory } from '../factory/EggPrototypeFactory'; import { LoadUnitFactory } from '../factory/LoadUnitFactory'; import { EggPrototypeCreatorFactory } from '../factory/EggPrototypeCreatorFactory'; import { MultiPrototypeFound } from '../errors'; import { FrameworkErrorFormater } from 'egg-errors'; import { PrototypeUtil, QualifierUtil } from '@eggjs/core-decorator'; import { ClassProtoDescriptor } from '../model/ProtoDescriptor/ClassProtoDescriptor'; import { GlobalGraph } from '../model/graph/GlobalGraph'; let id = 0; // TODO del ModuleGraph in next major version class ProtoNode implements GraphNodeObj { readonly clazz: EggProtoImplClass; readonly name: EggPrototypeName; readonly id: string; readonly qualifiers: QualifierInfo[]; readonly initType: ObjectInitTypeLike; private PrototypeUtil: any; constructor( clazz: EggProtoImplClass, objName: EggPrototypeName, initType: ObjectInitTypeLike, qualifiers: QualifierInfo[], ) { this.name = objName; this.id = '' + (id++); this.clazz = clazz; this.qualifiers = qualifiers; this.initType = initType; } verifyQualifiers(qualifiers: QualifierInfo[]): boolean { for (const qualifier of qualifiers) { if (!this.verifyQualifier(qualifier)) { return false; } } return true; } verifyQualifier(qualifier: QualifierInfo): boolean { const selfQualifiers = this.qualifiers.find(t => t.attribute === qualifier.attribute); return selfQualifiers?.value === qualifier.value; } toString(): string { return `${this.clazz.name}@${this.PrototypeUtil.getFilePath(this.clazz)}`; } } export class ModuleGraph { private graph: Graph; clazzList: EggProtoImplClass[]; readonly unitPath: string; readonly name: string; constructor(clazzList: EggProtoImplClass[], unitPath: string, name: string) { this.clazzList = clazzList; this.graph = new Graph(); this.unitPath = unitPath; this.name = name; this.build(); } private findInjectNode(objName: EggPrototypeName, qualifiers: QualifierInfo[], parentInitTye: ObjectInitTypeLike): GraphNode | undefined { let nodes = Array.from(this.graph.nodes.values()) .filter(t => t.val.name === objName) .filter(t => t.val.verifyQualifiers(qualifiers)); if (nodes.length === 0) { return undefined; } if (nodes.length === 1) { return nodes[0]; } const initTypeQualifier = { attribute: InitTypeQualifierAttribute, value: parentInitTye, }; nodes = nodes.filter(t => t.val.verifyQualifiers([ initTypeQualifier ])); if (nodes.length === 1) { return nodes[0]; } const temp: Map> = new Map(); for (const node of nodes) { temp.set(node.val.clazz, node); } nodes = Array.from(temp.values()); if (nodes.length === 1) { return nodes[0]; } const result = nodes.map(node => node.val.toString()); throw FrameworkErrorFormater.formatError(new MultiPrototypeFound(String(objName), qualifiers, JSON.stringify(result))); } private build() { const protoGraphNodes: GraphNode[] = []; for (const clazz of this.clazzList) { if (PrototypeUtil.isEggMultiInstancePrototype(clazz)) { const properties = PrototypeUtil.getMultiInstanceProperty(clazz, { unitPath: this.unitPath, moduleName: this.name, }); if (properties) { const qualifiers = QualifierUtil.getProtoQualifiers(clazz); for (const obj of properties.objects || []) { const instanceQualifiers = [ ...qualifiers, ...obj.qualifiers, ]; protoGraphNodes.push(new GraphNode(new ProtoNode(clazz, obj.name, properties.initType, instanceQualifiers))); } } } else { const qualifiers = QualifierUtil.getProtoQualifiers(clazz); const property = PrototypeUtil.getProperty(clazz); if (property) { protoGraphNodes.push(new GraphNode(new ProtoNode(clazz, property.name, property.initType, qualifiers))); } } } for (const node of protoGraphNodes) { if (!this.graph.addVertex(node)) { throw new Error(`duplicate proto: ${node.val.id}`); } } for (const node of protoGraphNodes) { if (PrototypeUtil.isEggMultiInstancePrototype(node.val.clazz)) { const property = PrototypeUtil.getMultiInstanceProperty(node.val.clazz, { moduleName: this.name, unitPath: this.unitPath, }); for (const objectInfo of property?.objects || []) { const injectObjects = PrototypeUtil.getInjectObjects(node.val.clazz); for (const injectObject of injectObjects) { const qualifiers = [ ...QualifierUtil.getProperQualifiers(node.val.clazz, injectObject.refName), ...objectInfo.properQualifiers?.[injectObject.refName] ?? [], ]; const injectNode = this.findInjectNode(injectObject.objName, qualifiers, node.val.initType); // If not found maybe in other module if (injectNode) { this.graph.addEdge(node, injectNode); } } } } else { const injectObjects = PrototypeUtil.getInjectObjects(node.val.clazz); for (const injectObject of injectObjects) { const qualifiers = QualifierUtil.getProperQualifiers(node.val.clazz, injectObject.refName); const injectNode = this.findInjectNode(injectObject.objName, qualifiers, node.val.initType); // If not found maybe in other module if (injectNode) { this.graph.addEdge(node, injectNode); } } } } } sort() { const loopPath = this.graph.loopPath(); if (loopPath) { throw new Error('proto has recursive deps: ' + loopPath); } const clazzSet = new Set(); for (const clazz of this.graph.sort()) { clazzSet.add(clazz.val.clazz); } this.clazzList = Array.from(clazzSet); } } export class ModuleLoadUnit implements LoadUnit { // private loader: Loader; private protoMap: Map = new Map(); private protos: ClassProtoDescriptor[]; private clazzList: EggProtoImplClass[]; readonly id: string; readonly name: string; readonly unitPath: string; readonly type = EggLoadUnitType.MODULE; get globalGraph(): GlobalGraph { return GlobalGraph.instance!; } constructor(name: string, unitPath: string) { this.id = IdenticalUtil.createLoadUnitId(name); this.name = name; this.unitPath = unitPath; } private doLoadClazz() { const protos = this.globalGraph.moduleProtoDescriptorMap.get(this.name); if (protos) { // TODO ModuleLoadUnit should support all proto descriptor this.protos = protos!.filter(t => ClassProtoDescriptor.isClassProtoDescriptor(t)); this.clazzList = this.protos.map(t => t.clazz); } else { this.protos = []; this.clazzList = []; } } private loadClazz() { if (!this.clazzList) { this.doLoadClazz(); } } async preLoad() { this.loadClazz(); for (const protoClass of this.clazzList) { // TODO refactor lifecycle hook to ProtoDescriptor or EggPrototype // ModuleLoadUnit should not use clazz list const fnName = LifecycleUtil.getStaticLifecycleHook('preLoad', protoClass); if (fnName) { await protoClass[fnName]?.(); } } } async init() { this.loadClazz(); for (const protoDescriptor of this.protos) { const proto = await EggPrototypeCreatorFactory.createProtoByDescriptor(protoDescriptor, this); EggPrototypeFactory.instance.registerPrototype(proto, this); } } containPrototype(proto: EggPrototype): boolean { return !!(this.protoMap.get(proto.name)?.find(t => t === proto)); } getEggPrototype(name: string, qualifiers: QualifierInfo[]): EggPrototype[] { const protos = this.protoMap.get(name); return protos?.filter(proto => proto.verifyQualifiers(qualifiers)) || []; } registerEggPrototype(proto: EggPrototype) { const protoList = MapUtil.getOrStore(this.protoMap, proto.name, []); protoList.push(proto); } deletePrototype(proto: EggPrototype) { const protos = this.protoMap.get(proto.name); if (protos) { const index = protos.indexOf(proto); if (index !== -1) { protos.splice(index, 1); } } } async destroy() { for (const namedProtoMap of this.protoMap.values()) { for (const proto of namedProtoMap.slice()) { EggPrototypeFactory.instance.deletePrototype(proto, this); } } this.protoMap.clear(); } iterateEggPrototype(): IterableIterator { const protos: EggPrototype[] = Array.from(this.protoMap.values()) .reduce((p, c) => { p = p.concat(c); return p; }, []); return protos.values(); } static createModule(ctx: LoadUnitLifecycleContext): ModuleLoadUnit { const pkgPath = path.join(ctx.unitPath, 'package.json'); // eslint-disable-next-line @typescript-eslint/no-var-requires const pkg = require(pkgPath); assert(pkg.eggModule, `module config not found in package ${pkgPath}`); const { name } = pkg.eggModule; return new ModuleLoadUnit(name, ctx.unitPath); } } LoadUnitFactory.registerLoadUnitCreator(EggLoadUnitType.MODULE, ModuleLoadUnit.createModule); ================================================ FILE: core/metadata/src/model/AppGraph.ts ================================================ import assert from 'node:assert'; import util from 'node:util'; import { Graph, GraphNode, ModuleConfigUtil } from '@eggjs/tegg-common-util'; import { PrototypeUtil, QualifierUtil } from '@eggjs/core-decorator'; import { AccessLevel, INIT_TYPE_TRY_ORDER, InitTypeQualifierAttribute, LoadUnitNameQualifierAttribute } from '@eggjs/tegg-types'; import type { EggProtoImplClass, EggPrototypeName, GraphNodeObj, ModuleReference, QualifierInfo } from '@eggjs/tegg-types'; export interface InstanceClazzMeta { name: PropertyKey; qualifiers: QualifierInfo[]; properQualifiers: Record; accessLevel: AccessLevel, instanceModule: GraphNode; ownerModule: GraphNode; } export type ClazzMetaMap = Record; function verifyQualifier(clazzQualifiers: QualifierInfo[], qualifier: QualifierInfo) { const selfQualifiers = clazzQualifiers.find(t => t.attribute === qualifier.attribute); return selfQualifiers?.value === qualifier.value; } function verifyQualifiers(clazzQualifiers: QualifierInfo[], qualifiers: QualifierInfo[]) { for (const qualifier of qualifiers) { if (!verifyQualifier(clazzQualifiers, qualifier)) { return false; } } return true; } export class ClazzMap { private clazzMap: ClazzMetaMap; constructor(graph: Graph) { this.build(graph); } private build(graph: Graph) { /** * 1. iterate all module get all MultiInstanceClazz * 2. iterate MultiInstanceClazz and all module get object meta * 3. iterate object meta and build clazz map */ const clazzMap: ClazzMetaMap = {}; for (const ownerNode of graph.nodes.values()) { for (const clazz of ownerNode.val.getClazzList()) { const qualifiers = QualifierUtil.getProtoQualifiers(clazz); if (PrototypeUtil.isEggMultiInstancePrototype(clazz)) { for (const instanceNode of graph.nodes.values()) { const property = PrototypeUtil.getMultiInstanceProperty(clazz, { unitPath: instanceNode.val.moduleConfig.path, moduleName: instanceNode.val.moduleConfig.name, }); assert(property, `multi instance property not found for ${clazz.name}`); for (const info of property.objects) { const instanceQualifiers = [ ...qualifiers, ...info.qualifiers, ]; clazzMap[info.name] = clazzMap[info.name] || []; clazzMap[info.name].push({ name: info.name, accessLevel: PrototypeUtil.getAccessLevel(clazz, { unitPath: instanceNode.val.moduleConfig.path, moduleName: instanceNode.val.moduleConfig.name, }) as AccessLevel, qualifiers: instanceQualifiers, properQualifiers: info.properQualifiers || {}, instanceModule: instanceNode, ownerModule: ownerNode, }); } } } else { const property = PrototypeUtil.getProperty(clazz); assert(property, `property not found for ${clazz.name}`); clazzMap[property.name] = clazzMap[property.name] || []; clazzMap[property.name].push({ name: property.name, accessLevel: PrototypeUtil.getAccessLevel(clazz, { unitPath: ownerNode.val.moduleConfig.path, moduleName: ownerNode.val.moduleConfig.name, }) as AccessLevel, qualifiers, properQualifiers: {}, ownerModule: ownerNode, instanceModule: ownerNode, }); } } } this.clazzMap = clazzMap; } findDependencyModule(objName: EggPrototypeName, properQualifiers: QualifierInfo[], intoModule: GraphNode): GraphNode[] { const result: Set> = new Set(); const objInfo = this.clazzMap[objName]; if (!objInfo) { return []; } let mayObjs = objInfo.filter(obj => { // 1. check accessLevel if (obj.instanceModule !== intoModule && obj.accessLevel === AccessLevel.PRIVATE) { return false; } // 2. check qualifier return verifyQualifiers(obj.qualifiers, properQualifiers); }); // 3. auto set init type qualifier if (mayObjs.length > 1) { const initTypeQualifiers = INIT_TYPE_TRY_ORDER.map(type => ({ attribute: InitTypeQualifierAttribute, value: type, })); for (const initTypeQualifier of initTypeQualifiers) { const mayInitTypeObjs = mayObjs.filter(obj => { return verifyQualifiers(obj.qualifiers, [ ...properQualifiers, initTypeQualifier, ]); }); if (mayInitTypeObjs.length > 0) { mayObjs = mayInitTypeObjs; } } } // 4. auto set load unit name qualifier if (mayObjs.length > 1) { const moduleNameQualifiers = { attribute: LoadUnitNameQualifierAttribute, value: intoModule.val.name, }; const mayLoadUnitNameObjs = mayObjs.filter(obj => { return verifyQualifiers(obj.qualifiers, [ ...properQualifiers, moduleNameQualifiers, ]); }); if (mayLoadUnitNameObjs.length > 0) { mayObjs = mayLoadUnitNameObjs; } } if (mayObjs.length > 1) { const message = util.format('multi class found for %s@%o in module %j', objName, properQualifiers, mayObjs.map(t => { return t.instanceModule.val.moduleConfig.path; })); throw new Error(message); } for (const obj of mayObjs) { result.add(obj.instanceModule); // result.add(obj.ownerModule); } return Array.from(result); } } export class ModuleNode implements GraphNodeObj { readonly id: string; readonly name: string; readonly moduleConfig: ModuleReference; private readonly clazzList: EggProtoImplClass[]; constructor(moduleConfig: ModuleReference) { this.moduleConfig = moduleConfig; this.id = moduleConfig.path; this.name = ModuleConfigUtil.readModuleNameSync(moduleConfig.path); this.clazzList = []; } addClazz(clazz: EggProtoImplClass) { if (!this.clazzList.includes(clazz)) { this.clazzList.push(clazz); } if (!PrototypeUtil.isEggMultiInstancePrototype(clazz)) { const defaultQualifier = [{ attribute: InitTypeQualifierAttribute, value: PrototypeUtil.getInitType(clazz, { unitPath: this.moduleConfig.path, moduleName: this.moduleConfig.name, })!, }, { attribute: LoadUnitNameQualifierAttribute, value: this.name, }]; for (const qualifier of defaultQualifier) { QualifierUtil.addProtoQualifier(clazz, qualifier.attribute, qualifier.value); } } } toString() { return `${this.name}@${this.moduleConfig.path}`; } getClazzList(): readonly EggProtoImplClass[] { return this.clazzList; } } export class AppGraph { private graph: Graph; private clazzMap: ClazzMap; moduleConfigList: Array; constructor() { this.graph = new Graph(); } addNode(moduleNode: ModuleNode) { if (!this.graph.addVertex(new GraphNode(moduleNode))) { throw new Error(`duplicate module: ${moduleNode}`); } } getClazzList(): readonly EggProtoImplClass[] { const clazzSet = new Set(); for (const node of this.graph.nodes.values()) { for (const clazz of node.val.getClazzList()) { clazzSet.add(clazz); } } return Array.from(clazzSet); } build() { this.clazzMap = new ClazzMap(this.graph); // 1. iterate all modules for (const node of this.graph.nodes.values()) { // 2. iterate all class for (const clazz of node.val.getClazzList()) { const injectObjects = PrototypeUtil.getInjectObjects(clazz); // 3. iterate all inject objects for (const injectObject of injectObjects) { if (PrototypeUtil.isEggMultiInstancePrototype(clazz)) { for (const instanceNode of this.graph.nodes.values()) { const property = PrototypeUtil.getMultiInstanceProperty(clazz, { unitPath: instanceNode.val.moduleConfig.path, moduleName: instanceNode.val.moduleConfig.name, }); for (const info of property?.objects || []) { const properQualifiers = [ ...QualifierUtil.getProperQualifiers(clazz, injectObject.refName), ...info.properQualifiers?.[injectObject.refName] ?? [], ]; // 4. find dependency module const dependencyModules = this.clazzMap.findDependencyModule(injectObject.objName, properQualifiers, node); for (const moduleNode of dependencyModules) { // 5. add edge if (instanceNode !== moduleNode) { this.graph.addEdge(instanceNode, moduleNode); } } } } } else { const properQualifiers = [ ...QualifierUtil.getProperQualifiers(clazz, injectObject.refName), ]; // 4. find dependency module const dependencyModules = this.clazzMap.findDependencyModule(injectObject.objName, properQualifiers, node); for (const moduleNode of dependencyModules) { // 5. add edge if (node !== moduleNode) { this.graph.addEdge(node, moduleNode); } } } } } } } sort() { const loopPath = this.graph.loopPath(); if (loopPath) { throw new Error('module has recursive deps: ' + loopPath); } this.moduleConfigList = this.graph.sort() .filter(t => { return t.val.moduleConfig.optional !== true || t.fromNodeMap.size > 0; }) .map(t => t.val.moduleConfig); } } ================================================ FILE: core/metadata/src/model/EggPrototype.ts ================================================ import { LifecycleUtil } from '@eggjs/tegg-lifecycle'; import type { EggPrototype, EggPrototypeLifecycleContext } from '@eggjs/tegg-types'; export const EggPrototypeLifecycleUtil = new LifecycleUtil(); ================================================ FILE: core/metadata/src/model/LoadUnit.ts ================================================ import { LifecycleUtil } from '@eggjs/tegg-lifecycle'; import type { LoadUnit, LoadUnitLifecycleContext } from '@eggjs/tegg-types'; export const LoadUnitLifecycleUtil = new LifecycleUtil(); ================================================ FILE: core/metadata/src/model/ModuleDescriptor.ts ================================================ import path from 'node:path'; import fs from 'node:fs/promises'; import { EggProtoImplClass, ProtoDescriptor } from '@eggjs/tegg-types'; import { PrototypeUtil } from '@eggjs/core-decorator'; const DUMP_PATH = process.env.MODULE_DUMP_PATH; export interface ModuleDescriptor { name: string; unitPath: string; optional?: boolean; clazzList: EggProtoImplClass[]; multiInstanceClazzList: EggProtoImplClass[]; protos: ProtoDescriptor[]; } export interface ModuleDumpOptions { dumpDir?: string; } export class ModuleDescriptorDumper { static stringifyDescriptor(moduleDescriptor: ModuleDescriptor): string { return '{' + `"name": "${moduleDescriptor.name}",` + `"unitPath": "${moduleDescriptor.unitPath}",` + (typeof moduleDescriptor.optional !== 'undefined' ? `"optional": ${moduleDescriptor.optional},` : '') + `"clazzList": [${moduleDescriptor.clazzList.map(t => { return ModuleDescriptorDumper.stringifyClazz(t, moduleDescriptor); }).join(',')}],` + `"multiInstanceClazzList": [${moduleDescriptor.multiInstanceClazzList.map(t => { return ModuleDescriptorDumper.stringifyClazz(t, moduleDescriptor); }).join(',')}],` + `"protos": [${moduleDescriptor.protos.map(t => { return JSON.stringify(t); }).join(',')}]` + '}'; } static stringifyClazz(clazz: EggProtoImplClass, moduleDescriptor: ModuleDescriptor): string { return '{' + `"name": "${clazz.name}",` + (PrototypeUtil.getFilePath(clazz) ? `"filePath": "${path.relative(moduleDescriptor.unitPath, PrototypeUtil.getFilePath(clazz)!)}"` : '') + '}'; } static dumpPath(desc: ModuleDescriptor, options?: ModuleDumpOptions) { const dumpDir = DUMP_PATH ?? options?.dumpDir ?? desc.unitPath; return path.join(dumpDir, '.egg', `${desc.name}_module_desc.json`); } static async dump(desc: ModuleDescriptor, options?: ModuleDumpOptions) { const dumpPath = ModuleDescriptorDumper.dumpPath(desc, options); await fs.mkdir(path.dirname(dumpPath), { recursive: true }); await fs.writeFile(dumpPath, ModuleDescriptorDumper.stringifyDescriptor(desc)); } } ================================================ FILE: core/metadata/src/model/ProtoDescriptor/AbstractProtoDescriptor.ts ================================================ import { AccessLevel, InjectObjectDescriptor, ObjectInitTypeLike, ProtoDescriptor, ProtoDescriptorTypeLike, QualifierInfo, } from '@eggjs/tegg-types'; export interface AbstractProtoDescriptorOptions { name: PropertyKey; accessLevel: AccessLevel; initType: ObjectInitTypeLike; qualifiers: QualifierInfo[]; protoImplType: string; injectObjects: InjectObjectDescriptor[]; defineModuleName: string; defineUnitPath: string; instanceModuleName: string; instanceDefineUnitPath: string; type: ProtoDescriptorTypeLike; properQualifiers: Record; } export abstract class AbstractProtoDescriptor implements ProtoDescriptor { name: PropertyKey; accessLevel: AccessLevel; initType: ObjectInitTypeLike; qualifiers: QualifierInfo[]; injectObjects: InjectObjectDescriptor[]; protoImplType: string; defineModuleName: string; defineUnitPath: string; instanceModuleName: string; instanceDefineUnitPath: string; className?: string; properQualifiers: Record; type: ProtoDescriptorTypeLike; protected constructor(options: AbstractProtoDescriptorOptions) { this.name = options.name; this.accessLevel = options.accessLevel; this.initType = options.initType; this.qualifiers = options.qualifiers; this.protoImplType = options.protoImplType; this.injectObjects = options.injectObjects; this.defineModuleName = options.defineModuleName; this.defineUnitPath = options.defineUnitPath; this.instanceModuleName = options.instanceModuleName; this.instanceDefineUnitPath = options.instanceDefineUnitPath; this.type = options.type; this.properQualifiers = options.properQualifiers; } abstract equal(protoDescriptor: ProtoDescriptor): boolean; } ================================================ FILE: core/metadata/src/model/ProtoDescriptor/ClassProtoDescriptor.ts ================================================ import { QualifierUtil } from '@eggjs/core-decorator'; import { AbstractProtoDescriptor, AbstractProtoDescriptorOptions } from './AbstractProtoDescriptor'; import { EggProtoImplClass, ProtoDescriptor } from '@eggjs/tegg-types'; import { ProtoDescriptorType } from '@eggjs/tegg-types/metadata/enum/ProtoDescriptorType'; export interface ClassProtoDescriptorOptions extends Omit { clazz: EggProtoImplClass; } export class ClassProtoDescriptor extends AbstractProtoDescriptor { clazz: EggProtoImplClass; clazzName: string; static isClassProtoDescriptor(descriptor: ProtoDescriptor): descriptor is ClassProtoDescriptor { return (descriptor as AbstractProtoDescriptor).type === ProtoDescriptorType.CLASS; } constructor(options: ClassProtoDescriptorOptions) { super({ type: ProtoDescriptorType.CLASS, ...options, }); this.clazz = options.clazz; this.className = this.clazz.name; } equal(protoDescriptor: ProtoDescriptor): boolean { if (!ClassProtoDescriptor.isClassProtoDescriptor(protoDescriptor)) { return false; } return this.clazz === protoDescriptor.clazz && this.name === protoDescriptor.name && this.accessLevel === protoDescriptor.accessLevel && this.initType === protoDescriptor.initType && this.instanceModuleName === protoDescriptor.instanceModuleName && QualifierUtil.equalQualifiers(this.qualifiers, protoDescriptor.qualifiers); } } ================================================ FILE: core/metadata/src/model/ProtoDescriptorHelper.ts ================================================ import { EggMultiInstancePrototypeInfo, PrototypeUtil, QualifierUtil, } from '@eggjs/core-decorator'; import { EggProtoImplClass, InitTypeQualifierAttribute, InjectObjectDescriptor, LoadUnitNameQualifierAttribute, ObjectInitTypeLike, ProtoDescriptor, QualifierInfo, AccessLevel, MultiInstancePrototypeGetObjectsContext, MultiInstanceType, } from '@eggjs/tegg-types'; import assert from 'node:assert'; import { ProtoSelectorContext } from './graph/ProtoSelector'; import { ClassProtoDescriptor } from './ProtoDescriptor/ClassProtoDescriptor'; export class ProtoDescriptorHelper { static addDefaultQualifier(qualifiers: QualifierInfo[], initType: ObjectInitTypeLike, loadUnitName: string): QualifierInfo[] { const defaultQualifiers = [{ attribute: InitTypeQualifierAttribute, value: initType, }, { attribute: LoadUnitNameQualifierAttribute, value: loadUnitName, }]; const res = [ ...qualifiers, ]; for (const defaultQualifier of defaultQualifiers) { if (!qualifiers.find(t => t.attribute === defaultQualifier.attribute)) { res.push(defaultQualifier); } } return res; } static createByMultiInstanceClazz(clazz: EggProtoImplClass, options: { defineModuleName: string; defineUnitPath: string; instanceModuleName: string; instanceDefineUnitPath: string; }): ProtoDescriptor[] { assert(PrototypeUtil.isEggMultiInstancePrototype(clazz), `clazz ${clazz.name} is not MultiInstancePrototype`); const type = PrototypeUtil.getEggMultiInstancePrototypeType(clazz); if (type === MultiInstanceType.DYNAMIC) { return ProtoDescriptorHelper.createByDynamicMultiInstanceClazz(clazz, options); } else if (type === MultiInstanceType.STATIC) { // static multi instance proto create only in self module if (options.defineModuleName === options.instanceModuleName) { return ProtoDescriptorHelper.createByStaticMultiInstanceClazz(clazz, options); } } return []; } static createByDynamicMultiInstanceClazz(clazz: EggProtoImplClass, options: { defineModuleName: string; defineUnitPath: string; instanceModuleName: string; instanceDefineUnitPath: string; }): ProtoDescriptor[] { assert(PrototypeUtil.isEggMultiInstancePrototype(clazz), `clazz ${clazz.name} is not MultiInstancePrototype`); const instanceProperty = PrototypeUtil.getDynamicMultiInstanceProperty(clazz, { moduleName: options.instanceModuleName, unitPath: options.instanceDefineUnitPath, }); assert(instanceProperty, `not found PrototypeInfo for clazz ${clazz.name}`); return ProtoDescriptorHelper.#createByMultiInstanceClazz(clazz, instanceProperty, options); } static createByStaticMultiInstanceClazz(clazz: EggProtoImplClass, options: { defineModuleName: string; defineUnitPath: string; instanceModuleName: string; instanceDefineUnitPath: string; }): ProtoDescriptor[] { assert(PrototypeUtil.isEggMultiInstancePrototype(clazz), `clazz ${clazz.name} is not MultiInstancePrototype`); const instanceProperty = PrototypeUtil.getStaticMultiInstanceProperty(clazz); assert(instanceProperty, `not found PrototypeInfo for clazz ${clazz.name}`); return ProtoDescriptorHelper.#createByMultiInstanceClazz(clazz, instanceProperty, options); } static #createByMultiInstanceClazz(clazz: EggProtoImplClass, instanceProperty: EggMultiInstancePrototypeInfo, options: { defineModuleName: string; defineUnitPath: string; instanceModuleName: string; instanceDefineUnitPath: string; }): ProtoDescriptor[] { const res: ProtoDescriptor[] = []; for (const obj of instanceProperty.objects) { let qualifiers = QualifierUtil.mergeQualifiers( QualifierUtil.getProtoQualifiers(clazz), obj.qualifiers, ); qualifiers = ProtoDescriptorHelper.addDefaultQualifier(qualifiers, instanceProperty.initType, options.instanceModuleName); const injectObjects: InjectObjectDescriptor[] = PrototypeUtil.getInjectObjects(clazz) .map(t => { const qualifiers = QualifierUtil.getProperQualifiers(clazz, t.refName); const instanceQualifier = obj.properQualifiers?.[t.refName] ?? []; return { ...t, qualifiers: QualifierUtil.mergeQualifiers( qualifiers, instanceQualifier, ), }; }); res.push(new ClassProtoDescriptor({ name: obj.name, accessLevel: instanceProperty.accessLevel, initType: instanceProperty.initType, protoImplType: instanceProperty.protoImplType, qualifiers, injectObjects, instanceModuleName: options.instanceModuleName, instanceDefineUnitPath: options.instanceDefineUnitPath, defineModuleName: options.defineModuleName, defineUnitPath: options.defineUnitPath, clazz, properQualifiers: obj.properQualifiers || {}, })); } return res; } static createByInstanceClazz(clazz: EggProtoImplClass, ctx: MultiInstancePrototypeGetObjectsContext): ProtoDescriptor { assert(PrototypeUtil.isEggPrototype(clazz), `clazz ${clazz.name} is not EggPrototype`); assert(!PrototypeUtil.isEggMultiInstancePrototype(clazz), `clazz ${clazz.name} is not Prototype`); const property = PrototypeUtil.getProperty(clazz); assert(property, `not found PrototypeInfo for clazz ${clazz.name}`); const protoQualifiers = ProtoDescriptorHelper.addDefaultQualifier(QualifierUtil.getProtoQualifiers(clazz), property.initType, ctx.moduleName); const injectObjects = PrototypeUtil.getInjectObjects(clazz) .map(t => { const qualifiers = QualifierUtil.getProperQualifiers(clazz, t.refName); return { ...t, qualifiers, }; }); return new ClassProtoDescriptor({ name: property.name, protoImplType: property.protoImplType, accessLevel: property.accessLevel, initType: property.initType, qualifiers: protoQualifiers, injectObjects, instanceDefineUnitPath: ctx.unitPath, instanceModuleName: ctx.moduleName, defineUnitPath: ctx.unitPath, defineModuleName: ctx.moduleName, clazz, properQualifiers: {}, }); } static selectProto(proto: ProtoDescriptor, ctx: ProtoSelectorContext): boolean { // 1. name match if (proto.name !== ctx.name) { return false; } // 2. access level match if (proto.accessLevel !== AccessLevel.PUBLIC && proto.instanceModuleName !== ctx.moduleName) { return false; } // 3. qualifier match if (!QualifierUtil.matchQualifiers(proto.qualifiers, ctx.qualifiers)) { return false; } return true; } } ================================================ FILE: core/metadata/src/model/graph/GlobalGraph.ts ================================================ import { Graph, GraphNode, ModuleReference } from '@eggjs/tegg-common-util'; import { InitTypeQualifierAttribute, InjectObjectDescriptor, LoadUnitNameQualifierAttribute, ObjectInitType, ProtoDescriptor, QualifierInfo, } from '@eggjs/tegg-types'; import { ModuleDependencyMeta, GlobalModuleNode } from './GlobalModuleNode'; import { ProtoDependencyMeta, ProtoNode } from './ProtoNode'; import { FrameworkErrorFormater } from 'egg-errors'; import { EggPrototypeNotFound, MultiPrototypeFound } from '../../errors'; import { GlobalModuleNodeBuilder } from './GlobalModuleNodeBuilder'; import { ModuleDescriptor } from '../ModuleDescriptor'; import { QualifierUtil } from '@eggjs/core-decorator'; export interface GlobalGraphOptions { // TODO next major version refactor to force strict // all proto should be load before build global graph strict?: boolean; } export type GlobalGraphBuildHook = (globalGraph: GlobalGraph) => void; /** * Sort all prototypes and modules in app. * - 1. LoaderFactory.loadApp: get ModuleDescriptors * - 2. GlobalGraph.create: create global graph instance * - 3. graph.build: * - check duplicated prototypes exits * - check inject object exists (only in strict mode, * can register proto in hooks now, in next major version, * should use load to create dynamic ProtoDescriptor and delete * strict false options * ) * - 4. graph.sort: build moduleConfigList and moduleProtoDescriptorMap */ export class GlobalGraph { /** * Vertex: ModuleNode, collect prototypes in module * Edge: ModuleDependencyMeta, prototype and it's inject object * @private */ moduleGraph: Graph; /** * Vertex: ProtoNode, collect all prototypes in app * Edge: ProtoDependencyMeta, inject object * @private */ protoGraph: Graph; /** * The order of the moduleConfigList is the order in which they are instantiated */ moduleConfigList: readonly ModuleReference[]; /** * key: module name * value: ProtoDescriptor in module, the order is the order in which they are instantiated */ moduleProtoDescriptorMap: Map; strict: boolean; private buildHooks: GlobalGraphBuildHook[]; /** * The global instance used in ModuleLoadUnit */ static instance?: GlobalGraph; constructor(options?: GlobalGraphOptions) { this.moduleGraph = new Graph(); this.protoGraph = new Graph(); this.strict = options?.strict ?? false; this.moduleProtoDescriptorMap = new Map(); this.buildHooks = []; } registerBuildHook(hook: GlobalGraphBuildHook) { this.buildHooks.push(hook); } addModuleNode(moduleNode: GlobalModuleNode) { if (!this.moduleGraph.addVertex(new GraphNode(moduleNode))) { throw new Error(`duplicate module: ${moduleNode}`); } for (const protoNode of moduleNode.protos) { if (!this.protoGraph.addVertex(protoNode)) { throw new Error(`duplicate proto: ${protoNode.val}`); } } } build() { for (const moduleNode of this.moduleGraph.nodes.values()) { for (const protoNode of moduleNode.val.protos) { for (const injectObj of protoNode.val.proto.injectObjects) { this.buildInjectEdge(moduleNode, protoNode, injectObj); } } } for (const buildHook of this.buildHooks) { buildHook(this); } } buildInjectEdge(moduleNode: GraphNode, protoNode: GraphNode, injectObj: InjectObjectDescriptor) { const injectProto = this.findDependencyProtoNode(protoNode.val.proto, injectObj); if (!injectProto) { if (!this.strict) { return; } throw FrameworkErrorFormater.formatError(new EggPrototypeNotFound(injectObj.objName, protoNode.val.proto.instanceModuleName)); } this.addInject(moduleNode, protoNode, injectProto, injectObj.objName); } addInject( moduleNode: GraphNode, protoNode: GraphNode, injectNode: GraphNode, injectName: PropertyKey, ) { this.protoGraph.addEdge(protoNode, injectNode, new ProtoDependencyMeta({ injectObj: injectName, })); const injectModule = this.findModuleNode(injectNode.val.proto.instanceModuleName); if (!injectModule) { if (!this.strict) { return; } throw new Error(`not found module ${injectNode.val.proto.instanceModuleName}`); } if (moduleNode.val.id !== injectModule.val.id) { this.moduleGraph.addEdge(moduleNode, injectModule, new ModuleDependencyMeta(protoNode.val.proto, injectName)); } } findInjectProto(proto: ProtoDescriptor, injectObject: InjectObjectDescriptor): ProtoDescriptor | undefined { const edge = this.protoGraph.findToNode(ProtoNode.createProtoId(proto), new ProtoDependencyMeta({ injectObj: injectObject.objName, })); return edge?.val.proto; } #findDependencyProtoWithDefaultQualifiers(proto: ProtoDescriptor, injectObject: InjectObjectDescriptor, qualifiers: QualifierInfo[]): GraphNode[] { // TODO perf O(n(proto count)*m(inject count)*n) const result: GraphNode[] = []; for (const node of this.protoGraph.nodes.values()) { if (node.val.selectProto({ name: injectObject.objName, qualifiers: QualifierUtil.mergeQualifiers( injectObject.qualifiers, qualifiers, ), moduleName: proto.instanceModuleName, })) { result.push(node); } } return result; } findDependencyProtoNode(proto: ProtoDescriptor, injectObject: InjectObjectDescriptor): GraphNode | undefined { // 1. find proto with request // 2. try to add Context qualifier to find // 3. try to add self init type qualifier to find const protos = this.#findDependencyProtoWithDefaultQualifiers(proto, injectObject, []); if (protos.length === 0) { return; // throw FrameworkErrorFormater.formatError(new EggPrototypeNotFound(injectObject.objName, proto.instanceModuleName)); } if (protos.length === 1) { return protos[0]; } const protoWithContext = this.#findDependencyProtoWithDefaultQualifiers(proto, injectObject, [{ attribute: InitTypeQualifierAttribute, value: ObjectInitType.CONTEXT, }]); if (protoWithContext.length === 1) { return protoWithContext[0]; } const protoWithSelfInitType = this.#findDependencyProtoWithDefaultQualifiers(proto, injectObject, [{ attribute: InitTypeQualifierAttribute, value: proto.initType, }]); if (protoWithSelfInitType.length === 1) { return protoWithSelfInitType[0]; } const loadUnitQualifier = injectObject.qualifiers.find(t => t.attribute === LoadUnitNameQualifierAttribute); if (!loadUnitQualifier) { return this.findDependencyProtoNode(proto, { ...injectObject, qualifiers: QualifierUtil.mergeQualifiers( injectObject.qualifiers, [{ attribute: LoadUnitNameQualifierAttribute, value: proto.instanceModuleName, }], ), }); } throw FrameworkErrorFormater.formatError(new MultiPrototypeFound(injectObject.objName, injectObject.qualifiers)); } findModuleNode(moduleName: string) { for (const node of this.moduleGraph.nodes.values()) { if (node.val.name === moduleName) { return node; } } } #sortModule() { const loopPath = this.moduleGraph.loopPath(); if (loopPath) { throw new Error('module has recursive deps: ' + loopPath); } this.moduleConfigList = this.moduleGraph.sort() .filter(t => { return t.val.optional !== true || t.fromNodeMap.size > 0; }) .map(t => { return { name: t.val.name, path: t.val.unitPath, optional: t.val.optional, }; }); } #sortClazz() { const loopPath = this.protoGraph.loopPath(); if (loopPath) { throw new Error('proto has recursive deps: ' + loopPath); } for (const proto of this.protoGraph.sort()) { // // ignore the proto has no dependent // if (proto.fromNodeMap.size === 0) continue; const instanceModuleName = proto.val.proto.instanceModuleName; let moduleProtoList = this.moduleProtoDescriptorMap.get(instanceModuleName); if (!moduleProtoList) { moduleProtoList = []; this.moduleProtoDescriptorMap.set(instanceModuleName, moduleProtoList); } moduleProtoList.push(proto.val.proto); } } sort() { this.#sortModule(); this.#sortClazz(); } static create(moduleDescriptors: ModuleDescriptor[], options?: GlobalGraphOptions): GlobalGraph { const graph = new GlobalGraph(options); for (const moduleDescriptor of moduleDescriptors) { const moduleNodeBuilder = new GlobalModuleNodeBuilder({ name: moduleDescriptor.name, unitPath: moduleDescriptor.unitPath, optional: moduleDescriptor.optional ?? false, }); for (const clazz of moduleDescriptor.clazzList) { moduleNodeBuilder.addClazz(clazz); } for (const clazz of moduleDescriptor.multiInstanceClazzList) { moduleNodeBuilder.addMultiInstanceClazz(clazz, moduleDescriptor.name, moduleDescriptor.unitPath); } graph.addModuleNode(moduleNodeBuilder.build()); } return graph; } } ================================================ FILE: core/metadata/src/model/graph/GlobalModuleNode.ts ================================================ import { GraphNode, GraphNodeObj, EdgeMeta } from '@eggjs/tegg-common-util'; import { ProtoDependencyMeta, ProtoNode } from './ProtoNode'; import { ProtoDescriptor } from '@eggjs/tegg-types'; export interface GlobalModuleNodeOptions { name: string; unitPath: string; optional: boolean; } export class ModuleDependencyMeta implements EdgeMeta { constructor(readonly obj: ProtoDescriptor, readonly injectObj: PropertyKey) { } equal(meta: ModuleDependencyMeta): boolean { return this.obj.equal(meta.obj) && this.injectObj === meta.injectObj; } toString(): string { return `Object ${String(this.obj.name)} inject ${String(this.injectObj)}`; } } export class GlobalModuleNode implements GraphNodeObj { readonly id: string; readonly name: string; readonly unitPath: string; readonly optional: boolean; readonly protos: GraphNode[]; constructor(options: GlobalModuleNodeOptions) { this.id = options.unitPath; this.name = options.name; this.unitPath = options.unitPath; this.optional = options.optional; this.protos = []; } addProto(proto: ProtoDescriptor) { this.protos.push(new GraphNode(new ProtoNode(proto))); } toString() { return `${this.name}@${this.unitPath}`; } } ================================================ FILE: core/metadata/src/model/graph/GlobalModuleNodeBuilder.ts ================================================ import { EggProtoImplClass, ProtoDescriptor } from '@eggjs/tegg-types'; import { GlobalModuleNode, GlobalModuleNodeOptions } from './GlobalModuleNode'; import { ModuleConfigUtil } from '@eggjs/tegg-common-util'; import { ProtoDescriptorHelper } from '../ProtoDescriptorHelper'; export class GlobalModuleNodeBuilder { private readonly name: string; private readonly unitPath: string; private readonly optional: boolean; private readonly protos: ProtoDescriptor[]; constructor(options: GlobalModuleNodeOptions) { this.name = options.name; this.unitPath = options.unitPath; this.optional = options.optional; this.protos = []; } addClazz(clazz: EggProtoImplClass) { const proto = ProtoDescriptorHelper.createByInstanceClazz(clazz, { moduleName: this.name, unitPath: this.unitPath, }); this.protos.push(proto); return this; } addMultiInstanceClazz(clazz: EggProtoImplClass, defineModuleName: string, defineUnitPath: string) { const protos = ProtoDescriptorHelper.createByMultiInstanceClazz(clazz, { defineModuleName, defineUnitPath, instanceModuleName: this.name, instanceDefineUnitPath: this.unitPath, }); this.protos.push(...protos); return this; } build() { const node = new GlobalModuleNode({ name: this.name, unitPath: this.unitPath, optional: this.optional, }); for (const proto of this.protos) { node.addProto(proto); } return node; } static create(unitPath: string, optional = false) { const name = ModuleConfigUtil.readModuleNameSync(unitPath); return new GlobalModuleNodeBuilder({ name, unitPath, optional, }); } } ================================================ FILE: core/metadata/src/model/graph/ProtoNode.ts ================================================ import { GraphNodeObj, ProtoDescriptor } from '@eggjs/tegg-types'; import { ProtoSelectorContext } from './ProtoSelector'; import { EdgeMeta } from '@eggjs/tegg-common-util'; import { ProtoDescriptorHelper } from '../ProtoDescriptorHelper'; export class ProtoDependencyMeta implements EdgeMeta { injectObj: PropertyKey; constructor({ injectObj, }: { injectObj: PropertyKey; }) { this.injectObj = injectObj; } equal(meta: ProtoDependencyMeta): boolean { return this.injectObj === meta.injectObj; } toString(): string { return `inject ${String(this.injectObj)}`; } } export class ProtoNode implements GraphNodeObj { readonly id: string; readonly proto: ProtoDescriptor; constructor(proto: ProtoDescriptor) { this.id = ProtoNode.createProtoId(proto); this.proto = proto; } toString() { return `${String(this.proto.name)}@${this.proto.instanceDefineUnitPath}`; } selectProto(ctx: ProtoSelectorContext): boolean { return ProtoDescriptorHelper.selectProto(this.proto, ctx); } static createProtoId(proto: ProtoDescriptor) { const id = [ proto.name, proto.instanceModuleName, proto.initType, ...proto.qualifiers.map(t => String(t.attribute) + String(t.value)), ]; return id.join('@'); } } ================================================ FILE: core/metadata/src/model/graph/ProtoSelector.ts ================================================ import { QualifierInfo } from '@eggjs/tegg-types'; export interface ProtoSelectorContext { name: PropertyKey; qualifiers: QualifierInfo[]; moduleName: string; } ================================================ FILE: core/metadata/src/util/ClassUtil.ts ================================================ import { PrototypeUtil } from '@eggjs/core-decorator'; import type { EggProtoImplClass } from '@eggjs/tegg-types'; export class ClassUtil { static classDescription(clazz: EggProtoImplClass): string { const filePath = PrototypeUtil.getFilePath(clazz); const name = this.className(clazz); let desc = `class:${String(name)}`; if (filePath) { desc += `@${filePath}`; } return desc; } static className(clazz: EggProtoImplClass): string { const property = PrototypeUtil.getProperty(clazz); return (property?.name || clazz.name) as string; } } ================================================ FILE: core/metadata/test/AppGraph.test.ts ================================================ import assert from 'assert'; import path from 'path'; import { AppGraph, ModuleNode } from '../src/model/AppGraph'; import { RootProto } from './fixtures/modules/app-graph-modules/root/Root'; import { UsedProto } from './fixtures/modules/app-graph-modules/used/Used'; import { UnusedProto } from './fixtures/modules/app-graph-modules/unused/Unused'; import { App } from './fixtures/modules/app-multi-inject-multi/app/modules/app/App'; import { BizManager } from './fixtures/modules/app-multi-inject-multi/app/modules/bar/BizManager'; import { Secret } from './fixtures/modules/app-multi-inject-multi/app/modules/foo/Secret'; import { App2 } from './fixtures/modules/app-multi-inject-multi/app/modules/app2/App'; describe('test/LoadUnit/AppGraph.test.ts', () => { it('optional module dep should work', () => { const graph = new AppGraph(); const rootModuleNode = new ModuleNode({ name: 'foo', path: path.join(__dirname, './fixtures/modules/app-graph-modules/root'), }); rootModuleNode.addClazz(RootProto); graph.addNode(rootModuleNode); const usedOptionalModuleNode = new ModuleNode({ name: 'usedOptionalModuleNode', path: path.join(__dirname, './fixtures/modules/app-graph-modules/used'), optional: true, }); usedOptionalModuleNode.addClazz(UsedProto); graph.addNode(usedOptionalModuleNode); const unusedOptionalModuleNode = new ModuleNode({ name: 'unusedOptionalModuleNode', path: path.join(__dirname, './fixtures/modules/app-graph-modules/unused'), optional: true, }); unusedOptionalModuleNode.addClazz(UnusedProto); graph.addNode(unusedOptionalModuleNode); graph.build(); graph.sort(); assert(graph.moduleConfigList.length === 2); }); it('multi instance inject multi instance should work', () => { const graph = new AppGraph(); const appModuleNode = new ModuleNode({ name: 'app', path: path.join(__dirname, './fixtures/modules/app-multi-inject-multi/app/modules/app'), }); appModuleNode.addClazz(App); graph.addNode(appModuleNode); const app2ModuleNode = new ModuleNode({ name: 'app2', path: path.join(__dirname, './fixtures/modules/app-multi-inject-multi/app/modules/app2'), }); app2ModuleNode.addClazz(App2); graph.addNode(app2ModuleNode); const barOptionalModuleNode = new ModuleNode({ name: 'bar', path: path.join(__dirname, './fixtures/modules/app-multi-inject-multi/app/modules/bar'), }); barOptionalModuleNode.addClazz(BizManager); graph.addNode(barOptionalModuleNode); const fooOptionalModuleNode = new ModuleNode({ name: 'foo', path: path.join(__dirname, './fixtures/modules/app-multi-inject-multi/app/modules/foo'), }); fooOptionalModuleNode.addClazz(Secret); graph.addNode(fooOptionalModuleNode); graph.build(); graph.sort(); assert.deepStrictEqual(graph.moduleConfigList.map(t => t.name), [ 'app', 'app2', 'bar', 'foo', ]); }); }); ================================================ FILE: core/metadata/test/GlobalGraph.test.ts ================================================ import { GlobalGraph } from '../src/model/graph/GlobalGraph'; import path from 'node:path'; import { RootProto } from './fixtures/modules/app-graph-modules/root/Root'; import { UsedProto } from './fixtures/modules/app-graph-modules/used/Used'; import { UnusedProto } from './fixtures/modules/app-graph-modules/unused/Unused'; import assert from 'node:assert'; import { App } from './fixtures/modules/app-multi-inject-multi/app/modules/app/App'; import { App2 } from './fixtures/modules/app-multi-inject-multi/app/modules/app2/App'; import { BizManager } from './fixtures/modules/app-multi-inject-multi/app/modules/bar/BizManager'; import { Secret } from './fixtures/modules/app-multi-inject-multi/app/modules/foo/Secret'; import { TestLoader } from './fixtures/TestLoader'; import { buildModuleNode } from './fixtures/LoaderUtil'; describe('test/LoadUnit/GlobalGraph.test.ts', () => { it('optional module dep should work', () => { const graph = new GlobalGraph(); graph.addModuleNode(buildModuleNode( path.join(__dirname, './fixtures/modules/app-graph-modules/root'), [ RootProto ], [], )); graph.addModuleNode(buildModuleNode( path.join(__dirname, './fixtures/modules/app-graph-modules/used'), [ UsedProto ], [], true, )); graph.addModuleNode(buildModuleNode( path.join(__dirname, './fixtures/modules/app-graph-modules/unused'), [ UnusedProto ], [], true, )); graph.build(); graph.sort(); assert(graph.moduleConfigList.length === 2); }); it('multi instance inject multi instance should work', () => { const graph = new GlobalGraph(); const multiInstanceClazzList = [{ clazz: BizManager, unitPath: path.join(__dirname, './fixtures/modules/app-multi-inject-multi/app/modules/bar'), moduleName: 'bar', }, { clazz: Secret, unitPath: path.join(__dirname, './fixtures/modules/app-multi-inject-multi/app/modules/foo'), moduleName: 'foo', }]; graph.addModuleNode(buildModuleNode( path.join(__dirname, './fixtures/modules/app-multi-inject-multi/app/modules/app'), [ App ], multiInstanceClazzList, )); graph.addModuleNode(buildModuleNode( path.join(__dirname, './fixtures/modules/app-multi-inject-multi/app/modules/app2'), [ App2 ], multiInstanceClazzList, )); graph.addModuleNode(buildModuleNode( path.join(__dirname, './fixtures/modules/app-multi-inject-multi/app/modules/bar'), [], multiInstanceClazzList, )); graph.addModuleNode(buildModuleNode( path.join(__dirname, './fixtures/modules/app-multi-inject-multi/app/modules/foo'), [], multiInstanceClazzList, )); graph.build(); graph.sort(); assert.deepStrictEqual(graph.moduleConfigList.map(t => t.name), [ 'app', 'app2', 'bar', 'foo', ]); }); it('should sort extends class success', () => { const graph = new GlobalGraph(); const moduleDir = path.join(__dirname, './fixtures/modules/extends-module'); const loader = new TestLoader(moduleDir); const clazzList = loader.load(); graph.addModuleNode(buildModuleNode( moduleDir, clazzList, [], )); graph.build(); graph.sort(); const moduleProtoDescriptors = graph.moduleProtoDescriptorMap.get('extendsModule'); assert(moduleProtoDescriptors); assert.deepStrictEqual(moduleProtoDescriptors!.map(t => t.name), [ 'logger', 'base', 'foo', ]); }); it('should sort constructor extends class success', () => { const graph = new GlobalGraph(); const moduleDir = path.join(__dirname, './fixtures/modules/extends-constructor-module'); const loader = new TestLoader(moduleDir); const clazzList = loader.load(); graph.addModuleNode(buildModuleNode( moduleDir, clazzList, [], )); graph.build(); graph.sort(); const moduleProtoDescriptors = graph.moduleProtoDescriptorMap.get('extendsModule'); assert.deepStrictEqual(moduleProtoDescriptors!.map(t => t.name), [ 'logger', 'bar', 'constructorBase', 'fooConstructor', 'fooConstructorLogger', ]); }); }); ================================================ FILE: core/metadata/test/LoadUnit.test.ts ================================================ import path from 'path'; import assert from 'assert'; import { EggLoadUnitType, GlobalGraph, LoadUnitFactory, LoadUnitLifecycleUtil, LoadUnitMultiInstanceProtoHook, } from '..'; import { InitTypeQualifierAttribute, ObjectInitType } from '@eggjs/core-decorator'; import { TestLoader } from './fixtures/TestLoader'; import { FOO_ATTRIBUTE } from './fixtures/modules/multi-instance-module/MultiInstance'; // import { App } from './fixtures/modules/app-multi-inject-multi/app/modules/app/App'; // import { App2 } from './fixtures/modules/app-multi-inject-multi/app/modules/app2/App'; // import { BizManager } from './fixtures/modules/app-multi-inject-multi/app/modules/bar/BizManager'; // import { Secret } from './fixtures/modules/app-multi-inject-multi/app/modules/foo/Secret'; import { buildGlobalGraph } from './fixtures/LoaderUtil'; describe('test/LoadUnit/LoadUnit.test.ts', () => { beforeEach(() => { GlobalGraph.instance = undefined; }); describe('inject with constructor', () => { it('should not inherit parent class', async () => { const extendsConstructorModule = path.join(__dirname, './fixtures/modules/extends-constructor-module'); const loader = new TestLoader(extendsConstructorModule); buildGlobalGraph([ extendsConstructorModule ], [ loader ]); const loadUnit = await LoadUnitFactory.createLoadUnit(extendsConstructorModule, EggLoadUnitType.MODULE, loader); const fooConstructor = loadUnit.getEggPrototype('fooConstructor', [{ attribute: InitTypeQualifierAttribute, value: ObjectInitType.CONTEXT }]); const fooConstructorLogger = loadUnit.getEggPrototype('fooConstructorLogger', [{ attribute: InitTypeQualifierAttribute, value: ObjectInitType.CONTEXT }]); assert.strictEqual(fooConstructor.length, 1); assert.strictEqual(fooConstructor[0].injectObjects!.length, 1); assert.strictEqual(fooConstructor[0].injectObjects![0].refName, 'bar'); assert.strictEqual(fooConstructorLogger.length, 1); assert.strictEqual(fooConstructorLogger[0].injectObjects!.length, 2); assert.strictEqual(fooConstructorLogger[0].injectObjects![0].refName, 'bar'); assert.strictEqual(fooConstructorLogger[0].injectObjects![1].refName, 'logger'); await LoadUnitFactory.destroyLoadUnit(loadUnit); }); }); describe('ModuleLoadUnit', () => { it('should create success', async () => { const repoModulePath = path.join(__dirname, './fixtures/modules/load-unit'); const loader = new TestLoader(repoModulePath); buildGlobalGraph([ repoModulePath ], [ loader ]); const loadUnit = await LoadUnitFactory.createLoadUnit(repoModulePath, EggLoadUnitType.MODULE, loader); assert(loadUnit.id === 'LOAD_UNIT:app-repo'); assert(loadUnit.unitPath === repoModulePath); const appRepoProto = loadUnit.getEggPrototype('appRepo', [{ attribute: InitTypeQualifierAttribute, value: ObjectInitType.SINGLETON }]); const sprintRepoProto = loadUnit.getEggPrototype('sprintRepo', [{ attribute: InitTypeQualifierAttribute, value: ObjectInitType.SINGLETON }]); const userRepoProto = loadUnit.getEggPrototype('userRepo', [{ attribute: InitTypeQualifierAttribute, value: ObjectInitType.SINGLETON }]); assert.strictEqual(appRepoProto.length, 1); assert.strictEqual(appRepoProto[0].className, 'AppRepo'); assert.strictEqual(sprintRepoProto.length, 1); assert.strictEqual(userRepoProto.length, 1); await LoadUnitFactory.destroyLoadUnit(loadUnit); }); it('recursive deps should should throw error', async () => { const repoModulePath = path.join(__dirname, './fixtures/modules/recursive-load-unit'); const loader = new TestLoader(repoModulePath); await assert.rejects(async () => { return buildGlobalGraph([ repoModulePath ], [ loader ]); }, /proto has recursive deps/); }); }); describe('optional inject', () => { it('should success', async () => { const optionalInjectModulePath = path.join(__dirname, './fixtures/modules/optional-inject-module'); const loader = new TestLoader(optionalInjectModulePath); buildGlobalGraph([ optionalInjectModulePath ], [ loader ]); const loadUnit = await LoadUnitFactory.createLoadUnit(optionalInjectModulePath, EggLoadUnitType.MODULE, loader); const optionalInjectServiceProto = loadUnit.getEggPrototype('optionalInjectService', [{ attribute: InitTypeQualifierAttribute, value: ObjectInitType.SINGLETON }]); assert.deepStrictEqual(optionalInjectServiceProto[0].injectObjects, []); }); }); describe('invalidate load unit', () => { it('should init failed', async () => { const invalidateModulePath = path.join(__dirname, './fixtures/modules/invalidate-module'); const loader = new TestLoader(invalidateModulePath); await assert.rejects(async () => { buildGlobalGraph([ invalidateModulePath ], [ loader ]); await LoadUnitFactory.createLoadUnit(invalidateModulePath, EggLoadUnitType.MODULE, loader); }, (e: Error) => { assert(e.message.includes('Object persistenceService not found')); assert(e.message.includes('faq/TEGG_EGG_PROTO_NOT_FOUND')); return true; }); }); it('should init failed with multi proto', async () => { const invalidateModulePath = path.join(__dirname, './fixtures/modules/invalid-multimodule'); const loader = new TestLoader(invalidateModulePath); await assert.rejects(async () => { buildGlobalGraph([ invalidateModulePath ], [ loader ]); }, (e: Error) => { assert(e.message.includes('duplicate proto: invalidateService')); return true; }); }); }); describe('try use obj init type as property init type qualifier', () => { it('should get the right proto', async () => { const sameObjectModulePath = path.join(__dirname, './fixtures/modules/same-name-object'); const loader = new TestLoader(sameObjectModulePath); buildGlobalGraph([ sameObjectModulePath ], [ loader ]); const loadUnit = await LoadUnitFactory.createLoadUnit(sameObjectModulePath, EggLoadUnitType.MODULE, loader); const countServiceProto = loadUnit.getEggPrototype('countService', [])[0]; assert(countServiceProto); }); it('should use context proto first', async () => { const sameObjectModulePath = path.join(__dirname, './fixtures/modules/same-name-object'); const loader = new TestLoader(sameObjectModulePath); buildGlobalGraph([ sameObjectModulePath ], [ loader ]); const loadUnit = await LoadUnitFactory.createLoadUnit(sameObjectModulePath, EggLoadUnitType.MODULE, loader); const singletonProto = loadUnit.getEggPrototype('singletonCountService', [])[0]; assert(singletonProto); const injectProto = singletonProto.injectObjects.find(t => t.objName === 'appCache'); assert(injectProto); assert(injectProto.proto.initType === ObjectInitType.CONTEXT); }); }); describe('MultiInstance proto', () => { let loadUnitMultiInstanceProtoHook: LoadUnitMultiInstanceProtoHook; beforeEach(() => { loadUnitMultiInstanceProtoHook = new LoadUnitMultiInstanceProtoHook(); LoadUnitLifecycleUtil.registerLifecycle(loadUnitMultiInstanceProtoHook); }); afterEach(() => { LoadUnitLifecycleUtil.deleteLifecycle(loadUnitMultiInstanceProtoHook); }); it('should load static work', async () => { const multiInstanceModule = path.join(__dirname, './fixtures/modules/multi-instance-module'); const loader = new TestLoader(multiInstanceModule); buildGlobalGraph([ multiInstanceModule ], [ loader ]); const loadUnit = await LoadUnitFactory.createLoadUnit(multiInstanceModule, EggLoadUnitType.MODULE, loader); assert(loadUnit.id === 'LOAD_UNIT:multiInstanceModule'); assert(loadUnit.unitPath === multiInstanceModule); const foo1Prototype = loadUnit.getEggPrototype('foo', [{ attribute: FOO_ATTRIBUTE, value: 'foo1' }]); const foo2Prototype = loadUnit.getEggPrototype('foo', [{ attribute: FOO_ATTRIBUTE, value: 'foo2' }]); assert(foo1Prototype); assert(foo1Prototype.length === 1); assert.strictEqual(foo1Prototype[0].className, 'FooLogger'); assert(foo2Prototype); assert(foo2Prototype.length === 1); await LoadUnitFactory.destroyLoadUnit(loadUnit); }); it('should load callback work', async () => { const multiCallbackInstanceModule = path.join(__dirname, './fixtures/modules/multi-callback-instance-module'); const loader = new TestLoader(multiCallbackInstanceModule); buildGlobalGraph([ multiCallbackInstanceModule ], [ loader ]); const loadUnit = await LoadUnitFactory.createLoadUnit(multiCallbackInstanceModule, EggLoadUnitType.MODULE, loader); assert(loadUnit.id === 'LOAD_UNIT:multiCallbackInstanceModule'); assert(loadUnit.unitPath === multiCallbackInstanceModule); const foo1Prototype = loadUnit.getEggPrototype('foo', [{ attribute: FOO_ATTRIBUTE, value: 'foo' }]); const foo2Prototype = loadUnit.getEggPrototype('foo', [{ attribute: FOO_ATTRIBUTE, value: 'bar' }]); assert(foo1Prototype); assert(foo1Prototype.length === 1); assert(foo2Prototype); assert(foo2Prototype.length === 1); await LoadUnitFactory.destroyLoadUnit(loadUnit); }); it('should multi instance inject multi instance work', async () => { const appInstanceModule = path.join(__dirname, './fixtures/modules/app-multi-inject-multi/app/modules/app'); const app2InstanceModule = path.join(__dirname, './fixtures/modules/app-multi-inject-multi/app/modules/app2'); const loader = new TestLoader(appInstanceModule); const loader2 = new TestLoader(app2InstanceModule); const fooInstanceModule = path.join(__dirname, './fixtures/modules/app-multi-inject-multi/app/modules/foo'); const barInstanceModule = path.join(__dirname, './fixtures/modules/app-multi-inject-multi/app/modules/bar'); const fooLoader = new TestLoader(fooInstanceModule); const barLoader = new TestLoader(barInstanceModule); buildGlobalGraph([ appInstanceModule, app2InstanceModule, fooInstanceModule, barInstanceModule, ], [ loader, loader2, fooLoader, barLoader, ]); const loadUnit = await LoadUnitFactory.createLoadUnit(appInstanceModule, EggLoadUnitType.MODULE, loader); const loadUnit2 = await LoadUnitFactory.createLoadUnit(app2InstanceModule, EggLoadUnitType.MODULE, loader2); const app1Prototype = loadUnit.getEggPrototype('app', []); const app2Prototype = loadUnit.getEggPrototype('app2', []); assert(app1Prototype); assert(app2Prototype); await LoadUnitFactory.destroyLoadUnit(loadUnit); await LoadUnitFactory.destroyLoadUnit(loadUnit2); LoadUnitMultiInstanceProtoHook.clear(); }); }); }); ================================================ FILE: core/metadata/test/ModuleGraph.test.ts ================================================ import assert from 'node:assert'; import path from 'path'; import { TestLoader } from './fixtures/TestLoader'; import { ModuleGraph } from '../src/impl/ModuleLoadUnit'; describe('test/ModuleGraph.test.ts', () => { it('should sort extends class success', () => { const modulePath = path.join(__dirname, './fixtures/modules/extends-module'); const loader = new TestLoader(modulePath); const clazzList = loader.load(); const graph = new ModuleGraph(clazzList, modulePath, 'foo'); graph.sort(); }); it('should sort constructor extends class success', () => { const modulePath = path.join(__dirname, './fixtures/modules/extends-constructor-module'); const loader = new TestLoader(modulePath); const clazzList = loader.load(); const graph = new ModuleGraph(clazzList, modulePath, 'foo'); graph.sort(); assert.deepStrictEqual(graph.clazzList.map(t => t.name), [ 'Logger', 'Bar', 'ConstructorBase', 'FooConstructor', 'FooConstructorLogger', ]); }); }); ================================================ FILE: core/metadata/test/fixtures/LoaderUtil.ts ================================================ import { EggProtoImplClass, PrototypeUtil } from '@eggjs/core-decorator'; import is from 'is-type-of'; import { GlobalModuleNodeBuilder } from '../../src/model/graph/GlobalModuleNodeBuilder'; import { GlobalGraph } from '../../src/model/graph/GlobalGraph'; import { Loader } from '@eggjs/tegg-types'; import { ModuleConfigUtil } from '@eggjs/tegg-common-util'; export class LoaderUtil { static loadFile(filePath: string): EggProtoImplClass[] { let exports; try { exports = require(filePath); } catch (e) { e.message = '[tegg/loader] load ' + filePath + ' failed: ' + e.message; throw e; } const clazzList: EggProtoImplClass[] = []; const exportNames = Object.keys(exports); for (const exportName of exportNames) { const clazz = exports[exportName]; const isEggProto = is.class(clazz) && (PrototypeUtil.isEggPrototype(clazz) || PrototypeUtil.isEggMultiInstancePrototype(clazz)); if (!isEggProto) { continue; } PrototypeUtil.setFilePath(clazz, filePath); clazzList.push(clazz); } return clazzList; } } export function buildModuleNode(modulePath: string, clazzList: EggProtoImplClass[], multiInstanceClazzList: { clazz: any; unitPath: string; moduleName: string; }[], optional = false) { const builder = GlobalModuleNodeBuilder.create(modulePath, optional); for (const clazz of clazzList) { builder.addClazz(clazz); } for (const { clazz, moduleName, unitPath } of multiInstanceClazzList) { builder.addMultiInstanceClazz(clazz, moduleName, unitPath); } return builder.build(); } export function buildGlobalGraph(modulePaths: string[], loaders: Loader[]) { GlobalGraph.instance = new GlobalGraph(); const multiInstanceEggProtoClass: { clazz: any; unitPath: string; moduleName: string; }[] = []; for (let i = 0; i < modulePaths.length; i++) { const modulePath = modulePaths[i]; const loader = loaders[i]; const clazzList = loader.load(); const moduleName = ModuleConfigUtil.readModuleNameSync(modulePath); for (const clazz of clazzList) { if (PrototypeUtil.isEggMultiInstancePrototype(clazz)) { multiInstanceEggProtoClass.push({ clazz, unitPath: modulePath, moduleName, }) } } } for (let i = 0; i < modulePaths.length; i++) { const modulePath = modulePaths[i]; const loader = loaders[i]; const clazzList = loader.load(); const eggProtoClass: EggProtoImplClass[] = []; for (const clazz of clazzList) { if (PrototypeUtil.isEggPrototype(clazz)) { eggProtoClass.push(clazz); } } GlobalGraph.instance.addModuleNode(buildModuleNode( modulePath, eggProtoClass, multiInstanceEggProtoClass, )); } GlobalGraph.instance.build(); GlobalGraph.instance.sort(); } ================================================ FILE: core/metadata/test/fixtures/TestLoader.ts ================================================ import globby from 'globby'; import path from 'path'; import { LoaderUtil } from './LoaderUtil'; import { EggProtoImplClass } from '@eggjs/core-decorator'; import { Loader } from '../..'; export class TestLoader implements Loader { private readonly moduleDir: string; constructor(moduleDir: string) { this.moduleDir = moduleDir; } load(): EggProtoImplClass[] { const protoClassList: EggProtoImplClass[] = []; const files = globby.sync([ '**/*.(js|ts)' ], { cwd: this.moduleDir }); for (const file of files) { const realPath = path.join(this.moduleDir, file); const protoClazz = LoaderUtil.loadFile(realPath); if (!protoClazz) { continue; } protoClassList.push(...protoClazz); } return protoClassList; } } ================================================ FILE: core/metadata/test/fixtures/modules/app-graph-modules/root/Root.ts ================================================ import { SingletonProto, Inject } from '@eggjs/core-decorator'; import { UsedProto } from '../used/Used'; @SingletonProto() export class RootProto { @Inject() usedProto: UsedProto; } ================================================ FILE: core/metadata/test/fixtures/modules/app-graph-modules/root/RootConstructor.ts ================================================ import { SingletonProto, Inject } from '@eggjs/core-decorator'; import { UsedProto } from '../used/Used'; @SingletonProto() export class RootConstructorProto { constructor(@Inject() readonly usedProto: UsedProto) { } } ================================================ FILE: core/metadata/test/fixtures/modules/app-graph-modules/root/package.json ================================================ { "name": "root", "eggModule": { "name": "root" } } ================================================ FILE: core/metadata/test/fixtures/modules/app-graph-modules/unused/Unused.ts ================================================ import { SingletonProto } from '@eggjs/core-decorator'; @SingletonProto() export class UnusedProto { } ================================================ FILE: core/metadata/test/fixtures/modules/app-graph-modules/unused/package.json ================================================ { "name": "unused", "eggModule": { "name": "unused" } } ================================================ FILE: core/metadata/test/fixtures/modules/app-graph-modules/used/Used.ts ================================================ import { AccessLevel, SingletonProto } from '@eggjs/core-decorator'; @SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) export class UsedProto { } ================================================ FILE: core/metadata/test/fixtures/modules/app-graph-modules/used/package.json ================================================ { "name": "used", "eggModule": { "name": "used" } } ================================================ FILE: core/metadata/test/fixtures/modules/app-multi-inject-multi/app/modules/app/App.ts ================================================ import { Inject, SingletonProto } from '@eggjs/core-decorator'; import { BizManager, BizManagerQualifier } from '../bar/BizManager'; @SingletonProto() export class App { @Inject() @BizManagerQualifier('foo') bizManager: BizManager; } ================================================ FILE: core/metadata/test/fixtures/modules/app-multi-inject-multi/app/modules/app/module.yml ================================================ BizManager: clients: foo: secret: '1' bar: secret: '2' secret: keys: - '1' - '2' ================================================ FILE: core/metadata/test/fixtures/modules/app-multi-inject-multi/app/modules/app/package.json ================================================ { "name": "app", "eggModule": { "name": "app" } } ================================================ FILE: core/metadata/test/fixtures/modules/app-multi-inject-multi/app/modules/app2/App.ts ================================================ import { Inject, ModuleQualifier, SingletonProto } from '@eggjs/core-decorator'; import { Secret, SecretQualifier } from '../foo/Secret'; @SingletonProto() export class App2 { @Inject() @ModuleQualifier('app2') @SecretQualifier('1') secret: Secret; } ================================================ FILE: core/metadata/test/fixtures/modules/app-multi-inject-multi/app/modules/app2/module.yml ================================================ secret: keys: - '1' - '2' ================================================ FILE: core/metadata/test/fixtures/modules/app-multi-inject-multi/app/modules/app2/package.json ================================================ { "name": "app2", "eggModule": { "name": "app2" } } ================================================ FILE: core/metadata/test/fixtures/modules/app-multi-inject-multi/app/modules/bar/BizManager.ts ================================================ import { MultiInstanceProto, AccessLevel, Inject, ObjectInitType, ObjectInfo, MultiInstancePrototypeGetObjectsContext, MultiInstanceInfo, } from '@eggjs/tegg'; import { ModuleConfigUtil } from '@eggjs/tegg-common-util'; import { EggProtoImplClass, LoadUnitNameQualifierAttribute, QualifierUtil } from '@eggjs/core-decorator'; import { Secret, SecretQualifierAttribute } from '../foo/Secret'; export const BizManagerQualifierAttribute = Symbol.for('Qualifier.BizManager'); export const BizManagerInjectName = 'bizManager'; export function BizManagerQualifier(chatModelName: string) { return function(target: any, propertyKey: PropertyKey) { QualifierUtil.addProperQualifier(target.constructor as EggProtoImplClass, propertyKey, BizManagerQualifierAttribute, chatModelName); }; } @MultiInstanceProto({ accessLevel: AccessLevel.PUBLIC, initType: ObjectInitType.SINGLETON, // 从 module.yml 中动态获取配置来决定需要初始化几个对象 getObjects(ctx: MultiInstancePrototypeGetObjectsContext) { const config = ModuleConfigUtil.loadModuleConfigSync(ctx.unitPath) as any; const name = ModuleConfigUtil.readModuleNameSync(ctx.unitPath); const clients = config?.BizManager?.clients; if (!clients) return []; return Object.keys(clients).map((clientName: string) => { return { name: BizManagerInjectName, qualifiers: [{ attribute: BizManagerQualifierAttribute, value: clientName, }], properQualifiers: { secret: [{ attribute: SecretQualifierAttribute, value: clients[clientName].secret, }, { attribute: LoadUnitNameQualifierAttribute, value: name, }], }, }; }); }, }) export class BizManager { readonly name: string; readonly secret: string; constructor( @Inject() secret: Secret, @MultiInstanceInfo([ BizManagerQualifierAttribute ]) objInfo: ObjectInfo, ) { this.name = objInfo.name as string; this.secret = secret.getSecret(this.name); } } ================================================ FILE: core/metadata/test/fixtures/modules/app-multi-inject-multi/app/modules/bar/package.json ================================================ { "name": "bar", "eggModule": { "name": "bar" } } ================================================ FILE: core/metadata/test/fixtures/modules/app-multi-inject-multi/app/modules/foo/Secret.ts ================================================ import { MultiInstanceProto, MultiInstancePrototypeGetObjectsContext, ObjectInitType, AccessLevel, QualifierUtil, } from '@eggjs/tegg'; import { ModuleConfigUtil } from '@eggjs/tegg/helper'; import { EggProtoImplClass } from '@eggjs/tegg-types'; export const SecretQualifierAttribute = Symbol.for('Qualifier.Secret'); export const SecretInjectName = 'secret'; export function SecretQualifier(chatModelName: string) { return function(target: any, propertyKey: PropertyKey) { QualifierUtil.addProperQualifier(target.constructor as EggProtoImplClass, propertyKey, SecretQualifierAttribute, chatModelName); }; } @MultiInstanceProto({ accessLevel: AccessLevel.PUBLIC, initType: ObjectInitType.SINGLETON, getObjects(ctx: MultiInstancePrototypeGetObjectsContext) { const config = ModuleConfigUtil.loadModuleConfigSync(ctx.unitPath) as any; const keys = config?.secret?.keys; if (!keys || keys.length === 0) return []; return keys.map(t => { return { name: SecretInjectName, qualifiers: [{ attribute: SecretQualifierAttribute, value: t, }], }; }); }, }) export class Secret { getSecret(key: string): string { return key + '233'; } } ================================================ FILE: core/metadata/test/fixtures/modules/app-multi-inject-multi/app/modules/foo/package.json ================================================ { "name": "foo", "eggModule": { "name": "foo" } } ================================================ FILE: core/metadata/test/fixtures/modules/app-multi-inject-multi/package.json ================================================ { "name": "app-multi-inject-multi" } ================================================ FILE: core/metadata/test/fixtures/modules/extends-constructor-module/Base.ts ================================================ import { ContextProto, Inject } from '@eggjs/core-decorator'; @ContextProto() export class Logger { } @ContextProto() export class Bar { } @ContextProto() export class ConstructorBase { constructor(@Inject() readonly logger: Logger) { } } @ContextProto() export class FooConstructor extends ConstructorBase { constructor(@Inject() readonly bar: Bar) { super(console); } } @ContextProto() export class FooConstructorLogger extends ConstructorBase { constructor(@Inject() readonly bar: Bar, @Inject() readonly logger: Logger) { super(logger); } } ================================================ FILE: core/metadata/test/fixtures/modules/extends-constructor-module/package.json ================================================ { "name": "multi-module-service", "eggModule": { "name": "extendsModule" } } ================================================ FILE: core/metadata/test/fixtures/modules/extends-module/Base.ts ================================================ import { ContextProto, Inject } from '@eggjs/core-decorator'; @ContextProto() export class Logger { } @ContextProto() export class Base { @Inject() logger: Logger; } @ContextProto() export class Foo extends Base { } ================================================ FILE: core/metadata/test/fixtures/modules/extends-module/package.json ================================================ { "name": "multi-module-service", "eggModule": { "name": "extendsModule" } } ================================================ FILE: core/metadata/test/fixtures/modules/incompatible-proto-inject/package.json ================================================ { "name": "incompatible-proto-inject", "eggModule": { "name": "extendsModule" } } ================================================ FILE: core/metadata/test/fixtures/modules/incompatible-proto-inject/test.ts ================================================ import { ContextProto, Inject, SingletonProto } from '@eggjs/core-decorator'; @ContextProto() export class Logger { } @SingletonProto() export class Base { @Inject() logger: Logger; } ================================================ FILE: core/metadata/test/fixtures/modules/invalid-multimodule/invalidService.ts ================================================ import { Prototype, AccessLevel } from '@eggjs/core-decorator'; @Prototype({ accessLevel: AccessLevel.PUBLIC, }) export default class InvalidateService { } ================================================ FILE: core/metadata/test/fixtures/modules/invalid-multimodule/invalidService2.ts ================================================ import { Prototype, AccessLevel } from '@eggjs/core-decorator'; @Prototype({ accessLevel: AccessLevel.PUBLIC, }) export default class InvalidateService { } ================================================ FILE: core/metadata/test/fixtures/modules/invalid-multimodule/package.json ================================================ { "name": "multi-module-service", "eggModule": { "name": "multiModuleInvalidateService" } } ================================================ FILE: core/metadata/test/fixtures/modules/invalid-multimodule/test.ts ================================================ import { Prototype, Inject } from '@eggjs/core-decorator'; @Prototype() export default class testService { @Inject() invalidateService: any; } ================================================ FILE: core/metadata/test/fixtures/modules/invalidate-module/InvalidateService.ts ================================================ import { Prototype, Inject } from '@eggjs/core-decorator'; interface PersistenceService { } @Prototype() export default class InvalidateService { @Inject() persistenceService: PersistenceService; } ================================================ FILE: core/metadata/test/fixtures/modules/invalidate-module/package.json ================================================ { "name": "multi-module-service", "eggModule": { "name": "multiModuleInvalidateService" } } ================================================ FILE: core/metadata/test/fixtures/modules/load-unit/AppRepo.ts ================================================ import { Prototype } from '@eggjs/core-decorator'; interface App { name: string; } @Prototype() export default class AppRepo { async findAppByName(): Promise { return { name: 'hello', }; } } ================================================ FILE: core/metadata/test/fixtures/modules/load-unit/SprintRepo.ts ================================================ import { Prototype } from '@eggjs/core-decorator'; @Prototype() export default class SprintRepo { async save() { return Promise.resolve(); } } ================================================ FILE: core/metadata/test/fixtures/modules/load-unit/UserRepo.ts ================================================ import { Prototype } from '@eggjs/core-decorator'; @Prototype() export default class UserRepo { } ================================================ FILE: core/metadata/test/fixtures/modules/load-unit/package.json ================================================ { "name": "demo-app-repo", "eggModule": { "name": "app-repo" }, "main": "index.js" } ================================================ FILE: core/metadata/test/fixtures/modules/multi-callback-instance-module/MultiInstance.ts ================================================ import { AccessLevel, ObjectInitType, MultiInstanceProto, MultiInstancePrototypeGetObjectsContext, } from '@eggjs/core-decorator'; import { ModuleConfigUtil } from '@eggjs/tegg-common-util'; export const FOO_ATTRIBUTE = Symbol.for('FOO_ATTRIBUTE'); @MultiInstanceProto({ accessLevel: AccessLevel.PUBLIC, initType: ObjectInitType.SINGLETON, getObjects(ctx: MultiInstancePrototypeGetObjectsContext) { const config = ModuleConfigUtil.loadModuleConfigSync(ctx.unitPath); return (config as any).features.logger.map(name => { return { name: 'foo', qualifiers: [{ attribute: FOO_ATTRIBUTE, value: name, }], } }); }, }) export class FooDynamicLogger { } ================================================ FILE: core/metadata/test/fixtures/modules/multi-callback-instance-module/module.yml ================================================ features: logger: - foo - bar ================================================ FILE: core/metadata/test/fixtures/modules/multi-callback-instance-module/package.json ================================================ { "name": "multi-callback-instance-module", "eggModule": { "name": "multiCallbackInstanceModule" } } ================================================ FILE: core/metadata/test/fixtures/modules/multi-instance-module/MultiInstance.ts ================================================ import { AccessLevel, ObjectInitType, MultiInstanceProto } from '@eggjs/core-decorator'; export const FOO_ATTRIBUTE = Symbol.for('FOO_ATTRIBUTE'); @MultiInstanceProto({ accessLevel: AccessLevel.PUBLIC, initType: ObjectInitType.SINGLETON, objects: [{ name: 'foo', qualifiers: [{ attribute: FOO_ATTRIBUTE, value: 'foo1', }], }, { name: 'foo', qualifiers: [{ attribute: FOO_ATTRIBUTE, value: 'foo2', }], }], }) export class FooLogger { } ================================================ FILE: core/metadata/test/fixtures/modules/multi-instance-module/package.json ================================================ { "name": "multi-instance-module", "eggModule": { "name": "multiInstanceModule" } } ================================================ FILE: core/metadata/test/fixtures/modules/optional-inject-module/OptionalInjectService.ts ================================================ import { Inject, InjectOptional, SingletonProto } from '@eggjs/core-decorator'; interface PersistenceService { } @SingletonProto() export default class OptionalInjectService { @Inject({ optional: true }) persistenceService?: PersistenceService; @InjectOptional() persistenceService2?: PersistenceService; } ================================================ FILE: core/metadata/test/fixtures/modules/optional-inject-module/package.json ================================================ { "name": "optional-inject-service", "eggModule": { "name": "optionalInjectService" } } ================================================ FILE: core/metadata/test/fixtures/modules/recursive-load-unit/AppRepo.ts ================================================ import { Prototype, Inject } from '@eggjs/core-decorator'; import SprintRepo from './SprintRepo'; interface App { name: string; } @Prototype() export default class AppRepo { @Inject() sprintRepo: SprintRepo; async findAppByName(): Promise { return { name: 'hello', }; } } ================================================ FILE: core/metadata/test/fixtures/modules/recursive-load-unit/SprintRepo.ts ================================================ import { Prototype, Inject } from '@eggjs/core-decorator'; import UserRepo from './UserRepo'; @Prototype() export default class SprintRepo { @Inject() userRepo: UserRepo; async save() { return Promise.resolve(); } } ================================================ FILE: core/metadata/test/fixtures/modules/recursive-load-unit/UserRepo.ts ================================================ import { Prototype, Inject } from '@eggjs/core-decorator'; import AppRepo from './AppRepo'; @Prototype() export default class UserRepo { @Inject() appRepo: AppRepo; } ================================================ FILE: core/metadata/test/fixtures/modules/recursive-load-unit/package.json ================================================ { "name": "demo-app-repo", "eggModule": { "name": "app-repo" }, "main": "index.js" } ================================================ FILE: core/metadata/test/fixtures/modules/same-name-object/AppCache.ts ================================================ export interface AppCache { getCount(): number; } ================================================ FILE: core/metadata/test/fixtures/modules/same-name-object/ContextAppCache.ts ================================================ import { ContextProto } from '@eggjs/core-decorator'; @ContextProto() export default class AppCache { count = 0; async getCount(): Promise { return this.count++; } } ================================================ FILE: core/metadata/test/fixtures/modules/same-name-object/CountService.ts ================================================ import { ContextProto, Inject, SingletonProto } from '@eggjs/core-decorator'; import { AppCache } from './AppCache'; @ContextProto() export class CountService { @Inject() appCache: AppCache; async getCount(): Promise { return this.appCache.getCount(); } } @SingletonProto() export class SingletonCountService { @Inject() appCache: AppCache; async getCount(): Promise { return this.appCache.getCount(); } } ================================================ FILE: core/metadata/test/fixtures/modules/same-name-object/SingletonAppCache.ts ================================================ import { SingletonProto } from '@eggjs/core-decorator'; @SingletonProto() export default class AppCache { count = 0; async getCount(): Promise { return this.count++; } } ================================================ FILE: core/metadata/test/fixtures/modules/same-name-object/package.json ================================================ { "name": "same-name-object", "eggModule": { "name": "samename" } } ================================================ FILE: core/metadata/tsconfig.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "dist", "baseUrl": "./" }, "exclude": [ "dist", "node_modules", "test" ] } ================================================ FILE: core/metadata/tsconfig.pub.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "dist", "baseUrl": "./" }, "exclude": [ "dist", "node_modules", "test" ] } ================================================ FILE: core/orm-decorator/CHANGELOG.md ================================================ # Change Log All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. ## [3.78.15](https://github.com/eggjs/tegg/compare/v3.78.14...v3.78.15) (2026-04-23) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.78.14](https://github.com/eggjs/tegg/compare/v3.78.13...v3.78.14) (2026-04-21) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.78.13](https://github.com/eggjs/tegg/compare/v3.78.12...v3.78.13) (2026-04-21) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.78.12](https://github.com/eggjs/tegg/compare/v3.78.11...v3.78.12) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.78.11](https://github.com/eggjs/tegg/compare/v3.78.10...v3.78.11) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.78.10](https://github.com/eggjs/tegg/compare/v3.78.9...v3.78.10) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.78.9](https://github.com/eggjs/tegg/compare/v3.78.8...v3.78.9) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.78.8](https://github.com/eggjs/tegg/compare/v3.78.7...v3.78.8) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.78.7](https://github.com/eggjs/tegg/compare/v3.78.6...v3.78.7) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.78.6](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.6) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.78.5](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.5) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.78.4](https://github.com/eggjs/tegg/compare/v3.78.3...v3.78.4) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.78.3](https://github.com/eggjs/tegg/compare/v3.78.2...v3.78.3) (2026-04-15) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.78.2](https://github.com/eggjs/tegg/compare/v3.78.1...v3.78.2) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.78.1](https://github.com/eggjs/tegg/compare/v3.78.0...v3.78.1) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.78.0](https://github.com/eggjs/tegg/compare/v3.77.2...v3.78.0) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.77.2](https://github.com/eggjs/tegg/compare/v3.77.1...v3.77.2) (2026-04-07) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.77.1](https://github.com/eggjs/tegg/compare/v3.77.0...v3.77.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.77.0](https://github.com/eggjs/tegg/compare/v3.76.1...v3.77.0) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.76.1](https://github.com/eggjs/tegg/compare/v3.76.0...v3.76.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.76.0](https://github.com/eggjs/tegg/compare/v3.75.1...v3.76.0) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.75.1](https://github.com/eggjs/tegg/compare/v3.75.0...v3.75.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.75.0](https://github.com/eggjs/tegg/compare/v3.74.0...v3.75.0) (2026-03-30) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.74.0](https://github.com/eggjs/tegg/compare/v3.73.0...v3.74.0) (2026-03-30) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.73.0](https://github.com/eggjs/tegg/compare/v3.72.0...v3.73.0) (2026-03-25) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.72.0](https://github.com/eggjs/tegg/compare/v3.71.2...v3.72.0) (2026-02-04) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.71.2](https://github.com/eggjs/tegg/compare/v3.71.1...v3.71.2) (2026-01-30) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.71.1](https://github.com/eggjs/tegg/compare/v3.71.0...v3.71.1) (2026-01-28) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.71.0](https://github.com/eggjs/tegg/compare/v3.70.1...v3.71.0) (2026-01-26) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.70.1](https://github.com/eggjs/tegg/compare/v3.70.0...v3.70.1) (2026-01-14) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.70.0](https://github.com/eggjs/tegg/compare/v3.69.0...v3.70.0) (2026-01-07) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.69.0](https://github.com/eggjs/tegg/compare/v3.68.0...v3.69.0) (2026-01-06) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.68.0](https://github.com/eggjs/tegg/compare/v3.67.2...v3.68.0) (2025-12-29) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.67.2](https://github.com/eggjs/tegg/compare/v3.67.1...v3.67.2) (2025-12-24) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.67.1](https://github.com/eggjs/tegg/compare/v3.67.0...v3.67.1) (2025-12-23) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.67.0](https://github.com/eggjs/tegg/compare/v3.66.0...v3.67.0) (2025-12-18) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.66.0](https://github.com/eggjs/tegg/compare/v3.65.3...v3.66.0) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.65.3](https://github.com/eggjs/tegg/compare/v3.65.2...v3.65.3) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.65.2](https://github.com/eggjs/tegg/compare/v3.65.1...v3.65.2) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.65.1](https://github.com/eggjs/tegg/compare/v3.65.0...v3.65.1) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.65.0](https://github.com/eggjs/tegg/compare/v3.64.5...v3.65.0) (2025-12-10) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.64.5](https://github.com/eggjs/tegg/compare/v3.64.4...v3.64.5) (2025-12-08) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.64.4](https://github.com/eggjs/tegg/compare/v3.64.3...v3.64.4) (2025-12-06) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.64.3](https://github.com/eggjs/tegg/compare/v3.64.2...v3.64.3) (2025-12-06) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.64.2](https://github.com/eggjs/tegg/compare/v3.64.1...v3.64.2) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.64.1](https://github.com/eggjs/tegg/compare/v3.64.0...v3.64.1) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.64.0](https://github.com/eggjs/tegg/compare/v3.63.2...v3.64.0) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.63.2](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.2) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.63.1](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.1) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.63.0](https://github.com/eggjs/tegg/compare/v3.62.3...v3.63.0) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.62.3](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.3) (2025-11-05) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.62.2](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.2) (2025-11-05) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.62.1](https://github.com/eggjs/tegg/compare/v3.62.0...v3.62.1) (2025-11-03) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.62.0](https://github.com/eggjs/tegg/compare/v3.61.0...v3.62.0) (2025-09-24) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.61.0](https://github.com/eggjs/tegg/compare/v3.60.3...v3.61.0) (2025-08-15) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.60.3](https://github.com/eggjs/tegg/compare/v3.60.2...v3.60.3) (2025-08-06) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.60.2](https://github.com/eggjs/tegg/compare/v3.60.1...v3.60.2) (2025-08-06) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.60.1](https://github.com/eggjs/tegg/compare/v3.60.0...v3.60.1) (2025-07-28) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.60.0](https://github.com/eggjs/tegg/compare/v3.59.1...v3.60.0) (2025-07-28) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.59.1](https://github.com/eggjs/tegg/compare/v3.59.0...v3.59.1) (2025-07-16) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.59.0](https://github.com/eggjs/tegg/compare/v3.58.0...v3.59.0) (2025-07-07) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.58.0](https://github.com/eggjs/tegg/compare/v3.57.14...v3.58.0) (2025-07-01) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.57.14](https://github.com/eggjs/tegg/compare/v3.57.13...v3.57.14) (2025-06-18) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.57.13](https://github.com/eggjs/tegg/compare/v3.57.12...v3.57.13) (2025-06-18) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.57.12](https://github.com/eggjs/tegg/compare/v3.57.11...v3.57.12) (2025-06-16) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.57.11](https://github.com/eggjs/tegg/compare/v3.57.10...v3.57.11) (2025-06-16) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.57.10](https://github.com/eggjs/tegg/compare/v3.57.9...v3.57.10) (2025-06-12) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.57.9](https://github.com/eggjs/tegg/compare/v3.57.8...v3.57.9) (2025-05-29) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.57.8](https://github.com/eggjs/tegg/compare/v3.57.7...v3.57.8) (2025-05-29) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.57.7](https://github.com/eggjs/tegg/compare/v3.57.6...v3.57.7) (2025-05-28) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.57.6](https://github.com/eggjs/tegg/compare/v3.57.5...v3.57.6) (2025-05-27) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.57.5](https://github.com/eggjs/tegg/compare/v3.57.4...v3.57.5) (2025-05-15) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.57.4](https://github.com/eggjs/tegg/compare/v3.57.3...v3.57.4) (2025-05-14) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.57.3](https://github.com/eggjs/tegg/compare/v3.57.2...v3.57.3) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.57.2](https://github.com/eggjs/tegg/compare/v3.57.1...v3.57.2) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.57.1](https://github.com/eggjs/tegg/compare/v3.57.0...v3.57.1) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.57.0](https://github.com/eggjs/tegg/compare/v3.56.3...v3.57.0) (2025-05-09) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.56.3](https://github.com/eggjs/tegg/compare/v3.56.2...v3.56.3) (2025-05-07) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.56.2](https://github.com/eggjs/tegg/compare/v3.56.1...v3.56.2) (2025-05-01) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.56.1](https://github.com/eggjs/tegg/compare/v3.56.0...v3.56.1) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.56.0](https://github.com/eggjs/tegg/compare/v3.55.0...v3.56.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.55.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.55.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.54.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.54.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.53.0](https://github.com/eggjs/tegg/compare/v3.52.1...v3.53.0) (2025-04-21) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.52.1](https://github.com/eggjs/tegg/compare/v3.52.0...v3.52.1) (2025-03-14) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.52.0](https://github.com/eggjs/tegg/compare/v3.51.2...v3.52.0) (2024-12-30) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.51.2](https://github.com/eggjs/tegg/compare/v3.51.1...v3.51.2) (2024-12-09) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.51.1](https://github.com/eggjs/tegg/compare/v3.51.0...v3.51.1) (2024-11-06) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.51.0](https://github.com/eggjs/tegg/compare/v3.50.1...v3.51.0) (2024-10-30) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.50.1](https://github.com/eggjs/tegg/compare/v3.50.0...v3.50.1) (2024-10-23) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.50.0](https://github.com/eggjs/tegg/compare/v3.49.0...v3.50.0) (2024-10-22) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.49.0](https://github.com/eggjs/tegg/compare/v3.48.1...v3.49.0) (2024-10-21) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.48.1](https://github.com/eggjs/tegg/compare/v3.48.0...v3.48.1) (2024-10-14) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.48.0](https://github.com/eggjs/tegg/compare/v3.47.2...v3.48.0) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.47.2](https://github.com/eggjs/tegg/compare/v3.47.1...v3.47.2) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.47.1](https://github.com/eggjs/tegg/compare/v3.47.0...v3.47.1) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.47.0](https://github.com/eggjs/tegg/compare/v3.46.4...v3.47.0) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.46.4](https://github.com/eggjs/tegg/compare/v3.46.3...v3.46.4) (2024-10-09) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.46.3](https://github.com/eggjs/tegg/compare/v3.46.2...v3.46.3) (2024-10-08) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.46.2](https://github.com/eggjs/tegg/compare/v3.46.1...v3.46.2) (2024-10-07) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.46.1](https://github.com/eggjs/tegg/compare/v3.46.0...v3.46.1) (2024-09-30) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.46.0](https://github.com/eggjs/tegg/compare/v3.45.0...v3.46.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.45.0](https://github.com/eggjs/tegg/compare/v3.44.1...v3.45.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.44.1](https://github.com/eggjs/tegg/compare/v3.44.0...v3.44.1) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.44.0](https://github.com/eggjs/tegg/compare/v3.43.2...v3.44.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.43.2](https://github.com/eggjs/tegg/compare/v3.43.1...v3.43.2) (2024-09-14) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.43.1](https://github.com/eggjs/tegg/compare/v3.43.0...v3.43.1) (2024-09-14) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.43.0](https://github.com/eggjs/tegg/compare/v3.42.0...v3.43.0) (2024-09-13) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.42.0](https://github.com/eggjs/tegg/compare/v3.41.0...v3.42.0) (2024-09-10) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.41.0](https://github.com/eggjs/tegg/compare/v3.40.1...v3.41.0) (2024-08-26) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.40.1](https://github.com/eggjs/tegg/compare/v3.40.0...v3.40.1) (2024-08-23) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.40.0](https://github.com/eggjs/tegg/compare/v3.39.5...v3.40.0) (2024-08-22) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.39.5](https://github.com/eggjs/tegg/compare/v3.39.4...v3.39.5) (2024-08-09) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.39.4](https://github.com/eggjs/tegg/compare/v3.39.3...v3.39.4) (2024-07-09) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.39.3](https://github.com/eggjs/tegg/compare/v3.39.2...v3.39.3) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.39.2](https://github.com/eggjs/tegg/compare/v3.39.1...v3.39.2) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.39.1](https://github.com/eggjs/tegg/compare/v3.39.0...v3.39.1) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.39.0](https://github.com/eggjs/tegg/compare/v3.38.0...v3.39.0) (2024-04-19) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.38.0](https://github.com/eggjs/tegg/compare/v3.37.3...v3.38.0) (2024-04-18) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.37.3](https://github.com/eggjs/tegg/compare/v3.37.2...v3.37.3) (2024-04-17) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.37.2](https://github.com/eggjs/tegg/compare/v3.37.1...v3.37.2) (2024-04-17) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.37.1](https://github.com/eggjs/tegg/compare/v3.37.0...v3.37.1) (2024-04-16) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.37.0](https://github.com/eggjs/tegg/compare/v3.36.3...v3.37.0) (2024-04-16) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.36.3](https://github.com/eggjs/tegg/compare/v3.36.2...v3.36.3) (2024-04-10) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.36.2](https://github.com/eggjs/tegg/compare/v3.36.1...v3.36.2) (2024-04-08) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.36.1](https://github.com/eggjs/tegg/compare/v3.36.0...v3.36.1) (2024-04-07) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.36.0](https://github.com/eggjs/tegg/compare/v3.35.1...v3.36.0) (2024-04-02) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.35.1](https://github.com/eggjs/tegg/compare/v3.35.0...v3.35.1) (2024-03-26) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.35.0](https://github.com/eggjs/tegg/compare/v3.34.0...v3.35.0) (2024-03-26) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.34.0](https://github.com/eggjs/tegg/compare/v3.33.1...v3.34.0) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.33.1](https://github.com/eggjs/tegg/compare/v3.33.0...v3.33.1) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.33.0](https://github.com/eggjs/tegg/compare/v3.32.0...v3.33.0) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.32.0](https://github.com/eggjs/tegg/compare/v3.31.0...v3.32.0) (2024-02-19) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.31.0](https://github.com/eggjs/tegg/compare/v3.30.1...v3.31.0) (2024-01-31) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.30.1](https://github.com/eggjs/tegg/compare/v3.30.0...v3.30.1) (2024-01-25) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.30.0](https://github.com/eggjs/tegg/compare/v3.29.0...v3.30.0) (2024-01-17) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.29.0](https://github.com/eggjs/tegg/compare/v3.28.2...v3.29.0) (2023-12-26) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.28.2](https://github.com/eggjs/tegg/compare/v3.28.1...v3.28.2) (2023-12-12) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.28.1](https://github.com/eggjs/tegg/compare/v3.28.0...v3.28.1) (2023-12-11) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.28.0](https://github.com/eggjs/tegg/compare/v3.27.0...v3.28.0) (2023-12-10) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.27.0](https://github.com/eggjs/tegg/compare/v3.26.0...v3.27.0) (2023-11-23) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.25.2](https://github.com/eggjs/tegg/compare/v3.25.1...v3.25.2) (2023-11-06) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.25.1](https://github.com/eggjs/tegg/compare/v3.25.0...v3.25.1) (2023-11-03) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.25.0](https://github.com/eggjs/tegg/compare/v3.24.0...v3.25.0) (2023-11-03) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.24.0](https://github.com/eggjs/tegg/compare/v3.23.0...v3.24.0) (2023-10-26) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.23.0](https://github.com/eggjs/tegg/compare/v3.22.0...v3.23.0) (2023-09-20) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.20.0](https://github.com/eggjs/tegg/compare/v3.19.0...v3.20.0) (2023-09-07) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.19.0](https://github.com/eggjs/tegg/compare/v3.18.1...v3.19.0) (2023-08-31) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.18.1](https://github.com/eggjs/tegg/compare/v3.18.0...v3.18.1) (2023-08-29) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.18.0](https://github.com/eggjs/tegg/compare/v3.17.0...v3.18.0) (2023-08-29) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.17.0](https://github.com/eggjs/tegg/compare/v3.16.0...v3.17.0) (2023-08-24) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.16.0](https://github.com/eggjs/tegg/compare/v3.15.0...v3.16.0) (2023-08-24) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.14.3](https://github.com/eggjs/tegg/compare/v3.14.2...v3.14.3) (2023-08-14) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.14.2](https://github.com/eggjs/tegg/compare/v3.14.1...v3.14.2) (2023-08-14) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.14.1](https://github.com/eggjs/tegg/compare/v3.14.0...v3.14.1) (2023-08-11) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.14.0](https://github.com/eggjs/tegg/compare/v3.13.0...v3.14.0) (2023-07-31) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.13.0](https://github.com/eggjs/tegg/compare/v3.12.0...v3.13.0) (2023-07-25) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.12.0](https://github.com/eggjs/tegg/compare/v3.11.1...v3.12.0) (2023-07-13) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.11.1](https://github.com/eggjs/tegg/compare/v3.11.0...v3.11.1) (2023-06-29) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.11.0](https://github.com/eggjs/tegg/compare/v3.10.0...v3.11.0) (2023-06-29) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.10.0](https://github.com/eggjs/tegg/compare/v3.9.0...v3.10.0) (2023-06-28) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.8.0](https://github.com/eggjs/tegg/compare/v3.7.0...v3.8.0) (2023-05-30) ### Features * impl EggObjectLifecycle hook in decorator ([#119](https://github.com/eggjs/tegg/issues/119)) ([cced8a2](https://github.com/eggjs/tegg/commit/cced8a2e009c33d5040fa21d00409fddef471b0e)) # [3.7.0](https://github.com/eggjs/tegg/compare/v3.6.3...v3.7.0) (2023-04-03) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.5.0](https://github.com/eggjs/tegg/compare/v3.4.1...v3.5.0) (2023-02-10) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.4.0](https://github.com/eggjs/tegg/compare/v3.3.4...v3.4.0) (2023-02-01) ### Features * use singleton model insteadof context ([#89](https://github.com/eggjs/tegg/issues/89)) ([cfdfc05](https://github.com/eggjs/tegg/commit/cfdfc05f13048806274de1a35b1207c073a8519d)) # [3.3.0](https://github.com/eggjs/tegg/compare/v3.2.4...v3.3.0) (2023-01-28) ### Features * export singleton orm client ([#82](https://github.com/eggjs/tegg/issues/82)) ([5320af7](https://github.com/eggjs/tegg/commit/5320af77d7e7c5c73b80560a576f2ce01fc21fff)) ## [3.2.3](https://github.com/eggjs/tegg/compare/v3.2.2...v3.2.3) (2023-01-16) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ## [3.2.2](https://github.com/eggjs/tegg/compare/v3.2.1...v3.2.2) (2023-01-06) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.1.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.1.0) (2022-12-27) ### Bug Fixes * use getMetaData for ModelMetadataUtil ([#44](https://github.com/eggjs/tegg/issues/44)) ([87a306c](https://github.com/eggjs/tegg/commit/87a306c4fba51fd519a47c0caaa79442643ea107)) ### Features * **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) * impl Inject Model ([#43](https://github.com/eggjs/tegg/issues/43)) ([ced2ce2](https://github.com/eggjs/tegg/commit/ced2ce2134964dcb410410c0192a34f77507c42d)) # [3.0.0](https://github.com/eggjs/tegg/compare/v3.0.0-alpha.0...v3.0.0) (2022-12-26) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [3.0.0-alpha.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.0.0-alpha.0) (2022-12-22) ### Bug Fixes * use getMetaData for ModelMetadataUtil ([#44](https://github.com/eggjs/tegg/issues/44)) ([87a306c](https://github.com/eggjs/tegg/commit/87a306c4fba51fd519a47c0caaa79442643ea107)) ### Features * **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) * impl Inject Model ([#43](https://github.com/eggjs/tegg/issues/43)) ([ced2ce2](https://github.com/eggjs/tegg/commit/ced2ce2134964dcb410410c0192a34f77507c42d)) ## [1.4.1](https://github.com/eggjs/tegg/compare/@eggjs/tegg-orm-decorator@1.4.0...@eggjs/tegg-orm-decorator@1.4.1) (2022-07-20) ### Bug Fixes * use getMetaData for ModelMetadataUtil ([#44](https://github.com/eggjs/tegg/issues/44)) ([87a306c](https://github.com/eggjs/tegg/commit/87a306c4fba51fd519a47c0caaa79442643ea107)) # [1.4.0](https://github.com/eggjs/tegg/compare/@eggjs/tegg-orm-decorator@1.3.1...@eggjs/tegg-orm-decorator@1.4.0) (2022-07-20) ### Features * impl Inject Model ([#43](https://github.com/eggjs/tegg/issues/43)) ([ced2ce2](https://github.com/eggjs/tegg/commit/ced2ce2134964dcb410410c0192a34f77507c42d)) # [1.3.0](https://github.com/eggjs/tegg/compare/v1.2.0...v1.3.0) (2022-07-01) **Note:** Version bump only for package @eggjs/tegg-orm-decorator # [0.2.0](https://github.com/eggjs/tegg/compare/v0.1.19...v0.2.0) (2022-01-20) **Note:** Version bump only for package @eggjs/tegg-orm-decorator ================================================ FILE: core/orm-decorator/README.md ================================================ # `@eggjs/tegg-orm-decorator` ## Install ```shell npm i --save @eggjs/tegg-orm-decorator ``` ## Define Model ```ts import { Model, Attribute } from '@eggjs/tegg-orm-decorator'; import { DataTypes, Bone } from 'leoric'; @Model() export class App extends Bone { @Attribute(DataTypes.STRING) name: string; @Attribute(DataTypes.STRING) desc: string; } ``` ## Use Model ```ts import { SingletonProto, Inject } from '@eggjs/tegg'; import { App } from './model/App'; @SingletonProto() export class AppService { @Inject() App: typeof App; async createApp(data: { name: string; desc: string; }): Promise { const bone = await this.App.create(data as any); return bone as App; } async findApp(name: string): Promise { const app = await this.App.findOne({ name }); return app as App; } } ``` ================================================ FILE: core/orm-decorator/index.ts ================================================ export * from '@eggjs/tegg-types/orm'; export * from './src/model/ModelMetadata'; export * from './src/model/AttributeMeta'; export * from './src/model/IndexMeta'; export * from './src/builder/AttributeMetaBuilder'; export * from './src/builder/ModelMetaBuilder'; export * from './src/builder/IndexMetaBuilder'; export * from './src/decorator/Index'; export * from './src/decorator/Model'; export * from './src/decorator/Attribute'; export * from './src/decorator/DataSource'; export * from './src/util/ModelInfoUtil'; export * from './src/util/ModelMetadataUtil'; ================================================ FILE: core/orm-decorator/package.json ================================================ { "name": "@eggjs/tegg-orm-decorator", "version": "3.78.15", "description": "tegg orm decorator", "main": "dist/index.js", "files": [ "dist/**/*.js", "dist/**/*.d.ts" ], "typings": "dist/index.d.ts", "keywords": [ "egg", "typescript", "runtime", "tegg" ], "scripts": { "test": "cross-env NODE_ENV=test NODE_OPTIONS='--no-deprecation' mocha", "clean": "tsc -b --clean", "tsc": "ut run clean && tsc -p ./tsconfig.json", "tsc:pub": "ut run clean && tsc -p ./tsconfig.pub.json", "prepublishOnly": "ut tsc:pub" }, "author": "killagu ", "license": "MIT", "homepage": "https://github.com/eggjs/tegg", "bugs": { "url": "https://github.com/eggjs/tegg/issues" }, "repository": { "type": "git", "url": "git@github.com:eggjs/tegg.git", "directory": "core/orm-decorator" }, "engines": { "node": ">=14.0.0" }, "dependencies": { "@eggjs/core-decorator": "^3.78.15", "@eggjs/tegg-common-util": "^3.78.15", "@eggjs/tegg-metadata": "^3.78.15", "@eggjs/tegg-types": "^3.78.15", "lodash": "^4.17.21", "pluralize": "^8.0.0" }, "peerDependencies": { "leoric": "^2.6.1" }, "publishConfig": { "access": "public" }, "devDependencies": { "@types/mocha": "^10.0.1", "@types/node": "^20.2.4", "cross-env": "^7.0.3", "mocha": "^10.2.0", "ts-node": "^10.9.1", "typescript": "^5.0.4" }, "gitHead": "240a3e9f40fda65ebb7589727d197db5ce17916c" } ================================================ FILE: core/orm-decorator/src/builder/AttributeMetaBuilder.ts ================================================ import type { EggProtoImplClass, ModelAttributeInfo } from '@eggjs/tegg-types'; import { AttributeMeta } from '../model/AttributeMeta'; import { ModelInfoUtil } from '../util/ModelInfoUtil'; import { NameUtil } from '../util/NameUtil'; export class AttributeMetaBuilder { private readonly clazz: EggProtoImplClass; constructor(clazz: EggProtoImplClass) { this.clazz = clazz; } build(): Array { const modelAttributes = ModelInfoUtil.getModelAttributes(this.clazz); const attributes: Array = []; if (!modelAttributes) { throw new Error(`model ${this.clazz.name} has no attributes`); } for (const [ propertyName, attributeInfo ] of modelAttributes) { const attribute = this.buildAttributeMeta(propertyName, attributeInfo); attributes.push(attribute); } return attributes; } private buildAttributeMeta(propertyName: string, attributeInfo: ModelAttributeInfo) { return new AttributeMeta( attributeInfo.dataType, propertyName, attributeInfo.options?.name ?? NameUtil.getAttributeName(propertyName), attributeInfo.options?.allowNull ?? true, attributeInfo.options?.autoIncrement ?? false, attributeInfo.options?.primary ?? false, attributeInfo.options?.unique ?? false, ); } } ================================================ FILE: core/orm-decorator/src/builder/IndexMetaBuilder.ts ================================================ import type { EggProtoImplClass, ModelIndexInfo } from '@eggjs/tegg-types'; import { IndexMeta } from '../model/IndexMeta'; import { ModelInfoUtil } from '../util/ModelInfoUtil'; import { NameUtil } from '../util/NameUtil'; import { AttributeMeta } from '../model/AttributeMeta'; export class IndexMetaBuilder { private readonly clazz: EggProtoImplClass; private readonly attributes: Array; constructor(clazz: EggProtoImplClass, attributes: Array) { this.clazz = clazz; this.attributes = attributes; } build(): Array { return ModelInfoUtil.getModelIndices(this.clazz) .map(indexInfo => this.buildIndexMeta(indexInfo)); } private buildIndexMeta(indexInfo: ModelIndexInfo): IndexMeta { const fields: string[] = []; for (const field of indexInfo.fields) { const attribute = this.attributes.find(t => t.propertyName === field); if (!attribute) { throw new Error(`model ${this.clazz.name} has no attribute named ${field}`); } fields.push(attribute.attributeName); } let indexName: string; if (indexInfo.options?.name) { indexName = indexInfo.options!.name; } else { indexName = NameUtil.getIndexName(fields, { unique: indexInfo.options?.unique }); } return new IndexMeta( indexName, fields, indexInfo.options?.unique ?? false, indexInfo.options?.primary ?? false); } } ================================================ FILE: core/orm-decorator/src/builder/ModelMetaBuilder.ts ================================================ import type { EggProtoImplClass } from '@eggjs/tegg-types'; import { ModelMetadata } from '../model/ModelMetadata'; import { ModelInfoUtil } from '../util/ModelInfoUtil'; import { NameUtil } from '../util/NameUtil'; import { IndexMetaBuilder } from './IndexMetaBuilder'; import { AttributeMetaBuilder } from './AttributeMetaBuilder'; export class ModelMetaBuilder { private readonly clazz: EggProtoImplClass; constructor(clazz: EggProtoImplClass) { this.clazz = clazz; } build(): ModelMetadata { const dataSource = ModelInfoUtil.getDataSource(this.clazz); const tableName = ModelInfoUtil.getTableName(this.clazz) || NameUtil.getTableName(this.clazz.name); const attributeMetaBuilder = new AttributeMetaBuilder(this.clazz); const attributes = attributeMetaBuilder.build(); const indexMetaBuilder = new IndexMetaBuilder(this.clazz, attributes); const indices = indexMetaBuilder.build(); return new ModelMetadata(dataSource, tableName, attributes, indices); } } ================================================ FILE: core/orm-decorator/src/decorator/Attribute.ts ================================================ import assert from 'node:assert'; import type { AttributeOptions, EggProtoImplClass } from '@eggjs/tegg-types'; import { ModelInfoUtil } from '../util/ModelInfoUtil'; export function Attribute(dataType: string, options?: AttributeOptions) { return function(target: any, propertyKey: PropertyKey) { const clazz = target.constructor as EggProtoImplClass; assert(typeof propertyKey === 'string', `[model/${clazz.name}] expect method name be typeof string, but now is ${String(propertyKey)}`); ModelInfoUtil.addModelAttribute(dataType, options, clazz, propertyKey); }; } ================================================ FILE: core/orm-decorator/src/decorator/DataSource.ts ================================================ import type { EggProtoImplClass } from '@eggjs/tegg-types'; import { ModelInfoUtil } from '../util/ModelInfoUtil'; export function DataSource(dataSource: string) { return function(clazz: EggProtoImplClass) { ModelInfoUtil.setDataSource(dataSource, clazz); }; } ================================================ FILE: core/orm-decorator/src/decorator/Index.ts ================================================ import type { EggProtoImplClass, IndexOptions } from '@eggjs/tegg-types'; import { ModelInfoUtil } from '../util/ModelInfoUtil'; export function Index(fields: string[], params?: IndexOptions) { return function(clazz: EggProtoImplClass) { ModelInfoUtil.addModelIndex(fields, params, clazz); }; } ================================================ FILE: core/orm-decorator/src/decorator/Model.ts ================================================ import { SingletonProto } from '@eggjs/core-decorator'; import { AccessLevel, MODEL_PROTO_IMPL_TYPE } from '@eggjs/tegg-types'; import type { EggProtoImplClass, ModelParams } from '@eggjs/tegg-types'; import { ModelInfoUtil } from '../util/ModelInfoUtil'; export function Model(param?: ModelParams) { return function(clazz: EggProtoImplClass) { ModelInfoUtil.setIsModel(true, clazz); const func = SingletonProto({ name: clazz.name, accessLevel: AccessLevel.PUBLIC, protoImplType: MODEL_PROTO_IMPL_TYPE, }); if (param?.tableName) { ModelInfoUtil.setTableName(param.tableName, clazz); } if (param?.dataSource) { ModelInfoUtil.setDataSource(param.dataSource, clazz); } func(clazz); }; } ================================================ FILE: core/orm-decorator/src/model/AttributeMeta.ts ================================================ export class AttributeMeta { readonly dataType: string; readonly propertyName: string; readonly attributeName: string; readonly allowNull: boolean; readonly autoIncrement: boolean; readonly primary: boolean; readonly unique: boolean; constructor( dataType: string, propertyName: string, attributeName: string, allowNull: boolean, autoIncrement: boolean, primary: boolean, unique: boolean) { this.dataType = dataType; this.propertyName = propertyName; this.attributeName = attributeName; this.allowNull = allowNull; this.autoIncrement = autoIncrement; this.primary = primary; this.unique = unique; } } ================================================ FILE: core/orm-decorator/src/model/IndexMeta.ts ================================================ export class IndexMeta { readonly name: string; readonly fields: string[]; readonly unique: boolean; readonly primary: boolean; constructor( name: string, fields: string[], unique: boolean, primary: boolean, ) { this.name = name; this.fields = fields; this.unique = unique; this.primary = primary; } } ================================================ FILE: core/orm-decorator/src/model/ModelMetadata.ts ================================================ import { AttributeMeta } from './AttributeMeta'; import { IndexMeta } from './IndexMeta'; export class ModelMetadata { readonly dataSource: string | undefined; readonly tableName: string; readonly attributes: Array; readonly indices: Array; constructor(dataSource: string | undefined, tableName: string, attributes: Array, indices: Array) { this.dataSource = dataSource; this.tableName = tableName; this.attributes = attributes; this.indices = indices; } } ================================================ FILE: core/orm-decorator/src/util/ModelInfoUtil.ts ================================================ import { MetadataUtil } from '@eggjs/core-decorator'; import type { AttributeOptions, EggProtoImplClass, IndexOptions, ModelAttributeInfo, ModelIndexInfo, } from '@eggjs/tegg-types'; import { IS_MODEL, MODEL_DATA_ATTRIBUTES, MODEL_DATA_INDICES, MODEL_DATA_SOURCE, MODEL_DATA_TABLE_NAME, } from '@eggjs/tegg-types'; type ModelAttributeMap = Map; export class ModelInfoUtil { static setIsModel(isModel: boolean, clazz: EggProtoImplClass) { MetadataUtil.defineMetaData(IS_MODEL, isModel, clazz); } static getIsModel(clazz: EggProtoImplClass): boolean { return MetadataUtil.getBooleanMetaData(IS_MODEL, clazz); } static setDataSource(dataSource: string, clazz: EggProtoImplClass) { MetadataUtil.defineMetaData(MODEL_DATA_SOURCE, dataSource, clazz); } static getDataSource(clazz: EggProtoImplClass): string | undefined { return MetadataUtil.getMetaData(MODEL_DATA_SOURCE, clazz); } static setTableName(tableName: string, clazz: EggProtoImplClass) { MetadataUtil.defineMetaData(MODEL_DATA_TABLE_NAME, tableName, clazz); } static getTableName(clazz: EggProtoImplClass): string | undefined { return MetadataUtil.getMetaData(MODEL_DATA_TABLE_NAME, clazz); } static addModelIndex(fields: string[], options: IndexOptions | undefined, clazz: EggProtoImplClass) { const indexInfo: Array = MetadataUtil.initOwnArrayMetaData(MODEL_DATA_INDICES, clazz, []); indexInfo.push({ fields, options, }); } static getModelIndices(clazz: EggProtoImplClass): Array { return MetadataUtil.getArrayMetaData(MODEL_DATA_INDICES, clazz); } static addModelAttribute(dataType: string, options: AttributeOptions | undefined, clazz: EggProtoImplClass, property: string) { const attributeMap: ModelAttributeMap = MetadataUtil.initOwnMapMetaData(MODEL_DATA_ATTRIBUTES, clazz, new Map()); attributeMap.set(property, { dataType, options, }); } static getModelAttributes(clazz: EggProtoImplClass): ModelAttributeMap | undefined { return MetadataUtil.getMetaData(MODEL_DATA_ATTRIBUTES, clazz); } } ================================================ FILE: core/orm-decorator/src/util/ModelMetadataUtil.ts ================================================ import { MetadataUtil } from '@eggjs/core-decorator'; import type { EggProtoImplClass } from '@eggjs/tegg-types'; import { ModelMetadata } from '../model/ModelMetadata'; export const MODEL_METADATA = Symbol.for('EggPrototype#model#metadata'); export class ModelMetadataUtil { static setModelMetadata(clazz: EggProtoImplClass, metaData: ModelMetadata) { MetadataUtil.defineMetaData(MODEL_METADATA, metaData, clazz); } static getModelMetadata(clazz): ModelMetadata | undefined { return MetadataUtil.getMetaData(MODEL_METADATA, clazz); } } ================================================ FILE: core/orm-decorator/src/util/NameUtil.ts ================================================ import _ from 'lodash'; import pluralize from 'pluralize'; export class NameUtil { /** * get table name * StudentScore -> student_scores */ static getTableName(modelName: string): string { const modelNames = pluralize(modelName); return _.snakeCase(modelNames); } /** * get attribute name * userName -> user_name */ static getAttributeName(propertyName: string): string { return _.snakeCase(propertyName); } /** * [ 'user_name' ], unique * uk_user_name * * [ 'user_name', 'gender' ] * idx_user_name_gender */ static getIndexName(fields: string[], options?: { unique?: boolean }): string { const prefix = options?.unique ? 'uk_' : 'idx_'; const names = fields.join('_'); return prefix + names; } } ================================================ FILE: core/orm-decorator/test/builder/AttributeMetaBuilder.test.ts ================================================ import assert from 'assert'; import { AttributeMetaBuilder } from '../../src/builder/AttributeMetaBuilder'; import { DefaultAttributeModel } from '../fixtures/DefaultAttributeModel'; import { AttributeModel } from '../fixtures/AttributeModel'; import { AttributeMeta } from '../../src/model/AttributeMeta'; describe('test/builder/AttributeMetaBuilder.test.ts', () => { describe('default value', () => { it('should set default value', () => { const attributeMetaBuilder = new AttributeMetaBuilder(DefaultAttributeModel); const attributes = attributeMetaBuilder.build(); assert.deepStrictEqual(attributes, [ new AttributeMeta( 'varchar', 'foo', 'foo', true, false, false, false, ), ]); }); }); describe('not default value', () => { it('should use decorator value', () => { const attributeMetaBuilder = new AttributeMetaBuilder(AttributeModel); const attributes = attributeMetaBuilder.build(); assert.deepStrictEqual(attributes, [ new AttributeMeta( 'varchar', 'foo', 'foo_field', false, false, true, true, ), ]); }); }); }); ================================================ FILE: core/orm-decorator/test/builder/IndexMetaBuilder.test.ts ================================================ import assert from 'assert'; import { AttributeMetaBuilder } from '../../src/builder/AttributeMetaBuilder'; import { DefaultIndexModel } from '../fixtures/DefaultIndexModel'; import { IndexMetaBuilder } from '../../src/builder/IndexMetaBuilder'; import { IndexModel } from '../fixtures/IndexModel'; import { InvalidateIndexModel } from '../fixtures/InvalidateIndexModel'; import { IndexMeta } from '../../src/model/IndexMeta'; describe('test/builder/AttributeMetaBuilder.test.ts', () => { describe('default value', () => { it('should set default value', () => { const attributeMetaBuilder = new AttributeMetaBuilder(DefaultIndexModel); const indexMetaBuilder = new IndexMetaBuilder(DefaultIndexModel, attributeMetaBuilder.build()); const indices = indexMetaBuilder.build(); assert.deepStrictEqual(indices, [ new IndexMeta( 'idx_foo', [ 'foo' ], false, false, ), ]); }); }); describe('not default value', () => { it('should use decorator value', () => { const attributeMetaBuilder = new AttributeMetaBuilder(IndexModel); const indexMetaBuilder = new IndexMetaBuilder(IndexModel, attributeMetaBuilder.build()); const indices = indexMetaBuilder.build(); assert.deepStrictEqual(indices, [ new IndexMeta( 'idx_foo_name', [ 'foo' ], true, true, ), ]); }); }); describe('field not exits', () => { it('should throw error', () => { const attributeMetaBuilder = new AttributeMetaBuilder(InvalidateIndexModel); const indexMetaBuilder = new IndexMetaBuilder(InvalidateIndexModel, attributeMetaBuilder.build()); assert.throws(() => { indexMetaBuilder.build(); }, /model InvalidateIndexModel has no attribute named not_exist_field/); }); }); }); ================================================ FILE: core/orm-decorator/test/builder/ModelMetaBuilder.test.ts ================================================ import assert from 'assert'; import { ModelMetaBuilder } from '../../src/builder/ModelMetaBuilder'; import { Foo } from '../fixtures/Foo'; import { ModelMetadata } from '../../src/model/ModelMetadata'; import { AttributeMeta } from '../../src/model/AttributeMeta'; import { IndexMeta } from '../../src/model/IndexMeta'; describe('test/builder/ModelMetaBuilder.test.ts', () => { it('should work', () => { const builder = new ModelMetaBuilder(Foo); const meta = builder.build(); assert.deepStrictEqual(meta, new ModelMetadata( 'a_db', 'a_foo_table', [ new AttributeMeta( 'int', 'id', 'pid', false, true, true, false, ), new AttributeMeta( 'varchar(20)', 'name', 'name', true, false, false, false, ), new AttributeMeta( 'varchar(20)', 'foo', 'foo', true, false, false, true, ), ], [ new IndexMeta( 'uk_name', [ 'name' ], true, false, ), ], )); }); }); ================================================ FILE: core/orm-decorator/test/decorator.test.ts ================================================ import assert from 'assert'; import { Foo } from './fixtures/Foo'; import { ModelInfoUtil } from '../src/util/ModelInfoUtil'; describe('test/decorator.test.ts', () => { it('should work', () => { const attributes = ModelInfoUtil.getModelAttributes(Foo); const indices = ModelInfoUtil.getModelIndices(Foo); const tableName = ModelInfoUtil.getTableName(Foo); const dataSource = ModelInfoUtil.getDataSource(Foo); assert.deepStrictEqual(attributes, new Map([ [ 'id', { dataType: 'int', options: { name: 'pid', allowNull: false, autoIncrement: true, primary: true, }, }, ], [ 'name', { dataType: 'varchar(20)', options: undefined, }, ], [ 'foo', { dataType: 'varchar(20)', options: { unique: true }, }, ], ])); assert.deepStrictEqual(indices, [ { fields: [ 'name' ], options: { unique: true }, }, ]); assert(tableName === 'a_foo_table'); assert(dataSource === 'a_db'); }); }); ================================================ FILE: core/orm-decorator/test/fixtures/AttributeModel.ts ================================================ import { Model } from '../../src/decorator/Model'; import { Attribute } from '../../src/decorator/Attribute'; @Model() export class AttributeModel { @Attribute('varchar', { name: 'foo_field', allowNull: false, autoIncrement: false, primary: true, unique: true, }) foo: string; } ================================================ FILE: core/orm-decorator/test/fixtures/DefaultAttributeModel.ts ================================================ import { Model } from '../../src/decorator/Model'; import { Attribute } from '../../src/decorator/Attribute'; @Model() export class DefaultAttributeModel { @Attribute('varchar') foo: string; } ================================================ FILE: core/orm-decorator/test/fixtures/DefaultIndexModel.ts ================================================ import { Model } from '../../src/decorator/Model'; import { Attribute } from '../../src/decorator/Attribute'; import { Index } from '../../src/decorator/Index'; @Model() @Index([ 'foo' ]) export class DefaultIndexModel { @Attribute('varchar') foo: string; } ================================================ FILE: core/orm-decorator/test/fixtures/Foo.ts ================================================ import { Model } from '../../src/decorator/Model'; import { DataSource } from '../../src/decorator/DataSource'; import { Index } from '../../src/decorator/Index'; import { Attribute } from '../../src/decorator/Attribute'; @Model({ tableName: 'a_foo_table', }) @DataSource('a_db') @Index([ 'name' ], { unique: true, }) export class Foo { @Attribute('int', { name: 'pid', allowNull: false, autoIncrement: true, primary: true, }) id: number; @Attribute('varchar(20)') name: string; @Attribute('varchar(20)', { unique: true, }) foo: string; } ================================================ FILE: core/orm-decorator/test/fixtures/IndexModel.ts ================================================ import { Model } from '../../src/decorator/Model'; import { Attribute } from '../../src/decorator/Attribute'; import { Index } from '../../src/decorator/Index'; @Model() @Index([ 'foo' ], { primary: true, unique: true, name: 'idx_foo_name', }) export class IndexModel { @Attribute('varchar') foo: string; } ================================================ FILE: core/orm-decorator/test/fixtures/InvalidateIndexModel.ts ================================================ import { Model } from '../../src/decorator/Model'; import { Attribute } from '../../src/decorator/Attribute'; import { Index } from '../../src/decorator/Index'; @Model() @Index([ 'not_exist_field' ]) export class InvalidateIndexModel { @Attribute('varchar') foo: string; } ================================================ FILE: core/orm-decorator/tsconfig.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "dist", "baseUrl": "./" }, "exclude": [ "dist", "node_modules", "test" ] } ================================================ FILE: core/orm-decorator/tsconfig.pub.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "dist", "baseUrl": "./" }, "exclude": [ "dist", "node_modules", "test" ] } ================================================ FILE: core/runtime/CHANGELOG.md ================================================ # Change Log All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. ## [3.78.15](https://github.com/eggjs/tegg/compare/v3.78.14...v3.78.15) (2026-04-23) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.78.14](https://github.com/eggjs/tegg/compare/v3.78.13...v3.78.14) (2026-04-21) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.78.13](https://github.com/eggjs/tegg/compare/v3.78.12...v3.78.13) (2026-04-21) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.78.12](https://github.com/eggjs/tegg/compare/v3.78.11...v3.78.12) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.78.11](https://github.com/eggjs/tegg/compare/v3.78.10...v3.78.11) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.78.10](https://github.com/eggjs/tegg/compare/v3.78.9...v3.78.10) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.78.9](https://github.com/eggjs/tegg/compare/v3.78.8...v3.78.9) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.78.8](https://github.com/eggjs/tegg/compare/v3.78.7...v3.78.8) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.78.7](https://github.com/eggjs/tegg/compare/v3.78.6...v3.78.7) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.78.6](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.6) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.78.5](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.5) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.78.4](https://github.com/eggjs/tegg/compare/v3.78.3...v3.78.4) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.78.3](https://github.com/eggjs/tegg/compare/v3.78.2...v3.78.3) (2026-04-15) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.78.2](https://github.com/eggjs/tegg/compare/v3.78.1...v3.78.2) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.78.1](https://github.com/eggjs/tegg/compare/v3.78.0...v3.78.1) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-runtime # [3.78.0](https://github.com/eggjs/tegg/compare/v3.77.2...v3.78.0) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.77.2](https://github.com/eggjs/tegg/compare/v3.77.1...v3.77.2) (2026-04-07) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.77.1](https://github.com/eggjs/tegg/compare/v3.77.0...v3.77.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-runtime # [3.77.0](https://github.com/eggjs/tegg/compare/v3.76.1...v3.77.0) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.76.1](https://github.com/eggjs/tegg/compare/v3.76.0...v3.76.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-runtime # [3.76.0](https://github.com/eggjs/tegg/compare/v3.75.1...v3.76.0) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.75.1](https://github.com/eggjs/tegg/compare/v3.75.0...v3.75.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-runtime # [3.75.0](https://github.com/eggjs/tegg/compare/v3.74.0...v3.75.0) (2026-03-30) **Note:** Version bump only for package @eggjs/tegg-runtime # [3.74.0](https://github.com/eggjs/tegg/compare/v3.73.0...v3.74.0) (2026-03-30) **Note:** Version bump only for package @eggjs/tegg-runtime # [3.73.0](https://github.com/eggjs/tegg/compare/v3.72.0...v3.73.0) (2026-03-25) ### Features * add agent-runtime package with @AgentController decorator ([#411](https://github.com/eggjs/tegg/issues/411)) ([d4d0006](https://github.com/eggjs/tegg/commit/d4d00061e90230f82c0958bcf5268f8a511395db)) # [3.72.0](https://github.com/eggjs/tegg/compare/v3.71.2...v3.72.0) (2026-02-04) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.71.2](https://github.com/eggjs/tegg/compare/v3.71.1...v3.71.2) (2026-01-30) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.71.1](https://github.com/eggjs/tegg/compare/v3.71.0...v3.71.1) (2026-01-28) **Note:** Version bump only for package @eggjs/tegg-runtime # [3.71.0](https://github.com/eggjs/tegg/compare/v3.70.1...v3.71.0) (2026-01-26) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.70.1](https://github.com/eggjs/tegg/compare/v3.70.0...v3.70.1) (2026-01-14) **Note:** Version bump only for package @eggjs/tegg-runtime # [3.70.0](https://github.com/eggjs/tegg/compare/v3.69.0...v3.70.0) (2026-01-07) **Note:** Version bump only for package @eggjs/tegg-runtime # [3.69.0](https://github.com/eggjs/tegg/compare/v3.68.0...v3.69.0) (2026-01-06) **Note:** Version bump only for package @eggjs/tegg-runtime # [3.68.0](https://github.com/eggjs/tegg/compare/v3.67.2...v3.68.0) (2025-12-29) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.67.2](https://github.com/eggjs/tegg/compare/v3.67.1...v3.67.2) (2025-12-24) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.67.1](https://github.com/eggjs/tegg/compare/v3.67.0...v3.67.1) (2025-12-23) **Note:** Version bump only for package @eggjs/tegg-runtime # [3.67.0](https://github.com/eggjs/tegg/compare/v3.66.0...v3.67.0) (2025-12-18) **Note:** Version bump only for package @eggjs/tegg-runtime # [3.66.0](https://github.com/eggjs/tegg/compare/v3.65.3...v3.66.0) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.65.3](https://github.com/eggjs/tegg/compare/v3.65.2...v3.65.3) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.65.2](https://github.com/eggjs/tegg/compare/v3.65.1...v3.65.2) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.65.1](https://github.com/eggjs/tegg/compare/v3.65.0...v3.65.1) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-runtime # [3.65.0](https://github.com/eggjs/tegg/compare/v3.64.5...v3.65.0) (2025-12-10) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.64.5](https://github.com/eggjs/tegg/compare/v3.64.4...v3.64.5) (2025-12-08) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.64.4](https://github.com/eggjs/tegg/compare/v3.64.3...v3.64.4) (2025-12-06) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.64.3](https://github.com/eggjs/tegg/compare/v3.64.2...v3.64.3) (2025-12-06) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.64.2](https://github.com/eggjs/tegg/compare/v3.64.1...v3.64.2) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.64.1](https://github.com/eggjs/tegg/compare/v3.64.0...v3.64.1) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-runtime # [3.64.0](https://github.com/eggjs/tegg/compare/v3.63.2...v3.64.0) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.63.2](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.2) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.63.1](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.1) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg-runtime # [3.63.0](https://github.com/eggjs/tegg/compare/v3.62.3...v3.63.0) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.62.3](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.3) (2025-11-05) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.62.2](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.2) (2025-11-05) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.62.1](https://github.com/eggjs/tegg/compare/v3.62.0...v3.62.1) (2025-11-03) **Note:** Version bump only for package @eggjs/tegg-runtime # [3.62.0](https://github.com/eggjs/tegg/compare/v3.61.0...v3.62.0) (2025-09-24) **Note:** Version bump only for package @eggjs/tegg-runtime # [3.61.0](https://github.com/eggjs/tegg/compare/v3.60.3...v3.61.0) (2025-08-15) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.60.3](https://github.com/eggjs/tegg/compare/v3.60.2...v3.60.3) (2025-08-06) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.60.2](https://github.com/eggjs/tegg/compare/v3.60.1...v3.60.2) (2025-08-06) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.60.1](https://github.com/eggjs/tegg/compare/v3.60.0...v3.60.1) (2025-07-28) **Note:** Version bump only for package @eggjs/tegg-runtime # [3.60.0](https://github.com/eggjs/tegg/compare/v3.59.1...v3.60.0) (2025-07-28) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.59.1](https://github.com/eggjs/tegg/compare/v3.59.0...v3.59.1) (2025-07-16) ### Bug Fixes * concurrent init ([#336](https://github.com/eggjs/tegg/issues/336)) ([bfafeff](https://github.com/eggjs/tegg/commit/bfafeff99f1b7221de85df3890b55145a9fe2b35)) # [3.59.0](https://github.com/eggjs/tegg/compare/v3.58.0...v3.59.0) (2025-07-07) **Note:** Version bump only for package @eggjs/tegg-runtime # [3.58.0](https://github.com/eggjs/tegg/compare/v3.57.14...v3.58.0) (2025-07-01) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.57.14](https://github.com/eggjs/tegg/compare/v3.57.13...v3.57.14) (2025-06-18) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.57.13](https://github.com/eggjs/tegg/compare/v3.57.12...v3.57.13) (2025-06-18) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.57.12](https://github.com/eggjs/tegg/compare/v3.57.11...v3.57.12) (2025-06-16) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.57.11](https://github.com/eggjs/tegg/compare/v3.57.10...v3.57.11) (2025-06-16) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.57.10](https://github.com/eggjs/tegg/compare/v3.57.9...v3.57.10) (2025-06-12) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.57.9](https://github.com/eggjs/tegg/compare/v3.57.8...v3.57.9) (2025-05-29) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.57.8](https://github.com/eggjs/tegg/compare/v3.57.7...v3.57.8) (2025-05-29) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.57.7](https://github.com/eggjs/tegg/compare/v3.57.6...v3.57.7) (2025-05-28) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.57.6](https://github.com/eggjs/tegg/compare/v3.57.5...v3.57.6) (2025-05-27) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.57.5](https://github.com/eggjs/tegg/compare/v3.57.4...v3.57.5) (2025-05-15) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.57.4](https://github.com/eggjs/tegg/compare/v3.57.3...v3.57.4) (2025-05-14) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.57.3](https://github.com/eggjs/tegg/compare/v3.57.2...v3.57.3) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.57.2](https://github.com/eggjs/tegg/compare/v3.57.1...v3.57.2) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.57.1](https://github.com/eggjs/tegg/compare/v3.57.0...v3.57.1) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-runtime # [3.57.0](https://github.com/eggjs/tegg/compare/v3.56.3...v3.57.0) (2025-05-09) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.56.3](https://github.com/eggjs/tegg/compare/v3.56.2...v3.56.3) (2025-05-07) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.56.2](https://github.com/eggjs/tegg/compare/v3.56.1...v3.56.2) (2025-05-01) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.56.1](https://github.com/eggjs/tegg/compare/v3.56.0...v3.56.1) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-runtime # [3.56.0](https://github.com/eggjs/tegg/compare/v3.55.0...v3.56.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-runtime # [3.55.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.55.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-runtime # [3.54.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.54.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-runtime # [3.53.0](https://github.com/eggjs/tegg/compare/v3.52.1...v3.53.0) (2025-04-21) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.52.1](https://github.com/eggjs/tegg/compare/v3.52.0...v3.52.1) (2025-03-14) **Note:** Version bump only for package @eggjs/tegg-runtime # [3.52.0](https://github.com/eggjs/tegg/compare/v3.51.2...v3.52.0) (2024-12-30) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.51.2](https://github.com/eggjs/tegg/compare/v3.51.1...v3.51.2) (2024-12-09) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.51.1](https://github.com/eggjs/tegg/compare/v3.51.0...v3.51.1) (2024-11-06) **Note:** Version bump only for package @eggjs/tegg-runtime # [3.51.0](https://github.com/eggjs/tegg/compare/v3.50.1...v3.51.0) (2024-10-30) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.50.1](https://github.com/eggjs/tegg/compare/v3.50.0...v3.50.1) (2024-10-23) **Note:** Version bump only for package @eggjs/tegg-runtime # [3.50.0](https://github.com/eggjs/tegg/compare/v3.49.0...v3.50.0) (2024-10-22) **Note:** Version bump only for package @eggjs/tegg-runtime # [3.49.0](https://github.com/eggjs/tegg/compare/v3.48.1...v3.49.0) (2024-10-21) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.48.1](https://github.com/eggjs/tegg/compare/v3.48.0...v3.48.1) (2024-10-14) ### Bug Fixes * fix merge qualifier ([#250](https://github.com/eggjs/tegg/issues/250)) ([d5a8a93](https://github.com/eggjs/tegg/commit/d5a8a93abad570f69881f9fa42f39d7b5cd436be)) # [3.48.0](https://github.com/eggjs/tegg/compare/v3.47.2...v3.48.0) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.47.2](https://github.com/eggjs/tegg/compare/v3.47.1...v3.47.2) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.47.1](https://github.com/eggjs/tegg/compare/v3.47.0...v3.47.1) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-runtime # [3.47.0](https://github.com/eggjs/tegg/compare/v3.46.4...v3.47.0) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.46.4](https://github.com/eggjs/tegg/compare/v3.46.3...v3.46.4) (2024-10-09) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.46.3](https://github.com/eggjs/tegg/compare/v3.46.2...v3.46.3) (2024-10-08) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.46.2](https://github.com/eggjs/tegg/compare/v3.46.1...v3.46.2) (2024-10-07) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.46.1](https://github.com/eggjs/tegg/compare/v3.46.0...v3.46.1) (2024-09-30) **Note:** Version bump only for package @eggjs/tegg-runtime # [3.46.0](https://github.com/eggjs/tegg/compare/v3.45.0...v3.46.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-runtime # [3.45.0](https://github.com/eggjs/tegg/compare/v3.44.1...v3.45.0) (2024-09-29) ### Features * impl MultiInstanceInfo decorator ([#239](https://github.com/eggjs/tegg/issues/239)) ([70d4d95](https://github.com/eggjs/tegg/commit/70d4d95bca4a0c3e11d0d7cc4f292b1315e49e81)) ## [3.44.1](https://github.com/eggjs/tegg/compare/v3.44.0...v3.44.1) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-runtime # [3.44.0](https://github.com/eggjs/tegg/compare/v3.43.2...v3.44.0) (2024-09-29) ### Features * support inject in constructor ([#237](https://github.com/eggjs/tegg/issues/237)) ([e68b1ed](https://github.com/eggjs/tegg/commit/e68b1ed6a90432f1cb35a6f562914b7b04cb5114)) ## [3.43.2](https://github.com/eggjs/tegg/compare/v3.43.1...v3.43.2) (2024-09-14) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.43.1](https://github.com/eggjs/tegg/compare/v3.43.0...v3.43.1) (2024-09-14) **Note:** Version bump only for package @eggjs/tegg-runtime # [3.43.0](https://github.com/eggjs/tegg/compare/v3.42.0...v3.43.0) (2024-09-13) **Note:** Version bump only for package @eggjs/tegg-runtime # [3.42.0](https://github.com/eggjs/tegg/compare/v3.41.0...v3.42.0) (2024-09-10) **Note:** Version bump only for package @eggjs/tegg-runtime # [3.41.0](https://github.com/eggjs/tegg/compare/v3.40.1...v3.41.0) (2024-08-26) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.40.1](https://github.com/eggjs/tegg/compare/v3.40.0...v3.40.1) (2024-08-23) **Note:** Version bump only for package @eggjs/tegg-runtime # [3.40.0](https://github.com/eggjs/tegg/compare/v3.39.5...v3.40.0) (2024-08-22) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.39.5](https://github.com/eggjs/tegg/compare/v3.39.4...v3.39.5) (2024-08-09) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.39.4](https://github.com/eggjs/tegg/compare/v3.39.3...v3.39.4) (2024-07-09) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.39.3](https://github.com/eggjs/tegg/compare/v3.39.2...v3.39.3) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.39.2](https://github.com/eggjs/tegg/compare/v3.39.1...v3.39.2) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.39.1](https://github.com/eggjs/tegg/compare/v3.39.0...v3.39.1) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-runtime # [3.39.0](https://github.com/eggjs/tegg/compare/v3.38.0...v3.39.0) (2024-04-19) **Note:** Version bump only for package @eggjs/tegg-runtime # [3.38.0](https://github.com/eggjs/tegg/compare/v3.37.3...v3.38.0) (2024-04-18) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.37.3](https://github.com/eggjs/tegg/compare/v3.37.2...v3.37.3) (2024-04-17) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.37.2](https://github.com/eggjs/tegg/compare/v3.37.1...v3.37.2) (2024-04-17) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.37.1](https://github.com/eggjs/tegg/compare/v3.37.0...v3.37.1) (2024-04-16) **Note:** Version bump only for package @eggjs/tegg-runtime # [3.37.0](https://github.com/eggjs/tegg/compare/v3.36.3...v3.37.0) (2024-04-16) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.36.3](https://github.com/eggjs/tegg/compare/v3.36.2...v3.36.3) (2024-04-10) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.36.2](https://github.com/eggjs/tegg/compare/v3.36.1...v3.36.2) (2024-04-08) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.36.1](https://github.com/eggjs/tegg/compare/v3.36.0...v3.36.1) (2024-04-07) **Note:** Version bump only for package @eggjs/tegg-runtime # [3.36.0](https://github.com/eggjs/tegg/compare/v3.35.1...v3.36.0) (2024-04-02) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.35.1](https://github.com/eggjs/tegg/compare/v3.35.0...v3.35.1) (2024-03-26) **Note:** Version bump only for package @eggjs/tegg-runtime # [3.35.0](https://github.com/eggjs/tegg/compare/v3.34.0...v3.35.0) (2024-03-26) **Note:** Version bump only for package @eggjs/tegg-runtime # [3.34.0](https://github.com/eggjs/tegg/compare/v3.33.1...v3.34.0) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.33.1](https://github.com/eggjs/tegg/compare/v3.33.0...v3.33.1) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-runtime # [3.33.0](https://github.com/eggjs/tegg/compare/v3.32.0...v3.33.0) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-runtime # [3.32.0](https://github.com/eggjs/tegg/compare/v3.31.0...v3.32.0) (2024-02-19) **Note:** Version bump only for package @eggjs/tegg-runtime # [3.31.0](https://github.com/eggjs/tegg/compare/v3.30.1...v3.31.0) (2024-01-31) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.30.1](https://github.com/eggjs/tegg/compare/v3.30.0...v3.30.1) (2024-01-25) **Note:** Version bump only for package @eggjs/tegg-runtime # [3.30.0](https://github.com/eggjs/tegg/compare/v3.29.0...v3.30.0) (2024-01-17) **Note:** Version bump only for package @eggjs/tegg-runtime # [3.29.0](https://github.com/eggjs/tegg/compare/v3.28.2...v3.29.0) (2023-12-26) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.28.2](https://github.com/eggjs/tegg/compare/v3.28.1...v3.28.2) (2023-12-12) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.28.1](https://github.com/eggjs/tegg/compare/v3.28.0...v3.28.1) (2023-12-11) **Note:** Version bump only for package @eggjs/tegg-runtime # [3.28.0](https://github.com/eggjs/tegg/compare/v3.27.0...v3.28.0) (2023-12-10) **Note:** Version bump only for package @eggjs/tegg-runtime # [3.27.0](https://github.com/eggjs/tegg/compare/v3.26.0...v3.27.0) (2023-11-23) ### Features * impl getObjectFromName ([#167](https://github.com/eggjs/tegg/issues/167)) ([95843c7](https://github.com/eggjs/tegg/commit/95843c74c201ecdfeb7023e16e3f8348a1cb32ea)) ## [3.25.2](https://github.com/eggjs/tegg/compare/v3.25.1...v3.25.2) (2023-11-06) ### Bug Fixes * verify isEggMultiInstancePrototype before proto exists ([#164](https://github.com/eggjs/tegg/issues/164)) ([db9a621](https://github.com/eggjs/tegg/commit/db9a62159886829de36b831f49f296fe05f0b228)) ## [3.25.1](https://github.com/eggjs/tegg/compare/v3.25.0...v3.25.1) (2023-11-03) **Note:** Version bump only for package @eggjs/tegg-runtime # [3.25.0](https://github.com/eggjs/tegg/compare/v3.24.0...v3.25.0) (2023-11-03) ### Features * getObject support MultiInstanceProto ([#161](https://github.com/eggjs/tegg/issues/161)) ([1a24e48](https://github.com/eggjs/tegg/commit/1a24e48cd9a38e906966a21c5f0d1304c4b40d7c)) # [3.24.0](https://github.com/eggjs/tegg/compare/v3.23.0...v3.24.0) (2023-10-26) **Note:** Version bump only for package @eggjs/tegg-runtime # [3.23.0](https://github.com/eggjs/tegg/compare/v3.22.0...v3.23.0) (2023-09-20) **Note:** Version bump only for package @eggjs/tegg-runtime # [3.20.0](https://github.com/eggjs/tegg/compare/v3.19.0...v3.20.0) (2023-09-07) **Note:** Version bump only for package @eggjs/tegg-runtime # [3.19.0](https://github.com/eggjs/tegg/compare/v3.18.1...v3.19.0) (2023-08-31) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.18.1](https://github.com/eggjs/tegg/compare/v3.18.0...v3.18.1) (2023-08-29) **Note:** Version bump only for package @eggjs/tegg-runtime # [3.18.0](https://github.com/eggjs/tegg/compare/v3.17.0...v3.18.0) (2023-08-29) ### Features * add helper to get EggObject from class ([#148](https://github.com/eggjs/tegg/issues/148)) ([77eaf38](https://github.com/eggjs/tegg/commit/77eaf38383ad974b30d13f4c30c489fb7fa7274d)) # [3.17.0](https://github.com/eggjs/tegg/compare/v3.16.0...v3.17.0) (2023-08-24) ### Features * impl MultiInstanceProto ([#145](https://github.com/eggjs/tegg/issues/145)) ([12fd5cf](https://github.com/eggjs/tegg/commit/12fd5cff4004578bcc737dcdf4f7e9d1159f5633)) # [3.16.0](https://github.com/eggjs/tegg/compare/v3.15.0...v3.16.0) (2023-08-24) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.14.3](https://github.com/eggjs/tegg/compare/v3.14.2...v3.14.3) (2023-08-14) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.14.2](https://github.com/eggjs/tegg/compare/v3.14.1...v3.14.2) (2023-08-14) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.14.1](https://github.com/eggjs/tegg/compare/v3.14.0...v3.14.1) (2023-08-11) **Note:** Version bump only for package @eggjs/tegg-runtime # [3.14.0](https://github.com/eggjs/tegg/compare/v3.13.0...v3.14.0) (2023-07-31) **Note:** Version bump only for package @eggjs/tegg-runtime # [3.13.0](https://github.com/eggjs/tegg/compare/v3.12.0...v3.13.0) (2023-07-25) **Note:** Version bump only for package @eggjs/tegg-runtime # [3.12.0](https://github.com/eggjs/tegg/compare/v3.11.1...v3.12.0) (2023-07-13) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.11.1](https://github.com/eggjs/tegg/compare/v3.11.0...v3.11.1) (2023-06-29) **Note:** Version bump only for package @eggjs/tegg-runtime # [3.11.0](https://github.com/eggjs/tegg/compare/v3.10.0...v3.11.0) (2023-06-29) **Note:** Version bump only for package @eggjs/tegg-runtime # [3.10.0](https://github.com/eggjs/tegg/compare/v3.9.0...v3.10.0) (2023-06-28) **Note:** Version bump only for package @eggjs/tegg-runtime # [3.8.0](https://github.com/eggjs/tegg/compare/v3.7.0...v3.8.0) (2023-05-30) ### Features * impl EggObjectLifecycle hook in decorator ([#119](https://github.com/eggjs/tegg/issues/119)) ([cced8a2](https://github.com/eggjs/tegg/commit/cced8a2e009c33d5040fa21d00409fddef471b0e)) # [3.7.0](https://github.com/eggjs/tegg/compare/v3.6.3...v3.7.0) (2023-04-03) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.6.3](https://github.com/eggjs/tegg/compare/v3.6.2...v3.6.3) (2023-03-02) ### Bug Fixes * fix contextEggObjectGetProperty conflict ([#105](https://github.com/eggjs/tegg/issues/105)) ([c570315](https://github.com/eggjs/tegg/commit/c570315ece6ef7443ecf3df2b45aa8c934a5aa38)) # [3.5.0](https://github.com/eggjs/tegg/compare/v3.4.1...v3.5.0) (2023-02-10) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.3.1](https://github.com/eggjs/tegg/compare/v3.3.0...v3.3.1) (2023-01-28) ### Bug Fixes * inject property should be configurable ([#85](https://github.com/eggjs/tegg/issues/85)) ([c13ab55](https://github.com/eggjs/tegg/commit/c13ab55d7b483a5c4a6e4293a6095aa98d070a8b)) ## [3.2.3](https://github.com/eggjs/tegg/compare/v3.2.2...v3.2.3) (2023-01-16) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.2.2](https://github.com/eggjs/tegg/compare/v3.2.1...v3.2.2) (2023-01-06) **Note:** Version bump only for package @eggjs/tegg-runtime ## [3.2.1](https://github.com/eggjs/tegg/compare/v3.2.0...v3.2.1) (2022-12-28) ### Bug Fixes * fix nest inject ctx obj to singleton obj ([#74](https://github.com/eggjs/tegg/issues/74)) ([e4b6252](https://github.com/eggjs/tegg/commit/e4b6252aa79925e16185e568bf7b220f367253ab)) # [3.1.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.1.0) (2022-12-27) ### Bug Fixes * inject context proto to singleton proto ([#72](https://github.com/eggjs/tegg/issues/72)) ([fcc0b2b](https://github.com/eggjs/tegg/commit/fcc0b2b48fc9bce580c1f2bcfcc38039ae909951)) ### Features * **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) # [3.0.0](https://github.com/eggjs/tegg/compare/v3.0.0-alpha.0...v3.0.0) (2022-12-26) **Note:** Version bump only for package @eggjs/tegg-runtime # [3.0.0-alpha.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.0.0-alpha.0) (2022-12-22) ### Features * **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) # [1.3.0](https://github.com/eggjs/tegg/compare/v1.2.0...v1.3.0) (2022-07-01) ### Features * allow inject proto and name ([#40](https://github.com/eggjs/tegg/issues/40)) ([abd1766](https://github.com/eggjs/tegg/commit/abd17665af2528c4c2e33f4c6b0fceddd8a4e76b)) # [0.2.0](https://github.com/eggjs/tegg/compare/v0.1.19...v0.2.0) (2022-01-20) **Note:** Version bump only for package @eggjs/tegg-runtime ================================================ FILE: core/runtime/README.md ================================================ # `@eggjs/tegg-runtime` # Usage This is an internal tegg library, you probably shouldn't use it directly. ================================================ FILE: core/runtime/index.ts ================================================ export * from '@eggjs/tegg-types/runtime'; export { EggRuntimeContext as EggContext } from '@eggjs/tegg-types/runtime'; export * from './src/model/EggContext'; export * from './src/model/AbstractEggContext'; export * from './src/model/LoadUnitInstance'; export * from './src/model/EggObject'; export * from './src/factory/EggContainerFactory'; export * from './src/factory/EggObjectFactory'; export * from './src/factory/LoadUnitInstanceFactory'; export * from './src/impl/ModuleLoadUnitInstance'; export * from './src/impl/EggObjectUtil'; export * from './src/model/ContextHandler'; import './src/impl/EggAlwaysNewObjectContainer'; import './src/impl/ModuleLoadUnitInstance'; ================================================ FILE: core/runtime/package.json ================================================ { "name": "@eggjs/tegg-runtime", "version": "3.78.15", "description": "tegg runtime", "main": "dist/index.js", "files": [ "dist/**/*.js", "dist/**/*.d.ts" ], "typings": "dist/index.d.ts", "keywords": [ "egg", "typescript", "runtime", "tegg" ], "scripts": { "test": "cross-env NODE_ENV=test NODE_OPTIONS='--no-deprecation' mocha", "clean": "tsc -b --clean", "tsc": "ut run clean && tsc -p ./tsconfig.json", "tsc:pub": "ut run clean && tsc -p ./tsconfig.pub.json", "prepublishOnly": "ut tsc:pub" }, "author": "killagu ", "license": "MIT", "homepage": "https://github.com/eggjs/tegg", "bugs": { "url": "https://github.com/eggjs/tegg/issues" }, "repository": { "type": "git", "url": "git@github.com:eggjs/tegg.git", "directory": "core/runtime" }, "engines": { "node": ">=14.0.0" }, "dependencies": { "@eggjs/core-decorator": "^3.78.15", "@eggjs/tegg-common-util": "^3.78.15", "@eggjs/tegg-lifecycle": "^3.78.15", "@eggjs/tegg-metadata": "^3.78.15", "@eggjs/tegg-types": "^3.78.15" }, "devDependencies": { "@types/mocha": "^10.0.1", "@types/node": "^20.2.4", "cross-env": "^7.0.3", "mm": "^3.2.1", "mocha": "^10.2.0", "ts-node": "^10.9.1", "typescript": "^5.0.4" }, "publishConfig": { "access": "public" }, "gitHead": "240a3e9f40fda65ebb7589727d197db5ce17916c" } ================================================ FILE: core/runtime/src/factory/EggContainerFactory.ts ================================================ import { EggPrototypeFactory } from '@eggjs/tegg-metadata'; import type { ContainerGetMethod, EggContainer, EggObject, EggObjectName, EggProtoImplClass, EggPrototype, LifecycleContext, ObjectInitTypeLike, QualifierInfo, } from '@eggjs/tegg-types'; import { PrototypeUtil } from '@eggjs/core-decorator'; import { ContextHandler } from '../model/ContextHandler'; import { ContextInitiator } from '../impl/ContextInitiator'; import { NameUtil } from '@eggjs/tegg-common-util'; export class EggContainerFactory { private static containerGetMethodMap: Map = new Map(); static registerContainerGetMethod(initType: ObjectInitTypeLike, method: ContainerGetMethod) { this.containerGetMethodMap.set(initType, method); } static getContainer(proto: EggPrototype): EggContainer { const method = this.containerGetMethodMap.get(proto.initType); if (!method) { throw new Error(`InitType ${proto.initType} has not register ContainerGetMethod`); } return method(proto); } /** * get or create egg object * If get singleton egg object in context, * will create context egg object for it. */ static async getOrCreateEggObject(proto: EggPrototype, name?: EggObjectName): Promise { const container = this.getContainer(proto); name = name || proto.name; const obj = await container.getOrCreateEggObject(name, proto); const ctx = ContextHandler.getContext(); if (ctx) { const initiator = ContextInitiator.createContextInitiator(ctx); await initiator.init(obj); } return obj; } /** * get or create egg object from the Class * If get singleton egg object in context, * will create context egg object for it. */ static async getOrCreateEggObjectFromClazz(clazz: EggProtoImplClass, name?: EggObjectName, qualifiers?: QualifierInfo[]): Promise { let proto = PrototypeUtil.getClazzProto(clazz as EggProtoImplClass) as EggPrototype | undefined; if (PrototypeUtil.isEggMultiInstancePrototype(clazz as EggProtoImplClass)) { const defaultName = NameUtil.getClassName(clazz as EggProtoImplClass); name = name ?? defaultName; proto = EggPrototypeFactory.instance.getPrototype(name, undefined, qualifiers); } else if (proto) { name = name ?? proto.name; } if (!proto) { throw new Error(`can not get proto for clazz ${clazz.name}`); } return await this.getOrCreateEggObject(proto, name); } /** * get or create egg object from the Name * If get singleton egg object in context, * will create context egg object for it. */ static async getOrCreateEggObjectFromName(name: EggObjectName, qualifiers?: QualifierInfo[]): Promise { const proto = EggPrototypeFactory.instance.getPrototype(name, undefined, qualifiers); if (!proto) { throw new Error(`can not get proto for clazz ${String(name)}`); } return await this.getOrCreateEggObject(proto, name); } static getEggObject(proto: EggPrototype, name?: EggObjectName): EggObject { const container = this.getContainer(proto); name = name || proto.name; return container.getEggObject(name, proto); } } ================================================ FILE: core/runtime/src/factory/EggObjectFactory.ts ================================================ import type { CreateObjectMethod, EggObject, EggObjectLifeCycleContext, EggObjectName, EggPrototype, EggPrototypeClass, } from '@eggjs/tegg-types'; import { LoadUnitFactory } from '@eggjs/tegg-metadata'; import EggObjectImpl from '../impl/EggObjectImpl'; import { EggObjectLifecycleUtil } from '../model/EggObject'; import { LoadUnitInstanceFactory } from './LoadUnitInstanceFactory'; interface EggObjectPair { obj: EggObject; ctx: EggObjectLifeCycleContext; } export class EggObjectFactory { static eggObjectMap: Map = new Map(); static eggObjectCreateMap: Map = new Map(); public static registerEggObjectCreateMethod(protoClass: EggPrototypeClass, method: CreateObjectMethod) { this.eggObjectCreateMap.set(protoClass, method); } public static getEggObjectCreateMethod(protoClass: EggPrototypeClass): CreateObjectMethod { if (this.eggObjectCreateMap.has(protoClass)) { return this.eggObjectCreateMap.get(protoClass)!; } return EggObjectImpl.createObject; } static async createObject(name: EggObjectName, proto: EggPrototype): Promise { const loadUnit = LoadUnitFactory.getLoadUnitById(proto.loadUnitId); if (!loadUnit) { throw new Error(`not found load unit ${proto.loadUnitId}`); } const loadUnitInstance = LoadUnitInstanceFactory.getLoadUnitInstance(loadUnit); const lifecycleContext: EggObjectLifeCycleContext = { loadUnit, loadUnitInstance: loadUnitInstance!, }; const method = this.getEggObjectCreateMethod(proto.constructor as EggPrototypeClass); const args = [ name, proto, lifecycleContext ]; const obj = await Reflect.apply(method, null, args); this.eggObjectMap.set(obj.id, { obj, ctx: lifecycleContext }); return obj; } static async destroyObject(obj: EggObject): Promise { const { ctx } = this.eggObjectMap.get(obj.id)!; try { if (obj.destroy) { await obj.destroy(ctx); } } finally { this.eggObjectMap.delete(obj.id); EggObjectLifecycleUtil.clearObjectLifecycle(obj); } } } ================================================ FILE: core/runtime/src/factory/LoadUnitInstanceFactory.ts ================================================ import { LoadUnitInstanceLifecycleUtil } from '../model/LoadUnitInstance'; import { ObjectInitType } from '@eggjs/tegg-types'; import type { EggLoadUnitTypeLike, EggPrototype, LoadUnit, LoadUnitInstance, LoadUnitInstanceLifecycleContext, } from '@eggjs/tegg-types'; import { IdenticalUtil } from '@eggjs/tegg-lifecycle'; import { EggContainerFactory } from './EggContainerFactory'; type LoadUnitInstanceCreator = (ctx: LoadUnitInstanceLifecycleContext) => LoadUnitInstance; interface LoadUnitInstancePair { instance: LoadUnitInstance; ctx: LoadUnitInstanceLifecycleContext; } export class LoadUnitInstanceFactory { private static creatorMap: Map = new Map(); private static instanceMap: Map = new Map(); static registerLoadUnitInstanceClass(type: EggLoadUnitTypeLike, creator: LoadUnitInstanceCreator) { this.creatorMap.set(type, creator); } static async createLoadUnitInstance(loadUnit: LoadUnit): Promise { const creator = this.creatorMap.get(loadUnit.type); if (!creator) { throw new Error(`load unit instance type ${loadUnit.type} is not implement`); } const instanceId = IdenticalUtil.createLoadUnitInstanceId(loadUnit.id); if (!this.instanceMap.has(instanceId)) { const ctx: LoadUnitInstanceLifecycleContext = { loadUnit, }; const instance = creator(ctx); this.instanceMap.set(instanceId, { instance, ctx }); if (instance.init) { // Module init method will create egg object // When inject objects, will find load unit instance // so should add instance to instanceMap first await instance.init(ctx); } } return this.instanceMap.get(instanceId)!.instance; } static getLoadUnitInstance(loadUnit: LoadUnit): LoadUnitInstance | undefined { const instanceId = IdenticalUtil.createLoadUnitInstanceId(loadUnit.id); return this.instanceMap.get(instanceId)?.instance; } static async destroyLoadUnitInstance(loadUnitInstance: LoadUnitInstance) { const { ctx } = this.instanceMap.get(loadUnitInstance.id)!; await LoadUnitInstanceLifecycleUtil.objectPreDestroy(ctx, loadUnitInstance); if (loadUnitInstance.destroy) { await loadUnitInstance.destroy(ctx); } this.instanceMap.delete(loadUnitInstance.id); LoadUnitInstanceLifecycleUtil.clearObjectLifecycle(loadUnitInstance); } static getLoadUnitInstanceByProto(proto: EggPrototype): LoadUnitInstance { for (const { instance } of this.instanceMap.values()) { if (instance.loadUnit.containPrototype(proto)) { return instance; } } throw new Error(`not found load unit for proto ${proto.id}`); } } EggContainerFactory.registerContainerGetMethod(ObjectInitType.SINGLETON, (proto: EggPrototype) => { return LoadUnitInstanceFactory.getLoadUnitInstanceByProto(proto); }); ================================================ FILE: core/runtime/src/impl/ContextInitiator.ts ================================================ import { LoadUnitFactory } from '@eggjs/tegg-metadata'; import type { EggRuntimeContext, EggObject } from '@eggjs/tegg-types'; import { ContextObjectGraph } from './ContextObjectGraph'; import { EggContainerFactory } from '../factory/EggContainerFactory'; const CONTEXT_INITIATOR = Symbol('EggContext#ContextInitiator'); export class ContextInitiator { private readonly eggContext: EggRuntimeContext; private readonly eggObjectInitRecorder: WeakMap; private readonly eggObjectInitPromise: WeakMap>; constructor(eggContext: EggRuntimeContext) { this.eggContext = eggContext; this.eggObjectInitRecorder = new WeakMap(); this.eggObjectInitPromise = new WeakMap(); this.eggContext.set(CONTEXT_INITIATOR, this); } async init(obj: EggObject) { if (this.eggObjectInitRecorder.get(obj) === true) { if (this.eggObjectInitPromise.has(obj)) { await this.eggObjectInitPromise.get(obj); } return; } this.eggObjectInitRecorder.set(obj, true); const injectObjectProtos = ContextObjectGraph.getContextProto(obj.proto); const initPromise = Promise.all(injectObjectProtos.map(async injectObject => { const proto = injectObject.proto; const loadUnit = LoadUnitFactory.getLoadUnitById(proto.loadUnitId); if (!loadUnit) { throw new Error(`can not find load unit: ${proto.loadUnitId}`); } await EggContainerFactory.getOrCreateEggObject(proto, injectObject.objName); })); this.eggObjectInitPromise.set(obj, initPromise); await initPromise; this.eggObjectInitPromise.delete(obj); } static createContextInitiator(context: EggRuntimeContext): ContextInitiator { let initiator = context.get(CONTEXT_INITIATOR); if (!initiator) { initiator = new ContextInitiator(context); context.set(CONTEXT_INITIATOR, initiator); } return initiator; } } ================================================ FILE: core/runtime/src/impl/ContextObjectGraph.ts ================================================ import { ObjectInitType } from '@eggjs/tegg-types'; import type { EggPrototype, InjectObjectProto } from '@eggjs/tegg-types'; class InjectProtoHolder { private idSet: Set = new Set(); private injectProtos: Array = []; addInjectProto(injectObjectProto: InjectObjectProto) { const id = `${String(injectObjectProto.objName)}:${injectObjectProto.proto.id}`; if (this.idSet.has(id)) { return; } this.idSet.add(id); this.injectProtos.push(injectObjectProto); } dumpProtos(): Array { return this.injectProtos; } } export class ContextObjectGraph { private static eggObjectInitRecorder: WeakMap> = new WeakMap(); static getContextProto(proto: EggPrototype): InjectObjectProto[] { if (ContextObjectGraph.eggObjectInitRecorder.has(proto)) { return ContextObjectGraph.eggObjectInitRecorder.get(proto)!; } const holder = new InjectProtoHolder(); this.doGetContextProto(proto, holder); const injectObjectProtos = holder.dumpProtos(); ContextObjectGraph.eggObjectInitRecorder.set(proto, injectObjectProtos); return injectObjectProtos; } private static doGetContextProto(proto: EggPrototype, holder: InjectProtoHolder) { for (const injectObject of proto.injectObjects) { if (injectObject.proto.initType === ObjectInitType.CONTEXT && proto.initType !== ObjectInitType.CONTEXT) { holder.addInjectProto(injectObject); } ContextObjectGraph.doGetContextProto(injectObject.proto, holder); } } } ================================================ FILE: core/runtime/src/impl/EggAlwaysNewObjectContainer.ts ================================================ import { ObjectInitType } from '@eggjs/tegg-types'; import type { EggContainer, EggObject, EggObjectName, EggPrototype, Id, LifecycleContext } from '@eggjs/tegg-types'; import { EggObjectFactory } from '../factory/EggObjectFactory'; import { EggContainerFactory } from '../factory/EggContainerFactory'; export class EggAlwaysNewObjectContainer implements EggContainer { static instance = new EggAlwaysNewObjectContainer(); readonly id: Id; constructor() { this.id = 'ALWAYS_NEW_OBJECT_CONTAINER'; } addProtoToCreate() { return; } deleteProtoToCreate() { return; } iterateProtoToCreate(): IterableIterator<[ EggObjectName, EggPrototype ]> { return new Map().entries(); } getEggObject(): EggObject { throw new Error('Always Object can not use getEggObject, should use getOrCreateEggObject'); } async getOrCreateEggObject(name: string, proto: EggPrototype): Promise { return EggObjectFactory.createObject(name, proto); } async destroy(): Promise { // do nothing } async init(): Promise { // do nothing } } EggContainerFactory.registerContainerGetMethod(ObjectInitType.ALWAYS_NEW, () => EggAlwaysNewObjectContainer.instance); ================================================ FILE: core/runtime/src/impl/EggObjectImpl.ts ================================================ import { LoadUnitFactory } from '@eggjs/tegg-metadata'; import type { EggObject, EggObjectLifecycle, EggObjectLifeCycleContext, EggObjectName, EggPrototype, ObjectInfo, QualifierInfo, } from '@eggjs/tegg-types'; import { EggObjectStatus, InjectType, ObjectInitType } from '@eggjs/tegg-types'; import { IdenticalUtil } from '@eggjs/tegg-lifecycle'; import { EggObjectLifecycleUtil } from '../model/EggObject'; import { EggContainerFactory } from '../factory/EggContainerFactory'; import { EggObjectUtil } from './EggObjectUtil'; import { ContextHandler } from '../model/ContextHandler'; export default class EggObjectImpl implements EggObject { private _obj: object; private status: EggObjectStatus = EggObjectStatus.PENDING; readonly proto: EggPrototype; readonly name: EggObjectName; readonly id: string; constructor(name: EggObjectName, proto: EggPrototype) { this.name = name; this.proto = proto; const ctx = ContextHandler.getContext(); this.id = IdenticalUtil.createObjectId(this.proto.id, ctx?.id); } async initWithInjectProperty(ctx: EggObjectLifeCycleContext) { // 1. create obj // 2. call obj lifecycle preCreate // 3. inject deps // 4. call obj lifecycle postCreate // 5. success create try { this._obj = this.proto.constructEggObject(); const objLifecycleHook = this._obj as EggObjectLifecycle; // global hook await EggObjectLifecycleUtil.objectPreCreate(ctx, this); // self hook const postConstructMethod = EggObjectLifecycleUtil.getLifecycleHook('postConstruct', this.proto) ?? 'postConstruct'; if (objLifecycleHook[postConstructMethod]) { await objLifecycleHook[postConstructMethod](ctx, this); } const preInjectMethod = EggObjectLifecycleUtil.getLifecycleHook('preInject', this.proto) ?? 'preInject'; if (objLifecycleHook[preInjectMethod]) { await objLifecycleHook[preInjectMethod](ctx, this); } await Promise.all(this.proto.injectObjects.map(async injectObject => { const proto = injectObject.proto; const loadUnit = LoadUnitFactory.getLoadUnitById(proto.loadUnitId); if (!loadUnit) { throw new Error(`can not find load unit: ${proto.loadUnitId}`); } if (this.proto.initType !== ObjectInitType.CONTEXT && injectObject.proto.initType === ObjectInitType.CONTEXT) { this.injectProperty(injectObject.refName, EggObjectUtil.contextEggObjectGetProperty(proto, injectObject.objName)); } else { const injectObj = await EggContainerFactory.getOrCreateEggObject(proto, injectObject.objName); this.injectProperty(injectObject.refName, EggObjectUtil.eggObjectGetProperty(injectObj)); } })); // global hook await EggObjectLifecycleUtil.objectPostCreate(ctx, this); // self hook const postInjectMethod = EggObjectLifecycleUtil.getLifecycleHook('postInject', this.proto) ?? 'postInject'; if (objLifecycleHook[postInjectMethod]) { await objLifecycleHook[postInjectMethod](ctx, this); } const initMethod = EggObjectLifecycleUtil.getLifecycleHook('init', this.proto) ?? 'init'; if (objLifecycleHook[initMethod]) { await objLifecycleHook[initMethod](ctx, this); } this.status = EggObjectStatus.READY; } catch (e) { this.status = EggObjectStatus.ERROR; throw e; } } async initWithInjectConstructor(ctx: EggObjectLifeCycleContext) { // 1. create inject deps // 2. create obj // 3. call obj lifecycle preCreate // 4. call obj lifecycle postCreate // 5. success create try { const constructArgs: any[] = await Promise.all(this.proto.injectObjects!.map(async injectObject => { const proto = injectObject.proto; const loadUnit = LoadUnitFactory.getLoadUnitById(proto.loadUnitId); if (!loadUnit) { throw new Error(`can not find load unit: ${proto.loadUnitId}`); } if (this.proto.initType !== ObjectInitType.CONTEXT && injectObject.proto.initType === ObjectInitType.CONTEXT) { return EggObjectUtil.contextEggObjectProxy(proto, injectObject.objName); } const injectObj = await EggContainerFactory.getOrCreateEggObject(proto, injectObject.objName); return EggObjectUtil.eggObjectProxy(injectObj); })); if (typeof this.proto.multiInstanceConstructorIndex !== 'undefined') { const qualifiers = this.proto.multiInstanceConstructorAttributes ?.map(t => { return { attribute: t, value: this.proto.getQualifier(t), } as QualifierInfo; }) ?.filter(t => typeof t.value !== 'undefined') ?? []; const objInfo: ObjectInfo = { name: this.proto.name, qualifiers, }; constructArgs.splice(this.proto.multiInstanceConstructorIndex, 0, objInfo); } this._obj = this.proto.constructEggObject(...constructArgs); const objLifecycleHook = this._obj as EggObjectLifecycle; // global hook await EggObjectLifecycleUtil.objectPreCreate(ctx, this); // self hook const postConstructMethod = EggObjectLifecycleUtil.getLifecycleHook('postConstruct', this.proto) ?? 'postConstruct'; if (objLifecycleHook[postConstructMethod]) { await objLifecycleHook[postConstructMethod](ctx, this); } const preInjectMethod = EggObjectLifecycleUtil.getLifecycleHook('preInject', this.proto) ?? 'preInject'; if (objLifecycleHook[preInjectMethod]) { await objLifecycleHook[preInjectMethod](ctx, this); } // global hook await EggObjectLifecycleUtil.objectPostCreate(ctx, this); // self hook const postInjectMethod = EggObjectLifecycleUtil.getLifecycleHook('postInject', this.proto) ?? 'postInject'; if (objLifecycleHook[postInjectMethod]) { await objLifecycleHook[postInjectMethod](ctx, this); } const initMethod = EggObjectLifecycleUtil.getLifecycleHook('init', this.proto) ?? 'init'; if (objLifecycleHook[initMethod]) { await objLifecycleHook[initMethod](ctx, this); } this.status = EggObjectStatus.READY; } catch (e) { this.status = EggObjectStatus.ERROR; throw e; } } async init(ctx: EggObjectLifeCycleContext) { if (this.proto.injectType === InjectType.CONSTRUCTOR) { await this.initWithInjectConstructor(ctx); } else { await this.initWithInjectProperty(ctx); } } async destroy(ctx: EggObjectLifeCycleContext) { if (this.status === EggObjectStatus.READY) { this.status = EggObjectStatus.DESTROYING; // global hook await EggObjectLifecycleUtil.objectPreDestroy(ctx, this); // self hook const objLifecycleHook = this._obj as EggObjectLifecycle; const preDestroyMethod = EggObjectLifecycleUtil.getLifecycleHook('preDestroy', this.proto) ?? 'preDestroy'; if (objLifecycleHook[preDestroyMethod]) { await objLifecycleHook[preDestroyMethod](ctx, this); } const destroyMethod = EggObjectLifecycleUtil.getLifecycleHook('destroy', this.proto) ?? 'destroy'; if (objLifecycleHook[destroyMethod]) { await objLifecycleHook[destroyMethod](ctx, this); } this.status = EggObjectStatus.DESTROYED; } } injectProperty(name: EggObjectName, descriptor: PropertyDescriptor) { Reflect.defineProperty(this._obj, name, descriptor); } get obj() { return this._obj; } get isReady() { return this.status === EggObjectStatus.READY; } static async createObject(name: EggObjectName, proto: EggPrototype, lifecycleContext: EggObjectLifeCycleContext): Promise { const obj = new EggObjectImpl(name, proto); await obj.init(lifecycleContext); return obj; } } ================================================ FILE: core/runtime/src/impl/EggObjectUtil.ts ================================================ import type { EggObject, EggPrototype } from '@eggjs/tegg-types'; import { EggContainerFactory } from '../factory/EggContainerFactory'; export class EggObjectUtil { static eggObjectGetProperty(eggObject: EggObject): PropertyDescriptor { return { get(): any { return eggObject.obj; }, configurable: true, enumerable: true, }; } static contextEggObjectGetProperty(proto: EggPrototype, objName: PropertyKey): PropertyDescriptor { const PROTO_OBJ_GETTER = Symbol(`EggPrototype#objGetter#${String(objName)}`); if (!proto[PROTO_OBJ_GETTER]) { proto[PROTO_OBJ_GETTER] = { get(): any { const eggObject = EggContainerFactory.getEggObject(proto, objName); return eggObject.obj; }, configurable: true, enumerable: true, }; } return proto[PROTO_OBJ_GETTER]; } static eggObjectProxy(eggObject: EggObject): PropertyDescriptor { let _obj: object; function getObj() { if (!_obj) { _obj = eggObject.obj; } return _obj; } const proxy = new Proxy({}, { defineProperty(_target: {}, property: string | symbol, attributes: PropertyDescriptor): boolean { const obj = getObj(); Object.defineProperty(obj, property, attributes); return true; }, deleteProperty(_target: {}, p: string | symbol): boolean { const obj = getObj(); delete obj[p]; return true; }, get(target: {}, p: string | symbol): any { // make get be lazy if (p === 'then') return; if (Object.prototype[p]) { return target[p]; } const obj = getObj(); const val = obj[p]; if (typeof val === 'function') { return val.bind(obj); } return val; }, getOwnPropertyDescriptor(_target: {}, p: string | symbol): PropertyDescriptor | undefined { const obj = getObj(); return Object.getOwnPropertyDescriptor(obj, p); }, getPrototypeOf(): object | null { const obj = getObj(); return Object.getPrototypeOf(obj); }, has(_target: {}, p: string | symbol): boolean { const obj = getObj(); return p in obj; }, isExtensible(): boolean { const obj = getObj(); return Object.isExtensible(obj); }, ownKeys(): ArrayLike { const obj = getObj(); return Reflect.ownKeys(obj); }, preventExtensions(): boolean { const obj = getObj(); Object.preventExtensions(obj); return true; }, set(_target: {}, p: string | symbol, newValue: any): boolean { const obj = getObj(); obj[p] = newValue; return true; }, setPrototypeOf(_target: {}, v: object | null): boolean { const obj = getObj(); Object.setPrototypeOf(obj, v); return true; }, }); return proxy; } static contextEggObjectProxy(proto: EggPrototype, objName: PropertyKey): PropertyDescriptor { const PROTO_OBJ_PROXY = Symbol(`EggPrototype#objProxy#${String(objName)}`); if (!proto[PROTO_OBJ_PROXY]) { proto[PROTO_OBJ_PROXY] = new Proxy({}, { defineProperty(_target: {}, property: string | symbol, attributes: PropertyDescriptor): boolean { const eggObject = EggContainerFactory.getEggObject(proto, objName); const obj = eggObject.obj; Object.defineProperty(obj, property, attributes); return true; }, deleteProperty(_target: {}, p: string | symbol): boolean { const eggObject = EggContainerFactory.getEggObject(proto, objName); const obj = eggObject.obj; delete obj[p]; return true; }, get(target: {}, p: string | symbol): any { // make get be lazy if (p === 'then') return; if (Object.prototype[p]) { return target[p]; } const eggObject = EggContainerFactory.getEggObject(proto, objName); const obj = eggObject.obj; return obj[p]; }, getOwnPropertyDescriptor(_target: {}, p: string | symbol): PropertyDescriptor | undefined { const eggObject = EggContainerFactory.getEggObject(proto, objName); const obj = eggObject.obj; return Object.getOwnPropertyDescriptor(obj, p); }, getPrototypeOf(): object | null { const eggObject = EggContainerFactory.getEggObject(proto, objName); const obj = eggObject.obj; return Object.getPrototypeOf(obj); }, has(_target: {}, p: string | symbol): boolean { const eggObject = EggContainerFactory.getEggObject(proto, objName); const obj = eggObject.obj; return p in obj; }, isExtensible(): boolean { const eggObject = EggContainerFactory.getEggObject(proto, objName); const obj = eggObject.obj; return Object.isExtensible(obj); }, ownKeys(): ArrayLike { const eggObject = EggContainerFactory.getEggObject(proto, objName); const obj = eggObject.obj; return Reflect.ownKeys(obj); }, preventExtensions(): boolean { const eggObject = EggContainerFactory.getEggObject(proto, objName); const obj = eggObject.obj; Object.preventExtensions(obj); return true; }, set(_target: {}, p: string | symbol, newValue: any): boolean { const eggObject = EggContainerFactory.getEggObject(proto, objName); const obj = eggObject.obj; obj[p] = newValue; return true; }, setPrototypeOf(_target: {}, v: object | null): boolean { const eggObject = EggContainerFactory.getEggObject(proto, objName); const obj = eggObject.obj; Object.setPrototypeOf(obj, v); return true; }, }); } return proto[PROTO_OBJ_PROXY]; } } ================================================ FILE: core/runtime/src/impl/ModuleLoadUnitInstance.ts ================================================ import { MapUtil } from '@eggjs/tegg-common-util'; import { IdenticalUtil } from '@eggjs/tegg-lifecycle'; import { EggLoadUnitType, ObjectInitType } from '@eggjs/tegg-types'; import type { EggObject, EggObjectName, EggPrototype, EggPrototypeName, Id, LoadUnit, LoadUnitInstance, LoadUnitInstanceLifecycleContext, } from '@eggjs/tegg-types'; import { EggObjectFactory } from '../factory/EggObjectFactory'; import { LoadUnitInstanceFactory } from '../factory/LoadUnitInstanceFactory'; import { LoadUnitInstanceLifecycleUtil } from '../model/LoadUnitInstance'; export class ModuleLoadUnitInstance implements LoadUnitInstance { readonly loadUnit: LoadUnit; readonly id: string; readonly name: string; private protoToCreateMap: [EggPrototypeName, EggPrototype][] = []; private eggObjectMap: Map> = new Map(); private eggObjectPromiseMap: Map>> = new Map(); constructor(loadUnit: LoadUnit) { this.loadUnit = loadUnit; this.name = loadUnit.name; const iterator = this.loadUnit.iterateEggPrototype(); for (const proto of iterator) { if (proto.initType === ObjectInitType.SINGLETON) { this.protoToCreateMap.push([ proto.name, proto, ]); } } this.id = IdenticalUtil.createLoadUnitInstanceId(loadUnit.id); } iterateProtoToCreate(): IterableIterator<[ EggObjectName, EggPrototype ]> { return this.protoToCreateMap[Symbol.iterator](); } addProtoToCreate(name: string, proto: EggPrototype) { this.protoToCreateMap.push([ name, proto ]); } deleteProtoToCreate(name: string) { const index = this.protoToCreateMap.findIndex(([ protoName ]) => protoName === name); if (index !== -1) { this.protoToCreateMap.splice(index, 1); } } async init(ctx: LoadUnitInstanceLifecycleContext): Promise { await LoadUnitInstanceLifecycleUtil.objectPreCreate(ctx, this); for (const [ name, proto ] of this.protoToCreateMap) { await this.getOrCreateEggObject(name, proto); } await LoadUnitInstanceLifecycleUtil.objectPostCreate(ctx, this); } async destroy(): Promise { const objs: EggObject[] = []; for (const protoObjMap of this.eggObjectMap.values()) { for (const obj of protoObjMap.values()) { objs.push(obj); } } this.eggObjectMap.clear(); await Promise.all(objs.map(async obj => { await EggObjectFactory.destroyObject(obj); })); } async getOrCreateEggObject(name: EggPrototypeName, proto: EggPrototype): Promise { if (!this.loadUnit.containPrototype(proto)) { throw new Error('load unit not contain proto'); } const protoObjMap = MapUtil.getOrStore(this.eggObjectMap, proto.id, new Map()); if (!protoObjMap.has(name)) { const protoObjPromiseMap = MapUtil.getOrStore(this.eggObjectPromiseMap, proto.id, new Map()); if (!protoObjPromiseMap.has(name)) { const objPromise = EggObjectFactory.createObject(name, proto); protoObjPromiseMap.set(name, objPromise); const obj = await objPromise; protoObjPromiseMap.delete(name); protoObjMap.set(name, obj); } else { await protoObjPromiseMap.get(name); } } return protoObjMap.get(name)!; } getEggObject(name: EggPrototypeName, proto: EggPrototype): EggObject { const protoObjMap = this.eggObjectMap.get(proto.id); if (!protoObjMap || !protoObjMap.has(name)) { throw new Error(`EggObject ${String(proto.name)} not found`); } return protoObjMap.get(name)!; } static createModuleLoadUnitInstance(ctx: LoadUnitInstanceLifecycleContext): LoadUnitInstance { return new ModuleLoadUnitInstance(ctx.loadUnit); } } LoadUnitInstanceFactory.registerLoadUnitInstanceClass(EggLoadUnitType.MODULE, ModuleLoadUnitInstance.createModuleLoadUnitInstance); ================================================ FILE: core/runtime/src/model/AbstractEggContext.ts ================================================ import { ObjectInitType } from '@eggjs/tegg-types'; import type { EggRuntimeContext, EggContextLifecycleContext, EggObject, EggObjectName, EggPrototype, EggPrototypeName, Id } from '@eggjs/tegg-types'; import { TeggError } from '@eggjs/tegg-metadata'; import { EggContextLifecycleUtil } from './EggContext'; import { MapUtil } from '@eggjs/tegg-common-util'; import { EggContainerFactory } from '../factory/EggContainerFactory'; import { EggObjectFactory } from '../factory/EggObjectFactory'; import { ContextHandler } from './ContextHandler'; export abstract class AbstractEggContext implements EggRuntimeContext { private contextData: Map = new Map(); private protoToCreate: Map = new Map(); private eggObjectMap: Map> = new Map(); private eggObjectPromiseMap: Map>> = new Map(); private destroyed = false; abstract id: string; addProtoToCreate(name: string, proto: EggPrototype) { this.protoToCreate.set(name, proto); } deleteProtoToCreate(name) { this.protoToCreate.delete(name); } async destroy(ctx: EggContextLifecycleContext): Promise { await EggContextLifecycleUtil.objectPreDestroy(ctx, this); const objs: EggObject[] = []; for (const protoObjMap of this.eggObjectMap.values()) { for (const protoObj of protoObjMap.values()) { objs.push(protoObj); } } this.eggObjectMap.clear(); await Promise.all(objs.map(async obj => { await EggObjectFactory.destroyObject(obj); })); this.contextData.clear(); this.destroyed = true; await EggContextLifecycleUtil.clearObjectLifecycle(this); } get(key: string | symbol): any | undefined { return this.contextData.get(key); } getEggObject(name: EggPrototypeName, proto: EggPrototype): EggObject { if (this.destroyed) { throw TeggError.create(`Can not read property \`${String(name)}\` because egg ctx has been destroyed`, 'read_after_ctx_destroyed'); } const protoObjMap = this.eggObjectMap.get(proto.id); if (!protoObjMap || !protoObjMap.has(name)) { throw new Error(`EggObject ${String(proto.name)} not found`); } return protoObjMap.get(name)!; } async getOrCreateEggObject(name: EggPrototypeName, proto: EggPrototype): Promise { const protoObjMap = MapUtil.getOrStore(this.eggObjectMap, proto.id, new Map()); if (!protoObjMap.has(name)) { const protoObjPromiseMap = MapUtil.getOrStore(this.eggObjectPromiseMap, proto.id, new Map()); if (!protoObjPromiseMap.has(name)) { const objPromise = EggObjectFactory.createObject(name, proto); protoObjPromiseMap.set(name, objPromise); const obj = await objPromise; protoObjPromiseMap.delete(name); if (!protoObjPromiseMap.size) { this.eggObjectPromiseMap.delete(proto.id); } protoObjMap.set(name, obj); } else { await protoObjPromiseMap.get(name); } } return protoObjMap.get(name)!; } async init(ctx: EggContextLifecycleContext): Promise { await EggContextLifecycleUtil.objectPreCreate(ctx, this); for (const [ name, proto ] of this.protoToCreate) { await this.getOrCreateEggObject(name, proto); } await EggContextLifecycleUtil.objectPostCreate(ctx, this); } iterateProtoToCreate(): IterableIterator<[ EggObjectName, EggPrototype ]> { return this.protoToCreate.entries(); } set(key: string | symbol, val: any) { this.contextData.set(key, val); } } EggContainerFactory.registerContainerGetMethod(ObjectInitType.CONTEXT, () => { const ctx = ContextHandler.getContext(); if (!ctx) { throw new Error('ctx is required'); } return ctx; }); ================================================ FILE: core/runtime/src/model/ContextHandler.ts ================================================ import assert from 'node:assert'; import type { EggRuntimeContext } from '@eggjs/tegg-types'; type runInContextCallback = (context: EggRuntimeContext, fn: () => Promise) => Promise; export class ContextHandler { static getContextCallback: () => EggRuntimeContext | undefined; static runInContextCallback: runInContextCallback; static getContext(): EggRuntimeContext | undefined { assert(this.getContextCallback, 'getContextCallback not set'); return this.getContextCallback ? this.getContextCallback() : undefined; } static run(context: EggRuntimeContext, fn: () => Promise): Promise { assert(this.runInContextCallback, 'runInContextCallback not set'); return this.runInContextCallback(context, fn); } } ================================================ FILE: core/runtime/src/model/EggContext.ts ================================================ import type { EggRuntimeContext, EggContextLifecycleContext } from '@eggjs/tegg-types'; import { LifecycleUtil } from '@eggjs/tegg-lifecycle'; export const EggContextLifecycleUtil = new LifecycleUtil(); ================================================ FILE: core/runtime/src/model/EggObject.ts ================================================ import { LifecycleUtil } from '@eggjs/tegg-lifecycle'; import type { EggObject, EggObjectLifeCycleContext } from '@eggjs/tegg-types'; export const EggObjectLifecycleUtil = new LifecycleUtil(); ================================================ FILE: core/runtime/src/model/LoadUnitInstance.ts ================================================ import type { LoadUnitInstance, LoadUnitInstanceLifecycleContext } from '@eggjs/tegg-types'; import { LifecycleUtil } from '@eggjs/tegg-lifecycle'; export const LoadUnitInstanceLifecycleUtil = new LifecycleUtil(); ================================================ FILE: core/runtime/test/EggObject.test.ts ================================================ import { strict as assert } from 'node:assert'; import mm from 'mm'; import { EggPrototypeFactory } from '@eggjs/tegg-metadata'; import { EggTestContext } from './fixtures/EggTestContext'; import TestUtil from './util'; import { EggContainerFactory } from '..'; import { Foo, Bar } from './fixtures/modules/lifecycle-hook/object'; import { Bar as ExtendsBar } from './fixtures/modules/extends-module/Base'; import { ContextHandler } from '../src/model/ContextHandler'; import { SingletonBar } from './fixtures/modules/inject-context-to-singleton/object'; import { SingletonConstructorBar } from './fixtures/modules/inject-constructor-context-to-singleton/object'; describe('test/EggObject.test.ts', () => { let ctx: EggTestContext; beforeEach(() => { ctx = new EggTestContext(); }); afterEach(() => { mm.restore(); }); describe('lifecycle', () => { beforeEach(() => { mm(ContextHandler, 'getContext', () => { return ctx; }); }); describe('context proto', () => { it('should work', async () => { const instance = await TestUtil.createLoadUnitInstance('lifecycle-hook'); const fooProto = EggPrototypeFactory.instance.getPrototype('foo'); const fooObj = await EggContainerFactory.getOrCreateEggObject(fooProto, fooProto.name); const foo = fooObj.obj as Foo; await TestUtil.destroyLoadUnitInstance(instance); const called = foo.getLifecycleCalled(); await ctx.destroy({}); assert.deepStrictEqual(called, [ 'construct', 'postConstruct', 'preInject', 'postInject', 'init', 'preDestroy', 'destroy', ]); }); it('should clear eggObjectMap/eggObjectPromiseMap/contextData after destroy', async () => { const instance = await TestUtil.createLoadUnitInstance('lifecycle-hook'); const fooProto = EggPrototypeFactory.instance.getPrototype('foo'); const fooObj = await EggContainerFactory.getOrCreateEggObject(fooProto, fooProto.name); assert(fooObj.obj); await ctx.destroy({}); await TestUtil.destroyLoadUnitInstance(instance); // should clear all maps const assertCtx = ctx as any; assert(!assertCtx.eggObjectMap.size); assert(!assertCtx.eggObjectPromiseMap.size); assert(!assertCtx.contextData.size); }); }); describe('singleton proto', () => { it('should work', async () => { const instance = await TestUtil.createLoadUnitInstance('lifecycle-hook'); const barProto = EggPrototypeFactory.instance.getPrototype('bar'); const barObj = await EggContainerFactory.getOrCreateEggObject(barProto, barProto.name); const bar = barObj.obj as Bar; // get obj from class const barObj2 = await EggContainerFactory.getOrCreateEggObjectFromClazz((barProto as any).clazz, barProto.name); assert.equal(barObj2, barObj); assert.equal(barObj2.obj, barObj.obj); // get obj from name const barObj3 = await EggContainerFactory.getOrCreateEggObjectFromName('bar'); assert.equal(barObj3, barObj); assert.equal(barObj3.obj, barObj.obj); await TestUtil.destroyLoadUnitInstance(instance); const called = bar.getLifecycleCalled(); assert.deepStrictEqual(called, [ 'construct', 'postConstruct', 'preInject', 'postInject', 'init', 'preDestroy', 'destroy', ]); }); }); }); describe('inject context to singleton', () => { it('should work', async () => { mm(ContextHandler, 'getContext', () => { return; }); const instance = await TestUtil.createLoadUnitInstance('inject-context-to-singleton'); const barProto = EggPrototypeFactory.instance.getPrototype('singletonBar'); mm(ContextHandler, 'getContext', () => { return ctx; }); const barObj = await EggContainerFactory.getOrCreateEggObject(barProto, barProto.name); const bar = barObj.obj as SingletonBar; const msg = await bar.hello(); assert(msg === 'hello from depth2'); await TestUtil.destroyLoadUnitInstance(instance); await ctx.destroy({}); }); }); describe('constructor inject context to singleton', () => { it('should work', async () => { mm(ContextHandler, 'getContext', () => { return; }); const instance = await TestUtil.createLoadUnitInstance('inject-constructor-context-to-singleton'); const barProto = EggPrototypeFactory.instance.getPrototype('singletonConstructorBar'); mm(ContextHandler, 'getContext', () => { return ctx; }); const barObj = await EggContainerFactory.getOrCreateEggObject(barProto, barProto.name); const bar = barObj.obj as SingletonConstructorBar; const msg = await bar.hello(); assert(msg === 'hello from depth2'); await TestUtil.destroyLoadUnitInstance(instance); await ctx.destroy({}); }); }); describe('property mock', () => { beforeEach(() => { mm(ContextHandler, 'getContext', () => { return ctx; }); }); it('should work', async () => { const instance = await TestUtil.createLoadUnitInstance('extends-module'); const barProto = EggPrototypeFactory.instance.getPrototype('bar', instance.loadUnit); const barObj = await EggContainerFactory.getOrCreateEggObject(barProto, barProto.name); const bar = barObj.obj as ExtendsBar; const foo = {}; mm(bar, 'foo', foo); assert(bar.foo === foo); await TestUtil.destroyLoadUnitInstance(instance); await ctx.destroy({}); }); }); describe('ContextInitiator', () => { it('should work for concurrent init', async () => { mm(ContextHandler, 'getContext', () => { return; }); const instance = await TestUtil.createLoadUnitInstance('inject-constructor-context-to-singleton'); const barProto = EggPrototypeFactory.instance.getPrototype('singletonConstructorBar'); mm(ContextHandler, 'getContext', () => { return ctx; }); const hello = async () => { const barObj = await EggContainerFactory.getOrCreateEggObject(barProto, barProto.name); const bar = barObj.obj as SingletonConstructorBar; return await bar.hello(); }; // concurrent init const res = await Promise.allSettled(new Array(2).fill(0).map(hello)); assert.deepEqual(res, [ { status: 'fulfilled', value: 'hello from depth2' }, { status: 'fulfilled', value: 'hello from depth2' }, ]); // retry after init success const res2 = await hello(); assert.equal(res2, 'hello from depth2'); await TestUtil.destroyLoadUnitInstance(instance); await ctx.destroy({}); }); }); }); ================================================ FILE: core/runtime/test/EggObjectUtil.test.ts ================================================ import assert from 'node:assert'; import mm from 'mm'; import { EggPrototypeFactory } from '@eggjs/tegg-metadata'; import { EggObjectUtil } from '../src/impl/EggObjectUtil'; import { ContextHandler } from '../src/model/ContextHandler'; import { EggTestContext } from './fixtures/EggTestContext'; import TestUtil from './util'; describe('test/EggObjectUtil.test.ts', () => { let ctx: EggTestContext; beforeEach(() => { ctx = new EggTestContext(); mm(ContextHandler, 'getContext', () => { return ctx; }); }); it('should name should has self descriptor', async () => { const instance = await TestUtil.createLoadUnitInstance('lifecycle-hook'); const fooProto = EggPrototypeFactory.instance.getPrototype('foo'); const fooDesc = EggObjectUtil.contextEggObjectGetProperty(fooProto, 'foo'); const barDesc = EggObjectUtil.contextEggObjectGetProperty(fooProto, 'bar'); assert(fooDesc !== barDesc); await TestUtil.destroyLoadUnitInstance(instance); }); }); ================================================ FILE: core/runtime/test/LoadUnitInstance.test.ts ================================================ import assert from 'node:assert'; import mm from 'mm'; import { EggPrototypeFactory } from '@eggjs/tegg-metadata'; import { LoadUnitInstance } from '@eggjs/tegg-types'; import { EggTestContext } from './fixtures/EggTestContext'; import TestUtil from './util'; import { EggContainerFactory } from '..'; import CountController from './fixtures/modules/module-for-load-unit-instance/CountController'; import AppService from './fixtures/modules/multi-module/multi-module-service/AppService'; import { Bar, Foo } from './fixtures/modules/extends-module/Base'; import { ContextHandler } from '../src/model/ContextHandler'; import { EggContextStorage } from './fixtures/EggContextStorage'; import { FOO_ATTRIBUTE, FooLogger } from './fixtures/modules/multi-instance-module/MultiInstance'; import { FooLoggerConstructor } from './fixtures/modules/multi-instance-module/MultiInstanceConstructor'; import path from 'node:path'; import { LoaderUtil } from '@eggjs/module-test-util'; describe('test/LoadUnit/LoadUnitInstance.test.ts', () => { describe('ModuleLoadUnitInstance', () => { let ctx: EggTestContext; beforeEach(() => { ctx = new EggTestContext(); mm(ContextHandler, 'getContext', () => { return ctx; }); }); afterEach(async () => { await ctx.destroy({}); mm.restore(); }); it('should create success', async () => { const instance = await TestUtil.createLoadUnitInstance('module-for-load-unit-instance'); const countControllerProto = EggPrototypeFactory.instance.getPrototype('countController'); const countControllerObj = await EggContainerFactory.getOrCreateEggObject(countControllerProto, countControllerProto.name); const countController = countControllerObj.obj as CountController; const countResult = await countController.getCount(); assert.deepStrictEqual(countResult, { serviceCount: 0, serviceTempCount: 0, controllerTempCount: 0, }); const countResult2 = await countController.getCount(); assert.deepStrictEqual(countResult2, { serviceCount: 1, serviceTempCount: 1, controllerTempCount: 1, }); await TestUtil.destroyLoadUnitInstance(instance); }); it('should load extends class success', async () => { const instance = await TestUtil.createLoadUnitInstance('extends-module'); const barProto = EggPrototypeFactory.instance.getPrototype('bar', instance.loadUnit); const barObj = await EggContainerFactory.getOrCreateEggObject(barProto, barProto.name); const bar = barObj.obj as Bar; const fooProto = EggPrototypeFactory.instance.getPrototype('foo', instance.loadUnit); const fooObj = await EggContainerFactory.getOrCreateEggObject(fooProto, fooProto.name); const foo = fooObj.obj as Foo; assert(bar.foo); assert(bar.logger); assert(foo.logger); await TestUtil.destroyLoadUnitInstance(instance); }); it('should load multi instance', async () => { const instance = await TestUtil.createLoadUnitInstance('multi-instance-module'); const foo1Proto = EggPrototypeFactory.instance.getPrototype('foo', instance.loadUnit, [{ attribute: FOO_ATTRIBUTE, value: 'foo1', }]); const foo1Obj = await EggContainerFactory.getOrCreateEggObject(foo1Proto, foo1Proto.name); const foo1 = foo1Obj.obj as FooLogger; const foo2Proto = EggPrototypeFactory.instance.getPrototype('foo', instance.loadUnit, [{ attribute: FOO_ATTRIBUTE, value: 'foo2', }]); const foo2Obj = await EggContainerFactory.getOrCreateEggObject(foo2Proto, foo2Proto.name); const foo2 = foo2Obj.obj as FooLogger; assert(foo1); assert(foo2); assert(foo1 !== foo2); assert(foo1.loadUnitPath); assert(foo1.foo === 'foo1'); assert(foo2.loadUnitPath); assert(foo2.foo === 'foo2'); const obj1 = await EggContainerFactory.getOrCreateEggObjectFromClazz(FooLogger, 'foo', [{ attribute: FOO_ATTRIBUTE, value: 'foo1', }]); const obj2 = await EggContainerFactory.getOrCreateEggObjectFromClazz(FooLogger, 'foo', [{ attribute: FOO_ATTRIBUTE, value: 'foo2', }]); assert(foo1Obj === obj1); assert(foo2Obj === obj2); await TestUtil.destroyLoadUnitInstance(instance); }); it('should load multi instance with constructor', async () => { const instance = await TestUtil.createLoadUnitInstance('multi-instance-module'); const foo1Proto = EggPrototypeFactory.instance.getPrototype('fooConstructor', instance.loadUnit, [{ attribute: FOO_ATTRIBUTE, value: 'foo1', }]); const foo1Obj = await EggContainerFactory.getOrCreateEggObject(foo1Proto, foo1Proto.name); const foo1 = foo1Obj.obj as FooLoggerConstructor; const foo2Proto = EggPrototypeFactory.instance.getPrototype('fooConstructor', instance.loadUnit, [{ attribute: FOO_ATTRIBUTE, value: 'foo2', }]); const foo2Obj = await EggContainerFactory.getOrCreateEggObject(foo2Proto, foo2Proto.name); const foo2 = foo2Obj.obj as FooLoggerConstructor; assert(foo1); assert(foo2); assert(foo1 !== foo2); assert(foo1.foo === 'foo1'); assert(foo2.foo === 'foo2'); assert(foo1.bar === 'bar'); assert(foo2.foo === 'foo2'); const obj1 = await EggContainerFactory.getOrCreateEggObjectFromClazz(FooLogger, 'fooConstructor', [{ attribute: FOO_ATTRIBUTE, value: 'foo1', }]); const obj2 = await EggContainerFactory.getOrCreateEggObjectFromClazz(FooLogger, 'fooConstructor', [{ attribute: FOO_ATTRIBUTE, value: 'foo2', }]); assert(foo1Obj === obj1); assert(foo2Obj === obj2); await TestUtil.destroyLoadUnitInstance(instance); }); }); describe('MultiModule', () => { let commonInstance: LoadUnitInstance; let repoInstance: LoadUnitInstance; let serviceInstance: LoadUnitInstance; before(async () => { EggContextStorage.register(); LoaderUtil.buildGlobalGraph([ path.join(__dirname, 'fixtures/modules/multi-module/multi-module-common'), path.join(__dirname, 'fixtures/modules/multi-module/multi-module-repo'), path.join(__dirname, 'fixtures/modules/multi-module/multi-module-service'), ]); commonInstance = await TestUtil.createLoadUnitInstance('multi-module/multi-module-common', false); repoInstance = await TestUtil.createLoadUnitInstance('multi-module/multi-module-repo', false); serviceInstance = await TestUtil.createLoadUnitInstance('multi-module/multi-module-service', false); }); after(async () => { await TestUtil.destroyLoadUnitInstance(commonInstance); await TestUtil.destroyLoadUnitInstance(repoInstance); await TestUtil.destroyLoadUnitInstance(serviceInstance); }); it('should get appService', async () => { const saveCtx = new EggTestContext(); const findCtx = new EggTestContext(); const saveAppServiceProto = EggPrototypeFactory.instance.getPrototype('appService', serviceInstance.loadUnit); const [ saveAppService, findAppService ] = await Promise.all([ ContextHandler.run(saveCtx, async () => { const saveAppServiceObj = await EggContainerFactory.getOrCreateEggObject(saveAppServiceProto, saveAppServiceProto.name); const saveAppService = saveAppServiceObj.obj as AppService; await saveAppService.save({ name: 'mock-app', desc: 'mock-desc', }); return saveAppService; }), ContextHandler.run(findCtx, async () => { const findAppServiceObj = await EggContainerFactory.getOrCreateEggObject(saveAppServiceProto, saveAppServiceProto.name); const findAppService = findAppServiceObj.obj as AppService; return findAppService; }), ]); // not same service because ctx is different assert(saveAppService !== findAppService); const app = await findAppService.findApp('mock-app'); assert.deepStrictEqual(app, { name: 'mock-app', desc: 'mock-desc', }); }); }); }); ================================================ FILE: core/runtime/test/QualifierLoadUnitInstance.test.ts ================================================ import assert from 'node:assert'; import mm from 'mm'; import { EggPrototypeFactory } from '@eggjs/tegg-metadata'; import TestUtil from './util'; import { EggTestContext } from './fixtures/EggTestContext'; import CacheService from './fixtures/modules/init-type-qualifier-module/CacheService'; import { EggContainerFactory } from '..'; import { ContextHandler } from '../src/model/ContextHandler'; describe('test/LoadUnit/QualifierLoadUnitInstance.test.ts', () => { let ctx: EggTestContext; beforeEach(() => { ctx = new EggTestContext(); mm(ContextHandler, 'getContext', () => { return ctx; }); }); afterEach(async () => { await ctx.destroy({}); mm.restore(); }); describe('init type qualifier', () => { it('should work', async () => { const instance = await TestUtil.createLoadUnitInstance('init-type-qualifier-module'); const cacheServiceProto = EggPrototypeFactory.instance.getPrototype('cacheService', instance.loadUnit); const cacheServiceObj = await EggContainerFactory.getOrCreateEggObject(cacheServiceProto, cacheServiceProto.name); const cacheService = cacheServiceObj.obj as CacheService; cacheService.setContextCache('cacheKey', 'cacheVal'); cacheService.setSingletonCache('cacheKey', 'cacheVal'); const contextCache = cacheService.getContextCache('cacheKey'); assert.deepStrictEqual(contextCache, { val: 'cacheVal', from: 'context', }); const singletonCache = cacheService.getSingletonCache('cacheKey'); assert.deepStrictEqual(singletonCache, { val: 'cacheVal', from: 'singleton', }); await TestUtil.destroyLoadUnitInstance(instance); }); }); }); ================================================ FILE: core/runtime/test/fixtures/EggContextStorage.ts ================================================ import { AsyncLocalStorage } from 'node:async_hooks'; import { EggRuntimeContext } from '@eggjs/tegg-types'; import { ContextHandler } from '../../src/model/ContextHandler'; export class EggContextStorage { static storage = new AsyncLocalStorage(); static register() { ContextHandler.getContextCallback = () => { return EggContextStorage.storage.getStore(); }; ContextHandler.runInContextCallback = (context: EggRuntimeContext, fn: () => Promise) => { return EggContextStorage.storage.run(context, fn); }; } } ================================================ FILE: core/runtime/test/fixtures/EggTestContext.ts ================================================ import { AbstractEggContext } from '../..'; import { IdenticalUtil } from '@eggjs/tegg-lifecycle'; const EGG_CTX = Symbol('TEgg#context'); export interface Tracer { readonly traceId: string; } export class EggTestContext extends AbstractEggContext { data: Map = new Map(); readonly id: string; constructor() { super(); const mockCtx = { tracer: { traceId: 'mock-traceId', }, }; this.id = IdenticalUtil.createContextId(); this.set(EGG_CTX, mockCtx); } } ================================================ FILE: core/runtime/test/fixtures/modules/extends-module/Base.ts ================================================ import { ContextProto, Inject } from '@eggjs/core-decorator'; @ContextProto() export class Logger { } @ContextProto() export class Base { @Inject() logger: Logger; } @ContextProto() export class Foo extends Base { } @ContextProto() export class Bar extends Base { @Inject() foo: Foo; } ================================================ FILE: core/runtime/test/fixtures/modules/extends-module/package.json ================================================ { "name": "multi-module-service", "eggModule": { "name": "extendsModule" } } ================================================ FILE: core/runtime/test/fixtures/modules/init-type-qualifier-module/Cache.ts ================================================ export interface CacheValue { from: string; val: string | undefined; } export interface ICache { get(key: string): CacheValue; set(key: string, val: string); } ================================================ FILE: core/runtime/test/fixtures/modules/init-type-qualifier-module/CacheService.ts ================================================ import { ICache, CacheValue } from './Cache'; import { ObjectInitType } from '@eggjs/tegg-types'; import { ContextProto, InitTypeQualifier, Inject } from '@eggjs/core-decorator'; @ContextProto() export default class CacheService { @InitTypeQualifier(ObjectInitType.SINGLETON) @Inject({ name: 'cache' }) singletonCache: ICache; @InitTypeQualifier(ObjectInitType.CONTEXT) @Inject({ name: 'cache' }) contextCache: ICache; setSingletonCache(key: string, val: string) { this.singletonCache.set(key, val); } getSingletonCache(key: string): CacheValue { return this.singletonCache.get(key); } setContextCache(key: string, val: string) { this.contextCache.set(key, val); } getContextCache(key: string): CacheValue { return this.contextCache.get(key); } } ================================================ FILE: core/runtime/test/fixtures/modules/init-type-qualifier-module/ContextCache.ts ================================================ import { ContextProto } from '@eggjs/core-decorator'; import { ICache, CacheValue } from './Cache'; @ContextProto({ name: 'cache', }) export default class ContextCache implements ICache { private map = new Map(); get(key: string): CacheValue { const val = this.map.get(key); return { val, from: 'context', }; } set(key: string, val: string) { this.map.set(key, val); } } ================================================ FILE: core/runtime/test/fixtures/modules/init-type-qualifier-module/SingletonCache.ts ================================================ import { ICache, CacheValue } from './Cache'; import { SingletonProto } from '@eggjs/core-decorator'; @SingletonProto({ name: 'cache', }) export default class SingletonCache implements ICache { private map = new Map(); get(key: string): CacheValue { const val = this.map.get(key); return { val, from: 'singleton', }; } set(key: string, val: string) { this.map.set(key, val); } } ================================================ FILE: core/runtime/test/fixtures/modules/init-type-qualifier-module/package.json ================================================ { "name": "demo-app-repo", "eggModule": { "name": "init-type-qualifier-module" }, "main": "index.js" } ================================================ FILE: core/runtime/test/fixtures/modules/inject-constructor-context-to-singleton/object.ts ================================================ import { AccessLevel } from '@eggjs/tegg-types'; import { ContextProto, Inject, SingletonProto } from '@eggjs/core-decorator'; @ContextProto({ accessLevel: AccessLevel.PUBLIC, }) export class ContextFooDepth2 { async hello() { return 'hello from depth2'; } } @SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) export class SingletonConstructorBarDepth3 { constructor(@Inject() readonly contextFooDepth2: ContextFooDepth2) { } async hello() { return this.contextFooDepth2.hello(); } } @SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) export class SingletonBarConstructorDepth2 { constructor(@Inject() readonly singletonConstructorBarDepth3: SingletonConstructorBarDepth3) { } async hello() { return this.singletonConstructorBarDepth3.hello(); } } @ContextProto({ accessLevel: AccessLevel.PUBLIC, }) export class ContextConstructorFoo { constructor(@Inject() readonly singletonBarConstructorDepth2: SingletonBarConstructorDepth2) { } async hello() { return this.singletonBarConstructorDepth2.hello(); } } @SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) export class SingletonConstructorBar { constructor(@Inject() readonly contextConstructorFoo: ContextConstructorFoo) { } async hello() { return this.contextConstructorFoo.hello(); } } ================================================ FILE: core/runtime/test/fixtures/modules/inject-constructor-context-to-singleton/package.json ================================================ { "name": "multi-module-service", "eggModule": { "name": "extendsModule" } } ================================================ FILE: core/runtime/test/fixtures/modules/inject-context-to-singleton/object.ts ================================================ import { AccessLevel } from '@eggjs/tegg-types'; import { ContextProto, Inject, SingletonProto } from '@eggjs/core-decorator'; @ContextProto({ accessLevel: AccessLevel.PUBLIC, }) export class ContextFooDepth2 { async hello() { return 'hello from depth2'; } } @SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) export class SingletonBarDepth3 { @Inject() contextFooDepth2: ContextFooDepth2; async hello() { return this.contextFooDepth2.hello(); } } @SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) export class SingletonBarDepth2 { @Inject() singletonBarDepth3: SingletonBarDepth3; async hello() { return this.singletonBarDepth3.hello(); } } @ContextProto({ accessLevel: AccessLevel.PUBLIC, }) export class ContextFoo { @Inject() private readonly singletonBarDepth2: SingletonBarDepth2; async hello() { return this.singletonBarDepth2.hello(); } } @SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) export class SingletonBar { @Inject() foo: ContextFoo; async hello() { return this.foo.hello(); } } ================================================ FILE: core/runtime/test/fixtures/modules/inject-context-to-singleton/package.json ================================================ { "name": "multi-module-service", "eggModule": { "name": "extendsModule" } } ================================================ FILE: core/runtime/test/fixtures/modules/lifecycle-hook/object.ts ================================================ import { AccessLevel, EggObjectLifecycle } from '@eggjs/tegg-types'; import { ContextProto, SingletonProto } from '@eggjs/core-decorator'; import { LifecyclePostConstruct, LifecyclePreInject, LifecyclePostInject, LifecycleInit, LifecyclePreDestroy, LifecycleDestroy, } from '@eggjs/tegg-lifecycle'; @ContextProto({ accessLevel: AccessLevel.PUBLIC, }) export class Foo implements EggObjectLifecycle { private called: string[] = []; getLifecycleCalled() { return this.called; } constructor() { this.called.push('construct'); } async postConstruct(): Promise { this.called.push('postConstruct'); } async preInject(): Promise { this.called.push('preInject'); } async postInject(): Promise { this.called.push('postInject'); } async init(): Promise { this.called.push('init'); } async preDestroy(): Promise { this.called.push('preDestroy'); } async destroy(): Promise { this.called.push('destroy'); } } @SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) export class Bar { private called: string[] = []; getLifecycleCalled() { return this.called; } constructor() { this.called.push('construct'); } @LifecyclePostConstruct() protected async _postConstruct() { this.called.push('postConstruct'); } @LifecyclePreInject() protected async _preInject() { this.called.push('preInject'); } @LifecyclePostInject() protected async _postInject() { this.called.push('postInject'); } protected async init() { this.called.push('init should not called'); } @LifecycleInit() protected async _init() { this.called.push('init'); } @LifecyclePreDestroy() protected async _preDestroy() { this.called.push('preDestroy'); } @LifecycleDestroy() protected async _destroy() { this.called.push('destroy'); } } ================================================ FILE: core/runtime/test/fixtures/modules/lifecycle-hook/package.json ================================================ { "name": "multi-module-service", "eggModule": { "name": "extendsModule" } } ================================================ FILE: core/runtime/test/fixtures/modules/module-for-load-unit-instance/AppCache.ts ================================================ import { SingletonProto } from '@eggjs/core-decorator'; @SingletonProto() export default class AppCache { count = 0; async getCount(): Promise { return this.count++; } } ================================================ FILE: core/runtime/test/fixtures/modules/module-for-load-unit-instance/CountController.ts ================================================ import { AccessLevel } from '@eggjs/tegg-types'; import { ContextProto, Inject } from '@eggjs/core-decorator'; import CountService from './CountService'; import TempObj from './TempObj'; interface CountResult { serviceCount: number; serviceTempCount: number; controllerTempCount: number; // traceId: string; } @ContextProto({ accessLevel: AccessLevel.PUBLIC, }) export default class CountController { @Inject() countService: CountService; @Inject() tempObj: TempObj; async getCount(): Promise { const count = await this.countService.getCount(); const serviceTempCount = await this.countService.getTempCount(); return { serviceCount: count, serviceTempCount, controllerTempCount: await this.tempObj.getCount(), }; } } ================================================ FILE: core/runtime/test/fixtures/modules/module-for-load-unit-instance/CountService.ts ================================================ import { ContextProto, Inject } from '@eggjs/core-decorator'; import AppCache from './AppCache'; import TempObj from './TempObj'; @ContextProto() export default class CountService { @Inject() appCache: AppCache; @Inject() tempObj: TempObj; async getCount(): Promise { return this.appCache.getCount(); } async getTempCount(): Promise { return this.tempObj.getCount(); } } ================================================ FILE: core/runtime/test/fixtures/modules/module-for-load-unit-instance/TempObj.ts ================================================ import { ObjectInitType } from '@eggjs/tegg-types'; import { Prototype } from '@eggjs/core-decorator'; @Prototype({ initType: ObjectInitType.ALWAYS_NEW, }) export default class TempObj { count = 0; async getCount(): Promise { return this.count++; } } ================================================ FILE: core/runtime/test/fixtures/modules/module-for-load-unit-instance/package.json ================================================ { "name": "demo-app-repo", "eggModule": { "name": "app-repo" }, "main": "index.js" } ================================================ FILE: core/runtime/test/fixtures/modules/multi-instance-module/MultiInstance.ts ================================================ import { AccessLevel, ObjectInitType, QualifierValue, EggObject, EggObjectLifeCycleContext } from '@eggjs/tegg-types'; import { MultiInstanceProto } from '@eggjs/core-decorator'; import { LifecycleInit } from '@eggjs/tegg-lifecycle'; export const FOO_ATTRIBUTE = Symbol.for('FOO_ATTRIBUTE'); @MultiInstanceProto({ accessLevel: AccessLevel.PUBLIC, initType: ObjectInitType.SINGLETON, objects: [{ name: 'foo', qualifiers: [{ attribute: FOO_ATTRIBUTE, value: 'foo1', }], }, { name: 'foo', qualifiers: [{ attribute: FOO_ATTRIBUTE, value: 'foo2', }], }], }) export class FooLogger { loadUnitPath: string; foo: QualifierValue | undefined; @LifecycleInit() async init(ctx: EggObjectLifeCycleContext, obj: EggObject) { this.loadUnitPath = ctx.loadUnit.unitPath; this.foo = obj.proto.getQualifier(FOO_ATTRIBUTE); } } ================================================ FILE: core/runtime/test/fixtures/modules/multi-instance-module/MultiInstanceConstructor.ts ================================================ import { AccessLevel, ObjectInitType, QualifierValue, ObjectInfo } from '@eggjs/tegg-types'; import { Inject, MultiInstanceInfo, MultiInstanceProto, SingletonProto } from '@eggjs/core-decorator'; export const FOO_ATTRIBUTE = Symbol.for('FOO_ATTRIBUTE'); @SingletonProto() export class Bar { bar = 'bar'; } @MultiInstanceProto({ accessLevel: AccessLevel.PUBLIC, initType: ObjectInitType.SINGLETON, objects: [{ name: 'fooConstructor', qualifiers: [{ attribute: FOO_ATTRIBUTE, value: 'foo1', }], }, { name: 'fooConstructor', qualifiers: [{ attribute: FOO_ATTRIBUTE, value: 'foo2', }], }], }) export class FooLoggerConstructor { foo: QualifierValue | undefined; bar: string; constructor(@Inject() bar: Bar, @MultiInstanceInfo([ FOO_ATTRIBUTE ]) objInfo: ObjectInfo) { this.foo = objInfo.qualifiers.find(t => t.attribute === FOO_ATTRIBUTE)?.value; this.bar = bar.bar; } } ================================================ FILE: core/runtime/test/fixtures/modules/multi-instance-module/package.json ================================================ { "name": "multi-instance-module", "eggModule": { "name": "multiInstanceModule" } } ================================================ FILE: core/runtime/test/fixtures/modules/multi-module/multi-module-common/model/App.ts ================================================ export default class App { name: string; desc: string; } ================================================ FILE: core/runtime/test/fixtures/modules/multi-module/multi-module-common/package.json ================================================ { "name": "multi-module-common", "eggModule": { "name": "multi-module-common" } } ================================================ FILE: core/runtime/test/fixtures/modules/multi-module/multi-module-repo/AppRepo.ts ================================================ import { AccessLevel } from '@eggjs/tegg-types'; import { ContextProto, Inject } from '@eggjs/core-decorator'; import App from '../multi-module-common/model/App'; import PersistenceService from './PersistenceService'; @ContextProto({ accessLevel: AccessLevel.PUBLIC, }) export default class AppRepo { @Inject() persistenceService: PersistenceService; public async findApp(name): Promise { const raw = this.persistenceService.get(name); if (!raw) { return null; } return JSON.parse(raw); } public async insertApp(app: App): Promise { this.persistenceService.set(app.name, JSON.stringify(app)); } } ================================================ FILE: core/runtime/test/fixtures/modules/multi-module/multi-module-repo/PersistenceService.ts ================================================ import { AccessLevel, ObjectInitType } from '@eggjs/tegg-types'; import { Prototype } from '@eggjs/core-decorator'; @Prototype({ initType: ObjectInitType.SINGLETON, accessLevel: AccessLevel.PRIVATE, }) export default class PersistenceService { private store: Map = new Map(); public set(key: string, val: string) { this.store.set(key, val); } public get(key: string): string | undefined { return this.store.get(key); } } ================================================ FILE: core/runtime/test/fixtures/modules/multi-module/multi-module-repo/package.json ================================================ { "name": "multi-module-repo", "eggModule": { "name": "multi-module-repo" } } ================================================ FILE: core/runtime/test/fixtures/modules/multi-module/multi-module-service/AppService.ts ================================================ import { ContextProto, Inject } from '@eggjs/core-decorator'; import AppRepo from '../multi-module-repo/AppRepo'; import App from '../multi-module-common/model/App'; @ContextProto() export default class AppService { @Inject() appRepo: AppRepo; findApp(name: string): Promise { return this.appRepo.findApp(name); } save(app: App) { return this.appRepo.insertApp(app); } } ================================================ FILE: core/runtime/test/fixtures/modules/multi-module/multi-module-service/package.json ================================================ { "name": "multi-module-service", "eggModule": { "name": "multiModuleService" } } ================================================ FILE: core/runtime/test/util.ts ================================================ import path from 'node:path'; import { EggLoadUnitType, LoadUnitInstance } from '@eggjs/tegg-types'; import { LoadUnitFactory } from '@eggjs/tegg-metadata'; import { LoadUnitInstanceFactory } from '..'; import { LoaderFactory } from '@eggjs/tegg-loader'; import { LoaderUtil } from '@eggjs/module-test-util'; export default class TestUtil { static async createLoadUnitInstance(modulePath: string, buildGraph = true) { const absolutePath = path.join(__dirname, 'fixtures/modules', modulePath); if (buildGraph) { LoaderUtil.buildGlobalGraph([ absolutePath ]); } const loader = LoaderFactory.createLoader(absolutePath, EggLoadUnitType.MODULE); const loadUnit = await LoadUnitFactory.createLoadUnit(absolutePath, EggLoadUnitType.MODULE, loader); return await LoadUnitInstanceFactory.createLoadUnitInstance(loadUnit); } static async destroyLoadUnitInstance(loadUnitInstance: LoadUnitInstance) { await LoadUnitInstanceFactory.destroyLoadUnitInstance(loadUnitInstance); await LoadUnitFactory.destroyLoadUnit(loadUnitInstance.loadUnit); } } ================================================ FILE: core/runtime/tsconfig.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "dist", "baseUrl": "./" }, "exclude": [ "dist", "node_modules", "test" ] } ================================================ FILE: core/runtime/tsconfig.pub.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "dist", "baseUrl": "./" }, "exclude": [ "dist", "node_modules", "test" ] } ================================================ FILE: core/schedule-decorator/CHANGELOG.md ================================================ # Change Log All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. ## [3.78.15](https://github.com/eggjs/tegg/compare/v3.78.14...v3.78.15) (2026-04-23) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.78.14](https://github.com/eggjs/tegg/compare/v3.78.13...v3.78.14) (2026-04-21) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.78.13](https://github.com/eggjs/tegg/compare/v3.78.12...v3.78.13) (2026-04-21) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.78.12](https://github.com/eggjs/tegg/compare/v3.78.11...v3.78.12) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.78.11](https://github.com/eggjs/tegg/compare/v3.78.10...v3.78.11) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.78.10](https://github.com/eggjs/tegg/compare/v3.78.9...v3.78.10) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.78.9](https://github.com/eggjs/tegg/compare/v3.78.8...v3.78.9) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.78.8](https://github.com/eggjs/tegg/compare/v3.78.7...v3.78.8) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.78.7](https://github.com/eggjs/tegg/compare/v3.78.6...v3.78.7) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.78.6](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.6) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.78.5](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.5) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.78.4](https://github.com/eggjs/tegg/compare/v3.78.3...v3.78.4) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.78.3](https://github.com/eggjs/tegg/compare/v3.78.2...v3.78.3) (2026-04-15) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.78.2](https://github.com/eggjs/tegg/compare/v3.78.1...v3.78.2) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.78.1](https://github.com/eggjs/tegg/compare/v3.78.0...v3.78.1) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator # [3.78.0](https://github.com/eggjs/tegg/compare/v3.77.2...v3.78.0) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.77.2](https://github.com/eggjs/tegg/compare/v3.77.1...v3.77.2) (2026-04-07) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.77.1](https://github.com/eggjs/tegg/compare/v3.77.0...v3.77.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator # [3.77.0](https://github.com/eggjs/tegg/compare/v3.76.1...v3.77.0) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.76.1](https://github.com/eggjs/tegg/compare/v3.76.0...v3.76.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator # [3.76.0](https://github.com/eggjs/tegg/compare/v3.75.1...v3.76.0) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.75.1](https://github.com/eggjs/tegg/compare/v3.75.0...v3.75.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator # [3.75.0](https://github.com/eggjs/tegg/compare/v3.74.0...v3.75.0) (2026-03-30) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator # [3.74.0](https://github.com/eggjs/tegg/compare/v3.73.0...v3.74.0) (2026-03-30) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator # [3.73.0](https://github.com/eggjs/tegg/compare/v3.72.0...v3.73.0) (2026-03-25) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator # [3.72.0](https://github.com/eggjs/tegg/compare/v3.71.2...v3.72.0) (2026-02-04) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.71.2](https://github.com/eggjs/tegg/compare/v3.71.1...v3.71.2) (2026-01-30) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.71.1](https://github.com/eggjs/tegg/compare/v3.71.0...v3.71.1) (2026-01-28) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator # [3.71.0](https://github.com/eggjs/tegg/compare/v3.70.1...v3.71.0) (2026-01-26) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.70.1](https://github.com/eggjs/tegg/compare/v3.70.0...v3.70.1) (2026-01-14) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator # [3.70.0](https://github.com/eggjs/tegg/compare/v3.69.0...v3.70.0) (2026-01-07) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator # [3.69.0](https://github.com/eggjs/tegg/compare/v3.68.0...v3.69.0) (2026-01-06) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator # [3.68.0](https://github.com/eggjs/tegg/compare/v3.67.2...v3.68.0) (2025-12-29) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.67.2](https://github.com/eggjs/tegg/compare/v3.67.1...v3.67.2) (2025-12-24) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.67.1](https://github.com/eggjs/tegg/compare/v3.67.0...v3.67.1) (2025-12-23) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator # [3.67.0](https://github.com/eggjs/tegg/compare/v3.66.0...v3.67.0) (2025-12-18) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator # [3.66.0](https://github.com/eggjs/tegg/compare/v3.65.3...v3.66.0) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.65.3](https://github.com/eggjs/tegg/compare/v3.65.2...v3.65.3) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.65.2](https://github.com/eggjs/tegg/compare/v3.65.1...v3.65.2) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.65.1](https://github.com/eggjs/tegg/compare/v3.65.0...v3.65.1) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator # [3.65.0](https://github.com/eggjs/tegg/compare/v3.64.5...v3.65.0) (2025-12-10) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.64.5](https://github.com/eggjs/tegg/compare/v3.64.4...v3.64.5) (2025-12-08) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.64.4](https://github.com/eggjs/tegg/compare/v3.64.3...v3.64.4) (2025-12-06) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.64.3](https://github.com/eggjs/tegg/compare/v3.64.2...v3.64.3) (2025-12-06) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.64.2](https://github.com/eggjs/tegg/compare/v3.64.1...v3.64.2) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.64.1](https://github.com/eggjs/tegg/compare/v3.64.0...v3.64.1) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator # [3.64.0](https://github.com/eggjs/tegg/compare/v3.63.2...v3.64.0) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.63.2](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.2) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.63.1](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.1) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator # [3.63.0](https://github.com/eggjs/tegg/compare/v3.62.3...v3.63.0) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.62.3](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.3) (2025-11-05) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.62.2](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.2) (2025-11-05) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.62.1](https://github.com/eggjs/tegg/compare/v3.62.0...v3.62.1) (2025-11-03) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator # [3.62.0](https://github.com/eggjs/tegg/compare/v3.61.0...v3.62.0) (2025-09-24) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator # [3.61.0](https://github.com/eggjs/tegg/compare/v3.60.3...v3.61.0) (2025-08-15) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.60.3](https://github.com/eggjs/tegg/compare/v3.60.2...v3.60.3) (2025-08-06) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.60.2](https://github.com/eggjs/tegg/compare/v3.60.1...v3.60.2) (2025-08-06) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.60.1](https://github.com/eggjs/tegg/compare/v3.60.0...v3.60.1) (2025-07-28) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator # [3.60.0](https://github.com/eggjs/tegg/compare/v3.59.1...v3.60.0) (2025-07-28) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.59.1](https://github.com/eggjs/tegg/compare/v3.59.0...v3.59.1) (2025-07-16) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator # [3.59.0](https://github.com/eggjs/tegg/compare/v3.58.0...v3.59.0) (2025-07-07) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator # [3.58.0](https://github.com/eggjs/tegg/compare/v3.57.14...v3.58.0) (2025-07-01) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.57.14](https://github.com/eggjs/tegg/compare/v3.57.13...v3.57.14) (2025-06-18) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.57.13](https://github.com/eggjs/tegg/compare/v3.57.12...v3.57.13) (2025-06-18) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.57.12](https://github.com/eggjs/tegg/compare/v3.57.11...v3.57.12) (2025-06-16) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.57.11](https://github.com/eggjs/tegg/compare/v3.57.10...v3.57.11) (2025-06-16) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.57.10](https://github.com/eggjs/tegg/compare/v3.57.9...v3.57.10) (2025-06-12) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.57.9](https://github.com/eggjs/tegg/compare/v3.57.8...v3.57.9) (2025-05-29) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.57.8](https://github.com/eggjs/tegg/compare/v3.57.7...v3.57.8) (2025-05-29) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.57.7](https://github.com/eggjs/tegg/compare/v3.57.6...v3.57.7) (2025-05-28) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.57.6](https://github.com/eggjs/tegg/compare/v3.57.5...v3.57.6) (2025-05-27) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.57.5](https://github.com/eggjs/tegg/compare/v3.57.4...v3.57.5) (2025-05-15) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.57.4](https://github.com/eggjs/tegg/compare/v3.57.3...v3.57.4) (2025-05-14) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.57.3](https://github.com/eggjs/tegg/compare/v3.57.2...v3.57.3) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.57.2](https://github.com/eggjs/tegg/compare/v3.57.1...v3.57.2) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.57.1](https://github.com/eggjs/tegg/compare/v3.57.0...v3.57.1) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator # [3.57.0](https://github.com/eggjs/tegg/compare/v3.56.3...v3.57.0) (2025-05-09) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.56.3](https://github.com/eggjs/tegg/compare/v3.56.2...v3.56.3) (2025-05-07) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.56.2](https://github.com/eggjs/tegg/compare/v3.56.1...v3.56.2) (2025-05-01) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.56.1](https://github.com/eggjs/tegg/compare/v3.56.0...v3.56.1) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator # [3.56.0](https://github.com/eggjs/tegg/compare/v3.55.0...v3.56.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator # [3.55.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.55.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator # [3.54.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.54.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator # [3.53.0](https://github.com/eggjs/tegg/compare/v3.52.1...v3.53.0) (2025-04-21) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.52.1](https://github.com/eggjs/tegg/compare/v3.52.0...v3.52.1) (2025-03-14) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator # [3.52.0](https://github.com/eggjs/tegg/compare/v3.51.2...v3.52.0) (2024-12-30) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.51.2](https://github.com/eggjs/tegg/compare/v3.51.1...v3.51.2) (2024-12-09) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.51.1](https://github.com/eggjs/tegg/compare/v3.51.0...v3.51.1) (2024-11-06) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator # [3.51.0](https://github.com/eggjs/tegg/compare/v3.50.1...v3.51.0) (2024-10-30) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.50.1](https://github.com/eggjs/tegg/compare/v3.50.0...v3.50.1) (2024-10-23) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator # [3.50.0](https://github.com/eggjs/tegg/compare/v3.49.0...v3.50.0) (2024-10-22) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator # [3.49.0](https://github.com/eggjs/tegg/compare/v3.48.1...v3.49.0) (2024-10-21) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.48.1](https://github.com/eggjs/tegg/compare/v3.48.0...v3.48.1) (2024-10-14) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator # [3.48.0](https://github.com/eggjs/tegg/compare/v3.47.2...v3.48.0) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.47.2](https://github.com/eggjs/tegg/compare/v3.47.1...v3.47.2) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.47.1](https://github.com/eggjs/tegg/compare/v3.47.0...v3.47.1) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator # [3.47.0](https://github.com/eggjs/tegg/compare/v3.46.4...v3.47.0) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.46.4](https://github.com/eggjs/tegg/compare/v3.46.3...v3.46.4) (2024-10-09) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.46.3](https://github.com/eggjs/tegg/compare/v3.46.2...v3.46.3) (2024-10-08) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.46.2](https://github.com/eggjs/tegg/compare/v3.46.1...v3.46.2) (2024-10-07) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.46.1](https://github.com/eggjs/tegg/compare/v3.46.0...v3.46.1) (2024-09-30) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator # [3.46.0](https://github.com/eggjs/tegg/compare/v3.45.0...v3.46.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator # [3.45.0](https://github.com/eggjs/tegg/compare/v3.44.1...v3.45.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.44.1](https://github.com/eggjs/tegg/compare/v3.44.0...v3.44.1) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator # [3.44.0](https://github.com/eggjs/tegg/compare/v3.43.2...v3.44.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.43.2](https://github.com/eggjs/tegg/compare/v3.43.1...v3.43.2) (2024-09-14) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.43.1](https://github.com/eggjs/tegg/compare/v3.43.0...v3.43.1) (2024-09-14) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator # [3.43.0](https://github.com/eggjs/tegg/compare/v3.42.0...v3.43.0) (2024-09-13) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator # [3.42.0](https://github.com/eggjs/tegg/compare/v3.41.0...v3.42.0) (2024-09-10) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator # [3.41.0](https://github.com/eggjs/tegg/compare/v3.40.1...v3.41.0) (2024-08-26) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.40.1](https://github.com/eggjs/tegg/compare/v3.40.0...v3.40.1) (2024-08-23) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator # [3.40.0](https://github.com/eggjs/tegg/compare/v3.39.5...v3.40.0) (2024-08-22) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.39.5](https://github.com/eggjs/tegg/compare/v3.39.4...v3.39.5) (2024-08-09) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.39.4](https://github.com/eggjs/tegg/compare/v3.39.3...v3.39.4) (2024-07-09) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.39.3](https://github.com/eggjs/tegg/compare/v3.39.2...v3.39.3) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.39.2](https://github.com/eggjs/tegg/compare/v3.39.1...v3.39.2) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.39.1](https://github.com/eggjs/tegg/compare/v3.39.0...v3.39.1) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator # [3.39.0](https://github.com/eggjs/tegg/compare/v3.38.0...v3.39.0) (2024-04-19) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator # [3.38.0](https://github.com/eggjs/tegg/compare/v3.37.3...v3.38.0) (2024-04-18) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.37.3](https://github.com/eggjs/tegg/compare/v3.37.2...v3.37.3) (2024-04-17) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.37.2](https://github.com/eggjs/tegg/compare/v3.37.1...v3.37.2) (2024-04-17) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.37.1](https://github.com/eggjs/tegg/compare/v3.37.0...v3.37.1) (2024-04-16) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator # [3.37.0](https://github.com/eggjs/tegg/compare/v3.36.3...v3.37.0) (2024-04-16) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.36.3](https://github.com/eggjs/tegg/compare/v3.36.2...v3.36.3) (2024-04-10) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.36.2](https://github.com/eggjs/tegg/compare/v3.36.1...v3.36.2) (2024-04-08) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.36.1](https://github.com/eggjs/tegg/compare/v3.36.0...v3.36.1) (2024-04-07) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator # [3.36.0](https://github.com/eggjs/tegg/compare/v3.35.1...v3.36.0) (2024-04-02) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.35.1](https://github.com/eggjs/tegg/compare/v3.35.0...v3.35.1) (2024-03-26) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator # [3.35.0](https://github.com/eggjs/tegg/compare/v3.34.0...v3.35.0) (2024-03-26) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator # [3.34.0](https://github.com/eggjs/tegg/compare/v3.33.1...v3.34.0) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.33.1](https://github.com/eggjs/tegg/compare/v3.33.0...v3.33.1) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator # [3.33.0](https://github.com/eggjs/tegg/compare/v3.32.0...v3.33.0) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator # [3.32.0](https://github.com/eggjs/tegg/compare/v3.31.0...v3.32.0) (2024-02-19) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator # [3.31.0](https://github.com/eggjs/tegg/compare/v3.30.1...v3.31.0) (2024-01-31) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.30.1](https://github.com/eggjs/tegg/compare/v3.30.0...v3.30.1) (2024-01-25) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator # [3.30.0](https://github.com/eggjs/tegg/compare/v3.29.0...v3.30.0) (2024-01-17) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator # [3.29.0](https://github.com/eggjs/tegg/compare/v3.28.2...v3.29.0) (2023-12-26) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.28.2](https://github.com/eggjs/tegg/compare/v3.28.1...v3.28.2) (2023-12-12) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.28.1](https://github.com/eggjs/tegg/compare/v3.28.0...v3.28.1) (2023-12-11) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator # [3.28.0](https://github.com/eggjs/tegg/compare/v3.27.0...v3.28.0) (2023-12-10) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator # [3.27.0](https://github.com/eggjs/tegg/compare/v3.26.0...v3.27.0) (2023-11-23) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.25.2](https://github.com/eggjs/tegg/compare/v3.25.1...v3.25.2) (2023-11-06) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.25.1](https://github.com/eggjs/tegg/compare/v3.25.0...v3.25.1) (2023-11-03) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator # [3.25.0](https://github.com/eggjs/tegg/compare/v3.24.0...v3.25.0) (2023-11-03) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator # [3.24.0](https://github.com/eggjs/tegg/compare/v3.23.0...v3.24.0) (2023-10-26) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator # [3.23.0](https://github.com/eggjs/tegg/compare/v3.22.0...v3.23.0) (2023-09-20) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator # [3.20.0](https://github.com/eggjs/tegg/compare/v3.19.0...v3.20.0) (2023-09-07) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator # [3.19.0](https://github.com/eggjs/tegg/compare/v3.18.1...v3.19.0) (2023-08-31) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.18.1](https://github.com/eggjs/tegg/compare/v3.18.0...v3.18.1) (2023-08-29) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator # [3.18.0](https://github.com/eggjs/tegg/compare/v3.17.0...v3.18.0) (2023-08-29) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator # [3.17.0](https://github.com/eggjs/tegg/compare/v3.16.0...v3.17.0) (2023-08-24) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator # [3.16.0](https://github.com/eggjs/tegg/compare/v3.15.0...v3.16.0) (2023-08-24) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.14.3](https://github.com/eggjs/tegg/compare/v3.14.2...v3.14.3) (2023-08-14) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.14.2](https://github.com/eggjs/tegg/compare/v3.14.1...v3.14.2) (2023-08-14) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.14.1](https://github.com/eggjs/tegg/compare/v3.14.0...v3.14.1) (2023-08-11) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator # [3.14.0](https://github.com/eggjs/tegg/compare/v3.13.0...v3.14.0) (2023-07-31) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator # [3.13.0](https://github.com/eggjs/tegg/compare/v3.12.0...v3.13.0) (2023-07-25) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator # [3.12.0](https://github.com/eggjs/tegg/compare/v3.11.1...v3.12.0) (2023-07-13) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.11.1](https://github.com/eggjs/tegg/compare/v3.11.0...v3.11.1) (2023-06-29) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator # [3.11.0](https://github.com/eggjs/tegg/compare/v3.10.0...v3.11.0) (2023-06-29) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator # [3.10.0](https://github.com/eggjs/tegg/compare/v3.9.0...v3.10.0) (2023-06-28) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator # [3.8.0](https://github.com/eggjs/tegg/compare/v3.7.0...v3.8.0) (2023-05-30) ### Features * impl EggObjectLifecycle hook in decorator ([#119](https://github.com/eggjs/tegg/issues/119)) ([cced8a2](https://github.com/eggjs/tegg/commit/cced8a2e009c33d5040fa21d00409fddef471b0e)) # [3.7.0](https://github.com/eggjs/tegg/compare/v3.6.3...v3.7.0) (2023-04-03) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator # [3.5.0](https://github.com/eggjs/tegg/compare/v3.4.1...v3.5.0) (2023-02-10) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.2.3](https://github.com/eggjs/tegg/compare/v3.2.2...v3.2.3) (2023-01-16) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator ## [3.2.2](https://github.com/eggjs/tegg/compare/v3.2.1...v3.2.2) (2023-01-06) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator # [3.1.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.1.0) (2022-12-27) ### Features * **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) * impl Schedule decorator ([#52](https://github.com/eggjs/tegg/issues/52)) ([7f95005](https://github.com/eggjs/tegg/commit/7f950050b548ca542addbd7b466675da4e81ce3f)) # [3.0.0](https://github.com/eggjs/tegg/compare/v3.0.0-alpha.0...v3.0.0) (2022-12-26) **Note:** Version bump only for package @eggjs/tegg-schedule-decorator # [3.0.0-alpha.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.0.0-alpha.0) (2022-12-22) ### Features * **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) * impl Schedule decorator ([#52](https://github.com/eggjs/tegg/issues/52)) ([7f95005](https://github.com/eggjs/tegg/commit/7f950050b548ca542addbd7b466675da4e81ce3f)) # 1.5.0 (2022-09-04) ### Features * impl Schedule decorator ([#52](https://github.com/eggjs/tegg/issues/52)) ([7f95005](https://github.com/eggjs/tegg/commit/7f950050b548ca542addbd7b466675da4e81ce3f)) ================================================ FILE: core/schedule-decorator/README.md ================================================ # `@eggjs/tegg-schedule-decorator` ## Install ```shell npm i --save @eggjs/tegg-schedule-decorator ``` ## Define schedule subscriber ```ts import { Schedule } from '@eggjs/tegg'; // use number to define schedule interval @Schedule({ type: ScheduleType.WORKER, scheduleData: { // run every 100ms interval: 100, }, }) export class FooSubscriber { @Inject() private readonly logger: EggLogger; async subscribe() { this.logger.info('schedule called'); } } // use cron to define schedule interval @Schedule({ type: ScheduleType.WORKER, scheduleData: { cron: '0 0 3 * * *', }, }) export class FooSubscriber { @Inject() private readonly logger: EggLogger; async subscribe() { this.logger.info('schedule called'); } } ``` ================================================ FILE: core/schedule-decorator/index.ts ================================================ export * from '@eggjs/tegg-types/schedule'; export * from './src/model/ScheduleMetadata'; export * from './src/util/ScheduleInfoUtil'; export * from './src/util/ScheduleMetadataUtil'; export * from './src/decorator/Schedule'; export * from './src/builder/ScheduleMetaBuilder'; ================================================ FILE: core/schedule-decorator/package.json ================================================ { "name": "@eggjs/tegg-schedule-decorator", "version": "3.78.15", "description": "tegg schedule decorator", "main": "dist/index.js", "files": [ "dist/**/*.js", "dist/**/*.d.ts" ], "typings": "dist/index.d.ts", "keywords": [ "egg", "typescript", "runtime", "tegg" ], "scripts": { "clean": "tsc -b --clean", "tsc": "ut run clean && tsc -p ./tsconfig.json", "tsc:pub": "ut run clean && tsc -p ./tsconfig.pub.json", "prepublishOnly": "ut tsc:pub" }, "author": "killagu ", "license": "MIT", "homepage": "https://github.com/eggjs/tegg", "bugs": { "url": "https://github.com/eggjs/tegg/issues" }, "repository": { "type": "git", "url": "git@github.com:eggjs/tegg.git", "directory": "core/schedule-decorator" }, "engines": { "node": ">=14.0.0" }, "dependencies": { "@eggjs/core-decorator": "^3.78.15", "@eggjs/tegg-common-util": "^3.78.15", "@eggjs/tegg-metadata": "^3.78.15", "@eggjs/tegg-types": "^3.78.15", "cron-parser": "^2.18.0" }, "devDependencies": { "@types/mocha": "^10.0.1", "@types/node": "^20.2.4", "cross-env": "^7.0.3", "egg-schedule": "^3.6.6", "mocha": "^10.2.0", "ts-node": "^10.9.1", "typescript": "^5.0.4" }, "publishConfig": { "access": "public" }, "gitHead": "240a3e9f40fda65ebb7589727d197db5ce17916c" } ================================================ FILE: core/schedule-decorator/src/builder/ScheduleMetaBuilder.ts ================================================ import type { EggProtoImplClass, ScheduleOptions } from '@eggjs/tegg-types'; import { ScheduleMetadata } from '../model/ScheduleMetadata'; import { ScheduleInfoUtil } from '../util/ScheduleInfoUtil'; const DEFAULT_SCHEDULE_OPTIONS: ScheduleOptions = { immediate: false, disable: false, env: undefined, }; export class ScheduleMetaBuilder { private readonly clazz: EggProtoImplClass; constructor(clazz: EggProtoImplClass) { this.clazz = clazz; } build(): ScheduleMetadata { const params = ScheduleInfoUtil.getScheduleParams(this.clazz); if (!params) { throw new Error(`class ${this.clazz.name} is not a schedule`); } const options = ScheduleInfoUtil.getScheduleOptions(this.clazz); const scheduleOptions = Object.assign({}, DEFAULT_SCHEDULE_OPTIONS, options); return new ScheduleMetadata( params.type, params.scheduleData, scheduleOptions.immediate!, scheduleOptions.disable!, scheduleOptions.env); } } ================================================ FILE: core/schedule-decorator/src/decorator/Schedule.ts ================================================ import { PrototypeUtil, SingletonProto } from '@eggjs/core-decorator'; import { AccessLevel } from '@eggjs/tegg-types'; import type { EggProtoImplClass, ScheduleOptions, ScheduleParams, ScheduleSubscriber } from '@eggjs/tegg-types'; import { ScheduleInfoUtil } from '../util/ScheduleInfoUtil'; import { StackUtil } from '@eggjs/tegg-common-util'; export function Schedule(param: ScheduleParams, options?: ScheduleOptions) { return function(clazz: EggProtoImplClass) { ScheduleInfoUtil.setIsSchedule(true, clazz); ScheduleInfoUtil.setScheduleParams(param, clazz); if (options) { ScheduleInfoUtil.setScheduleOptions(options, clazz); } const func = SingletonProto({ name: clazz.name, accessLevel: AccessLevel.PUBLIC, }); func(clazz); PrototypeUtil.setFilePath(clazz, StackUtil.getCalleeFromStack(false, 5)); }; } ================================================ FILE: core/schedule-decorator/src/model/ScheduleMetadata.ts ================================================ import type { ScheduleTypeLike } from '@eggjs/tegg-types'; export class ScheduleMetadata { type: ScheduleTypeLike; scheduleData: T; immediate: boolean; disable: boolean; env: undefined | Array; constructor(type: ScheduleTypeLike, data: T, immediate: boolean, disable: boolean, env: undefined | Array) { this.type = type; this.scheduleData = data; this.immediate = immediate; this.disable = disable; this.env = env; } shouldRegister(env: string): boolean { if (!this.env) return true; return this.env.includes(env); } } ================================================ FILE: core/schedule-decorator/src/util/ScheduleInfoUtil.ts ================================================ import { MetadataUtil } from '@eggjs/core-decorator'; import { IS_SCHEDULE, SCHEDULE_PARAMS, SCHEDULE_OPTIONS } from '@eggjs/tegg-types'; import type { EggProtoImplClass, ScheduleOptions, ScheduleParams } from '@eggjs/tegg-types'; export class ScheduleInfoUtil { static isSchedule(clazz: EggProtoImplClass): boolean { return MetadataUtil.getBooleanMetaData(IS_SCHEDULE, clazz); } static setIsSchedule(isSchedule: boolean, clazz: EggProtoImplClass) { MetadataUtil.defineMetaData(IS_SCHEDULE, isSchedule, clazz); } static setScheduleParams(scheduleParams: ScheduleParams, clazz: EggProtoImplClass) { MetadataUtil.defineMetaData(SCHEDULE_PARAMS, scheduleParams, clazz); } static setScheduleOptions(scheduleParams: ScheduleOptions, clazz: EggProtoImplClass) { MetadataUtil.defineMetaData(SCHEDULE_OPTIONS, scheduleParams, clazz); } static getScheduleOptions(clazz: EggProtoImplClass): ScheduleOptions | undefined { return MetadataUtil.getMetaData(SCHEDULE_OPTIONS, clazz); } static getScheduleParams(clazz: EggProtoImplClass): ScheduleParams | undefined { return MetadataUtil.getMetaData(SCHEDULE_PARAMS, clazz); } } ================================================ FILE: core/schedule-decorator/src/util/ScheduleMetadataUtil.ts ================================================ import { MetadataUtil } from '@eggjs/core-decorator'; import { SCHEDULE_METADATA } from '@eggjs/tegg-types'; import type { EggProtoImplClass } from '@eggjs/tegg-types'; import { ScheduleMetadata } from '../model/ScheduleMetadata'; export class ScheduleMetadataUtil { static setScheduleMetadata(clazz: EggProtoImplClass, metaData: ScheduleMetadata) { MetadataUtil.defineMetaData(SCHEDULE_METADATA, metaData, clazz); } static getScheduleMetadata(clazz): ScheduleMetadata | undefined { return MetadataUtil.getMetaData(SCHEDULE_METADATA, clazz); } } ================================================ FILE: core/schedule-decorator/tsconfig.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "dist", "baseUrl": "./" }, "exclude": [ "dist", "node_modules", "test" ] } ================================================ FILE: core/schedule-decorator/tsconfig.pub.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "dist", "baseUrl": "./" }, "exclude": [ "dist", "node_modules", "test" ] } ================================================ FILE: core/standalone-decorator/CHANGELOG.md ================================================ # Change Log All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. ## [3.78.15](https://github.com/eggjs/tegg/compare/v3.78.14...v3.78.15) (2026-04-23) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.78.14](https://github.com/eggjs/tegg/compare/v3.78.13...v3.78.14) (2026-04-21) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.78.13](https://github.com/eggjs/tegg/compare/v3.78.12...v3.78.13) (2026-04-21) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.78.12](https://github.com/eggjs/tegg/compare/v3.78.11...v3.78.12) (2026-04-18) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.78.11](https://github.com/eggjs/tegg/compare/v3.78.10...v3.78.11) (2026-04-18) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.78.10](https://github.com/eggjs/tegg/compare/v3.78.9...v3.78.10) (2026-04-18) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.78.9](https://github.com/eggjs/tegg/compare/v3.78.8...v3.78.9) (2026-04-18) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.78.8](https://github.com/eggjs/tegg/compare/v3.78.7...v3.78.8) (2026-04-18) ### Bug Fixes * remove egg module ([#438](https://github.com/eggjs/tegg/issues/438)) ([c82c26e](https://github.com/eggjs/tegg/commit/c82c26ebfa8272e32477f9b5be51da85e70904a6)) ## [3.78.7](https://github.com/eggjs/tegg/compare/v3.78.6...v3.78.7) (2026-04-18) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.78.6](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.6) (2026-04-18) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.78.5](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.5) (2026-04-18) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.78.4](https://github.com/eggjs/tegg/compare/v3.78.3...v3.78.4) (2026-04-18) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.78.3](https://github.com/eggjs/tegg/compare/v3.78.2...v3.78.3) (2026-04-15) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.78.2](https://github.com/eggjs/tegg/compare/v3.78.1...v3.78.2) (2026-04-09) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.78.1](https://github.com/eggjs/tegg/compare/v3.78.0...v3.78.1) (2026-04-09) **Note:** Version bump only for package @eggjs/standalone-decorator # [3.78.0](https://github.com/eggjs/tegg/compare/v3.77.2...v3.78.0) (2026-04-09) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.77.2](https://github.com/eggjs/tegg/compare/v3.77.1...v3.77.2) (2026-04-07) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.77.1](https://github.com/eggjs/tegg/compare/v3.77.0...v3.77.1) (2026-04-01) **Note:** Version bump only for package @eggjs/standalone-decorator # [3.77.0](https://github.com/eggjs/tegg/compare/v3.76.1...v3.77.0) (2026-04-01) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.76.1](https://github.com/eggjs/tegg/compare/v3.76.0...v3.76.1) (2026-04-01) **Note:** Version bump only for package @eggjs/standalone-decorator # [3.76.0](https://github.com/eggjs/tegg/compare/v3.75.1...v3.76.0) (2026-04-01) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.75.1](https://github.com/eggjs/tegg/compare/v3.75.0...v3.75.1) (2026-04-01) **Note:** Version bump only for package @eggjs/standalone-decorator # [3.75.0](https://github.com/eggjs/tegg/compare/v3.74.0...v3.75.0) (2026-03-30) **Note:** Version bump only for package @eggjs/standalone-decorator # [3.74.0](https://github.com/eggjs/tegg/compare/v3.73.0...v3.74.0) (2026-03-30) **Note:** Version bump only for package @eggjs/standalone-decorator # [3.73.0](https://github.com/eggjs/tegg/compare/v3.72.0...v3.73.0) (2026-03-25) **Note:** Version bump only for package @eggjs/standalone-decorator # [3.72.0](https://github.com/eggjs/tegg/compare/v3.71.2...v3.72.0) (2026-02-04) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.71.2](https://github.com/eggjs/tegg/compare/v3.71.1...v3.71.2) (2026-01-30) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.71.1](https://github.com/eggjs/tegg/compare/v3.71.0...v3.71.1) (2026-01-28) **Note:** Version bump only for package @eggjs/standalone-decorator # [3.71.0](https://github.com/eggjs/tegg/compare/v3.70.1...v3.71.0) (2026-01-26) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.70.1](https://github.com/eggjs/tegg/compare/v3.70.0...v3.70.1) (2026-01-14) **Note:** Version bump only for package @eggjs/standalone-decorator # [3.70.0](https://github.com/eggjs/tegg/compare/v3.69.0...v3.70.0) (2026-01-07) **Note:** Version bump only for package @eggjs/standalone-decorator # [3.69.0](https://github.com/eggjs/tegg/compare/v3.68.0...v3.69.0) (2026-01-06) **Note:** Version bump only for package @eggjs/standalone-decorator # [3.68.0](https://github.com/eggjs/tegg/compare/v3.67.2...v3.68.0) (2025-12-29) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.67.2](https://github.com/eggjs/tegg/compare/v3.67.1...v3.67.2) (2025-12-24) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.67.1](https://github.com/eggjs/tegg/compare/v3.67.0...v3.67.1) (2025-12-23) **Note:** Version bump only for package @eggjs/standalone-decorator # [3.67.0](https://github.com/eggjs/tegg/compare/v3.66.0...v3.67.0) (2025-12-18) **Note:** Version bump only for package @eggjs/standalone-decorator # [3.66.0](https://github.com/eggjs/tegg/compare/v3.65.3...v3.66.0) (2025-12-16) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.65.3](https://github.com/eggjs/tegg/compare/v3.65.2...v3.65.3) (2025-12-16) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.65.2](https://github.com/eggjs/tegg/compare/v3.65.1...v3.65.2) (2025-12-16) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.65.1](https://github.com/eggjs/tegg/compare/v3.65.0...v3.65.1) (2025-12-16) **Note:** Version bump only for package @eggjs/standalone-decorator # [3.65.0](https://github.com/eggjs/tegg/compare/v3.64.5...v3.65.0) (2025-12-10) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.64.5](https://github.com/eggjs/tegg/compare/v3.64.4...v3.64.5) (2025-12-08) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.64.4](https://github.com/eggjs/tegg/compare/v3.64.3...v3.64.4) (2025-12-06) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.64.3](https://github.com/eggjs/tegg/compare/v3.64.2...v3.64.3) (2025-12-06) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.64.2](https://github.com/eggjs/tegg/compare/v3.64.1...v3.64.2) (2025-11-27) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.64.1](https://github.com/eggjs/tegg/compare/v3.64.0...v3.64.1) (2025-11-27) **Note:** Version bump only for package @eggjs/standalone-decorator # [3.64.0](https://github.com/eggjs/tegg/compare/v3.63.2...v3.64.0) (2025-11-27) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.63.2](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.2) (2025-11-18) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.63.1](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.1) (2025-11-18) **Note:** Version bump only for package @eggjs/standalone-decorator # [3.63.0](https://github.com/eggjs/tegg/compare/v3.62.3...v3.63.0) (2025-11-18) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.62.3](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.3) (2025-11-05) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.62.2](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.2) (2025-11-05) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.62.1](https://github.com/eggjs/tegg/compare/v3.62.0...v3.62.1) (2025-11-03) **Note:** Version bump only for package @eggjs/standalone-decorator # [3.62.0](https://github.com/eggjs/tegg/compare/v3.61.0...v3.62.0) (2025-09-24) **Note:** Version bump only for package @eggjs/standalone-decorator # [3.61.0](https://github.com/eggjs/tegg/compare/v3.60.3...v3.61.0) (2025-08-15) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.60.3](https://github.com/eggjs/tegg/compare/v3.60.2...v3.60.3) (2025-08-06) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.60.2](https://github.com/eggjs/tegg/compare/v3.60.1...v3.60.2) (2025-08-06) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.60.1](https://github.com/eggjs/tegg/compare/v3.60.0...v3.60.1) (2025-07-28) **Note:** Version bump only for package @eggjs/standalone-decorator # [3.60.0](https://github.com/eggjs/tegg/compare/v3.59.1...v3.60.0) (2025-07-28) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.59.1](https://github.com/eggjs/tegg/compare/v3.59.0...v3.59.1) (2025-07-16) **Note:** Version bump only for package @eggjs/standalone-decorator # [3.59.0](https://github.com/eggjs/tegg/compare/v3.58.0...v3.59.0) (2025-07-07) **Note:** Version bump only for package @eggjs/standalone-decorator # [3.58.0](https://github.com/eggjs/tegg/compare/v3.57.14...v3.58.0) (2025-07-01) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.57.14](https://github.com/eggjs/tegg/compare/v3.57.13...v3.57.14) (2025-06-18) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.57.13](https://github.com/eggjs/tegg/compare/v3.57.12...v3.57.13) (2025-06-18) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.57.12](https://github.com/eggjs/tegg/compare/v3.57.11...v3.57.12) (2025-06-16) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.57.11](https://github.com/eggjs/tegg/compare/v3.57.10...v3.57.11) (2025-06-16) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.57.10](https://github.com/eggjs/tegg/compare/v3.57.9...v3.57.10) (2025-06-12) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.57.9](https://github.com/eggjs/tegg/compare/v3.57.8...v3.57.9) (2025-05-29) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.57.8](https://github.com/eggjs/tegg/compare/v3.57.7...v3.57.8) (2025-05-29) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.57.7](https://github.com/eggjs/tegg/compare/v3.57.6...v3.57.7) (2025-05-28) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.57.6](https://github.com/eggjs/tegg/compare/v3.57.5...v3.57.6) (2025-05-27) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.57.5](https://github.com/eggjs/tegg/compare/v3.57.4...v3.57.5) (2025-05-15) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.57.4](https://github.com/eggjs/tegg/compare/v3.57.3...v3.57.4) (2025-05-14) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.57.3](https://github.com/eggjs/tegg/compare/v3.57.2...v3.57.3) (2025-05-13) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.57.2](https://github.com/eggjs/tegg/compare/v3.57.1...v3.57.2) (2025-05-13) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.57.1](https://github.com/eggjs/tegg/compare/v3.57.0...v3.57.1) (2025-05-13) **Note:** Version bump only for package @eggjs/standalone-decorator # [3.57.0](https://github.com/eggjs/tegg/compare/v3.56.3...v3.57.0) (2025-05-09) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.56.3](https://github.com/eggjs/tegg/compare/v3.56.2...v3.56.3) (2025-05-07) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.56.2](https://github.com/eggjs/tegg/compare/v3.56.1...v3.56.2) (2025-05-01) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.56.1](https://github.com/eggjs/tegg/compare/v3.56.0...v3.56.1) (2025-04-29) **Note:** Version bump only for package @eggjs/standalone-decorator # [3.56.0](https://github.com/eggjs/tegg/compare/v3.55.0...v3.56.0) (2025-04-29) **Note:** Version bump only for package @eggjs/standalone-decorator # [3.55.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.55.0) (2025-04-29) **Note:** Version bump only for package @eggjs/standalone-decorator # [3.54.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.54.0) (2025-04-29) **Note:** Version bump only for package @eggjs/standalone-decorator # [3.53.0](https://github.com/eggjs/tegg/compare/v3.52.1...v3.53.0) (2025-04-21) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.52.1](https://github.com/eggjs/tegg/compare/v3.52.0...v3.52.1) (2025-03-14) **Note:** Version bump only for package @eggjs/standalone-decorator # [3.52.0](https://github.com/eggjs/tegg/compare/v3.51.2...v3.52.0) (2024-12-30) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.51.2](https://github.com/eggjs/tegg/compare/v3.51.1...v3.51.2) (2024-12-09) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.51.1](https://github.com/eggjs/tegg/compare/v3.51.0...v3.51.1) (2024-11-06) **Note:** Version bump only for package @eggjs/standalone-decorator # [3.51.0](https://github.com/eggjs/tegg/compare/v3.50.1...v3.51.0) (2024-10-30) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.50.1](https://github.com/eggjs/tegg/compare/v3.50.0...v3.50.1) (2024-10-23) **Note:** Version bump only for package @eggjs/standalone-decorator # [3.50.0](https://github.com/eggjs/tegg/compare/v3.49.0...v3.50.0) (2024-10-22) **Note:** Version bump only for package @eggjs/standalone-decorator # [3.49.0](https://github.com/eggjs/tegg/compare/v3.48.1...v3.49.0) (2024-10-21) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.48.1](https://github.com/eggjs/tegg/compare/v3.48.0...v3.48.1) (2024-10-14) **Note:** Version bump only for package @eggjs/standalone-decorator # [3.48.0](https://github.com/eggjs/tegg/compare/v3.47.2...v3.48.0) (2024-10-10) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.47.2](https://github.com/eggjs/tegg/compare/v3.47.1...v3.47.2) (2024-10-10) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.47.1](https://github.com/eggjs/tegg/compare/v3.47.0...v3.47.1) (2024-10-10) **Note:** Version bump only for package @eggjs/standalone-decorator # [3.47.0](https://github.com/eggjs/tegg/compare/v3.46.4...v3.47.0) (2024-10-10) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.46.4](https://github.com/eggjs/tegg/compare/v3.46.3...v3.46.4) (2024-10-09) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.46.3](https://github.com/eggjs/tegg/compare/v3.46.2...v3.46.3) (2024-10-08) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.46.2](https://github.com/eggjs/tegg/compare/v3.46.1...v3.46.2) (2024-10-07) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.46.1](https://github.com/eggjs/tegg/compare/v3.46.0...v3.46.1) (2024-09-30) **Note:** Version bump only for package @eggjs/standalone-decorator # [3.46.0](https://github.com/eggjs/tegg/compare/v3.45.0...v3.46.0) (2024-09-29) **Note:** Version bump only for package @eggjs/standalone-decorator # [3.45.0](https://github.com/eggjs/tegg/compare/v3.44.1...v3.45.0) (2024-09-29) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.44.1](https://github.com/eggjs/tegg/compare/v3.44.0...v3.44.1) (2024-09-29) **Note:** Version bump only for package @eggjs/standalone-decorator # [3.44.0](https://github.com/eggjs/tegg/compare/v3.43.2...v3.44.0) (2024-09-29) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.43.2](https://github.com/eggjs/tegg/compare/v3.43.1...v3.43.2) (2024-09-14) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.43.1](https://github.com/eggjs/tegg/compare/v3.43.0...v3.43.1) (2024-09-14) **Note:** Version bump only for package @eggjs/standalone-decorator # [3.43.0](https://github.com/eggjs/tegg/compare/v3.42.0...v3.43.0) (2024-09-13) **Note:** Version bump only for package @eggjs/standalone-decorator # [3.42.0](https://github.com/eggjs/tegg/compare/v3.41.0...v3.42.0) (2024-09-10) **Note:** Version bump only for package @eggjs/standalone-decorator # [3.41.0](https://github.com/eggjs/tegg/compare/v3.40.1...v3.41.0) (2024-08-26) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.40.1](https://github.com/eggjs/tegg/compare/v3.40.0...v3.40.1) (2024-08-23) **Note:** Version bump only for package @eggjs/standalone-decorator # [3.40.0](https://github.com/eggjs/tegg/compare/v3.39.5...v3.40.0) (2024-08-22) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.39.5](https://github.com/eggjs/tegg/compare/v3.39.4...v3.39.5) (2024-08-09) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.39.4](https://github.com/eggjs/tegg/compare/v3.39.3...v3.39.4) (2024-07-09) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.39.3](https://github.com/eggjs/tegg/compare/v3.39.2...v3.39.3) (2024-04-28) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.39.2](https://github.com/eggjs/tegg/compare/v3.39.1...v3.39.2) (2024-04-28) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.39.1](https://github.com/eggjs/tegg/compare/v3.39.0...v3.39.1) (2024-04-28) **Note:** Version bump only for package @eggjs/standalone-decorator # [3.39.0](https://github.com/eggjs/tegg/compare/v3.38.0...v3.39.0) (2024-04-19) **Note:** Version bump only for package @eggjs/standalone-decorator # [3.38.0](https://github.com/eggjs/tegg/compare/v3.37.3...v3.38.0) (2024-04-18) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.37.3](https://github.com/eggjs/tegg/compare/v3.37.2...v3.37.3) (2024-04-17) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.37.2](https://github.com/eggjs/tegg/compare/v3.37.1...v3.37.2) (2024-04-17) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.37.1](https://github.com/eggjs/tegg/compare/v3.37.0...v3.37.1) (2024-04-16) **Note:** Version bump only for package @eggjs/standalone-decorator # [3.37.0](https://github.com/eggjs/tegg/compare/v3.36.3...v3.37.0) (2024-04-16) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.36.3](https://github.com/eggjs/tegg/compare/v3.36.2...v3.36.3) (2024-04-10) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.36.2](https://github.com/eggjs/tegg/compare/v3.36.1...v3.36.2) (2024-04-08) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.36.1](https://github.com/eggjs/tegg/compare/v3.36.0...v3.36.1) (2024-04-07) **Note:** Version bump only for package @eggjs/standalone-decorator # [3.36.0](https://github.com/eggjs/tegg/compare/v3.35.1...v3.36.0) (2024-04-02) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.35.1](https://github.com/eggjs/tegg/compare/v3.35.0...v3.35.1) (2024-03-26) **Note:** Version bump only for package @eggjs/standalone-decorator # [3.35.0](https://github.com/eggjs/tegg/compare/v3.34.0...v3.35.0) (2024-03-26) **Note:** Version bump only for package @eggjs/standalone-decorator # [3.34.0](https://github.com/eggjs/tegg/compare/v3.33.1...v3.34.0) (2024-03-22) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.33.1](https://github.com/eggjs/tegg/compare/v3.33.0...v3.33.1) (2024-03-22) **Note:** Version bump only for package @eggjs/standalone-decorator # [3.33.0](https://github.com/eggjs/tegg/compare/v3.32.0...v3.33.0) (2024-03-22) **Note:** Version bump only for package @eggjs/standalone-decorator # [3.32.0](https://github.com/eggjs/tegg/compare/v3.31.0...v3.32.0) (2024-02-19) **Note:** Version bump only for package @eggjs/standalone-decorator # [3.31.0](https://github.com/eggjs/tegg/compare/v3.30.1...v3.31.0) (2024-01-31) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.30.1](https://github.com/eggjs/tegg/compare/v3.30.0...v3.30.1) (2024-01-25) **Note:** Version bump only for package @eggjs/standalone-decorator # [3.30.0](https://github.com/eggjs/tegg/compare/v3.29.0...v3.30.0) (2024-01-17) **Note:** Version bump only for package @eggjs/standalone-decorator # [3.29.0](https://github.com/eggjs/tegg/compare/v3.28.2...v3.29.0) (2023-12-26) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.28.2](https://github.com/eggjs/tegg/compare/v3.28.1...v3.28.2) (2023-12-12) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.28.1](https://github.com/eggjs/tegg/compare/v3.28.0...v3.28.1) (2023-12-11) **Note:** Version bump only for package @eggjs/standalone-decorator # [3.28.0](https://github.com/eggjs/tegg/compare/v3.27.0...v3.28.0) (2023-12-10) **Note:** Version bump only for package @eggjs/standalone-decorator # [3.27.0](https://github.com/eggjs/tegg/compare/v3.26.0...v3.27.0) (2023-11-23) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.25.2](https://github.com/eggjs/tegg/compare/v3.25.1...v3.25.2) (2023-11-06) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.25.1](https://github.com/eggjs/tegg/compare/v3.25.0...v3.25.1) (2023-11-03) **Note:** Version bump only for package @eggjs/standalone-decorator # [3.25.0](https://github.com/eggjs/tegg/compare/v3.24.0...v3.25.0) (2023-11-03) **Note:** Version bump only for package @eggjs/standalone-decorator # [3.24.0](https://github.com/eggjs/tegg/compare/v3.23.0...v3.24.0) (2023-10-26) **Note:** Version bump only for package @eggjs/standalone-decorator # [3.23.0](https://github.com/eggjs/tegg/compare/v3.22.0...v3.23.0) (2023-09-20) **Note:** Version bump only for package @eggjs/standalone-decorator # [3.20.0](https://github.com/eggjs/tegg/compare/v3.19.0...v3.20.0) (2023-09-07) **Note:** Version bump only for package @eggjs/standalone-decorator # [3.19.0](https://github.com/eggjs/tegg/compare/v3.18.1...v3.19.0) (2023-08-31) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.18.1](https://github.com/eggjs/tegg/compare/v3.18.0...v3.18.1) (2023-08-29) **Note:** Version bump only for package @eggjs/standalone-decorator # [3.18.0](https://github.com/eggjs/tegg/compare/v3.17.0...v3.18.0) (2023-08-29) **Note:** Version bump only for package @eggjs/standalone-decorator # [3.17.0](https://github.com/eggjs/tegg/compare/v3.16.0...v3.17.0) (2023-08-24) **Note:** Version bump only for package @eggjs/standalone-decorator # [3.16.0](https://github.com/eggjs/tegg/compare/v3.15.0...v3.16.0) (2023-08-24) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.14.3](https://github.com/eggjs/tegg/compare/v3.14.2...v3.14.3) (2023-08-14) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.14.2](https://github.com/eggjs/tegg/compare/v3.14.1...v3.14.2) (2023-08-14) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.14.1](https://github.com/eggjs/tegg/compare/v3.14.0...v3.14.1) (2023-08-11) **Note:** Version bump only for package @eggjs/standalone-decorator # [3.14.0](https://github.com/eggjs/tegg/compare/v3.13.0...v3.14.0) (2023-07-31) **Note:** Version bump only for package @eggjs/standalone-decorator # [3.13.0](https://github.com/eggjs/tegg/compare/v3.12.0...v3.13.0) (2023-07-25) **Note:** Version bump only for package @eggjs/standalone-decorator # [3.12.0](https://github.com/eggjs/tegg/compare/v3.11.1...v3.12.0) (2023-07-13) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.11.1](https://github.com/eggjs/tegg/compare/v3.11.0...v3.11.1) (2023-06-29) **Note:** Version bump only for package @eggjs/standalone-decorator # [3.11.0](https://github.com/eggjs/tegg/compare/v3.10.0...v3.11.0) (2023-06-29) **Note:** Version bump only for package @eggjs/standalone-decorator # [3.10.0](https://github.com/eggjs/tegg/compare/v3.9.0...v3.10.0) (2023-06-28) **Note:** Version bump only for package @eggjs/standalone-decorator # [3.8.0](https://github.com/eggjs/tegg/compare/v3.7.0...v3.8.0) (2023-05-30) ### Features * impl EggObjectLifecycle hook in decorator ([#119](https://github.com/eggjs/tegg/issues/119)) ([cced8a2](https://github.com/eggjs/tegg/commit/cced8a2e009c33d5040fa21d00409fddef471b0e)) # [3.7.0](https://github.com/eggjs/tegg/compare/v3.6.3...v3.7.0) (2023-04-03) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.2.3](https://github.com/eggjs/tegg/compare/v3.2.2...v3.2.3) (2023-01-16) **Note:** Version bump only for package @eggjs/standalone-decorator ## [3.2.2](https://github.com/eggjs/tegg/compare/v3.2.1...v3.2.2) (2023-01-06) **Note:** Version bump only for package @eggjs/standalone-decorator # [0.2.0](https://github.com/eggjs/tegg/compare/v0.1.19...v0.2.0) (2022-01-20) **Note:** Version bump only for package @eggjs/standalone-decorator ================================================ FILE: core/standalone-decorator/README.md ================================================ # `@eggjs/standalone-decorator` ## Usage Please read [@eggjs/tegg-standalone](../../standalone/standalone) ================================================ FILE: core/standalone-decorator/index.ts ================================================ export * from './src/typing'; export * from './src/util/StandaloneUtil'; export * from './src/decorator/Runner'; export * from './src/event/EventHandler'; ================================================ FILE: core/standalone-decorator/package.json ================================================ { "name": "@eggjs/standalone-decorator", "version": "3.78.15", "description": "tegg standalone decorator", "keywords": [ "egg", "typescript", "decorator", "tegg", "standalone" ], "main": "dist/index.js", "files": [ "dist/**/*.js", "dist/**/*.d.ts" ], "typings": "dist/index.d.ts", "scripts": { "clean": "tsc -b --clean", "tsc": "ut run clean && tsc -p ./tsconfig.json", "tsc:pub": "ut run clean && tsc -p ./tsconfig.pub.json", "prepublishOnly": "ut tsc:pub" }, "author": "killagu ", "license": "MIT", "homepage": "https://github.com/eggjs/tegg", "bugs": { "url": "https://github.com/eggjs/tegg/issues" }, "repository": { "type": "git", "url": "git@github.com:eggjs/tegg.git", "directory": "core/standalone-decorator" }, "engines": { "node": ">=14.0.0" }, "dependencies": { "@eggjs/tegg-common-util": "^3.78.15", "reflect-metadata": "^0.1.13" }, "publishConfig": { "access": "public" }, "devDependencies": { "@types/mocha": "^10.0.1", "@types/node": "^20.2.4", "cross-env": "^7.0.3", "mocha": "^10.2.0", "ts-node": "^10.9.1", "typescript": "^5.0.4" }, "gitHead": "240a3e9f40fda65ebb7589727d197db5ce17916c" } ================================================ FILE: core/standalone-decorator/src/decorator/Runner.ts ================================================ import { MainRunnerClass } from '../typing'; import { StandaloneUtil } from '../util/StandaloneUtil'; export function Runner() { return function(clazz: MainRunnerClass) { StandaloneUtil.setMainRunner(clazz as unknown as MainRunnerClass); }; } ================================================ FILE: core/standalone-decorator/src/event/EventHandler.ts ================================================ import { type EggProtoImplClass, ImplDecorator, SingletonProtoParams } from '@eggjs/tegg-types'; import { QualifierImplDecoratorUtil, SingletonProto } from '@eggjs/tegg'; export abstract class AbstractEventHandler { abstract handleEvent(event: E): Promise; } export const EVENT_HANDLER_ATTRIBUTE = Symbol('EVENT_HANDLER_ATTRIBUTE'); export type EventType = Record; export const EventHandler: ImplDecorator = QualifierImplDecoratorUtil.generatorDecorator(AbstractEventHandler, EVENT_HANDLER_ATTRIBUTE); export const EventHandlerProto = (type: EventType[keyof EventType], params?: SingletonProtoParams) => { return (clazz: EggProtoImplClass) => { EventHandler(type)(clazz); SingletonProto(params)(clazz); }; }; export interface FetchEventLike { type: string; request: Request; respondWith(response: Response | Promise): void; waitUntil?(promise: Promise): void; } ================================================ FILE: core/standalone-decorator/src/typing.ts ================================================ export interface MainRunner { main(): Promise; } export type MainRunnerClass = new() => MainRunner; ================================================ FILE: core/standalone-decorator/src/util/StandaloneUtil.ts ================================================ import { MainRunnerClass } from '../typing'; export class StandaloneUtil { private static runnerClass: MainRunnerClass | undefined; static setMainRunner(runnerClass: MainRunnerClass) { StandaloneUtil.runnerClass = runnerClass; } static getMainRunner(): MainRunnerClass | undefined { return StandaloneUtil.runnerClass; } } ================================================ FILE: core/standalone-decorator/tsconfig.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "dist", "baseUrl": "./" }, "exclude": [ "dist", "node_modules", "test" ] } ================================================ FILE: core/standalone-decorator/tsconfig.pub.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "dist", "baseUrl": "./" }, "exclude": [ "dist", "node_modules", "test" ] } ================================================ FILE: core/tegg/CHANGELOG.md ================================================ # Change Log All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. ## [3.78.15](https://github.com/eggjs/tegg/compare/v3.78.14...v3.78.15) (2026-04-23) **Note:** Version bump only for package @eggjs/tegg ## [3.78.14](https://github.com/eggjs/tegg/compare/v3.78.13...v3.78.14) (2026-04-21) **Note:** Version bump only for package @eggjs/tegg ## [3.78.13](https://github.com/eggjs/tegg/compare/v3.78.12...v3.78.13) (2026-04-21) **Note:** Version bump only for package @eggjs/tegg ## [3.78.12](https://github.com/eggjs/tegg/compare/v3.78.11...v3.78.12) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg ## [3.78.11](https://github.com/eggjs/tegg/compare/v3.78.10...v3.78.11) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg ## [3.78.10](https://github.com/eggjs/tegg/compare/v3.78.9...v3.78.10) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg ## [3.78.9](https://github.com/eggjs/tegg/compare/v3.78.8...v3.78.9) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg ## [3.78.8](https://github.com/eggjs/tegg/compare/v3.78.7...v3.78.8) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg ## [3.78.7](https://github.com/eggjs/tegg/compare/v3.78.6...v3.78.7) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg ## [3.78.6](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.6) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg ## [3.78.5](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.5) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg ## [3.78.4](https://github.com/eggjs/tegg/compare/v3.78.3...v3.78.4) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg ## [3.78.3](https://github.com/eggjs/tegg/compare/v3.78.2...v3.78.3) (2026-04-15) **Note:** Version bump only for package @eggjs/tegg ## [3.78.2](https://github.com/eggjs/tegg/compare/v3.78.1...v3.78.2) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg ## [3.78.1](https://github.com/eggjs/tegg/compare/v3.78.0...v3.78.1) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg # [3.78.0](https://github.com/eggjs/tegg/compare/v3.77.2...v3.78.0) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg ## [3.77.2](https://github.com/eggjs/tegg/compare/v3.77.1...v3.77.2) (2026-04-07) **Note:** Version bump only for package @eggjs/tegg ## [3.77.1](https://github.com/eggjs/tegg/compare/v3.77.0...v3.77.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg # [3.77.0](https://github.com/eggjs/tegg/compare/v3.76.1...v3.77.0) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg ## [3.76.1](https://github.com/eggjs/tegg/compare/v3.76.0...v3.76.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg # [3.76.0](https://github.com/eggjs/tegg/compare/v3.75.1...v3.76.0) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg ## [3.75.1](https://github.com/eggjs/tegg/compare/v3.75.0...v3.75.1) (2026-04-01) ### Bug Fixes * **agent-runtime:** preserve non-text content blocks in MessageConverter ([#426](https://github.com/eggjs/tegg/issues/426)) ([8c4382f](https://github.com/eggjs/tegg/commit/8c4382f33f68534218049cfbfadfd4f6800a348c)) # [3.75.0](https://github.com/eggjs/tegg/compare/v3.74.0...v3.75.0) (2026-03-30) **Note:** Version bump only for package @eggjs/tegg # [3.74.0](https://github.com/eggjs/tegg/compare/v3.73.0...v3.74.0) (2026-03-30) **Note:** Version bump only for package @eggjs/tegg # [3.73.0](https://github.com/eggjs/tegg/compare/v3.72.0...v3.73.0) (2026-03-25) ### Features * add agent-runtime package with @AgentController decorator ([#411](https://github.com/eggjs/tegg/issues/411)) ([d4d0006](https://github.com/eggjs/tegg/commit/d4d00061e90230f82c0958bcf5268f8a511395db)) # [3.72.0](https://github.com/eggjs/tegg/compare/v3.71.2...v3.72.0) (2026-02-04) **Note:** Version bump only for package @eggjs/tegg ## [3.71.2](https://github.com/eggjs/tegg/compare/v3.71.1...v3.71.2) (2026-01-30) **Note:** Version bump only for package @eggjs/tegg ## [3.71.1](https://github.com/eggjs/tegg/compare/v3.71.0...v3.71.1) (2026-01-28) **Note:** Version bump only for package @eggjs/tegg # [3.71.0](https://github.com/eggjs/tegg/compare/v3.70.1...v3.71.0) (2026-01-26) **Note:** Version bump only for package @eggjs/tegg ## [3.70.1](https://github.com/eggjs/tegg/compare/v3.70.0...v3.70.1) (2026-01-14) **Note:** Version bump only for package @eggjs/tegg # [3.70.0](https://github.com/eggjs/tegg/compare/v3.69.0...v3.70.0) (2026-01-07) **Note:** Version bump only for package @eggjs/tegg # [3.69.0](https://github.com/eggjs/tegg/compare/v3.68.0...v3.69.0) (2026-01-06) **Note:** Version bump only for package @eggjs/tegg # [3.68.0](https://github.com/eggjs/tegg/compare/v3.67.2...v3.68.0) (2025-12-29) **Note:** Version bump only for package @eggjs/tegg ## [3.67.2](https://github.com/eggjs/tegg/compare/v3.67.1...v3.67.2) (2025-12-24) **Note:** Version bump only for package @eggjs/tegg ## [3.67.1](https://github.com/eggjs/tegg/compare/v3.67.0...v3.67.1) (2025-12-23) **Note:** Version bump only for package @eggjs/tegg # [3.67.0](https://github.com/eggjs/tegg/compare/v3.66.0...v3.67.0) (2025-12-18) **Note:** Version bump only for package @eggjs/tegg # [3.66.0](https://github.com/eggjs/tegg/compare/v3.65.3...v3.66.0) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg ## [3.65.3](https://github.com/eggjs/tegg/compare/v3.65.2...v3.65.3) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg ## [3.65.2](https://github.com/eggjs/tegg/compare/v3.65.1...v3.65.2) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg ## [3.65.1](https://github.com/eggjs/tegg/compare/v3.65.0...v3.65.1) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg # [3.65.0](https://github.com/eggjs/tegg/compare/v3.64.5...v3.65.0) (2025-12-10) **Note:** Version bump only for package @eggjs/tegg ## [3.64.5](https://github.com/eggjs/tegg/compare/v3.64.4...v3.64.5) (2025-12-08) ### Bug Fixes * langchain build bug ([#373](https://github.com/eggjs/tegg/issues/373)) ([3d32355](https://github.com/eggjs/tegg/commit/3d323550cefe950c1b0025296670ea33d7afc242)) ## [3.64.4](https://github.com/eggjs/tegg/compare/v3.64.3...v3.64.4) (2025-12-06) **Note:** Version bump only for package @eggjs/tegg ## [3.64.3](https://github.com/eggjs/tegg/compare/v3.64.2...v3.64.3) (2025-12-06) ### Bug Fixes * update trace logger check ([#372](https://github.com/eggjs/tegg/issues/372)) ([b974762](https://github.com/eggjs/tegg/commit/b974762dfccf1bb7b188c233be33014b6336bfee)) ## [3.64.2](https://github.com/eggjs/tegg/compare/v3.64.1...v3.64.2) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg ## [3.64.1](https://github.com/eggjs/tegg/compare/v3.64.0...v3.64.1) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg # [3.64.0](https://github.com/eggjs/tegg/compare/v3.63.2...v3.64.0) (2025-11-27) ### Bug Fixes * mcp zod type and langchain test version ([#369](https://github.com/eggjs/tegg/issues/369)) ([8178168](https://github.com/eggjs/tegg/commit/81781685c392346d21c56b649bfe8bb7a99bc9fb)) ## [3.63.2](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.2) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg ## [3.63.1](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.1) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg # [3.63.0](https://github.com/eggjs/tegg/compare/v3.62.3...v3.63.0) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg ## [3.62.3](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.3) (2025-11-05) **Note:** Version bump only for package @eggjs/tegg ## [3.62.2](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.2) (2025-11-05) **Note:** Version bump only for package @eggjs/tegg ## [3.62.1](https://github.com/eggjs/tegg/compare/v3.62.0...v3.62.1) (2025-11-03) **Note:** Version bump only for package @eggjs/tegg # [3.62.0](https://github.com/eggjs/tegg/compare/v3.61.0...v3.62.0) (2025-09-24) **Note:** Version bump only for package @eggjs/tegg # [3.61.0](https://github.com/eggjs/tegg/compare/v3.60.3...v3.61.0) (2025-08-15) ### Features * allow inject MysqlDataSourceManager ([#342](https://github.com/eggjs/tegg/issues/342)) ([d13b2d7](https://github.com/eggjs/tegg/commit/d13b2d7cd11dd36960647cb40bfc4bf92ce704fd)) ## [3.60.3](https://github.com/eggjs/tegg/compare/v3.60.2...v3.60.3) (2025-08-06) **Note:** Version bump only for package @eggjs/tegg ## [3.60.2](https://github.com/eggjs/tegg/compare/v3.60.1...v3.60.2) (2025-08-06) **Note:** Version bump only for package @eggjs/tegg ## [3.60.1](https://github.com/eggjs/tegg/compare/v3.60.0...v3.60.1) (2025-07-28) **Note:** Version bump only for package @eggjs/tegg # [3.60.0](https://github.com/eggjs/tegg/compare/v3.59.1...v3.60.0) (2025-07-28) **Note:** Version bump only for package @eggjs/tegg ## [3.59.1](https://github.com/eggjs/tegg/compare/v3.59.0...v3.59.1) (2025-07-16) **Note:** Version bump only for package @eggjs/tegg # [3.59.0](https://github.com/eggjs/tegg/compare/v3.58.0...v3.59.0) (2025-07-07) **Note:** Version bump only for package @eggjs/tegg # [3.58.0](https://github.com/eggjs/tegg/compare/v3.57.14...v3.58.0) (2025-07-01) **Note:** Version bump only for package @eggjs/tegg ## [3.57.14](https://github.com/eggjs/tegg/compare/v3.57.13...v3.57.14) (2025-06-18) **Note:** Version bump only for package @eggjs/tegg ## [3.57.13](https://github.com/eggjs/tegg/compare/v3.57.12...v3.57.13) (2025-06-18) **Note:** Version bump only for package @eggjs/tegg ## [3.57.12](https://github.com/eggjs/tegg/compare/v3.57.11...v3.57.12) (2025-06-16) **Note:** Version bump only for package @eggjs/tegg ## [3.57.11](https://github.com/eggjs/tegg/compare/v3.57.10...v3.57.11) (2025-06-16) **Note:** Version bump only for package @eggjs/tegg ## [3.57.10](https://github.com/eggjs/tegg/compare/v3.57.9...v3.57.10) (2025-06-12) **Note:** Version bump only for package @eggjs/tegg ## [3.57.9](https://github.com/eggjs/tegg/compare/v3.57.8...v3.57.9) (2025-05-29) **Note:** Version bump only for package @eggjs/tegg ## [3.57.8](https://github.com/eggjs/tegg/compare/v3.57.7...v3.57.8) (2025-05-29) **Note:** Version bump only for package @eggjs/tegg ## [3.57.7](https://github.com/eggjs/tegg/compare/v3.57.6...v3.57.7) (2025-05-28) **Note:** Version bump only for package @eggjs/tegg ## [3.57.6](https://github.com/eggjs/tegg/compare/v3.57.5...v3.57.6) (2025-05-27) **Note:** Version bump only for package @eggjs/tegg ## [3.57.5](https://github.com/eggjs/tegg/compare/v3.57.4...v3.57.5) (2025-05-15) **Note:** Version bump only for package @eggjs/tegg ## [3.57.4](https://github.com/eggjs/tegg/compare/v3.57.3...v3.57.4) (2025-05-14) **Note:** Version bump only for package @eggjs/tegg ## [3.57.3](https://github.com/eggjs/tegg/compare/v3.57.2...v3.57.3) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg ## [3.57.2](https://github.com/eggjs/tegg/compare/v3.57.1...v3.57.2) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg ## [3.57.1](https://github.com/eggjs/tegg/compare/v3.57.0...v3.57.1) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg # [3.57.0](https://github.com/eggjs/tegg/compare/v3.56.3...v3.57.0) (2025-05-09) **Note:** Version bump only for package @eggjs/tegg ## [3.56.3](https://github.com/eggjs/tegg/compare/v3.56.2...v3.56.3) (2025-05-07) **Note:** Version bump only for package @eggjs/tegg ## [3.56.2](https://github.com/eggjs/tegg/compare/v3.56.1...v3.56.2) (2025-05-01) **Note:** Version bump only for package @eggjs/tegg ## [3.56.1](https://github.com/eggjs/tegg/compare/v3.56.0...v3.56.1) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg # [3.56.0](https://github.com/eggjs/tegg/compare/v3.55.0...v3.56.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg # [3.55.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.55.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg # [3.54.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.54.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg # [3.53.0](https://github.com/eggjs/tegg/compare/v3.52.1...v3.53.0) (2025-04-21) **Note:** Version bump only for package @eggjs/tegg ## [3.52.1](https://github.com/eggjs/tegg/compare/v3.52.0...v3.52.1) (2025-03-14) **Note:** Version bump only for package @eggjs/tegg # [3.52.0](https://github.com/eggjs/tegg/compare/v3.51.2...v3.52.0) (2024-12-30) **Note:** Version bump only for package @eggjs/tegg ## [3.51.2](https://github.com/eggjs/tegg/compare/v3.51.1...v3.51.2) (2024-12-09) **Note:** Version bump only for package @eggjs/tegg ## [3.51.1](https://github.com/eggjs/tegg/compare/v3.51.0...v3.51.1) (2024-11-06) **Note:** Version bump only for package @eggjs/tegg # [3.51.0](https://github.com/eggjs/tegg/compare/v3.50.1...v3.51.0) (2024-10-30) **Note:** Version bump only for package @eggjs/tegg ## [3.50.1](https://github.com/eggjs/tegg/compare/v3.50.0...v3.50.1) (2024-10-23) **Note:** Version bump only for package @eggjs/tegg # [3.50.0](https://github.com/eggjs/tegg/compare/v3.49.0...v3.50.0) (2024-10-22) **Note:** Version bump only for package @eggjs/tegg # [3.49.0](https://github.com/eggjs/tegg/compare/v3.48.1...v3.49.0) (2024-10-21) **Note:** Version bump only for package @eggjs/tegg ## [3.48.1](https://github.com/eggjs/tegg/compare/v3.48.0...v3.48.1) (2024-10-14) **Note:** Version bump only for package @eggjs/tegg # [3.48.0](https://github.com/eggjs/tegg/compare/v3.47.2...v3.48.0) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg ## [3.47.2](https://github.com/eggjs/tegg/compare/v3.47.1...v3.47.2) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg ## [3.47.1](https://github.com/eggjs/tegg/compare/v3.47.0...v3.47.1) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg # [3.47.0](https://github.com/eggjs/tegg/compare/v3.46.4...v3.47.0) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg ## [3.46.4](https://github.com/eggjs/tegg/compare/v3.46.3...v3.46.4) (2024-10-09) **Note:** Version bump only for package @eggjs/tegg ## [3.46.3](https://github.com/eggjs/tegg/compare/v3.46.2...v3.46.3) (2024-10-08) **Note:** Version bump only for package @eggjs/tegg ## [3.46.2](https://github.com/eggjs/tegg/compare/v3.46.1...v3.46.2) (2024-10-07) **Note:** Version bump only for package @eggjs/tegg ## [3.46.1](https://github.com/eggjs/tegg/compare/v3.46.0...v3.46.1) (2024-09-30) **Note:** Version bump only for package @eggjs/tegg # [3.46.0](https://github.com/eggjs/tegg/compare/v3.45.0...v3.46.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg # [3.45.0](https://github.com/eggjs/tegg/compare/v3.44.1...v3.45.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg ## [3.44.1](https://github.com/eggjs/tegg/compare/v3.44.0...v3.44.1) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg # [3.44.0](https://github.com/eggjs/tegg/compare/v3.43.2...v3.44.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg ## [3.43.2](https://github.com/eggjs/tegg/compare/v3.43.1...v3.43.2) (2024-09-14) **Note:** Version bump only for package @eggjs/tegg ## [3.43.1](https://github.com/eggjs/tegg/compare/v3.43.0...v3.43.1) (2024-09-14) **Note:** Version bump only for package @eggjs/tegg # [3.43.0](https://github.com/eggjs/tegg/compare/v3.42.0...v3.43.0) (2024-09-13) **Note:** Version bump only for package @eggjs/tegg # [3.42.0](https://github.com/eggjs/tegg/compare/v3.41.0...v3.42.0) (2024-09-10) **Note:** Version bump only for package @eggjs/tegg # [3.41.0](https://github.com/eggjs/tegg/compare/v3.40.1...v3.41.0) (2024-08-26) **Note:** Version bump only for package @eggjs/tegg ## [3.40.1](https://github.com/eggjs/tegg/compare/v3.40.0...v3.40.1) (2024-08-23) **Note:** Version bump only for package @eggjs/tegg # [3.40.0](https://github.com/eggjs/tegg/compare/v3.39.5...v3.40.0) (2024-08-22) **Note:** Version bump only for package @eggjs/tegg ## [3.39.5](https://github.com/eggjs/tegg/compare/v3.39.4...v3.39.5) (2024-08-09) **Note:** Version bump only for package @eggjs/tegg ## [3.39.4](https://github.com/eggjs/tegg/compare/v3.39.3...v3.39.4) (2024-07-09) **Note:** Version bump only for package @eggjs/tegg ## [3.39.3](https://github.com/eggjs/tegg/compare/v3.39.2...v3.39.3) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg ## [3.39.2](https://github.com/eggjs/tegg/compare/v3.39.1...v3.39.2) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg ## [3.39.1](https://github.com/eggjs/tegg/compare/v3.39.0...v3.39.1) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg # [3.39.0](https://github.com/eggjs/tegg/compare/v3.38.0...v3.39.0) (2024-04-19) **Note:** Version bump only for package @eggjs/tegg # [3.38.0](https://github.com/eggjs/tegg/compare/v3.37.3...v3.38.0) (2024-04-18) **Note:** Version bump only for package @eggjs/tegg ## [3.37.3](https://github.com/eggjs/tegg/compare/v3.37.2...v3.37.3) (2024-04-17) **Note:** Version bump only for package @eggjs/tegg ## [3.37.2](https://github.com/eggjs/tegg/compare/v3.37.1...v3.37.2) (2024-04-17) **Note:** Version bump only for package @eggjs/tegg ## [3.37.1](https://github.com/eggjs/tegg/compare/v3.37.0...v3.37.1) (2024-04-16) **Note:** Version bump only for package @eggjs/tegg # [3.37.0](https://github.com/eggjs/tegg/compare/v3.36.3...v3.37.0) (2024-04-16) **Note:** Version bump only for package @eggjs/tegg ## [3.36.3](https://github.com/eggjs/tegg/compare/v3.36.2...v3.36.3) (2024-04-10) **Note:** Version bump only for package @eggjs/tegg ## [3.36.2](https://github.com/eggjs/tegg/compare/v3.36.1...v3.36.2) (2024-04-08) **Note:** Version bump only for package @eggjs/tegg ## [3.36.1](https://github.com/eggjs/tegg/compare/v3.36.0...v3.36.1) (2024-04-07) **Note:** Version bump only for package @eggjs/tegg # [3.36.0](https://github.com/eggjs/tegg/compare/v3.35.1...v3.36.0) (2024-04-02) ### Features * impl ajv + typebox Validator ([#201](https://github.com/eggjs/tegg/issues/201)) ([9fd585d](https://github.com/eggjs/tegg/commit/9fd585de9b613466c96b73494a08a494db34ea57)) ## [3.35.1](https://github.com/eggjs/tegg/compare/v3.35.0...v3.35.1) (2024-03-26) **Note:** Version bump only for package @eggjs/tegg # [3.35.0](https://github.com/eggjs/tegg/compare/v3.34.0...v3.35.0) (2024-03-26) **Note:** Version bump only for package @eggjs/tegg # [3.34.0](https://github.com/eggjs/tegg/compare/v3.33.1...v3.34.0) (2024-03-22) ### Features * impl dal for standalone tegg ([#197](https://github.com/eggjs/tegg/issues/197)) ([56b259d](https://github.com/eggjs/tegg/commit/56b259d7215a9d9542b36e421996623819369846)) ## [3.33.1](https://github.com/eggjs/tegg/compare/v3.33.0...v3.33.1) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg # [3.33.0](https://github.com/eggjs/tegg/compare/v3.32.0...v3.33.0) (2024-03-22) ### Features * impl dal ([#192](https://github.com/eggjs/tegg/issues/192)) ([1c7d145](https://github.com/eggjs/tegg/commit/1c7d1454bc8c600cd58c3ec7b9cda4e8a98c7287)) # [3.32.0](https://github.com/eggjs/tegg/compare/v3.31.0...v3.32.0) (2024-02-19) **Note:** Version bump only for package @eggjs/tegg # [3.31.0](https://github.com/eggjs/tegg/compare/v3.30.1...v3.31.0) (2024-01-31) **Note:** Version bump only for package @eggjs/tegg ## [3.30.1](https://github.com/eggjs/tegg/compare/v3.30.0...v3.30.1) (2024-01-25) **Note:** Version bump only for package @eggjs/tegg # [3.30.0](https://github.com/eggjs/tegg/compare/v3.29.0...v3.30.0) (2024-01-17) **Note:** Version bump only for package @eggjs/tegg # [3.29.0](https://github.com/eggjs/tegg/compare/v3.28.2...v3.29.0) (2023-12-26) **Note:** Version bump only for package @eggjs/tegg ## [3.28.2](https://github.com/eggjs/tegg/compare/v3.28.1...v3.28.2) (2023-12-12) **Note:** Version bump only for package @eggjs/tegg ## [3.28.1](https://github.com/eggjs/tegg/compare/v3.28.0...v3.28.1) (2023-12-11) **Note:** Version bump only for package @eggjs/tegg # [3.28.0](https://github.com/eggjs/tegg/compare/v3.27.0...v3.28.0) (2023-12-10) **Note:** Version bump only for package @eggjs/tegg # [3.27.0](https://github.com/eggjs/tegg/compare/v3.26.0...v3.27.0) (2023-11-23) **Note:** Version bump only for package @eggjs/tegg # [3.26.0](https://github.com/eggjs/tegg/compare/v3.25.2...v3.26.0) (2023-11-17) **Note:** Version bump only for package @eggjs/tegg ## [3.25.2](https://github.com/eggjs/tegg/compare/v3.25.1...v3.25.2) (2023-11-06) **Note:** Version bump only for package @eggjs/tegg ## [3.25.1](https://github.com/eggjs/tegg/compare/v3.25.0...v3.25.1) (2023-11-03) **Note:** Version bump only for package @eggjs/tegg # [3.25.0](https://github.com/eggjs/tegg/compare/v3.24.0...v3.25.0) (2023-11-03) ### Features * tegg plugin support ModuleConfig ([#162](https://github.com/eggjs/tegg/issues/162)) ([58bd9fa](https://github.com/eggjs/tegg/commit/58bd9fafdd0d56aabdde5f7c33f17c45568bada8)) # [3.24.0](https://github.com/eggjs/tegg/compare/v3.23.0...v3.24.0) (2023-10-26) **Note:** Version bump only for package @eggjs/tegg # [3.23.0](https://github.com/eggjs/tegg/compare/v3.22.0...v3.23.0) (2023-09-20) **Note:** Version bump only for package @eggjs/tegg # [3.20.0](https://github.com/eggjs/tegg/compare/v3.19.0...v3.20.0) (2023-09-07) **Note:** Version bump only for package @eggjs/tegg # [3.19.0](https://github.com/eggjs/tegg/compare/v3.18.1...v3.19.0) (2023-08-31) **Note:** Version bump only for package @eggjs/tegg ## [3.18.1](https://github.com/eggjs/tegg/compare/v3.18.0...v3.18.1) (2023-08-29) **Note:** Version bump only for package @eggjs/tegg # [3.18.0](https://github.com/eggjs/tegg/compare/v3.17.0...v3.18.0) (2023-08-29) **Note:** Version bump only for package @eggjs/tegg # [3.17.0](https://github.com/eggjs/tegg/compare/v3.16.0...v3.17.0) (2023-08-24) **Note:** Version bump only for package @eggjs/tegg # [3.16.0](https://github.com/eggjs/tegg/compare/v3.15.0...v3.16.0) (2023-08-24) ### Features * implement RuntimeConfig ([#144](https://github.com/eggjs/tegg/issues/144)) ([0862655](https://github.com/eggjs/tegg/commit/0862655846f6765349d406ee697c036cec2a37bd)) ## [3.14.3](https://github.com/eggjs/tegg/compare/v3.14.2...v3.14.3) (2023-08-14) **Note:** Version bump only for package @eggjs/tegg ## [3.14.2](https://github.com/eggjs/tegg/compare/v3.14.1...v3.14.2) (2023-08-14) **Note:** Version bump only for package @eggjs/tegg ## [3.14.1](https://github.com/eggjs/tegg/compare/v3.14.0...v3.14.1) (2023-08-11) **Note:** Version bump only for package @eggjs/tegg # [3.14.0](https://github.com/eggjs/tegg/compare/v3.13.0...v3.14.0) (2023-07-31) **Note:** Version bump only for package @eggjs/tegg # [3.13.0](https://github.com/eggjs/tegg/compare/v3.12.0...v3.13.0) (2023-07-25) **Note:** Version bump only for package @eggjs/tegg # [3.12.0](https://github.com/eggjs/tegg/compare/v3.11.1...v3.12.0) (2023-07-13) **Note:** Version bump only for package @eggjs/tegg ## [3.11.1](https://github.com/eggjs/tegg/compare/v3.11.0...v3.11.1) (2023-06-29) **Note:** Version bump only for package @eggjs/tegg # [3.11.0](https://github.com/eggjs/tegg/compare/v3.10.0...v3.11.0) (2023-06-29) ### Features * export transaction decorator from tegg ([8be0521](https://github.com/eggjs/tegg/commit/8be05212b62fe7f111688efaec935be64d623918)) # [3.10.0](https://github.com/eggjs/tegg/compare/v3.9.0...v3.10.0) (2023-06-28) **Note:** Version bump only for package @eggjs/tegg # [3.9.0](https://github.com/eggjs/tegg/compare/v3.8.0...v3.9.0) (2023-06-20) **Note:** Version bump only for package @eggjs/tegg # [3.8.0](https://github.com/eggjs/tegg/compare/v3.7.0...v3.8.0) (2023-05-30) ### Features * impl EggObjectLifecycle hook in decorator ([#119](https://github.com/eggjs/tegg/issues/119)) ([cced8a2](https://github.com/eggjs/tegg/commit/cced8a2e009c33d5040fa21d00409fddef471b0e)) # [3.7.0](https://github.com/eggjs/tegg/compare/v3.6.3...v3.7.0) (2023-04-03) **Note:** Version bump only for package @eggjs/tegg ## [3.6.3](https://github.com/eggjs/tegg/compare/v3.6.2...v3.6.3) (2023-03-02) **Note:** Version bump only for package @eggjs/tegg # [3.6.0](https://github.com/eggjs/tegg/compare/v3.5.2...v3.6.0) (2023-02-13) **Note:** Version bump only for package @eggjs/tegg ## [3.5.2](https://github.com/eggjs/tegg/compare/v3.5.1...v3.5.2) (2023-02-10) **Note:** Version bump only for package @eggjs/tegg # [3.5.0](https://github.com/eggjs/tegg/compare/v3.4.1...v3.5.0) (2023-02-10) **Note:** Version bump only for package @eggjs/tegg ## [3.4.1](https://github.com/eggjs/tegg/compare/v3.4.0...v3.4.1) (2023-02-02) **Note:** Version bump only for package @eggjs/tegg # [3.4.0](https://github.com/eggjs/tegg/compare/v3.3.4...v3.4.0) (2023-02-01) **Note:** Version bump only for package @eggjs/tegg ## [3.3.1](https://github.com/eggjs/tegg/compare/v3.3.0...v3.3.1) (2023-01-28) **Note:** Version bump only for package @eggjs/tegg # [3.3.0](https://github.com/eggjs/tegg/compare/v3.2.4...v3.3.0) (2023-01-28) **Note:** Version bump only for package @eggjs/tegg ## [3.2.3](https://github.com/eggjs/tegg/compare/v3.2.2...v3.2.3) (2023-01-16) **Note:** Version bump only for package @eggjs/tegg ## [3.2.2](https://github.com/eggjs/tegg/compare/v3.2.1...v3.2.2) (2023-01-06) **Note:** Version bump only for package @eggjs/tegg ## [3.2.1](https://github.com/eggjs/tegg/compare/v3.2.0...v3.2.1) (2022-12-28) **Note:** Version bump only for package @eggjs/tegg # [3.1.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.1.0) (2022-12-27) ### Features * impl Schedule decorator ([#52](https://github.com/eggjs/tegg/issues/52)) ([7f95005](https://github.com/eggjs/tegg/commit/7f950050b548ca542addbd7b466675da4e81ce3f)) # [3.0.0](https://github.com/eggjs/tegg/compare/v3.0.0-alpha.0...v3.0.0) (2022-12-26) **Note:** Version bump only for package @eggjs/tegg # [3.0.0-alpha.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.0.0-alpha.0) (2022-12-22) ### Features * impl Schedule decorator ([#52](https://github.com/eggjs/tegg/issues/52)) ([7f95005](https://github.com/eggjs/tegg/commit/7f950050b548ca542addbd7b466675da4e81ce3f)) # [1.4.0](https://github.com/eggjs/tegg/compare/@eggjs/tegg@1.3.5...@eggjs/tegg@1.4.0) (2022-09-04) ### Features * impl Schedule decorator ([#52](https://github.com/eggjs/tegg/issues/52)) ([7f95005](https://github.com/eggjs/tegg/commit/7f950050b548ca542addbd7b466675da4e81ce3f)) ## [1.3.5](https://github.com/eggjs/tegg/compare/@eggjs/tegg@1.3.4...@eggjs/tegg@1.3.5) (2022-08-16) **Note:** Version bump only for package @eggjs/tegg ## [1.3.4](https://github.com/eggjs/tegg/compare/@eggjs/tegg@1.3.3...@eggjs/tegg@1.3.4) (2022-07-28) **Note:** Version bump only for package @eggjs/tegg ## [1.3.3](https://github.com/eggjs/tegg/compare/@eggjs/tegg@1.3.2...@eggjs/tegg@1.3.3) (2022-07-20) **Note:** Version bump only for package @eggjs/tegg ## [1.3.2](https://github.com/eggjs/tegg/compare/@eggjs/tegg@1.3.1...@eggjs/tegg@1.3.2) (2022-07-20) **Note:** Version bump only for package @eggjs/tegg # [1.3.0](https://github.com/eggjs/tegg/compare/v1.2.0...v1.3.0) (2022-07-01) **Note:** Version bump only for package @eggjs/tegg # [0.2.0](https://github.com/eggjs/tegg/compare/v0.1.19...v0.2.0) (2022-01-20) ### Bug Fixes * invalid value of main ([#27](https://github.com/eggjs/tegg/issues/27)) ([47f22d6](https://github.com/eggjs/tegg/commit/47f22d60f7ab01cf3c0e68bd078cdd0bb75169d5)) ### Features * impl aop ([c53df00](https://github.com/eggjs/tegg/commit/c53df001d1455a0a105689694775d880541d9d2f)) ================================================ FILE: core/tegg/README.md ================================================ # `@eggjs/tegg` ## Install ```sh npm i @eggjs/tegg @eggjs/tegg-plugin ``` ## Usage Check out our documentation [here](https://github.com/eggjs/tegg#readme). ================================================ FILE: core/tegg/agent.ts ================================================ // AgentController decorator from controller-decorator export { AgentController } from '@eggjs/controller-decorator'; export type { AgentHandler } from '@eggjs/controller-decorator'; // Implementation classes from agent-runtime export { AgentNotFoundError, AgentConflictError, HttpSSEWriter } from '@eggjs/agent-runtime'; // Types (re-exported from agent-runtime, which re-exports @eggjs/tegg-types) export type { AgentStore, ThreadRecord, RunRecord, CreateRunInput, RunObject, ThreadObject, ThreadObjectWithMessages, InputMessage, InputContentPart, TextInputContentPart, ToolUseInputContentPart, ToolResultInputContentPart, GenericInputContentPart, AgentRunConfig, GetThreadOptions, RunStatus, // SDK-aligned message types AgentMessage, SDKSystemMessage, SDKStreamEvent, SDKUserMessage, SDKAssistantMessage, SDKResultMessage, SDKGenericMessage, } from '@eggjs/agent-runtime'; ================================================ FILE: core/tegg/ajv.ts ================================================ export * from '@eggjs/ajv-decorator'; ================================================ FILE: core/tegg/aop.ts ================================================ export * from '@eggjs/aop-decorator'; ================================================ FILE: core/tegg/dal.ts ================================================ export * from '@eggjs/dal-decorator'; export * from '@eggjs/tegg-dal-plugin'; ================================================ FILE: core/tegg/helper.ts ================================================ export * from '@eggjs/tegg-runtime'; export * from '@eggjs/tegg-loader'; export * from '@eggjs/tegg-metadata'; export * from '@eggjs/tegg-common-util'; ================================================ FILE: core/tegg/index.ts ================================================ export * from '@eggjs/core-decorator'; export * from '@eggjs/tegg-lifecycle'; export * from '@eggjs/controller-decorator'; export * from '@eggjs/eventbus-decorator'; export * from '@eggjs/tegg-dynamic-inject'; export * from '@eggjs/tegg-background-task'; export * as aop from '@eggjs/aop-decorator'; export * as orm from '@eggjs/tegg-orm-decorator'; export * as schedule from '@eggjs/tegg-schedule-decorator'; export { RuntimeConfig, ModuleConfigs, ModuleConfigHolder } from '@eggjs/tegg-common-util'; export { Logger } from '@eggjs/tegg-types'; ================================================ FILE: core/tegg/orm.ts ================================================ export * from '@eggjs/tegg-orm-decorator'; ================================================ FILE: core/tegg/package.json ================================================ { "name": "@eggjs/tegg", "version": "3.78.15", "description": "tegg decorator packages", "keywords": [ "egg", "typescript", "metadata", "tegg" ], "main": "./index.js", "files": [ "*.js", "*.d.ts" ], "typings": "index.d.ts", "scripts": { "test": "cross-env NODE_ENV=test NODE_OPTIONS='--no-deprecation' mocha", "clean": "tsc -b --clean", "tsc:pub": "ut run clean && tsc -p ./tsconfig.pub.json", "prepublishOnly": "ut tsc:pub" }, "author": "killagu ", "license": "MIT", "homepage": "https://github.com/eggjs/tegg", "bugs": { "url": "https://github.com/eggjs/tegg/issues" }, "repository": { "type": "git", "url": "git@github.com:eggjs/tegg.git", "directory": "core/test-util" }, "engines": { "node": ">=14.0.0" }, "dependencies": { "@eggjs/agent-runtime": "^3.78.15", "@eggjs/ajv-decorator": "^3.78.15", "@eggjs/aop-decorator": "^3.78.15", "@eggjs/controller-decorator": "^3.78.15", "@eggjs/core-decorator": "^3.78.15", "@eggjs/dal-decorator": "^3.78.15", "@eggjs/eventbus-decorator": "^3.78.15", "@eggjs/standalone-decorator": "^3.78.15", "@eggjs/tegg-background-task": "^3.78.15", "@eggjs/tegg-common-util": "^3.78.15", "@eggjs/tegg-dynamic-inject": "^3.78.15", "@eggjs/tegg-lifecycle": "^3.78.15", "@eggjs/tegg-loader": "^3.78.15", "@eggjs/tegg-metadata": "^3.78.15", "@eggjs/tegg-orm-decorator": "^3.78.15", "@eggjs/tegg-runtime": "^3.78.15", "@eggjs/tegg-schedule-decorator": "^3.78.15", "@eggjs/tegg-transaction-decorator": "^3.78.15", "@eggjs/tegg-types": "^3.78.15" }, "peerDependencies": { "@eggjs/tegg-dal-plugin": "^3.60.1" }, "publishConfig": { "access": "public" }, "devDependencies": { "@types/mocha": "^10.0.1", "@types/node": "^20.2.4", "cross-env": "^7.0.3", "mocha": "^10.2.0", "ts-node": "^10.9.1", "typescript": "^5.0.4" }, "gitHead": "240a3e9f40fda65ebb7589727d197db5ce17916c" } ================================================ FILE: core/tegg/schedule.ts ================================================ export * from '@eggjs/tegg-schedule-decorator'; ================================================ FILE: core/tegg/standalone.ts ================================================ export * from '@eggjs/standalone-decorator'; ================================================ FILE: core/tegg/test/helper.test.ts ================================================ import assert = require('assert'); import { AbstractEggContext, LoadUnitInstanceLifecycleUtil, LoaderUtil, ModuleConfigUtil, LoadUnitLifecycleUtil, } from '../helper'; describe('test/helper.test.ts', () => { it('should ok', async () => { assert(AbstractEggContext); assert(LoadUnitInstanceLifecycleUtil); assert(LoaderUtil); assert(ModuleConfigUtil); assert(LoadUnitLifecycleUtil); }); }); ================================================ FILE: core/tegg/test/index.test.ts ================================================ import assert = require('assert'); import { Acl, Context, ContextProto, Inject, AccessLevel, EventInfoUtil, QualifierImplUtil, BackgroundTaskHelper, orm, aop, } from '..'; describe('test/index.test.ts', () => { it('should ok', async () => { assert(Acl); assert(Context); assert(ContextProto); assert(Inject); assert(AccessLevel); assert(EventInfoUtil); assert(QualifierImplUtil); assert(BackgroundTaskHelper); assert(orm.DataSource); assert(orm.Attribute); assert(aop.Advice); }); }); ================================================ FILE: core/tegg/transaction.ts ================================================ export * from '@eggjs/tegg-transaction-decorator'; ================================================ FILE: core/tegg/tsconfig.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "baseUrl": "./" }, "exclude": [ "dist", "node_modules", "test" ] } ================================================ FILE: core/tegg/tsconfig.pub.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "baseUrl": "./" }, "exclude": [ "dist", "node_modules", "test" ] } ================================================ FILE: core/tegg/zod.ts ================================================ export * as z from 'zod/v4'; ================================================ FILE: core/test-util/CHANGELOG.md ================================================ # Change Log All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. ## [3.78.15](https://github.com/eggjs/tegg/compare/v3.78.14...v3.78.15) (2026-04-23) **Note:** Version bump only for package @eggjs/module-test-util ## [3.78.14](https://github.com/eggjs/tegg/compare/v3.78.13...v3.78.14) (2026-04-21) **Note:** Version bump only for package @eggjs/module-test-util ## [3.78.13](https://github.com/eggjs/tegg/compare/v3.78.12...v3.78.13) (2026-04-21) **Note:** Version bump only for package @eggjs/module-test-util ## [3.78.12](https://github.com/eggjs/tegg/compare/v3.78.11...v3.78.12) (2026-04-18) **Note:** Version bump only for package @eggjs/module-test-util ## [3.78.11](https://github.com/eggjs/tegg/compare/v3.78.10...v3.78.11) (2026-04-18) **Note:** Version bump only for package @eggjs/module-test-util ## [3.78.10](https://github.com/eggjs/tegg/compare/v3.78.9...v3.78.10) (2026-04-18) **Note:** Version bump only for package @eggjs/module-test-util ## [3.78.9](https://github.com/eggjs/tegg/compare/v3.78.8...v3.78.9) (2026-04-18) **Note:** Version bump only for package @eggjs/module-test-util ## [3.78.8](https://github.com/eggjs/tegg/compare/v3.78.7...v3.78.8) (2026-04-18) **Note:** Version bump only for package @eggjs/module-test-util ## [3.78.7](https://github.com/eggjs/tegg/compare/v3.78.6...v3.78.7) (2026-04-18) **Note:** Version bump only for package @eggjs/module-test-util ## [3.78.6](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.6) (2026-04-18) **Note:** Version bump only for package @eggjs/module-test-util ## [3.78.5](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.5) (2026-04-18) **Note:** Version bump only for package @eggjs/module-test-util ## [3.78.4](https://github.com/eggjs/tegg/compare/v3.78.3...v3.78.4) (2026-04-18) **Note:** Version bump only for package @eggjs/module-test-util ## [3.78.3](https://github.com/eggjs/tegg/compare/v3.78.2...v3.78.3) (2026-04-15) **Note:** Version bump only for package @eggjs/module-test-util ## [3.78.2](https://github.com/eggjs/tegg/compare/v3.78.1...v3.78.2) (2026-04-09) **Note:** Version bump only for package @eggjs/module-test-util ## [3.78.1](https://github.com/eggjs/tegg/compare/v3.78.0...v3.78.1) (2026-04-09) **Note:** Version bump only for package @eggjs/module-test-util # [3.78.0](https://github.com/eggjs/tegg/compare/v3.77.2...v3.78.0) (2026-04-09) **Note:** Version bump only for package @eggjs/module-test-util ## [3.77.2](https://github.com/eggjs/tegg/compare/v3.77.1...v3.77.2) (2026-04-07) **Note:** Version bump only for package @eggjs/module-test-util ## [3.77.1](https://github.com/eggjs/tegg/compare/v3.77.0...v3.77.1) (2026-04-01) **Note:** Version bump only for package @eggjs/module-test-util # [3.77.0](https://github.com/eggjs/tegg/compare/v3.76.1...v3.77.0) (2026-04-01) **Note:** Version bump only for package @eggjs/module-test-util ## [3.76.1](https://github.com/eggjs/tegg/compare/v3.76.0...v3.76.1) (2026-04-01) **Note:** Version bump only for package @eggjs/module-test-util # [3.76.0](https://github.com/eggjs/tegg/compare/v3.75.1...v3.76.0) (2026-04-01) **Note:** Version bump only for package @eggjs/module-test-util ## [3.75.1](https://github.com/eggjs/tegg/compare/v3.75.0...v3.75.1) (2026-04-01) **Note:** Version bump only for package @eggjs/module-test-util # [3.75.0](https://github.com/eggjs/tegg/compare/v3.74.0...v3.75.0) (2026-03-30) **Note:** Version bump only for package @eggjs/module-test-util # [3.74.0](https://github.com/eggjs/tegg/compare/v3.73.0...v3.74.0) (2026-03-30) **Note:** Version bump only for package @eggjs/module-test-util # [3.73.0](https://github.com/eggjs/tegg/compare/v3.72.0...v3.73.0) (2026-03-25) **Note:** Version bump only for package @eggjs/module-test-util # [3.72.0](https://github.com/eggjs/tegg/compare/v3.71.2...v3.72.0) (2026-02-04) **Note:** Version bump only for package @eggjs/module-test-util ## [3.71.2](https://github.com/eggjs/tegg/compare/v3.71.1...v3.71.2) (2026-01-30) **Note:** Version bump only for package @eggjs/module-test-util ## [3.71.1](https://github.com/eggjs/tegg/compare/v3.71.0...v3.71.1) (2026-01-28) **Note:** Version bump only for package @eggjs/module-test-util # [3.71.0](https://github.com/eggjs/tegg/compare/v3.70.1...v3.71.0) (2026-01-26) **Note:** Version bump only for package @eggjs/module-test-util ## [3.70.1](https://github.com/eggjs/tegg/compare/v3.70.0...v3.70.1) (2026-01-14) **Note:** Version bump only for package @eggjs/module-test-util # [3.70.0](https://github.com/eggjs/tegg/compare/v3.69.0...v3.70.0) (2026-01-07) **Note:** Version bump only for package @eggjs/module-test-util # [3.69.0](https://github.com/eggjs/tegg/compare/v3.68.0...v3.69.0) (2026-01-06) **Note:** Version bump only for package @eggjs/module-test-util # [3.68.0](https://github.com/eggjs/tegg/compare/v3.67.2...v3.68.0) (2025-12-29) **Note:** Version bump only for package @eggjs/module-test-util ## [3.67.2](https://github.com/eggjs/tegg/compare/v3.67.1...v3.67.2) (2025-12-24) **Note:** Version bump only for package @eggjs/module-test-util ## [3.67.1](https://github.com/eggjs/tegg/compare/v3.67.0...v3.67.1) (2025-12-23) **Note:** Version bump only for package @eggjs/module-test-util # [3.67.0](https://github.com/eggjs/tegg/compare/v3.66.0...v3.67.0) (2025-12-18) **Note:** Version bump only for package @eggjs/module-test-util # [3.66.0](https://github.com/eggjs/tegg/compare/v3.65.3...v3.66.0) (2025-12-16) **Note:** Version bump only for package @eggjs/module-test-util ## [3.65.3](https://github.com/eggjs/tegg/compare/v3.65.2...v3.65.3) (2025-12-16) **Note:** Version bump only for package @eggjs/module-test-util ## [3.65.2](https://github.com/eggjs/tegg/compare/v3.65.1...v3.65.2) (2025-12-16) **Note:** Version bump only for package @eggjs/module-test-util ## [3.65.1](https://github.com/eggjs/tegg/compare/v3.65.0...v3.65.1) (2025-12-16) **Note:** Version bump only for package @eggjs/module-test-util # [3.65.0](https://github.com/eggjs/tegg/compare/v3.64.5...v3.65.0) (2025-12-10) **Note:** Version bump only for package @eggjs/module-test-util ## [3.64.5](https://github.com/eggjs/tegg/compare/v3.64.4...v3.64.5) (2025-12-08) **Note:** Version bump only for package @eggjs/module-test-util ## [3.64.4](https://github.com/eggjs/tegg/compare/v3.64.3...v3.64.4) (2025-12-06) **Note:** Version bump only for package @eggjs/module-test-util ## [3.64.3](https://github.com/eggjs/tegg/compare/v3.64.2...v3.64.3) (2025-12-06) **Note:** Version bump only for package @eggjs/module-test-util ## [3.64.2](https://github.com/eggjs/tegg/compare/v3.64.1...v3.64.2) (2025-11-27) **Note:** Version bump only for package @eggjs/module-test-util ## [3.64.1](https://github.com/eggjs/tegg/compare/v3.64.0...v3.64.1) (2025-11-27) **Note:** Version bump only for package @eggjs/module-test-util # [3.64.0](https://github.com/eggjs/tegg/compare/v3.63.2...v3.64.0) (2025-11-27) **Note:** Version bump only for package @eggjs/module-test-util ## [3.63.2](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.2) (2025-11-18) **Note:** Version bump only for package @eggjs/module-test-util ## [3.63.1](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.1) (2025-11-18) **Note:** Version bump only for package @eggjs/module-test-util # [3.63.0](https://github.com/eggjs/tegg/compare/v3.62.3...v3.63.0) (2025-11-18) **Note:** Version bump only for package @eggjs/module-test-util ## [3.62.3](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.3) (2025-11-05) **Note:** Version bump only for package @eggjs/module-test-util ## [3.62.2](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.2) (2025-11-05) **Note:** Version bump only for package @eggjs/module-test-util ## [3.62.1](https://github.com/eggjs/tegg/compare/v3.62.0...v3.62.1) (2025-11-03) **Note:** Version bump only for package @eggjs/module-test-util # [3.62.0](https://github.com/eggjs/tegg/compare/v3.61.0...v3.62.0) (2025-09-24) **Note:** Version bump only for package @eggjs/module-test-util # [3.61.0](https://github.com/eggjs/tegg/compare/v3.60.3...v3.61.0) (2025-08-15) **Note:** Version bump only for package @eggjs/module-test-util ## [3.60.3](https://github.com/eggjs/tegg/compare/v3.60.2...v3.60.3) (2025-08-06) **Note:** Version bump only for package @eggjs/module-test-util ## [3.60.2](https://github.com/eggjs/tegg/compare/v3.60.1...v3.60.2) (2025-08-06) **Note:** Version bump only for package @eggjs/module-test-util ## [3.60.1](https://github.com/eggjs/tegg/compare/v3.60.0...v3.60.1) (2025-07-28) **Note:** Version bump only for package @eggjs/module-test-util # [3.60.0](https://github.com/eggjs/tegg/compare/v3.59.1...v3.60.0) (2025-07-28) **Note:** Version bump only for package @eggjs/module-test-util ## [3.59.1](https://github.com/eggjs/tegg/compare/v3.59.0...v3.59.1) (2025-07-16) **Note:** Version bump only for package @eggjs/module-test-util # [3.59.0](https://github.com/eggjs/tegg/compare/v3.58.0...v3.59.0) (2025-07-07) **Note:** Version bump only for package @eggjs/module-test-util # [3.58.0](https://github.com/eggjs/tegg/compare/v3.57.14...v3.58.0) (2025-07-01) **Note:** Version bump only for package @eggjs/module-test-util ## [3.57.14](https://github.com/eggjs/tegg/compare/v3.57.13...v3.57.14) (2025-06-18) **Note:** Version bump only for package @eggjs/module-test-util ## [3.57.13](https://github.com/eggjs/tegg/compare/v3.57.12...v3.57.13) (2025-06-18) **Note:** Version bump only for package @eggjs/module-test-util ## [3.57.12](https://github.com/eggjs/tegg/compare/v3.57.11...v3.57.12) (2025-06-16) **Note:** Version bump only for package @eggjs/module-test-util ## [3.57.11](https://github.com/eggjs/tegg/compare/v3.57.10...v3.57.11) (2025-06-16) **Note:** Version bump only for package @eggjs/module-test-util ## [3.57.10](https://github.com/eggjs/tegg/compare/v3.57.9...v3.57.10) (2025-06-12) **Note:** Version bump only for package @eggjs/module-test-util ## [3.57.9](https://github.com/eggjs/tegg/compare/v3.57.8...v3.57.9) (2025-05-29) **Note:** Version bump only for package @eggjs/module-test-util ## [3.57.8](https://github.com/eggjs/tegg/compare/v3.57.7...v3.57.8) (2025-05-29) **Note:** Version bump only for package @eggjs/module-test-util ## [3.57.7](https://github.com/eggjs/tegg/compare/v3.57.6...v3.57.7) (2025-05-28) **Note:** Version bump only for package @eggjs/module-test-util ## [3.57.6](https://github.com/eggjs/tegg/compare/v3.57.5...v3.57.6) (2025-05-27) **Note:** Version bump only for package @eggjs/module-test-util ## [3.57.5](https://github.com/eggjs/tegg/compare/v3.57.4...v3.57.5) (2025-05-15) **Note:** Version bump only for package @eggjs/module-test-util ## [3.57.4](https://github.com/eggjs/tegg/compare/v3.57.3...v3.57.4) (2025-05-14) **Note:** Version bump only for package @eggjs/module-test-util ## [3.57.3](https://github.com/eggjs/tegg/compare/v3.57.2...v3.57.3) (2025-05-13) **Note:** Version bump only for package @eggjs/module-test-util ## [3.57.2](https://github.com/eggjs/tegg/compare/v3.57.1...v3.57.2) (2025-05-13) **Note:** Version bump only for package @eggjs/module-test-util ## [3.57.1](https://github.com/eggjs/tegg/compare/v3.57.0...v3.57.1) (2025-05-13) **Note:** Version bump only for package @eggjs/module-test-util # [3.57.0](https://github.com/eggjs/tegg/compare/v3.56.3...v3.57.0) (2025-05-09) **Note:** Version bump only for package @eggjs/module-test-util ## [3.56.3](https://github.com/eggjs/tegg/compare/v3.56.2...v3.56.3) (2025-05-07) **Note:** Version bump only for package @eggjs/module-test-util ## [3.56.2](https://github.com/eggjs/tegg/compare/v3.56.1...v3.56.2) (2025-05-01) **Note:** Version bump only for package @eggjs/module-test-util ## [3.56.1](https://github.com/eggjs/tegg/compare/v3.56.0...v3.56.1) (2025-04-29) **Note:** Version bump only for package @eggjs/module-test-util # [3.56.0](https://github.com/eggjs/tegg/compare/v3.55.0...v3.56.0) (2025-04-29) **Note:** Version bump only for package @eggjs/module-test-util # [3.55.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.55.0) (2025-04-29) **Note:** Version bump only for package @eggjs/module-test-util # [3.54.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.54.0) (2025-04-29) **Note:** Version bump only for package @eggjs/module-test-util # [3.53.0](https://github.com/eggjs/tegg/compare/v3.52.1...v3.53.0) (2025-04-21) **Note:** Version bump only for package @eggjs/module-test-util ## [3.52.1](https://github.com/eggjs/tegg/compare/v3.52.0...v3.52.1) (2025-03-14) **Note:** Version bump only for package @eggjs/module-test-util # [3.52.0](https://github.com/eggjs/tegg/compare/v3.51.2...v3.52.0) (2024-12-30) **Note:** Version bump only for package @eggjs/module-test-util ## [3.51.2](https://github.com/eggjs/tegg/compare/v3.51.1...v3.51.2) (2024-12-09) **Note:** Version bump only for package @eggjs/module-test-util ## [3.51.1](https://github.com/eggjs/tegg/compare/v3.51.0...v3.51.1) (2024-11-06) **Note:** Version bump only for package @eggjs/module-test-util # [3.51.0](https://github.com/eggjs/tegg/compare/v3.50.1...v3.51.0) (2024-10-30) **Note:** Version bump only for package @eggjs/module-test-util ## [3.50.1](https://github.com/eggjs/tegg/compare/v3.50.0...v3.50.1) (2024-10-23) **Note:** Version bump only for package @eggjs/module-test-util # [3.50.0](https://github.com/eggjs/tegg/compare/v3.49.0...v3.50.0) (2024-10-22) **Note:** Version bump only for package @eggjs/module-test-util # [3.49.0](https://github.com/eggjs/tegg/compare/v3.48.1...v3.49.0) (2024-10-21) **Note:** Version bump only for package @eggjs/module-test-util ## [3.48.1](https://github.com/eggjs/tegg/compare/v3.48.0...v3.48.1) (2024-10-14) **Note:** Version bump only for package @eggjs/module-test-util # [3.48.0](https://github.com/eggjs/tegg/compare/v3.47.2...v3.48.0) (2024-10-10) **Note:** Version bump only for package @eggjs/module-test-util ## [3.47.2](https://github.com/eggjs/tegg/compare/v3.47.1...v3.47.2) (2024-10-10) **Note:** Version bump only for package @eggjs/module-test-util ## [3.47.1](https://github.com/eggjs/tegg/compare/v3.47.0...v3.47.1) (2024-10-10) **Note:** Version bump only for package @eggjs/module-test-util # [3.47.0](https://github.com/eggjs/tegg/compare/v3.46.4...v3.47.0) (2024-10-10) ### Features * impl GlobalGraph build hook ([#246](https://github.com/eggjs/tegg/issues/246)) ([48fce45](https://github.com/eggjs/tegg/commit/48fce4512e99259ec26a9b032bfcc9f4046ad235)) ## [3.46.4](https://github.com/eggjs/tegg/compare/v3.46.3...v3.46.4) (2024-10-09) **Note:** Version bump only for package @eggjs/module-test-util ## [3.46.3](https://github.com/eggjs/tegg/compare/v3.46.2...v3.46.3) (2024-10-08) **Note:** Version bump only for package @eggjs/module-test-util ## [3.46.2](https://github.com/eggjs/tegg/compare/v3.46.1...v3.46.2) (2024-10-07) **Note:** Version bump only for package @eggjs/module-test-util ## [3.46.1](https://github.com/eggjs/tegg/compare/v3.46.0...v3.46.1) (2024-09-30) **Note:** Version bump only for package @eggjs/module-test-util # [3.46.0](https://github.com/eggjs/tegg/compare/v3.45.0...v3.46.0) (2024-09-29) **Note:** Version bump only for package @eggjs/module-test-util # [3.45.0](https://github.com/eggjs/tegg/compare/v3.44.1...v3.45.0) (2024-09-29) **Note:** Version bump only for package @eggjs/module-test-util ## [3.44.1](https://github.com/eggjs/tegg/compare/v3.44.0...v3.44.1) (2024-09-29) **Note:** Version bump only for package @eggjs/module-test-util # [3.44.0](https://github.com/eggjs/tegg/compare/v3.43.2...v3.44.0) (2024-09-29) **Note:** Version bump only for package @eggjs/module-test-util ## [3.43.2](https://github.com/eggjs/tegg/compare/v3.43.1...v3.43.2) (2024-09-14) **Note:** Version bump only for package @eggjs/module-test-util ## [3.43.1](https://github.com/eggjs/tegg/compare/v3.43.0...v3.43.1) (2024-09-14) **Note:** Version bump only for package @eggjs/module-test-util # [3.43.0](https://github.com/eggjs/tegg/compare/v3.42.0...v3.43.0) (2024-09-13) **Note:** Version bump only for package @eggjs/module-test-util # [3.42.0](https://github.com/eggjs/tegg/compare/v3.41.0...v3.42.0) (2024-09-10) **Note:** Version bump only for package @eggjs/module-test-util # [3.41.0](https://github.com/eggjs/tegg/compare/v3.40.1...v3.41.0) (2024-08-26) **Note:** Version bump only for package @eggjs/module-test-util ## [3.40.1](https://github.com/eggjs/tegg/compare/v3.40.0...v3.40.1) (2024-08-23) **Note:** Version bump only for package @eggjs/module-test-util # [3.40.0](https://github.com/eggjs/tegg/compare/v3.39.5...v3.40.0) (2024-08-22) **Note:** Version bump only for package @eggjs/module-test-util ## [3.39.5](https://github.com/eggjs/tegg/compare/v3.39.4...v3.39.5) (2024-08-09) **Note:** Version bump only for package @eggjs/module-test-util ## [3.39.4](https://github.com/eggjs/tegg/compare/v3.39.3...v3.39.4) (2024-07-09) **Note:** Version bump only for package @eggjs/module-test-util ## [3.39.3](https://github.com/eggjs/tegg/compare/v3.39.2...v3.39.3) (2024-04-28) **Note:** Version bump only for package @eggjs/module-test-util ## [3.39.2](https://github.com/eggjs/tegg/compare/v3.39.1...v3.39.2) (2024-04-28) **Note:** Version bump only for package @eggjs/module-test-util ## [3.39.1](https://github.com/eggjs/tegg/compare/v3.39.0...v3.39.1) (2024-04-28) **Note:** Version bump only for package @eggjs/module-test-util # [3.39.0](https://github.com/eggjs/tegg/compare/v3.38.0...v3.39.0) (2024-04-19) **Note:** Version bump only for package @eggjs/module-test-util # [3.38.0](https://github.com/eggjs/tegg/compare/v3.37.3...v3.38.0) (2024-04-18) **Note:** Version bump only for package @eggjs/module-test-util ## [3.37.3](https://github.com/eggjs/tegg/compare/v3.37.2...v3.37.3) (2024-04-17) **Note:** Version bump only for package @eggjs/module-test-util ## [3.37.2](https://github.com/eggjs/tegg/compare/v3.37.1...v3.37.2) (2024-04-17) **Note:** Version bump only for package @eggjs/module-test-util ## [3.37.1](https://github.com/eggjs/tegg/compare/v3.37.0...v3.37.1) (2024-04-16) **Note:** Version bump only for package @eggjs/module-test-util # [3.37.0](https://github.com/eggjs/tegg/compare/v3.36.3...v3.37.0) (2024-04-16) **Note:** Version bump only for package @eggjs/module-test-util ## [3.36.3](https://github.com/eggjs/tegg/compare/v3.36.2...v3.36.3) (2024-04-10) **Note:** Version bump only for package @eggjs/module-test-util ## [3.36.2](https://github.com/eggjs/tegg/compare/v3.36.1...v3.36.2) (2024-04-08) **Note:** Version bump only for package @eggjs/module-test-util ## [3.36.1](https://github.com/eggjs/tegg/compare/v3.36.0...v3.36.1) (2024-04-07) **Note:** Version bump only for package @eggjs/module-test-util # [3.36.0](https://github.com/eggjs/tegg/compare/v3.35.1...v3.36.0) (2024-04-02) **Note:** Version bump only for package @eggjs/module-test-util ## [3.35.1](https://github.com/eggjs/tegg/compare/v3.35.0...v3.35.1) (2024-03-26) **Note:** Version bump only for package @eggjs/module-test-util # [3.35.0](https://github.com/eggjs/tegg/compare/v3.34.0...v3.35.0) (2024-03-26) **Note:** Version bump only for package @eggjs/module-test-util # [3.34.0](https://github.com/eggjs/tegg/compare/v3.33.1...v3.34.0) (2024-03-22) **Note:** Version bump only for package @eggjs/module-test-util ## [3.33.1](https://github.com/eggjs/tegg/compare/v3.33.0...v3.33.1) (2024-03-22) **Note:** Version bump only for package @eggjs/module-test-util # [3.33.0](https://github.com/eggjs/tegg/compare/v3.32.0...v3.33.0) (2024-03-22) **Note:** Version bump only for package @eggjs/module-test-util # [3.32.0](https://github.com/eggjs/tegg/compare/v3.31.0...v3.32.0) (2024-02-19) **Note:** Version bump only for package @eggjs/module-test-util # [3.31.0](https://github.com/eggjs/tegg/compare/v3.30.1...v3.31.0) (2024-01-31) **Note:** Version bump only for package @eggjs/module-test-util ## [3.30.1](https://github.com/eggjs/tegg/compare/v3.30.0...v3.30.1) (2024-01-25) **Note:** Version bump only for package @eggjs/module-test-util # [3.30.0](https://github.com/eggjs/tegg/compare/v3.29.0...v3.30.0) (2024-01-17) **Note:** Version bump only for package @eggjs/module-test-util # [3.29.0](https://github.com/eggjs/tegg/compare/v3.28.2...v3.29.0) (2023-12-26) **Note:** Version bump only for package @eggjs/module-test-util ## [3.28.2](https://github.com/eggjs/tegg/compare/v3.28.1...v3.28.2) (2023-12-12) **Note:** Version bump only for package @eggjs/module-test-util ## [3.28.1](https://github.com/eggjs/tegg/compare/v3.28.0...v3.28.1) (2023-12-11) **Note:** Version bump only for package @eggjs/module-test-util # [3.28.0](https://github.com/eggjs/tegg/compare/v3.27.0...v3.28.0) (2023-12-10) **Note:** Version bump only for package @eggjs/module-test-util # [3.27.0](https://github.com/eggjs/tegg/compare/v3.26.0...v3.27.0) (2023-11-23) **Note:** Version bump only for package @eggjs/module-test-util ## [3.25.2](https://github.com/eggjs/tegg/compare/v3.25.1...v3.25.2) (2023-11-06) **Note:** Version bump only for package @eggjs/module-test-util ## [3.25.1](https://github.com/eggjs/tegg/compare/v3.25.0...v3.25.1) (2023-11-03) **Note:** Version bump only for package @eggjs/module-test-util # [3.25.0](https://github.com/eggjs/tegg/compare/v3.24.0...v3.25.0) (2023-11-03) **Note:** Version bump only for package @eggjs/module-test-util # [3.24.0](https://github.com/eggjs/tegg/compare/v3.23.0...v3.24.0) (2023-10-26) **Note:** Version bump only for package @eggjs/module-test-util # [3.23.0](https://github.com/eggjs/tegg/compare/v3.22.0...v3.23.0) (2023-09-20) **Note:** Version bump only for package @eggjs/module-test-util # [3.20.0](https://github.com/eggjs/tegg/compare/v3.19.0...v3.20.0) (2023-09-07) **Note:** Version bump only for package @eggjs/module-test-util # [3.19.0](https://github.com/eggjs/tegg/compare/v3.18.1...v3.19.0) (2023-08-31) **Note:** Version bump only for package @eggjs/module-test-util ## [3.18.1](https://github.com/eggjs/tegg/compare/v3.18.0...v3.18.1) (2023-08-29) **Note:** Version bump only for package @eggjs/module-test-util # [3.18.0](https://github.com/eggjs/tegg/compare/v3.17.0...v3.18.0) (2023-08-29) **Note:** Version bump only for package @eggjs/module-test-util # [3.17.0](https://github.com/eggjs/tegg/compare/v3.16.0...v3.17.0) (2023-08-24) **Note:** Version bump only for package @eggjs/module-test-util # [3.16.0](https://github.com/eggjs/tegg/compare/v3.15.0...v3.16.0) (2023-08-24) **Note:** Version bump only for package @eggjs/module-test-util ## [3.14.3](https://github.com/eggjs/tegg/compare/v3.14.2...v3.14.3) (2023-08-14) **Note:** Version bump only for package @eggjs/module-test-util ## [3.14.2](https://github.com/eggjs/tegg/compare/v3.14.1...v3.14.2) (2023-08-14) **Note:** Version bump only for package @eggjs/module-test-util ## [3.14.1](https://github.com/eggjs/tegg/compare/v3.14.0...v3.14.1) (2023-08-11) **Note:** Version bump only for package @eggjs/module-test-util # [3.14.0](https://github.com/eggjs/tegg/compare/v3.13.0...v3.14.0) (2023-07-31) **Note:** Version bump only for package @eggjs/module-test-util # [3.12.0](https://github.com/eggjs/tegg/compare/v3.11.1...v3.12.0) (2023-07-13) **Note:** Version bump only for package @eggjs/module-test-util ## [3.11.1](https://github.com/eggjs/tegg/compare/v3.11.0...v3.11.1) (2023-06-29) **Note:** Version bump only for package @eggjs/module-test-util # [3.11.0](https://github.com/eggjs/tegg/compare/v3.10.0...v3.11.0) (2023-06-29) **Note:** Version bump only for package @eggjs/module-test-util # [3.10.0](https://github.com/eggjs/tegg/compare/v3.9.0...v3.10.0) (2023-06-28) **Note:** Version bump only for package @eggjs/module-test-util # [3.8.0](https://github.com/eggjs/tegg/compare/v3.7.0...v3.8.0) (2023-05-30) ### Features * impl EggObjectLifecycle hook in decorator ([#119](https://github.com/eggjs/tegg/issues/119)) ([cced8a2](https://github.com/eggjs/tegg/commit/cced8a2e009c33d5040fa21d00409fddef471b0e)) # [3.7.0](https://github.com/eggjs/tegg/compare/v3.6.3...v3.7.0) (2023-04-03) **Note:** Version bump only for package @eggjs/module-test-util ## [3.6.3](https://github.com/eggjs/tegg/compare/v3.6.2...v3.6.3) (2023-03-02) **Note:** Version bump only for package @eggjs/module-test-util # [3.5.0](https://github.com/eggjs/tegg/compare/v3.4.1...v3.5.0) (2023-02-10) **Note:** Version bump only for package @eggjs/module-test-util ## [3.3.1](https://github.com/eggjs/tegg/compare/v3.3.0...v3.3.1) (2023-01-28) **Note:** Version bump only for package @eggjs/module-test-util ## [3.2.3](https://github.com/eggjs/tegg/compare/v3.2.2...v3.2.3) (2023-01-16) **Note:** Version bump only for package @eggjs/module-test-util ## [3.2.2](https://github.com/eggjs/tegg/compare/v3.2.1...v3.2.2) (2023-01-06) **Note:** Version bump only for package @eggjs/module-test-util ## [3.2.1](https://github.com/eggjs/tegg/compare/v3.2.0...v3.2.1) (2022-12-28) **Note:** Version bump only for package @eggjs/module-test-util # [3.1.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.1.0) (2022-12-27) ### Features * **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) # [3.0.0](https://github.com/eggjs/tegg/compare/v3.0.0-alpha.0...v3.0.0) (2022-12-26) **Note:** Version bump only for package @eggjs/module-test-util # [3.0.0-alpha.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.0.0-alpha.0) (2022-12-22) ### Features * **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) # [1.3.0](https://github.com/eggjs/tegg/compare/v1.2.0...v1.3.0) (2022-07-01) **Note:** Version bump only for package @eggjs/module-test-util # [0.2.0](https://github.com/eggjs/tegg/compare/v0.1.19...v0.2.0) (2022-01-20) **Note:** Version bump only for package @eggjs/module-test-util ================================================ FILE: core/test-util/CoreTestHelper.ts ================================================ import { ContextHandler, EggContainerFactory, EggContext, LoadUnitInstance, LoadUnitInstanceFactory, } from '@eggjs/tegg-runtime'; import { EggLoadUnitType, EggPrototype, GlobalGraph, GlobalGraphBuildHook, LoadUnitFactory, } from '@eggjs/tegg-metadata'; import { LoaderFactory } from '@eggjs/tegg-loader'; import { EggProtoImplClass, PrototypeUtil } from '@eggjs/core-decorator'; import { AsyncLocalStorage } from 'async_hooks'; import { LoaderUtil } from './LoaderUtil'; export class EggContextStorage { static storage = new AsyncLocalStorage(); static register() { ContextHandler.getContextCallback = () => { return EggContextStorage.storage.getStore(); }; ContextHandler.runInContextCallback = (context, fn) => { return EggContextStorage.storage.run(context, fn); }; } } export class CoreTestHelper { static contextStorage = new AsyncLocalStorage(); static async getLoadUnitInstance(moduleDir: string): Promise { const loader = LoaderFactory.createLoader(moduleDir, EggLoadUnitType.MODULE); const loadUnit = await LoadUnitFactory.createLoadUnit(moduleDir, EggLoadUnitType.MODULE, loader); return await LoadUnitInstanceFactory.createLoadUnitInstance(loadUnit); } static async prepareModules(moduleDirs: string[], hooks?: GlobalGraphBuildHook[]): Promise> { LoaderUtil.buildGlobalGraph(moduleDirs, hooks); EggContextStorage.register(); const instances: Array = []; for (const { path } of GlobalGraph.instance!.moduleConfigList) { instances.push(await CoreTestHelper.getLoadUnitInstance(path)); } return instances; } static async getObject(clazz: EggProtoImplClass): Promise { const proto = PrototypeUtil.getClazzProto(clazz as any) as EggPrototype; const eggObj = await EggContainerFactory.getOrCreateEggObject(proto, proto.name); return eggObj.obj as unknown as T; } } ================================================ FILE: core/test-util/EggTestContext.ts ================================================ import mm from 'mm'; import { AbstractEggContext, ContextHandler } from '@eggjs/tegg-runtime'; import { IdenticalUtil } from '@eggjs/tegg-lifecycle'; const EGG_CTX = Symbol('TEgg#context'); export interface Tracer { readonly traceId: string; } export class EggTestContext extends AbstractEggContext { data: Map = new Map(); readonly id: string; constructor() { super(); const mockCtx = { tracer: { traceId: 'mock-traceId', }, }; this.id = IdenticalUtil.createContextId(); this.set(EGG_CTX, mockCtx); mm(ContextHandler, 'getContext', () => { return this; }); } static async mockContext(cb: (ctx: EggTestContext) => Promise): Promise { const ctx = new EggTestContext(); return await ContextHandler.run(ctx, () => { return cb(ctx); }); } } ================================================ FILE: core/test-util/LoaderUtil.ts ================================================ import { EggProtoImplClass, PrototypeUtil } from '@eggjs/core-decorator'; import { EggLoadUnitType, GlobalGraph, GlobalGraphBuildHook, GlobalModuleNodeBuilder } from '@eggjs/tegg-metadata'; import { ModuleConfigUtil } from '@eggjs/tegg-common-util'; import { LoaderFactory } from '@eggjs/tegg-loader'; export class LoaderUtil { static loadFile(filePath: string): EggProtoImplClass | null { let clazz; try { clazz = require(filePath); } catch (_) { return null; } clazz = clazz.__esModule && 'default' in clazz ? clazz.default : clazz; if (!PrototypeUtil.isEggPrototype(clazz)) { return null; } PrototypeUtil.setFilePath(clazz, filePath); return clazz; } static buildModuleNode(modulePath: string, clazzList: EggProtoImplClass[], multiInstanceClazzList: { clazz: any; unitPath: string; moduleName: string; }[], optional = false) { const builder = GlobalModuleNodeBuilder.create(modulePath, optional); for (const clazz of clazzList) { builder.addClazz(clazz); } for (const { clazz, unitPath, moduleName } of multiInstanceClazzList) { builder.addMultiInstanceClazz(clazz, moduleName, unitPath); } return builder.build(); } static buildGlobalGraph(modulePaths: string[], hooks?: GlobalGraphBuildHook[]) { GlobalGraph.instance = new GlobalGraph(); for (const hook of hooks ?? []) { GlobalGraph.instance.registerBuildHook(hook); } const multiInstanceEggProtoClass: { clazz: any; unitPath: string; moduleName: string; }[] = []; for (let i = 0; i < modulePaths.length; i++) { const modulePath = modulePaths[i]; const loader = LoaderFactory.createLoader(modulePath, EggLoadUnitType.MODULE); const clazzList = loader.load(); const moduleName = ModuleConfigUtil.readModuleNameSync(modulePath); for (const clazz of clazzList) { if (PrototypeUtil.isEggMultiInstancePrototype(clazz)) { multiInstanceEggProtoClass.push({ clazz, unitPath: modulePath, moduleName, }); } } } for (let i = 0; i < modulePaths.length; i++) { const modulePath = modulePaths[i]; const loader = LoaderFactory.createLoader(modulePath, EggLoadUnitType.MODULE); const clazzList = loader.load(); const eggProtoClass: EggProtoImplClass[] = []; for (const clazz of clazzList) { if (PrototypeUtil.isEggPrototype(clazz)) { eggProtoClass.push(clazz); } } GlobalGraph.instance.addModuleNode(LoaderUtil.buildModuleNode( modulePath, eggProtoClass, multiInstanceEggProtoClass, )); } GlobalGraph.instance.build(); GlobalGraph.instance.sort(); } } ================================================ FILE: core/test-util/StandaloneTestUtil.ts ================================================ import { createServer, IncomingMessage, OutgoingHttpHeaders, Server, ServerOptions, ServerResponse } from 'node:http'; import { pipeline } from 'node:stream'; import { Headers, BodyInit, Request, Response } from 'undici'; import { FetchEvent } from '@eggjs/tegg-types/standalone'; export type FetchEventListener = (event: FetchEvent) => Promise; export interface StartHTTPServerOptions extends ServerOptions { listener: FetchEventListener; } export class StandaloneTestUtil { static skipOnNode(minVersion = 18) { const version = parseInt(process.versions.node.split('.')[0], 10); return version < minVersion; } static #buildRequest(req: IncomingMessage): Request { const origin = `http://${req.headers.host ?? 'localhost'}`; const url = new URL(req.url ?? '', origin); const body: BodyInit | null = req.method === 'GET' || req.method === 'HEAD' ? null : req; req.headers.host = url.host; const headers = new Headers(); for (const [ name, values ] of Object.entries(req.headers)) { if (Array.isArray(values)) { for (const value of values) { headers.append(name, value); } } else if (values !== undefined) { headers.append(name, values); } } return new Request(url, { method: req.method, headers, body, duplex: body ? 'half' : undefined, }); } static #createHTTPServerListener(listener: FetchEventListener) { return async (req: IncomingMessage, res: ServerResponse) => { const request = StandaloneTestUtil.#buildRequest(req); // TODO currently fake FetchEvent const event: any = new Event('fetch'); event.request = request; const response = await listener(event); const headers: OutgoingHttpHeaders = {}; for (const [ key, value ] of response.headers) { headers[key.toLowerCase()] = value; } res.writeHead(response.status, headers); if (!response.body) { res.end(); return; } pipeline(response.body, res, e => { if (e) { console.error(`pipeline writing response error for url ${response.url}`, e); res.end(); } }); }; } static startHTTPServer(host: string, port: number, { listener, ...options }: StartHTTPServerOptions) { const serverListener = StandaloneTestUtil.#createHTTPServerListener(listener); const server = createServer(options ?? {}, serverListener); return new Promise(resolve => { server.listen(port, host, () => resolve(server)); }); } } ================================================ FILE: core/test-util/TestLoader.ts ================================================ import globby from 'globby'; import path from 'path'; import { LoaderUtil } from './LoaderUtil'; import { EggProtoImplClass } from '@eggjs/core-decorator'; import { Loader } from '@eggjs/tegg-metadata'; export class TestLoader implements Loader { private readonly moduleDir: string; constructor(moduleDir: string) { this.moduleDir = moduleDir; } load(): EggProtoImplClass[] { const protoClassList: EggProtoImplClass[] = []; const files = globby.sync([ '**/*', '!**/node_modules', '!**/*.d.ts', ], { cwd: this.moduleDir }); for (const file of files) { const realPath = path.join(this.moduleDir, file); const protoClazz = LoaderUtil.loadFile(realPath); if (!protoClazz) { continue; } protoClassList.push(protoClazz); } return protoClassList; } } ================================================ FILE: core/test-util/index.ts ================================================ export * from './LoaderUtil'; export * from './TestLoader'; export * from './EggTestContext'; export * from './CoreTestHelper'; ================================================ FILE: core/test-util/package.json ================================================ { "name": "@eggjs/module-test-util", "version": "3.78.15", "private": true, "description": "module test util", "keywords": [ "egg", "typescript", "metadata", "tegg" ], "main": "dist/index.js", "files": [ "dist/**/*.js", "dist/**/*.d.ts" ], "typings": "dist/index.d.ts", "scripts": { "clean": "tsc -b --clean", "tsc:pub": "ut run clean && tsc -p ./tsconfig.pub.json", "prepublishOnly": "ut tsc:pub" }, "author": "killagu ", "license": "MIT", "homepage": "https://github.com/eggjs/tegg", "bugs": { "url": "https://github.com/eggjs/tegg/issues" }, "repository": { "type": "git", "url": "git@github.com:eggjs/tegg.git", "directory": "core/test-util" }, "engines": { "node": ">=14.0.0" }, "dependencies": { "@eggjs/core-decorator": "^3.78.15", "@eggjs/tegg-lifecycle": "^3.78.15", "@eggjs/tegg-loader": "^3.78.15", "@eggjs/tegg-metadata": "^3.78.15", "@eggjs/tegg-runtime": "^3.78.15", "globby": "^11.1.0", "mm": "^3.2.1", "undici": "^5.26.5" }, "devDependencies": { "@types/mocha": "^10.0.1", "@types/node": "^20.2.4", "cross-env": "^7.0.3", "mocha": "^10.2.0", "ts-node": "^10.9.1", "typescript": "^5.0.4" } } ================================================ FILE: core/test-util/tsconfig.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "dist", "baseUrl": "./" }, "exclude": [ "dist", "node_modules", "test" ] } ================================================ FILE: core/test-util/tsconfig.pub.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "dist", "baseUrl": "./" }, "exclude": [ "dist", "node_modules", "test" ] } ================================================ FILE: core/transaction-decorator/CHANGELOG.md ================================================ # Change Log All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. ## [3.78.15](https://github.com/eggjs/tegg/compare/v3.78.14...v3.78.15) (2026-04-23) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.78.14](https://github.com/eggjs/tegg/compare/v3.78.13...v3.78.14) (2026-04-21) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.78.13](https://github.com/eggjs/tegg/compare/v3.78.12...v3.78.13) (2026-04-21) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.78.12](https://github.com/eggjs/tegg/compare/v3.78.11...v3.78.12) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.78.11](https://github.com/eggjs/tegg/compare/v3.78.10...v3.78.11) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.78.10](https://github.com/eggjs/tegg/compare/v3.78.9...v3.78.10) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.78.9](https://github.com/eggjs/tegg/compare/v3.78.8...v3.78.9) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.78.8](https://github.com/eggjs/tegg/compare/v3.78.7...v3.78.8) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.78.7](https://github.com/eggjs/tegg/compare/v3.78.6...v3.78.7) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.78.6](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.6) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.78.5](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.5) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.78.4](https://github.com/eggjs/tegg/compare/v3.78.3...v3.78.4) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.78.3](https://github.com/eggjs/tegg/compare/v3.78.2...v3.78.3) (2026-04-15) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.78.2](https://github.com/eggjs/tegg/compare/v3.78.1...v3.78.2) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.78.1](https://github.com/eggjs/tegg/compare/v3.78.0...v3.78.1) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator # [3.78.0](https://github.com/eggjs/tegg/compare/v3.77.2...v3.78.0) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.77.2](https://github.com/eggjs/tegg/compare/v3.77.1...v3.77.2) (2026-04-07) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.77.1](https://github.com/eggjs/tegg/compare/v3.77.0...v3.77.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator # [3.77.0](https://github.com/eggjs/tegg/compare/v3.76.1...v3.77.0) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.76.1](https://github.com/eggjs/tegg/compare/v3.76.0...v3.76.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator # [3.76.0](https://github.com/eggjs/tegg/compare/v3.75.1...v3.76.0) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.75.1](https://github.com/eggjs/tegg/compare/v3.75.0...v3.75.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator # [3.75.0](https://github.com/eggjs/tegg/compare/v3.74.0...v3.75.0) (2026-03-30) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator # [3.74.0](https://github.com/eggjs/tegg/compare/v3.73.0...v3.74.0) (2026-03-30) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator # [3.73.0](https://github.com/eggjs/tegg/compare/v3.72.0...v3.73.0) (2026-03-25) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator # [3.72.0](https://github.com/eggjs/tegg/compare/v3.71.2...v3.72.0) (2026-02-04) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.71.2](https://github.com/eggjs/tegg/compare/v3.71.1...v3.71.2) (2026-01-30) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.71.1](https://github.com/eggjs/tegg/compare/v3.71.0...v3.71.1) (2026-01-28) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator # [3.71.0](https://github.com/eggjs/tegg/compare/v3.70.1...v3.71.0) (2026-01-26) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.70.1](https://github.com/eggjs/tegg/compare/v3.70.0...v3.70.1) (2026-01-14) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator # [3.70.0](https://github.com/eggjs/tegg/compare/v3.69.0...v3.70.0) (2026-01-07) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator # [3.69.0](https://github.com/eggjs/tegg/compare/v3.68.0...v3.69.0) (2026-01-06) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator # [3.68.0](https://github.com/eggjs/tegg/compare/v3.67.2...v3.68.0) (2025-12-29) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.67.2](https://github.com/eggjs/tegg/compare/v3.67.1...v3.67.2) (2025-12-24) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.67.1](https://github.com/eggjs/tegg/compare/v3.67.0...v3.67.1) (2025-12-23) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator # [3.67.0](https://github.com/eggjs/tegg/compare/v3.66.0...v3.67.0) (2025-12-18) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator # [3.66.0](https://github.com/eggjs/tegg/compare/v3.65.3...v3.66.0) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.65.3](https://github.com/eggjs/tegg/compare/v3.65.2...v3.65.3) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.65.2](https://github.com/eggjs/tegg/compare/v3.65.1...v3.65.2) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.65.1](https://github.com/eggjs/tegg/compare/v3.65.0...v3.65.1) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator # [3.65.0](https://github.com/eggjs/tegg/compare/v3.64.5...v3.65.0) (2025-12-10) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.64.5](https://github.com/eggjs/tegg/compare/v3.64.4...v3.64.5) (2025-12-08) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.64.4](https://github.com/eggjs/tegg/compare/v3.64.3...v3.64.4) (2025-12-06) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.64.3](https://github.com/eggjs/tegg/compare/v3.64.2...v3.64.3) (2025-12-06) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.64.2](https://github.com/eggjs/tegg/compare/v3.64.1...v3.64.2) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.64.1](https://github.com/eggjs/tegg/compare/v3.64.0...v3.64.1) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator # [3.64.0](https://github.com/eggjs/tegg/compare/v3.63.2...v3.64.0) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.63.2](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.2) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.63.1](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.1) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator # [3.63.0](https://github.com/eggjs/tegg/compare/v3.62.3...v3.63.0) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.62.3](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.3) (2025-11-05) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.62.2](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.2) (2025-11-05) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.62.1](https://github.com/eggjs/tegg/compare/v3.62.0...v3.62.1) (2025-11-03) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator # [3.62.0](https://github.com/eggjs/tegg/compare/v3.61.0...v3.62.0) (2025-09-24) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator # [3.61.0](https://github.com/eggjs/tegg/compare/v3.60.3...v3.61.0) (2025-08-15) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.60.3](https://github.com/eggjs/tegg/compare/v3.60.2...v3.60.3) (2025-08-06) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.60.2](https://github.com/eggjs/tegg/compare/v3.60.1...v3.60.2) (2025-08-06) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.60.1](https://github.com/eggjs/tegg/compare/v3.60.0...v3.60.1) (2025-07-28) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator # [3.60.0](https://github.com/eggjs/tegg/compare/v3.59.1...v3.60.0) (2025-07-28) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.59.1](https://github.com/eggjs/tegg/compare/v3.59.0...v3.59.1) (2025-07-16) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator # [3.59.0](https://github.com/eggjs/tegg/compare/v3.58.0...v3.59.0) (2025-07-07) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator # [3.58.0](https://github.com/eggjs/tegg/compare/v3.57.14...v3.58.0) (2025-07-01) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.57.14](https://github.com/eggjs/tegg/compare/v3.57.13...v3.57.14) (2025-06-18) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.57.13](https://github.com/eggjs/tegg/compare/v3.57.12...v3.57.13) (2025-06-18) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.57.12](https://github.com/eggjs/tegg/compare/v3.57.11...v3.57.12) (2025-06-16) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.57.11](https://github.com/eggjs/tegg/compare/v3.57.10...v3.57.11) (2025-06-16) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.57.10](https://github.com/eggjs/tegg/compare/v3.57.9...v3.57.10) (2025-06-12) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.57.9](https://github.com/eggjs/tegg/compare/v3.57.8...v3.57.9) (2025-05-29) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.57.8](https://github.com/eggjs/tegg/compare/v3.57.7...v3.57.8) (2025-05-29) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.57.7](https://github.com/eggjs/tegg/compare/v3.57.6...v3.57.7) (2025-05-28) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.57.6](https://github.com/eggjs/tegg/compare/v3.57.5...v3.57.6) (2025-05-27) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.57.5](https://github.com/eggjs/tegg/compare/v3.57.4...v3.57.5) (2025-05-15) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.57.4](https://github.com/eggjs/tegg/compare/v3.57.3...v3.57.4) (2025-05-14) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.57.3](https://github.com/eggjs/tegg/compare/v3.57.2...v3.57.3) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.57.2](https://github.com/eggjs/tegg/compare/v3.57.1...v3.57.2) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.57.1](https://github.com/eggjs/tegg/compare/v3.57.0...v3.57.1) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator # [3.57.0](https://github.com/eggjs/tegg/compare/v3.56.3...v3.57.0) (2025-05-09) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.56.3](https://github.com/eggjs/tegg/compare/v3.56.2...v3.56.3) (2025-05-07) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.56.2](https://github.com/eggjs/tegg/compare/v3.56.1...v3.56.2) (2025-05-01) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.56.1](https://github.com/eggjs/tegg/compare/v3.56.0...v3.56.1) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator # [3.56.0](https://github.com/eggjs/tegg/compare/v3.55.0...v3.56.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator # [3.55.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.55.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator # [3.54.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.54.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator # [3.53.0](https://github.com/eggjs/tegg/compare/v3.52.1...v3.53.0) (2025-04-21) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.52.1](https://github.com/eggjs/tegg/compare/v3.52.0...v3.52.1) (2025-03-14) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator # [3.52.0](https://github.com/eggjs/tegg/compare/v3.51.2...v3.52.0) (2024-12-30) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.51.2](https://github.com/eggjs/tegg/compare/v3.51.1...v3.51.2) (2024-12-09) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.51.1](https://github.com/eggjs/tegg/compare/v3.51.0...v3.51.1) (2024-11-06) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator # [3.51.0](https://github.com/eggjs/tegg/compare/v3.50.1...v3.51.0) (2024-10-30) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.50.1](https://github.com/eggjs/tegg/compare/v3.50.0...v3.50.1) (2024-10-23) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator # [3.50.0](https://github.com/eggjs/tegg/compare/v3.49.0...v3.50.0) (2024-10-22) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator # [3.49.0](https://github.com/eggjs/tegg/compare/v3.48.1...v3.49.0) (2024-10-21) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.48.1](https://github.com/eggjs/tegg/compare/v3.48.0...v3.48.1) (2024-10-14) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator # [3.48.0](https://github.com/eggjs/tegg/compare/v3.47.2...v3.48.0) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.47.2](https://github.com/eggjs/tegg/compare/v3.47.1...v3.47.2) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.47.1](https://github.com/eggjs/tegg/compare/v3.47.0...v3.47.1) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator # [3.47.0](https://github.com/eggjs/tegg/compare/v3.46.4...v3.47.0) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.46.4](https://github.com/eggjs/tegg/compare/v3.46.3...v3.46.4) (2024-10-09) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.46.3](https://github.com/eggjs/tegg/compare/v3.46.2...v3.46.3) (2024-10-08) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.46.2](https://github.com/eggjs/tegg/compare/v3.46.1...v3.46.2) (2024-10-07) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.46.1](https://github.com/eggjs/tegg/compare/v3.46.0...v3.46.1) (2024-09-30) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator # [3.46.0](https://github.com/eggjs/tegg/compare/v3.45.0...v3.46.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator # [3.45.0](https://github.com/eggjs/tegg/compare/v3.44.1...v3.45.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.44.1](https://github.com/eggjs/tegg/compare/v3.44.0...v3.44.1) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator # [3.44.0](https://github.com/eggjs/tegg/compare/v3.43.2...v3.44.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.43.2](https://github.com/eggjs/tegg/compare/v3.43.1...v3.43.2) (2024-09-14) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.43.1](https://github.com/eggjs/tegg/compare/v3.43.0...v3.43.1) (2024-09-14) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator # [3.43.0](https://github.com/eggjs/tegg/compare/v3.42.0...v3.43.0) (2024-09-13) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator # [3.42.0](https://github.com/eggjs/tegg/compare/v3.41.0...v3.42.0) (2024-09-10) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator # [3.41.0](https://github.com/eggjs/tegg/compare/v3.40.1...v3.41.0) (2024-08-26) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.40.1](https://github.com/eggjs/tegg/compare/v3.40.0...v3.40.1) (2024-08-23) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator # [3.40.0](https://github.com/eggjs/tegg/compare/v3.39.5...v3.40.0) (2024-08-22) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.39.5](https://github.com/eggjs/tegg/compare/v3.39.4...v3.39.5) (2024-08-09) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.39.4](https://github.com/eggjs/tegg/compare/v3.39.3...v3.39.4) (2024-07-09) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.39.3](https://github.com/eggjs/tegg/compare/v3.39.2...v3.39.3) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.39.2](https://github.com/eggjs/tegg/compare/v3.39.1...v3.39.2) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.39.1](https://github.com/eggjs/tegg/compare/v3.39.0...v3.39.1) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator # [3.39.0](https://github.com/eggjs/tegg/compare/v3.38.0...v3.39.0) (2024-04-19) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator # [3.38.0](https://github.com/eggjs/tegg/compare/v3.37.3...v3.38.0) (2024-04-18) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.37.3](https://github.com/eggjs/tegg/compare/v3.37.2...v3.37.3) (2024-04-17) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.37.2](https://github.com/eggjs/tegg/compare/v3.37.1...v3.37.2) (2024-04-17) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.37.1](https://github.com/eggjs/tegg/compare/v3.37.0...v3.37.1) (2024-04-16) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator # [3.37.0](https://github.com/eggjs/tegg/compare/v3.36.3...v3.37.0) (2024-04-16) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.36.3](https://github.com/eggjs/tegg/compare/v3.36.2...v3.36.3) (2024-04-10) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.36.2](https://github.com/eggjs/tegg/compare/v3.36.1...v3.36.2) (2024-04-08) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.36.1](https://github.com/eggjs/tegg/compare/v3.36.0...v3.36.1) (2024-04-07) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator # [3.36.0](https://github.com/eggjs/tegg/compare/v3.35.1...v3.36.0) (2024-04-02) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.35.1](https://github.com/eggjs/tegg/compare/v3.35.0...v3.35.1) (2024-03-26) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator # [3.35.0](https://github.com/eggjs/tegg/compare/v3.34.0...v3.35.0) (2024-03-26) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator # [3.34.0](https://github.com/eggjs/tegg/compare/v3.33.1...v3.34.0) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.33.1](https://github.com/eggjs/tegg/compare/v3.33.0...v3.33.1) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator # [3.33.0](https://github.com/eggjs/tegg/compare/v3.32.0...v3.33.0) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator # [3.32.0](https://github.com/eggjs/tegg/compare/v3.31.0...v3.32.0) (2024-02-19) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator # [3.31.0](https://github.com/eggjs/tegg/compare/v3.30.1...v3.31.0) (2024-01-31) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.30.1](https://github.com/eggjs/tegg/compare/v3.30.0...v3.30.1) (2024-01-25) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator # [3.30.0](https://github.com/eggjs/tegg/compare/v3.29.0...v3.30.0) (2024-01-17) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator # [3.29.0](https://github.com/eggjs/tegg/compare/v3.28.2...v3.29.0) (2023-12-26) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.28.2](https://github.com/eggjs/tegg/compare/v3.28.1...v3.28.2) (2023-12-12) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.28.1](https://github.com/eggjs/tegg/compare/v3.28.0...v3.28.1) (2023-12-11) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator # [3.28.0](https://github.com/eggjs/tegg/compare/v3.27.0...v3.28.0) (2023-12-10) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator # [3.27.0](https://github.com/eggjs/tegg/compare/v3.26.0...v3.27.0) (2023-11-23) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.25.2](https://github.com/eggjs/tegg/compare/v3.25.1...v3.25.2) (2023-11-06) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.25.1](https://github.com/eggjs/tegg/compare/v3.25.0...v3.25.1) (2023-11-03) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator # [3.25.0](https://github.com/eggjs/tegg/compare/v3.24.0...v3.25.0) (2023-11-03) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator # [3.24.0](https://github.com/eggjs/tegg/compare/v3.23.0...v3.24.0) (2023-10-26) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator # [3.23.0](https://github.com/eggjs/tegg/compare/v3.22.0...v3.23.0) (2023-09-20) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator # [3.20.0](https://github.com/eggjs/tegg/compare/v3.19.0...v3.20.0) (2023-09-07) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator # [3.19.0](https://github.com/eggjs/tegg/compare/v3.18.1...v3.19.0) (2023-08-31) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.18.1](https://github.com/eggjs/tegg/compare/v3.18.0...v3.18.1) (2023-08-29) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator # [3.18.0](https://github.com/eggjs/tegg/compare/v3.17.0...v3.18.0) (2023-08-29) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator # [3.17.0](https://github.com/eggjs/tegg/compare/v3.16.0...v3.17.0) (2023-08-24) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator # [3.16.0](https://github.com/eggjs/tegg/compare/v3.15.0...v3.16.0) (2023-08-24) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.14.3](https://github.com/eggjs/tegg/compare/v3.14.2...v3.14.3) (2023-08-14) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.14.2](https://github.com/eggjs/tegg/compare/v3.14.1...v3.14.2) (2023-08-14) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.14.1](https://github.com/eggjs/tegg/compare/v3.14.0...v3.14.1) (2023-08-11) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator # [3.14.0](https://github.com/eggjs/tegg/compare/v3.13.0...v3.14.0) (2023-07-31) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator # [3.13.0](https://github.com/eggjs/tegg/compare/v3.12.0...v3.13.0) (2023-07-25) ### Features * 事务注解增加数据源选项 ([#135](https://github.com/eggjs/tegg/issues/135)) ([c33b3b5](https://github.com/eggjs/tegg/commit/c33b3b5ec9d32a8c6675d986013042f0cb8e4370)) # [3.12.0](https://github.com/eggjs/tegg/compare/v3.11.1...v3.12.0) (2023-07-13) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator ## [3.11.1](https://github.com/eggjs/tegg/compare/v3.11.0...v3.11.1) (2023-06-29) **Note:** Version bump only for package @eggjs/tegg-transaction-decorator # [3.11.0](https://github.com/eggjs/tegg/compare/v3.10.0...v3.11.0) (2023-06-29) ### Features * impl transaction decorator ([#124](https://github.com/eggjs/tegg/issues/124)) ([4896615](https://github.com/eggjs/tegg/commit/4896615af951bbff940cda7abc116df40ed486e5)) ================================================ FILE: core/transaction-decorator/README.md ================================================ # @eggjs/tegg-transaction-decorator 事务注解 ## Usage ### 传播机制 ```ts export class Foo { @Transactional({ propagation: PropagationType.ALWAYS_NEW }) async bar() { await this.foo(); } @Transactional({ propagation: PropagationType.REQUIRED }) async foo(msg) { console.log('has msg: ', msg); } } ``` ### 数据源 ```ts export class Bar { @Transactional({ dataSourceName: 'xx' }) async bar() { await this.foo(); } } ``` Foo.bar 始终会在一个独立的事务中执行,而 Foo.foo 会在 Foo.bar 的事务中执行 ================================================ FILE: core/transaction-decorator/index.ts ================================================ export * from '@eggjs/tegg-types/transaction'; export * from './src/decorator/Transactional'; export * from './src/builder/TransactionMetaBuilder'; export * from './src/util/TransactionMetadataUtil'; ================================================ FILE: core/transaction-decorator/package.json ================================================ { "name": "@eggjs/tegg-transaction-decorator", "version": "3.78.15", "description": "tegg transaction decorator", "keywords": [ "egg", "typescript", "decorator", "transaction", "tegg" ], "author": "qile222 ", "homepage": "https://github.com/eggjs/tegg", "repository": { "type": "git", "url": "git@github.com:eggjs/tegg.git", "directory": "core/transaction-decorator" }, "dependencies": { "@eggjs/core-decorator": "^3.78.15", "@eggjs/tegg-common-util": "^3.78.15", "@eggjs/tegg-metadata": "^3.78.15", "@eggjs/tegg-types": "^3.78.15" }, "scripts": { "test": "cross-env NODE_ENV=test NODE_OPTIONS='--no-deprecation' mocha", "clean": "tsc -b --clean", "tsc": "ut run clean && tsc -p ./tsconfig.json", "tsc:pub": "ut run clean && tsc -p ./tsconfig.pub.json", "prepublishOnly": "ut tsc:pub" }, "publishConfig": { "access": "public" }, "engines": { "node": ">=14.0.0" }, "license": "MIT", "main": "dist/index.js", "files": [ "dist/**/*.js", "dist/**/*.d.ts" ], "bugs": { "url": "https://github.com/eggjs/tegg/issues" }, "devDependencies": { "@types/mocha": "^10.0.1", "@types/node": "^20.2.4", "cross-env": "^7.0.3", "mocha": "^10.2.0", "ts-node": "^10.9.1", "typescript": "^5.0.4" }, "gitHead": "240a3e9f40fda65ebb7589727d197db5ce17916c" } ================================================ FILE: core/transaction-decorator/src/builder/TransactionMetaBuilder.ts ================================================ import type { EggProtoImplClass, TransactionMetadata } from '@eggjs/tegg-types'; import { TransactionMetadataUtil } from '../util/TransactionMetadataUtil'; export class TransactionMetaBuilder { private readonly clazz: EggProtoImplClass; constructor(clazz: EggProtoImplClass) { this.clazz = clazz; } build(): TransactionMetadata[] { if (!TransactionMetadataUtil.isTransactionClazz(this.clazz)) { return []; } return TransactionMetadataUtil.getTransactionMetadataList(this.clazz); } } ================================================ FILE: core/transaction-decorator/src/decorator/Transactional.ts ================================================ import { PropagationType } from '@eggjs/tegg-types'; import type { EggProtoImplClass, TransactionalParams } from '@eggjs/tegg-types'; import { TransactionMetadataUtil } from '../util/TransactionMetadataUtil'; export function Transactional(params?: TransactionalParams) { const propagation = params?.propagation || PropagationType.REQUIRED; if (!Object.values(PropagationType).includes(propagation)) { throw new Error(`unknown propagation type ${propagation}`); } const datasourceName = params?.datasourceName; return function(target: any, propertyKey: PropertyKey) { const constructor: EggProtoImplClass = target.constructor; TransactionMetadataUtil.setIsTransactionClazz(constructor); TransactionMetadataUtil.addTransactionMetadata(constructor, { propagation, method: propertyKey, datasourceName, }); }; } ================================================ FILE: core/transaction-decorator/src/util/TransactionMetadataUtil.ts ================================================ import { MetadataUtil } from '@eggjs/tegg'; import { IS_TRANSACTION_CLAZZ, TRANSACTION_META_DATA, TransactionMetadata } from '@eggjs/tegg-types'; import type { EggProtoImplClass } from '@eggjs/tegg-types'; export class TransactionMetadataUtil { static setIsTransactionClazz(clazz: EggProtoImplClass) { MetadataUtil.defineMetaData(IS_TRANSACTION_CLAZZ, true, clazz); } static isTransactionClazz(clazz: EggProtoImplClass): boolean { return MetadataUtil.getBooleanMetaData(IS_TRANSACTION_CLAZZ, clazz); } static addTransactionMetadata(clazz: EggProtoImplClass, data: TransactionMetadata) { const list = MetadataUtil.initOwnArrayMetaData(TRANSACTION_META_DATA, clazz, []); list.push(data); } static getTransactionMetadataList(clazz: EggProtoImplClass): TransactionMetadata[] { return MetadataUtil.getArrayMetaData(TRANSACTION_META_DATA, clazz); } } ================================================ FILE: core/transaction-decorator/test/builder/TransactionMetaBuilder.test.ts ================================================ import assert from 'node:assert'; import { PropagationType } from '@eggjs/tegg-types'; import { TransactionMetadataUtil } from '../../src/util/TransactionMetadataUtil'; import { TransactionMetaBuilder } from '../../src/builder/TransactionMetaBuilder'; import { Foo, Bar, FooBar, BarFoo } from '../fixtures/transaction'; import { Transactional } from '../../src/decorator/Transactional'; describe('test/builder/TransactionMetaBuilder.test.ts', () => { it('should build meta data success', () => { assert.ok(TransactionMetadataUtil.isTransactionClazz(Foo)); const fooBuilder = new TransactionMetaBuilder(Foo); assert.deepStrictEqual(fooBuilder.build(), [{ propagation: PropagationType.REQUIRED, method: 'defaultPropagation', datasourceName: undefined, }, { propagation: PropagationType.REQUIRED, method: 'requiredPropagation', datasourceName: 'testDatasourceName1', }, { propagation: PropagationType.ALWAYS_NEW, method: 'alwaysNewPropagation', datasourceName: undefined, }]); assert.ok(TransactionMetadataUtil.isTransactionClazz(Bar)); const barBuilder = new TransactionMetaBuilder(Bar); assert.deepStrictEqual(barBuilder.build(), [{ propagation: PropagationType.REQUIRED, method: 'foo', datasourceName: 'datasourceName2', }, { propagation: PropagationType.ALWAYS_NEW, method: 'bar', datasourceName: undefined, }]); assert.ok(TransactionMetadataUtil.isTransactionClazz(FooBar)); const fooBarBuilder = new TransactionMetaBuilder(FooBar); assert.deepStrictEqual(fooBarBuilder.build(), [{ propagation: PropagationType.ALWAYS_NEW, method: 'foo', datasourceName: undefined, }]); const barFooBuilder = new TransactionMetaBuilder(BarFoo); assert.ok(!TransactionMetadataUtil.isTransactionClazz(BarFoo)); assert.deepStrictEqual(barFooBuilder.build(), []); assert.throws(() => { Transactional({ propagation: 'xx' as PropagationType }); }, new Error('unknown propagation type xx')); }); }); ================================================ FILE: core/transaction-decorator/test/fixtures/transaction.ts ================================================ import { PropagationType } from '@eggjs/tegg-types'; import { Transactional } from '../../src/decorator/Transactional'; export class Foo { @Transactional() async defaultPropagation(msg) { console.log('msg: ', msg); } @Transactional({ datasourceName: 'testDatasourceName1', }) async requiredPropagation(msg) { console.log('msg: ', msg); } @Transactional({ propagation: PropagationType.ALWAYS_NEW }) async alwaysNewPropagation(msg) { console.log('msg: ', msg); } } export class Bar { @Transactional({ datasourceName: 'datasourceName2' }) async foo(msg) { console.log('msg: ', msg); } @Transactional({ propagation: PropagationType.ALWAYS_NEW }) async bar(msg) { console.log('msg: ', msg); } } export class FooBar { @Transactional({ propagation: PropagationType.ALWAYS_NEW }) async foo(msg) { console.log('msg: ', msg); } } export class BarFoo { async foo(msg) { console.log('msg: ', msg); } } ================================================ FILE: core/transaction-decorator/tsconfig.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "dist", "baseUrl": "./" }, "exclude": [ "dist", "node_modules" ] } ================================================ FILE: core/transaction-decorator/tsconfig.pub.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "dist", "baseUrl": "./" }, "exclude": [ "dist", "node_modules", "test" ] } ================================================ FILE: core/types/CHANGELOG.md ================================================ # Change Log All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. ## [3.78.15](https://github.com/eggjs/tegg/compare/v3.78.14...v3.78.15) (2026-04-23) ### Bug Fixes * **agent-runtime:** hold cancelRun until executor session is committed ([#441](https://github.com/eggjs/tegg/issues/441)) ([4e02a28](https://github.com/eggjs/tegg/commit/4e02a28bdfe9b924c1190482fd3d85f8cad1fcfa)) ## [3.78.14](https://github.com/eggjs/tegg/compare/v3.78.13...v3.78.14) (2026-04-21) **Note:** Version bump only for package @eggjs/tegg-types ## [3.78.13](https://github.com/eggjs/tegg/compare/v3.78.12...v3.78.13) (2026-04-21) **Note:** Version bump only for package @eggjs/tegg-types ## [3.78.12](https://github.com/eggjs/tegg/compare/v3.78.11...v3.78.12) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-types ## [3.78.11](https://github.com/eggjs/tegg/compare/v3.78.10...v3.78.11) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-types ## [3.78.10](https://github.com/eggjs/tegg/compare/v3.78.9...v3.78.10) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-types ## [3.78.9](https://github.com/eggjs/tegg/compare/v3.78.8...v3.78.9) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-types ## [3.78.8](https://github.com/eggjs/tegg/compare/v3.78.7...v3.78.8) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-types ## [3.78.7](https://github.com/eggjs/tegg/compare/v3.78.6...v3.78.7) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-types ## [3.78.6](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.6) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-types ## [3.78.5](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.5) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-types ## [3.78.4](https://github.com/eggjs/tegg/compare/v3.78.3...v3.78.4) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-types ## [3.78.3](https://github.com/eggjs/tegg/compare/v3.78.2...v3.78.3) (2026-04-15) **Note:** Version bump only for package @eggjs/tegg-types ## [3.78.2](https://github.com/eggjs/tegg/compare/v3.78.1...v3.78.2) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-types ## [3.78.1](https://github.com/eggjs/tegg/compare/v3.78.0...v3.78.1) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-types # [3.78.0](https://github.com/eggjs/tegg/compare/v3.77.2...v3.78.0) (2026-04-09) ### Features * **agent-runtime:** rewrite streamRun with StreamEvent format and reconnection ([#432](https://github.com/eggjs/tegg/issues/432)) ([d03dac2](https://github.com/eggjs/tegg/commit/d03dac2ddd78641acb47e19275488ad9fbfcda2a)) ## [3.77.2](https://github.com/eggjs/tegg/compare/v3.77.1...v3.77.2) (2026-04-07) **Note:** Version bump only for package @eggjs/tegg-types ## [3.77.1](https://github.com/eggjs/tegg/compare/v3.77.0...v3.77.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-types # [3.77.0](https://github.com/eggjs/tegg/compare/v3.76.1...v3.77.0) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-types ## [3.76.1](https://github.com/eggjs/tegg/compare/v3.76.0...v3.76.1) (2026-04-01) ### Bug Fixes * **agent-runtime:** merge content blocks and support accumulate control ([#428](https://github.com/eggjs/tegg/issues/428)) ([f4f904e](https://github.com/eggjs/tegg/commit/f4f904e357497fc5ad9a2c7d2ece4e9b305f5738)) # [3.76.0](https://github.com/eggjs/tegg/compare/v3.75.1...v3.76.0) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-types ## [3.75.1](https://github.com/eggjs/tegg/compare/v3.75.0...v3.75.1) (2026-04-01) ### Bug Fixes * **agent-runtime:** preserve non-text content blocks in MessageConverter ([#426](https://github.com/eggjs/tegg/issues/426)) ([8c4382f](https://github.com/eggjs/tegg/commit/8c4382f33f68534218049cfbfadfd4f6800a348c)) # [3.75.0](https://github.com/eggjs/tegg/compare/v3.74.0...v3.75.0) (2026-03-30) **Note:** Version bump only for package @eggjs/tegg-types # [3.74.0](https://github.com/eggjs/tegg/compare/v3.73.0...v3.74.0) (2026-03-30) **Note:** Version bump only for package @eggjs/tegg-types # [3.73.0](https://github.com/eggjs/tegg/compare/v3.72.0...v3.73.0) (2026-03-25) ### Features * add agent-runtime package with @AgentController decorator ([#411](https://github.com/eggjs/tegg/issues/411)) ([d4d0006](https://github.com/eggjs/tegg/commit/d4d00061e90230f82c0958bcf5268f8a511395db)) * **agent-runtime:** add isResume flag to CreateRunInput ([#414](https://github.com/eggjs/tegg/issues/414)) ([29ac989](https://github.com/eggjs/tegg/commit/29ac98995c0a37bb34d33f7ad81af7c664a67bce)) # [3.72.0](https://github.com/eggjs/tegg/compare/v3.71.2...v3.72.0) (2026-02-04) **Note:** Version bump only for package @eggjs/tegg-types ## [3.71.2](https://github.com/eggjs/tegg/compare/v3.71.1...v3.71.2) (2026-01-30) **Note:** Version bump only for package @eggjs/tegg-types ## [3.71.1](https://github.com/eggjs/tegg/compare/v3.71.0...v3.71.1) (2026-01-28) **Note:** Version bump only for package @eggjs/tegg-types # [3.71.0](https://github.com/eggjs/tegg/compare/v3.70.1...v3.71.0) (2026-01-26) **Note:** Version bump only for package @eggjs/tegg-types ## [3.70.1](https://github.com/eggjs/tegg/compare/v3.70.0...v3.70.1) (2026-01-14) **Note:** Version bump only for package @eggjs/tegg-types # [3.70.0](https://github.com/eggjs/tegg/compare/v3.69.0...v3.70.0) (2026-01-07) **Note:** Version bump only for package @eggjs/tegg-types # [3.69.0](https://github.com/eggjs/tegg/compare/v3.68.0...v3.69.0) (2026-01-06) **Note:** Version bump only for package @eggjs/tegg-types # [3.68.0](https://github.com/eggjs/tegg/compare/v3.67.2...v3.68.0) (2025-12-29) **Note:** Version bump only for package @eggjs/tegg-types ## [3.67.2](https://github.com/eggjs/tegg/compare/v3.67.1...v3.67.2) (2025-12-24) **Note:** Version bump only for package @eggjs/tegg-types ## [3.67.1](https://github.com/eggjs/tegg/compare/v3.67.0...v3.67.1) (2025-12-23) **Note:** Version bump only for package @eggjs/tegg-types # [3.67.0](https://github.com/eggjs/tegg/compare/v3.66.0...v3.67.0) (2025-12-18) **Note:** Version bump only for package @eggjs/tegg-types # [3.66.0](https://github.com/eggjs/tegg/compare/v3.65.3...v3.66.0) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-types ## [3.65.3](https://github.com/eggjs/tegg/compare/v3.65.2...v3.65.3) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-types ## [3.65.2](https://github.com/eggjs/tegg/compare/v3.65.1...v3.65.2) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-types ## [3.65.1](https://github.com/eggjs/tegg/compare/v3.65.0...v3.65.1) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-types # [3.65.0](https://github.com/eggjs/tegg/compare/v3.64.5...v3.65.0) (2025-12-10) **Note:** Version bump only for package @eggjs/tegg-types ## [3.64.5](https://github.com/eggjs/tegg/compare/v3.64.4...v3.64.5) (2025-12-08) **Note:** Version bump only for package @eggjs/tegg-types ## [3.64.4](https://github.com/eggjs/tegg/compare/v3.64.3...v3.64.4) (2025-12-06) **Note:** Version bump only for package @eggjs/tegg-types ## [3.64.3](https://github.com/eggjs/tegg/compare/v3.64.2...v3.64.3) (2025-12-06) **Note:** Version bump only for package @eggjs/tegg-types ## [3.64.2](https://github.com/eggjs/tegg/compare/v3.64.1...v3.64.2) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-types ## [3.64.1](https://github.com/eggjs/tegg/compare/v3.64.0...v3.64.1) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-types # [3.64.0](https://github.com/eggjs/tegg/compare/v3.63.2...v3.64.0) (2025-11-27) ### Bug Fixes * mcp zod type and langchain test version ([#369](https://github.com/eggjs/tegg/issues/369)) ([8178168](https://github.com/eggjs/tegg/commit/81781685c392346d21c56b649bfe8bb7a99bc9fb)) ## [3.63.2](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.2) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg-types ## [3.63.1](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.1) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg-types # [3.63.0](https://github.com/eggjs/tegg/compare/v3.62.3...v3.63.0) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg-types ## [3.62.3](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.3) (2025-11-05) **Note:** Version bump only for package @eggjs/tegg-types ## [3.62.2](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.2) (2025-11-05) **Note:** Version bump only for package @eggjs/tegg-types ## [3.62.1](https://github.com/eggjs/tegg/compare/v3.62.0...v3.62.1) (2025-11-03) **Note:** Version bump only for package @eggjs/tegg-types # [3.62.0](https://github.com/eggjs/tegg/compare/v3.61.0...v3.62.0) (2025-09-24) **Note:** Version bump only for package @eggjs/tegg-types # [3.61.0](https://github.com/eggjs/tegg/compare/v3.60.3...v3.61.0) (2025-08-15) **Note:** Version bump only for package @eggjs/tegg-types ## [3.60.3](https://github.com/eggjs/tegg/compare/v3.60.2...v3.60.3) (2025-08-06) **Note:** Version bump only for package @eggjs/tegg-types ## [3.60.2](https://github.com/eggjs/tegg/compare/v3.60.1...v3.60.2) (2025-08-06) **Note:** Version bump only for package @eggjs/tegg-types ## [3.60.1](https://github.com/eggjs/tegg/compare/v3.60.0...v3.60.1) (2025-07-28) **Note:** Version bump only for package @eggjs/tegg-types # [3.60.0](https://github.com/eggjs/tegg/compare/v3.59.1...v3.60.0) (2025-07-28) **Note:** Version bump only for package @eggjs/tegg-types ## [3.59.1](https://github.com/eggjs/tegg/compare/v3.59.0...v3.59.1) (2025-07-16) **Note:** Version bump only for package @eggjs/tegg-types # [3.59.0](https://github.com/eggjs/tegg/compare/v3.58.0...v3.59.0) (2025-07-07) **Note:** Version bump only for package @eggjs/tegg-types # [3.58.0](https://github.com/eggjs/tegg/compare/v3.57.14...v3.58.0) (2025-07-01) ### Features * add timeout ([#334](https://github.com/eggjs/tegg/issues/334)) ([6d5d94b](https://github.com/eggjs/tegg/commit/6d5d94b6f319388a94b4adf4d427b95d2b851c17)) ## [3.57.14](https://github.com/eggjs/tegg/compare/v3.57.13...v3.57.14) (2025-06-18) **Note:** Version bump only for package @eggjs/tegg-types ## [3.57.13](https://github.com/eggjs/tegg/compare/v3.57.12...v3.57.13) (2025-06-18) **Note:** Version bump only for package @eggjs/tegg-types ## [3.57.12](https://github.com/eggjs/tegg/compare/v3.57.11...v3.57.12) (2025-06-16) **Note:** Version bump only for package @eggjs/tegg-types ## [3.57.11](https://github.com/eggjs/tegg/compare/v3.57.10...v3.57.11) (2025-06-16) **Note:** Version bump only for package @eggjs/tegg-types ## [3.57.10](https://github.com/eggjs/tegg/compare/v3.57.9...v3.57.10) (2025-06-12) **Note:** Version bump only for package @eggjs/tegg-types ## [3.57.9](https://github.com/eggjs/tegg/compare/v3.57.8...v3.57.9) (2025-05-29) **Note:** Version bump only for package @eggjs/tegg-types ## [3.57.8](https://github.com/eggjs/tegg/compare/v3.57.7...v3.57.8) (2025-05-29) **Note:** Version bump only for package @eggjs/tegg-types ## [3.57.7](https://github.com/eggjs/tegg/compare/v3.57.6...v3.57.7) (2025-05-28) **Note:** Version bump only for package @eggjs/tegg-types ## [3.57.6](https://github.com/eggjs/tegg/compare/v3.57.5...v3.57.6) (2025-05-27) **Note:** Version bump only for package @eggjs/tegg-types ## [3.57.5](https://github.com/eggjs/tegg/compare/v3.57.4...v3.57.5) (2025-05-15) **Note:** Version bump only for package @eggjs/tegg-types ## [3.57.4](https://github.com/eggjs/tegg/compare/v3.57.3...v3.57.4) (2025-05-14) **Note:** Version bump only for package @eggjs/tegg-types ## [3.57.3](https://github.com/eggjs/tegg/compare/v3.57.2...v3.57.3) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-types ## [3.57.2](https://github.com/eggjs/tegg/compare/v3.57.1...v3.57.2) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-types ## [3.57.1](https://github.com/eggjs/tegg/compare/v3.57.0...v3.57.1) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-types # [3.57.0](https://github.com/eggjs/tegg/compare/v3.56.3...v3.57.0) (2025-05-09) **Note:** Version bump only for package @eggjs/tegg-types ## [3.56.3](https://github.com/eggjs/tegg/compare/v3.56.2...v3.56.3) (2025-05-07) ### Bug Fixes * mcp teggCtxLifecycleMiddleware ([#312](https://github.com/eggjs/tegg/issues/312)) ([5304384](https://github.com/eggjs/tegg/commit/53043840c3aaab0e485db50b7a2d9362266eef8c)) ## [3.56.2](https://github.com/eggjs/tegg/compare/v3.56.1...v3.56.2) (2025-05-01) **Note:** Version bump only for package @eggjs/tegg-types ## [3.56.1](https://github.com/eggjs/tegg/compare/v3.56.0...v3.56.1) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-types # [3.56.0](https://github.com/eggjs/tegg/compare/v3.55.0...v3.56.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-types # [3.55.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.55.0) (2025-04-29) ### Features * add mcp ([#307](https://github.com/eggjs/tegg/issues/307)) ([a9a57b4](https://github.com/eggjs/tegg/commit/a9a57b4d7102dd552e09d33c3f82fc15a245790a)) # [3.54.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.54.0) (2025-04-29) ### Features * add mcp ([#307](https://github.com/eggjs/tegg/issues/307)) ([a9a57b4](https://github.com/eggjs/tegg/commit/a9a57b4d7102dd552e09d33c3f82fc15a245790a)) # [3.53.0](https://github.com/eggjs/tegg/compare/v3.52.1...v3.53.0) (2025-04-21) ### Features * add timeout metadata for http controller ([#301](https://github.com/eggjs/tegg/issues/301)) ([68980c2](https://github.com/eggjs/tegg/commit/68980c23de81dbc9bd86c1d8df7b3952f52aa5ce)) ## [3.52.1](https://github.com/eggjs/tegg/compare/v3.52.0...v3.52.1) (2025-03-14) ### Bug Fixes * add qualifier check ([#295](https://github.com/eggjs/tegg/issues/295)) ([6744088](https://github.com/eggjs/tegg/commit/674408810d77fe0f4b95b25790bcb3975e543e26)) # [3.52.0](https://github.com/eggjs/tegg/compare/v3.51.2...v3.52.0) (2024-12-30) **Note:** Version bump only for package @eggjs/tegg-types ## [3.51.2](https://github.com/eggjs/tegg/compare/v3.51.1...v3.51.2) (2024-12-09) ### Features * add mgw stream types ([#259](https://github.com/eggjs/tegg/issues/259)) ([1379d38](https://github.com/eggjs/tegg/commit/1379d382635c6bc575ce4acf3d3a7b5168487a3d)) ## [3.51.1](https://github.com/eggjs/tegg/compare/v3.51.0...v3.51.1) (2024-11-06) **Note:** Version bump only for package @eggjs/tegg-types # [3.51.0](https://github.com/eggjs/tegg/compare/v3.50.1...v3.51.0) (2024-10-30) ### Features * support optional inject ([#254](https://github.com/eggjs/tegg/issues/254)) ([260470b](https://github.com/eggjs/tegg/commit/260470b766d5fdb323c1bd72cc6260a90468a161)) ## [3.50.1](https://github.com/eggjs/tegg/compare/v3.50.0...v3.50.1) (2024-10-23) **Note:** Version bump only for package @eggjs/tegg-types # [3.50.0](https://github.com/eggjs/tegg/compare/v3.49.0...v3.50.0) (2024-10-22) **Note:** Version bump only for package @eggjs/tegg-types # [3.49.0](https://github.com/eggjs/tegg/compare/v3.48.1...v3.49.0) (2024-10-21) **Note:** Version bump only for package @eggjs/tegg-types ## [3.48.1](https://github.com/eggjs/tegg/compare/v3.48.0...v3.48.1) (2024-10-14) **Note:** Version bump only for package @eggjs/tegg-types # [3.48.0](https://github.com/eggjs/tegg/compare/v3.47.2...v3.48.0) (2024-10-10) ### Features * add rpc stream type ([#249](https://github.com/eggjs/tegg/issues/249)) ([7f3d40b](https://github.com/eggjs/tegg/commit/7f3d40b95d7939534f245b08d9d06a9b10bac350)) ## [3.47.2](https://github.com/eggjs/tegg/compare/v3.47.1...v3.47.2) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-types ## [3.47.1](https://github.com/eggjs/tegg/compare/v3.47.0...v3.47.1) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-types # [3.47.0](https://github.com/eggjs/tegg/compare/v3.46.4...v3.47.0) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-types ## [3.46.4](https://github.com/eggjs/tegg/compare/v3.46.3...v3.46.4) (2024-10-09) **Note:** Version bump only for package @eggjs/tegg-types ## [3.46.3](https://github.com/eggjs/tegg/compare/v3.46.2...v3.46.3) (2024-10-08) **Note:** Version bump only for package @eggjs/tegg-types ## [3.46.2](https://github.com/eggjs/tegg/compare/v3.46.1...v3.46.2) (2024-10-07) **Note:** Version bump only for package @eggjs/tegg-types ## [3.46.1](https://github.com/eggjs/tegg/compare/v3.46.0...v3.46.1) (2024-09-30) ### Bug Fixes * fix miss MultiInstance proper qualifiers ([#241](https://github.com/eggjs/tegg/issues/241)) ([15666d3](https://github.com/eggjs/tegg/commit/15666d36c18b99eccc4f1a11d8e7702503694ee1)) # [3.46.0](https://github.com/eggjs/tegg/compare/v3.45.0...v3.46.0) (2024-09-29) ### Features * impl MultiInstance inject MultiInstance ([#240](https://github.com/eggjs/tegg/issues/240)) ([08e3b0c](https://github.com/eggjs/tegg/commit/08e3b0cc02f3d2dbba767298a6aec6c00147f9ed)) # [3.45.0](https://github.com/eggjs/tegg/compare/v3.44.1...v3.45.0) (2024-09-29) ### Features * impl MultiInstanceInfo decorator ([#239](https://github.com/eggjs/tegg/issues/239)) ([70d4d95](https://github.com/eggjs/tegg/commit/70d4d95bca4a0c3e11d0d7cc4f292b1315e49e81)) ## [3.44.1](https://github.com/eggjs/tegg/compare/v3.44.0...v3.44.1) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-types # [3.44.0](https://github.com/eggjs/tegg/compare/v3.43.2...v3.44.0) (2024-09-29) ### Features * support inject in constructor ([#237](https://github.com/eggjs/tegg/issues/237)) ([e68b1ed](https://github.com/eggjs/tegg/commit/e68b1ed6a90432f1cb35a6f562914b7b04cb5114)) ## [3.43.2](https://github.com/eggjs/tegg/compare/v3.43.1...v3.43.2) (2024-09-14) **Note:** Version bump only for package @eggjs/tegg-types ## [3.43.1](https://github.com/eggjs/tegg/compare/v3.43.0...v3.43.1) (2024-09-14) **Note:** Version bump only for package @eggjs/tegg-types # [3.43.0](https://github.com/eggjs/tegg/compare/v3.42.0...v3.43.0) (2024-09-13) ### Features * add http cookies ([#235](https://github.com/eggjs/tegg/issues/235)) ([f46efa5](https://github.com/eggjs/tegg/commit/f46efa54b03bad41504bf76f6ed2baa8c48858ce)) # [3.42.0](https://github.com/eggjs/tegg/compare/v3.41.0...v3.42.0) (2024-09-10) ### Features * add LifecyclePreLoad ([#234](https://github.com/eggjs/tegg/issues/234)) ([2b72163](https://github.com/eggjs/tegg/commit/2b7216387f02cd045952447eaa21baa3a7ee04a3)) # [3.41.0](https://github.com/eggjs/tegg/compare/v3.40.1...v3.41.0) (2024-08-26) **Note:** Version bump only for package @eggjs/tegg-types ## [3.40.1](https://github.com/eggjs/tegg/compare/v3.40.0...v3.40.1) (2024-08-23) ### Bug Fixes * use symbol.for instead of symbol ([#232](https://github.com/eggjs/tegg/issues/232)) ([4254ce5](https://github.com/eggjs/tegg/commit/4254ce558d22234f9dfff0ea9bc067075e21c654)) # [3.40.0](https://github.com/eggjs/tegg/compare/v3.39.5...v3.40.0) (2024-08-22) ### Features * @Middleware support Advice ([#231](https://github.com/eggjs/tegg/issues/231)) ([613a89d](https://github.com/eggjs/tegg/commit/613a89da7ea6dd70d50e34aa9f4152358a622625)) ## [3.39.5](https://github.com/eggjs/tegg/compare/v3.39.4...v3.39.5) (2024-08-09) **Note:** Version bump only for package @eggjs/tegg-types ## [3.39.4](https://github.com/eggjs/tegg/compare/v3.39.3...v3.39.4) (2024-07-09) **Note:** Version bump only for package @eggjs/tegg-types ## [3.39.3](https://github.com/eggjs/tegg/compare/v3.39.2...v3.39.3) (2024-04-28) ### Bug Fixes * mount clazzExtension/clazzExtension/tableSql to BaseDao ([#220](https://github.com/eggjs/tegg/issues/220)) ([ac322cf](https://github.com/eggjs/tegg/commit/ac322cfc4100841a1483b04b99e04d553af323eb)) ## [3.39.2](https://github.com/eggjs/tegg/compare/v3.39.1...v3.39.2) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-types ## [3.39.1](https://github.com/eggjs/tegg/compare/v3.39.0...v3.39.1) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-types # [3.39.0](https://github.com/eggjs/tegg/compare/v3.38.0...v3.39.0) (2024-04-19) **Note:** Version bump only for package @eggjs/tegg-types # [3.38.0](https://github.com/eggjs/tegg/compare/v3.37.3...v3.38.0) (2024-04-18) **Note:** Version bump only for package @eggjs/tegg-types ## [3.37.3](https://github.com/eggjs/tegg/compare/v3.37.2...v3.37.3) (2024-04-17) ### Bug Fixes * tegg-types publish ([#212](https://github.com/eggjs/tegg/issues/212)) ([98a4188](https://github.com/eggjs/tegg/commit/98a4188be2a307722c3df0c82a7af0d0fef685fd)) ## [3.37.2](https://github.com/eggjs/tegg/compare/v3.37.1...v3.37.2) (2024-04-17) **Note:** Version bump only for package @eggjs/tegg-types ================================================ FILE: core/types/README.md ================================================ # `@eggjs/tegg-types` ## why Not all types and enums are suitable to be exported by tegg, but some types and enums are useful for tegg plugin and application development. So we need this package to provide shared types and enums for tegg. ## content shared types (type and interface) and enums for tegg, including: - shared types and enums among multiple tegg core packages - shared types and enums provided for tegg plugin and application development ================================================ FILE: core/types/agent-runtime/AgentMessage.ts ================================================ // ===== Input content types (for CreateRunInput) ===== export interface TextInputContentPart { type: 'text'; text: string; } export interface ToolUseInputContentPart { type: 'tool_use'; id: string; name: string; input: Record; } export interface ToolResultInputContentPart { type: 'tool_result'; tool_use_id: string; content?: string | { type: string; text?: string; [key: string]: unknown }[]; is_error?: boolean; } export interface GenericInputContentPart { type: string; [key: string]: unknown; } export type InputContentPart = | TextInputContentPart | ToolUseInputContentPart | ToolResultInputContentPart | GenericInputContentPart; // ===== Input message (for CreateRunInput) ===== export interface InputMessage { role: string; content: string | InputContentPart[]; metadata?: Record; } // ===== AgentMessage — aligned with Claude Agent SDK SDKMessage ===== // Lightweight subset of SDK message types. The framework only needs to // discriminate on `type` for a handful of core message kinds; everything // else passes through as SDKGenericMessage. export interface SDKSystemMessage { type: 'system'; subtype: string; session_id?: string; [key: string]: unknown; } export interface SDKStreamEvent { type: 'stream_event'; event: unknown; session_id?: string; [key: string]: unknown; } export interface SDKUserMessage { type: 'user'; message: unknown; [key: string]: unknown; } export interface SDKAssistantMessage { type: 'assistant'; message: unknown; [key: string]: unknown; } export interface SDKResultMessage { type: 'result'; subtype: string; usage?: { input_tokens?: number; output_tokens?: number; cache_creation_input_tokens?: number; cache_read_input_tokens?: number; [key: string]: unknown; }; [key: string]: unknown; } export interface SDKGenericMessage { type: string; [key: string]: unknown; } export type AgentMessage = | SDKSystemMessage | SDKStreamEvent | SDKUserMessage | SDKAssistantMessage | SDKResultMessage | SDKGenericMessage; ================================================ FILE: core/types/agent-runtime/AgentRuntime.ts ================================================ import type { AgentMessage } from './AgentMessage'; import type { AgentRunConfig, RunStatus } from './AgentStore'; // ===== Error codes ===== export const AgentErrorCode = { ExecError: 'EXEC_ERROR', } as const; export type AgentErrorCode = (typeof AgentErrorCode)[keyof typeof AgentErrorCode]; // ===== Thread objects ===== export interface ThreadObject { id: string; object: 'thread'; createdAt: number; metadata: Record; } export interface ThreadObjectWithMessages extends ThreadObject { messages: AgentMessage[]; } // ===== Run objects ===== export interface RunObject { id: string; object: 'thread.run'; createdAt: number; threadId: string; status: RunStatus; lastError?: { code: string; message: string } | null; startedAt?: number | null; completedAt?: number | null; cancelledAt?: number | null; failedAt?: number | null; usage?: { promptTokens: number; completionTokens: number; totalTokens: number } | null; metadata?: Record; config?: AgentRunConfig; } // ===== Run input ===== export interface CreateRunInput { threadId?: string; /** * Populated by AgentRuntime before calling execRun. * - true: threadId was provided (resume existing conversation) * - false: no threadId provided, new thread created */ isResume?: boolean; input: { messages: import('./AgentMessage').InputMessage[]; }; config?: AgentRunConfig; metadata?: Record; } // ===== Stream event (TaskEvent-style wrapper) ===== export interface StreamEvent { seq: number; type: string; data: unknown; ts: number; } // ===== Get thread options ===== export interface GetThreadOptions { /** When true, return all message types (system, result, stream_event, etc.). * Defaults to false — only user and assistant messages are returned. */ includeAllMessages?: boolean; } ================================================ FILE: core/types/agent-runtime/AgentStore.ts ================================================ import type { AgentMessage, InputMessage } from './AgentMessage'; import type { GetThreadOptions } from './AgentRuntime'; // ===== Object types ===== export const AgentObjectType = { Thread: 'thread', ThreadRun: 'thread.run', } as const; export type AgentObjectType = (typeof AgentObjectType)[keyof typeof AgentObjectType]; // ===== Run statuses ===== export const RunStatus = { Queued: 'queued', InProgress: 'in_progress', Completed: 'completed', Failed: 'failed', Cancelled: 'cancelled', Cancelling: 'cancelling', Expired: 'expired', } as const; export type RunStatus = (typeof RunStatus)[keyof typeof RunStatus]; // ===== Run configuration ===== export interface AgentRunConfig { maxIterations?: number; timeoutMs?: number; } // ===== Store records ===== export interface ThreadRecord { id: string; object: typeof AgentObjectType.Thread; /** * All messages in the thread, stored as SDK-format AgentMessage objects. * In OSSAgentStore the messages are stored separately as a JSONL file * and assembled on read — callers should treat this as a unified view * regardless of the underlying storage layout. */ messages: AgentMessage[]; metadata: Record; createdAt: number; // Unix seconds } export interface RunRecord { id: string; object: typeof AgentObjectType.ThreadRun; threadId?: string; status: RunStatus; input: InputMessage[]; lastError?: { code: string; message: string } | null; usage?: { promptTokens: number; completionTokens: number; totalTokens: number } | null; config?: AgentRunConfig; metadata?: Record; createdAt: number; startedAt?: number | null; completedAt?: number | null; cancelledAt?: number | null; failedAt?: number | null; } // ===== Store interface ===== export interface AgentStore { init?(): Promise; destroy?(): Promise; createThread(metadata?: Record): Promise; getThread(threadId: string, options?: GetThreadOptions): Promise; appendMessages(threadId: string, messages: AgentMessage[]): Promise; createRun( input: InputMessage[], threadId?: string, config?: AgentRunConfig, metadata?: Record, ): Promise; getRun(runId: string): Promise; updateRun(runId: string, updates: Partial): Promise; } ================================================ FILE: core/types/agent-runtime/ObjectStorageClient.ts ================================================ /** * Abstract interface for object storage operations (e.g., OSS, S3, local fs). * Implementations must handle serialization/deserialization of values. */ export interface ObjectStorageClient { init?(): Promise; destroy?(): Promise; /** Overwrite (or create) the object at `key` with `value`. */ put(key: string, value: string): Promise; /** Read the full object at `key`. Returns `null` if the object does not exist. */ get(key: string): Promise; /** * Append `value` to an existing Appendable Object. * If the object does not exist yet, create it. * * Used by OSSAgentStore to incrementally write JSONL message lines without * reading the entire thread — much more efficient than read-modify-write for * append-only workloads. * * This method is optional: when absent, OSSAgentStore falls back to * get-concat-put (which is NOT atomic under concurrent writers). */ append?(key: string, value: string): Promise; } ================================================ FILE: core/types/agent-runtime/errors.ts ================================================ /** * Error thrown when a thread or run is not found. * The `status` property is recognized by Koa/Egg error handling * to set the corresponding HTTP response status code. */ export class AgentNotFoundError extends Error { status = 404; constructor(message: string) { super(message); this.name = 'AgentNotFoundError'; } } /** * Error thrown when an operation conflicts with the current state * (e.g., cancelling a completed run). */ export class AgentConflictError extends Error { status = 409; constructor(message: string) { super(message); this.name = 'AgentConflictError'; } } /** * Error thrown when a RunBuilder state transition is invalid * (e.g., calling `complete()` on a queued run). */ export class InvalidRunStateTransitionError extends Error { status = 409; constructor(from: string, to: string) { super(`Invalid run state transition: '${from}' -> '${to}'`); this.name = 'InvalidRunStateTransitionError'; } } /** * Error thrown when cancelRun waits for the executor's session to be * committed to persistent storage (e.g. Claude Code SDK jsonl on disk) * but the commit never arrives within the configured timeout. The run is * transitioned to `failed` rather than `cancelled` to reflect that the * executor never reached a resumable state. */ export class AgentTimeoutError extends Error { status = 408; constructor(message: string) { super(message); this.name = 'AgentTimeoutError'; } } ================================================ FILE: core/types/agent-runtime/index.ts ================================================ export * from './AgentMessage'; export * from './AgentStore'; export * from './AgentRuntime'; export * from './ObjectStorageClient'; export * from './errors'; ================================================ FILE: core/types/aop/Advice.ts ================================================ export interface AdviceContext { that: T; method: PropertyKey; args: any[]; adviceParams?: K; } /** * execute order: * 1. beforeCall * 1. around * 1. afterReturn/afterThrow * 1. afterFinally */ export interface IAdvice { /** * call before function * @param ctx */ beforeCall?(ctx: AdviceContext): Promise; /** * call after function succeed */ afterReturn?(ctx: AdviceContext, result: any): Promise; /** * call after function throw error */ afterThrow?(ctx: AdviceContext, error: Error): Promise; /** * always call after function done */ afterFinally?(ctx: AdviceContext): Promise; /** * execute the function * the only one can modify the function return value */ around?(ctx: AdviceContext, next: () => Promise): Promise; } ================================================ FILE: core/types/aop/Aspect.ts ================================================ import type { EggProtoImplClass } from '../core-decorator'; import type { IAdvice } from './Advice'; export interface AdviceInfo { clazz: EggProtoImplClass; order: number; adviceParams: any; } export interface AspectAdvice { name: string; clazz: EggProtoImplClass; adviceParams: any; } export const ASPECT_LIST = Symbol.for('EggPrototype#aspectList'); ================================================ FILE: core/types/aop/Crosscut.ts ================================================ import type { EggProtoImplClass } from '../core-decorator'; import type { AdviceInfo } from './Aspect'; import type { CustomPointcutCallback, PointcutInfo, PointcutType } from './Pointcut'; export interface CrosscutOptions { // 默认值 100 order?: number; adviceParams?: any; } // TODO type check for methodName export interface ClassCrosscutParam { type: PointcutType.CLASS; clazz: EggProtoImplClass; methodName: PropertyKey; } export interface NameCrosscutParam { type: PointcutType.NAME; className: RegExp; methodName: RegExp; } export interface CustomCrosscutParam { type: PointcutType.CUSTOM; callback: CustomPointcutCallback; } export type CrosscutParam = ClassCrosscutParam | NameCrosscutParam | CustomCrosscutParam; export const CROSSCUT_INFO_LIST = Symbol.for('EggPrototype#crosscutInfoList'); export const IS_CROSSCUT_ADVICE = Symbol.for('EggPrototype#isCrosscutAdvice'); export interface CrosscutInfo { pointcutInfo: PointcutInfo; adviceInfo: AdviceInfo; } ================================================ FILE: core/types/aop/Pointcut.ts ================================================ import { EggProtoImplClass } from '../core-decorator'; export interface PointcutOptions { // default is 1000 order?: number; adviceParams?: K; } export enum PointcutType { /** * use class type to match */ CLASS = 'CLASS', /** * use regexp to match className and methodName */ NAME = 'NAME', /** * use custom function to match */ CUSTOM = 'CUSTOM', } export interface PointcutInfo { type: PointcutType; match(clazz: EggProtoImplClass, method: PropertyKey): boolean; } export type CustomPointcutCallback = (clazz: EggProtoImplClass, method: PropertyKey) => boolean; export const POINTCUT_ADVICE_INFO_LIAR = Symbol.for('EggPrototype#pointcutAdviceInfoList'); ================================================ FILE: core/types/aop/index.ts ================================================ export * from './Advice'; export * from './Aspect'; export * from './Crosscut'; export * from './Pointcut'; ================================================ FILE: core/types/common/Graph.ts ================================================ export interface GraphNodeObj { readonly id: string; } ================================================ FILE: core/types/common/Logger.ts ================================================ export interface Logger { debug(message?: any, ...optionalParams: any[]): void; log(message?: any, ...optionalParams: any[]): void; info(message?: any, ...optionalParams: any[]): void; warn(message?: any, ...optionalParams: any[]): void; error(message?: any, ...optionalParams: any[]): void; } ================================================ FILE: core/types/common/ModuleConfig.ts ================================================ export interface ModuleReference { name: string; path: string; optional?: boolean; loaderType?: string; } export interface InlineModuleReferenceConfig { path: string; optional?: boolean; } export interface NpmModuleReferenceConfig { package: string; optional?: boolean; } export type ModuleReferenceConfig = InlineModuleReferenceConfig | NpmModuleReferenceConfig; export interface ModuleConfig { } export interface ReadModuleReferenceOptions { // module dir deep for globby when use auto scan module // default is 10 deep?: number; cwd?: string; extraFilePattern?: string[]; } export interface ModuleConfigHolder { name: string; config: ModuleConfig; reference: ModuleReference; } ================================================ FILE: core/types/common/RuntimeConfig.ts ================================================ export type EnvType = 'local' | 'unittest' | 'prod' | string; export interface RuntimeConfig { /** * Application name */ name: string; /** * Application environment */ env: EnvType; /** * Application directory */ baseDir: string; } ================================================ FILE: core/types/common/index.ts ================================================ export * from './Graph'; export * from './ModuleConfig'; export * from './RuntimeConfig'; export * from './Logger'; ================================================ FILE: core/types/controller-decorator/HTTPController.ts ================================================ export interface HTTPControllerParams { protoName?: string; controllerName?: string; path?: string; timeout?: number; } ================================================ FILE: core/types/controller-decorator/HTTPMethod.ts ================================================ import { HTTPMethodEnum } from './model/types'; export interface HTTPMethodParams { method: HTTPMethodEnum; path: string; priority?: number; timeout?: number; } ================================================ FILE: core/types/controller-decorator/HTTPParam.ts ================================================ export interface HTTPQueryParams { name?: string; } export interface HTTPQueriesParams { name?: string; } export interface HTTPParamParams { name?: string; } ================================================ FILE: core/types/controller-decorator/MCPController.ts ================================================ export interface MCPControllerParams { protoName?: string; controllerName?: string; name?: string; version?: string; timeout?: number; } ================================================ FILE: core/types/controller-decorator/MCPPromptParams.ts ================================================ import type { GetPromptResult } from '@modelcontextprotocol/sdk/types.js'; import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import type { ShapeOutput } from '@modelcontextprotocol/sdk/server/zod-compat.js'; export type PromptArgs['2']> = ShapeOutput; export type PromptExtra = Parameters['3']>['1']; export type MCPPromptResponse = GetPromptResult; export interface MCPPromptParams { name?: string; description?: string; timeout?: number; title?: string; } ================================================ FILE: core/types/controller-decorator/MCPResourceParams.ts ================================================ import type { ResourceTemplate, ResourceMetadata, McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import type { ReadResourceResult } from '@modelcontextprotocol/sdk/types.js'; export interface MCPResourceUriParams { name?: string; uri: string; metadata?: ResourceMetadata; timeout?: number; } export interface MCPResourceTemplateParams { name?: string; template: ConstructorParameters; metadata?: ResourceMetadata; timeout?: number; } export type ResourceExtra = Parameters['3']>['2']; export type ResourceVariables = Parameters['3']>['1']; export type MCPResourceParams = MCPResourceUriParams | MCPResourceTemplateParams; export type MCPResourceResponse = ReadResourceResult; ================================================ FILE: core/types/controller-decorator/MCPToolParams.ts ================================================ import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js'; import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import type { ShapeOutput } from '@modelcontextprotocol/sdk/server/zod-compat.js'; export type ToolArgs['2']> = ShapeOutput; export type ToolExtra = Parameters['4']>['1']; export type MCPToolResponse = CallToolResult; export interface MCPToolParams { name?: string; description?: string; timeout?: number; } ================================================ FILE: core/types/controller-decorator/MetadataKey.ts ================================================ export const CONTROLLER_TYPE = Symbol.for('EggPrototype#controllerType'); export const CONTROLLER_NAME = Symbol.for('EggPrototype#controllerName'); export const CONTROLLER_HOST = Symbol.for('EggPrototype#controllerHost'); export const CONTROLLER_MIDDLEWARES = Symbol.for('EggPrototype#controller#middlewares'); export const CONTROLLER_AOP_MIDDLEWARES = Symbol.for('EggPrototype#controller#aopMiddlewares'); export const CONTROLLER_ACL = Symbol.for('EggPrototype#controller#acl'); export const CONTROLLER_TIMEOUT_METADATA = Symbol.for('EggPrototype#controller#timeout'); export const CONTROLLER_META_DATA = Symbol.for('EggPrototype#controller#metaData'); export const CONTROLLER_HTTP_PATH = Symbol.for('EggPrototype#controller#http#path'); export const CONTROLLER_METHOD_METHOD_MAP = Symbol.for('EggPrototype#controller#method#http#method'); export const CONTROLLER_METHOD_PATH_MAP = Symbol.for('EggPrototype#controller#method#http#path'); export const CONTROLLER_METHOD_PARAM_TYPE_MAP = Symbol.for('EggPrototype#controller#method#http#params#type'); export const CONTROLLER_METHOD_PARAM_NAME_MAP = Symbol.for('EggPrototype#controller#method#http#params#name'); export const CONTROLLER_METHOD_PRIORITY = Symbol.for('EggPrototype#controller#method#http#priority'); export const METHOD_CONTROLLER_TYPE_MAP = Symbol.for('EggPrototype#controller#mthods'); export const METHOD_CONTROLLER_HOST = Symbol.for('EggPrototype#controller#mthods#host'); export const METHOD_CONTEXT_INDEX = Symbol.for('EggPrototype#controller#method#context'); export const METHOD_MIDDLEWARES = Symbol.for('EggPrototype#method#middlewares'); export const METHOD_AOP_MIDDLEWARES = Symbol.for('EggPrototype#method#aopMiddlewares'); export const METHOD_AOP_REGISTER_MAP = Symbol.for('EggPrototype#method#aopMiddlewaresRegister'); export const METHOD_ACL = Symbol.for('EggPrototype#method#acl'); export const METHOD_TIMEOUT_METADATA = Symbol.for('EggPrototype#method#timeout'); export const CONTROLLER_MCP_NAME = Symbol.for('EggPrototype#controller#mcp#name'); export const CONTROLLER_MCP_VERSION = Symbol.for('EggPrototype#controller#mcp#version'); export const CONTROLLER_MCP_CONTROLLER_PARAMS_MAP = Symbol.for('EggPrototype#controller#mcp#controller#params'); export const CONTROLLER_MCP_RESOURCE_MAP = Symbol.for('EggPrototype#controller#mcp#resource'); export const CONTROLLER_MCP_RESOURCE_PARAMS_MAP = Symbol.for('EggPrototype#controller#mcp#resource#params'); export const CONTROLLER_MCP_TOOL_MAP = Symbol.for('EggPrototype#controller#mcp#tool'); export const CONTROLLER_MCP_TOOL_PARAMS_MAP = Symbol.for('EggPrototype#controller#mcp#tool#params'); export const CONTROLLER_MCP_TOOL_ARGS_INDEX = Symbol.for('EggPrototype#controller#mcp#tool#args'); export const CONTROLLER_MCP_EXTRA_INDEX = Symbol.for('EggPrototype#controller#mcp#tool#extra'); export const CONTROLLER_MCP_PROMPT_MAP = Symbol.for('EggPrototype#controller#mcp#prompt'); export const CONTROLLER_MCP_PROMPT_PARAMS_MAP = Symbol.for('EggPrototype#controller#mcp#prompt#params'); export const CONTROLLER_MCP_PROMPT_ARGS_INDEX = Symbol.for('EggPrototype#controller#mcp#prompt#args'); export const CONTROLLER_AGENT_CONTROLLER = Symbol.for('EggPrototype#controller#agent#isAgent'); export const CONTROLLER_AGENT_NOT_IMPLEMENTED = Symbol.for('EggPrototype#controller#agent#notImplemented'); export const CONTROLLER_AGENT_ENHANCED = Symbol.for('EggPrototype#controller#agent#enhanced'); export const AGENT_CONTROLLER_PROTO_IMPL_TYPE = 'AGENT_CONTROLLER_PROTO'; ================================================ FILE: core/types/controller-decorator/builder.ts ================================================ import { EggProtoImplClass } from '../core-decorator'; import { ControllerMetadata } from './model/ControllerMetadata'; export interface ControllerMetaBuilder { build(): ControllerMetadata | undefined; } export type ControllerMetaBuilderCreator = (clazz: EggProtoImplClass) => ControllerMetaBuilder; ================================================ FILE: core/types/controller-decorator/index.ts ================================================ export * from './model/ControllerMetadata'; export * from './model/MethodMeta'; export * from './model/types'; export * from './builder'; export * from './HTTPController'; export * from './HTTPMethod'; export * from './HTTPParam'; export * from './MetadataKey'; export * from './MCPController'; export * from './MCPPromptParams'; export * from './MCPResourceParams'; export * from './MCPToolParams'; ================================================ FILE: core/types/controller-decorator/model/ControllerMetadata.ts ================================================ import type { EggPrototypeName } from '@eggjs/tegg-types'; import { ControllerTypeLike, MiddlewareFunc } from './types'; import { MethodMeta } from './MethodMeta'; export interface ControllerMetadata { readonly protoName: EggPrototypeName; readonly controllerName: string; readonly className: string; readonly type: ControllerTypeLike; readonly methods: readonly MethodMeta[]; readonly middlewares: readonly MiddlewareFunc[]; } ================================================ FILE: core/types/controller-decorator/model/MethodMeta.ts ================================================ import { MiddlewareFunc } from './types'; export interface MethodMeta { readonly name: string; readonly middlewares: readonly MiddlewareFunc[]; readonly contextParamIndex: number | undefined; } ================================================ FILE: core/types/controller-decorator/model/types.ts ================================================ import type { Context } from 'egg'; export type EggContext = Context; export type Next = () => Promise; export type MiddlewareFunc = (ctx: Context, next: Next) => Promise; export type { IncomingHttpHeaders } from 'node:http'; export enum ControllerType { HTTP = 'HTTP', MCP = 'MCP', SOFA_RPC = 'SOFA_RPC', SOFA_RPC_STREAM = 'SOFA_RPC_STREAM', MGW_RPC = 'MGW_RPC', MGW_RPC_STREAM = 'MGW_RPC_STREAM', MESSAGE = 'MESSAGE', SCHEDULE = 'SCHEDULE', HEADERS = 'HEADERS', } export type HostType = string | string []; export type ControllerTypeLike = ControllerType | string; export enum MethodType { HTTP = 'HTTP', SOFA_RPC = 'SOFA_RPC', SOFA_RPC_STREAM = 'SOFA_RPC_STREAM', MGW_RPC = 'MGW_RPC', MGW_RPC_STREAM = 'MGW_RPC_STREAM', MESSAGE = 'MESSAGE', SCHEDULE = 'SCHEDULE', } export type MethodTypeLike = ControllerType | string; export enum HTTPMethodEnum { GET = 'GET', POST = 'POST', PUT = 'PUT', DELETE = 'DELETE', PATCH = 'PATCH', OPTIONS = 'OPTIONS', HEAD = 'HEAD', } export enum HTTPParamType { QUERY = 'QUERY', QUERIES = 'QUERIES', BODY = 'BODY', PARAM = 'PARAM', REQUEST = 'REQUEST', HEADERS = 'HEADERS', COOKIES = 'COOKIES', } export enum MCPProtocols { STDIO = 'STDIO', SSE = 'SSE', STREAM = 'STREAM', } ================================================ FILE: core/types/core-decorator/ContextProto.ts ================================================ import { AccessLevel } from './enum/AccessLevel'; export interface ContextProtoParams { name?: string; accessLevel?: AccessLevel; protoImplType?: string; } ================================================ FILE: core/types/core-decorator/Inject.ts ================================================ export interface InjectParams { // obj instance name, default is property name name?: string; // optional inject, default is false which means it will throw error when there is no relative object optional?: boolean; } ================================================ FILE: core/types/core-decorator/Metadata.ts ================================================ export type MetaDataKey = symbol | string; ================================================ FILE: core/types/core-decorator/MultiInstanceProto.ts ================================================ import type { AccessLevel } from './enum/AccessLevel'; import type { ObjectInitTypeLike } from './enum/ObjectInitType'; import type { MultiInstancePrototypeGetObjectsContext, ObjectInfo } from './model/EggMultiInstancePrototypeInfo'; export interface BaseMultiInstancePrototypeCallbackParams { /** * obj init type */ initType?: ObjectInitTypeLike; /** * access level */ accessLevel?: AccessLevel; /** * EggPrototype implement type */ protoImplType?: string; } export interface MultiInstancePrototypeCallbackParams extends BaseMultiInstancePrototypeCallbackParams { getObjects(ctx: MultiInstancePrototypeGetObjectsContext): ObjectInfo[]; } export interface MultiInstancePrototypeStaticParams extends BaseMultiInstancePrototypeCallbackParams { /** * object info list */ objects: ObjectInfo[]; } export type MultiInstancePrototypeParams = MultiInstancePrototypeCallbackParams | MultiInstancePrototypeStaticParams; ================================================ FILE: core/types/core-decorator/Prototype.ts ================================================ import { AccessLevel } from './enum/AccessLevel'; import { ObjectInitTypeLike } from './enum/ObjectInitType'; export interface PrototypeParams { name?: string; initType?: ObjectInitTypeLike; accessLevel?: AccessLevel; protoImplType?: string; } export const DEFAULT_PROTO_IMPL_TYPE = 'DEFAULT'; ================================================ FILE: core/types/core-decorator/SingletonProto.ts ================================================ import type { AccessLevel } from './enum/AccessLevel'; export interface SingletonProtoParams { name?: string; accessLevel?: AccessLevel; protoImplType?: string; } ================================================ FILE: core/types/core-decorator/enum/AccessLevel.ts ================================================ export enum AccessLevel { // only access from self load unit PRIVATE = 'PRIVATE', // can access from parent load unit PUBLIC = 'PUBLIC', } ================================================ FILE: core/types/core-decorator/enum/EggType.ts ================================================ export enum EggType { APP = 'APP', CONTEXT = 'CONTEXT', } ================================================ FILE: core/types/core-decorator/enum/InjectType.ts ================================================ export enum InjectType { PROPERTY = 'PROPERTY', CONSTRUCTOR = 'CONSTRUCTOR', } ================================================ FILE: core/types/core-decorator/enum/MultiInstanceType.ts ================================================ export enum MultiInstanceType { STATIC = 'STATIC', DYNAMIC = 'DYNAMIC', } ================================================ FILE: core/types/core-decorator/enum/ObjectInitType.ts ================================================ export enum ObjectInitType { // new object every time ALWAYS_NEW = 'ALWAYS_NEW', // new object only once in one request CONTEXT = 'CONTEXT', // new object only once SINGLETON = 'SINGLETON', } export type ObjectInitTypeLike = ObjectInitType | string; export const INIT_TYPE_TRY_ORDER = [ ObjectInitType.CONTEXT, ObjectInitType.SINGLETON, ObjectInitType.ALWAYS_NEW, ]; ================================================ FILE: core/types/core-decorator/enum/Qualifier.ts ================================================ export const ConfigSourceQualifierAttribute = Symbol.for('Qualifier.ConfigSource'); export const EggQualifierAttribute = Symbol.for('Qualifier.Egg'); export const InitTypeQualifierAttribute = Symbol.for('Qualifier.InitType'); export const LoadUnitNameQualifierAttribute = Symbol.for('Qualifier.LoadUnitName'); export const QUALIFIER_META_DATA = Symbol.for('EggPrototype#qualifier'); export const PROPERTY_QUALIFIER_META_DATA = Symbol.for('EggPrototype#propertyQualifier'); export const CONSTRUCTOR_QUALIFIER_META_DATA = Symbol.for('EggPrototype#constructorQualifier'); ================================================ FILE: core/types/core-decorator/index.ts ================================================ export * from './enum/AccessLevel'; export * from './enum/EggType'; export * from './enum/ObjectInitType'; export * from './enum/Qualifier'; export * from './enum/InjectType'; export * from './enum/MultiInstanceType'; export * from './model/EggPrototypeInfo'; export * from './model/InjectObjectInfo'; export * from './model/InjectConstructorInfo'; export * from './model/QualifierInfo'; export * from './model/EggMultiInstancePrototypeInfo'; export * from './ContextProto'; export * from './Inject'; export * from './Metadata'; export * from './MultiInstanceProto'; export * from './Prototype'; export * from './SingletonProto'; ================================================ FILE: core/types/core-decorator/model/EggMultiInstancePrototypeInfo.ts ================================================ import { ObjectInitTypeLike } from '../enum/ObjectInitType'; import { AccessLevel } from '../enum/AccessLevel'; import { EggPrototypeName } from './EggPrototypeInfo'; import { QualifierInfo } from './QualifierInfo'; export interface ObjectInfo { name: EggPrototypeName; qualifiers: QualifierInfo[]; properQualifiers?: Record; } export interface MultiInstancePrototypeGetObjectsContext { // instance module, multi instance proto used in moduleName: string; unitPath: string; } export interface EggMultiInstancePrototypeInfo { /** * The class name of the object */ className?: string; /** * obj init type */ initType: ObjectInitTypeLike; /** * access level */ accessLevel: AccessLevel; /** * EggPrototype implement type */ protoImplType: string; /** * object info list */ objects: ObjectInfo[]; } export interface EggMultiInstanceCallbackPrototypeInfo { /** * The class name of the object */ className?: string; /** * obj init type */ initType: ObjectInitTypeLike; /** * access level */ accessLevel: AccessLevel; /** * EggPrototype implement type */ protoImplType: string; /** * get object callback * @param ctx */ getObjects(ctx: MultiInstancePrototypeGetObjectsContext): ObjectInfo[]; } ================================================ FILE: core/types/core-decorator/model/EggPrototypeInfo.ts ================================================ import { ObjectInitTypeLike } from '../enum/ObjectInitType'; import { AccessLevel } from '../enum/AccessLevel'; import { QualifierInfo } from './QualifierInfo'; export type EggProtoImplClass = new(...args: any[]) => T; export type EggPrototypeName = PropertyKey; export interface EggPrototypeInfo { /** * egg object name */ name: EggPrototypeName; /** * The class name of the object */ className?: string; /** * obj init type */ initType: ObjectInitTypeLike; /** * access level */ accessLevel: AccessLevel; /** * EggPrototype implement type */ protoImplType: string; /** * EggPrototype qualifiers */ qualifiers?: QualifierInfo[]; /** * EggPrototype properties qualifiers */ properQualifiers?: Record; } ================================================ FILE: core/types/core-decorator/model/InjectConstructorInfo.ts ================================================ import { EggObjectName } from './InjectObjectInfo'; export interface InjectConstructorInfo { /** * inject args index */ refIndex: number; /** * inject args name */ refName: string; /** * obj's name will be injected */ objName: EggObjectName; /** * optional inject */ optional?: boolean; } ================================================ FILE: core/types/core-decorator/model/InjectObjectInfo.ts ================================================ export type EggObjectName = PropertyKey; export interface InjectObjectInfo { /** * property name obj inject to */ refName: PropertyKey; /** * obj's name will be injected */ objName: EggObjectName; /** * optional inject */ optional?: boolean; } ================================================ FILE: core/types/core-decorator/model/QualifierInfo.ts ================================================ export type QualifierAttribute = symbol | string; export type QualifierValue = string | number; /** * To locate prototype * * Example: * Has a decorator named HelloService * value is: * - Email * - Message * * interface HelloService { * hello(name: string): Promise; * } * * \@ContextProto({ name: 'helloService' }) * \@HelloService(HelloServiceType.Email) * class EmailHelloService implement HelloService { * ... * } * * \@ContextProto({ name: 'helloService' }) * \@HelloService(HelloServiceType.Message) * class MessageHelloService implement HelloService { * ... * } * * \@ContextProto() * class HelloFacade { * \@Inject * \@HelloService(HelloServiceType.Message) * helloService: HelloService; * } */ export interface QualifierInfo { attribute: QualifierAttribute; value: QualifierValue; } ================================================ FILE: core/types/dal/Qualifier.ts ================================================ export const DAL_COLUMN_INFO_MAP = Symbol.for('EggPrototype#dalColumnInfoMap'); export const DAL_COLUMN_TYPE_MAP = Symbol.for('EggPrototype#dalColumnTypeMap'); export const DAL_INDEX_LIST = Symbol.for('EggPrototype#dalIndexList'); export const DAL_IS_TABLE = Symbol.for('EggPrototype#dalIsTable'); export const DAL_TABLE_PARAMS = Symbol.for('EggPrototype#dalTableParams'); export const DAL_IS_DAO = Symbol.for('EggPrototype#dalIsDao'); ================================================ FILE: core/types/dal/decorator/Column.ts ================================================ import { ColumnType } from '../enum/ColumnType'; import { ColumnFormat } from '../enum/ColumnFormat'; export interface ColumnParams { name?: string; default?: string; canNull?: boolean; comment?: string; visible?: boolean; autoIncrement?: boolean; uniqueKey?: boolean; primaryKey?: boolean; collate?: string; columnFormat?: ColumnFormat; engineAttribute?: string; secondaryEngineAttribute?: string; } export interface IColumnTypeParams { type: ColumnType; } export interface BitParams extends IColumnTypeParams { type: ColumnType.BIT, length?: number; } export interface BoolParams extends IColumnTypeParams { type: ColumnType.BOOL, } interface BaseNumericParams extends IColumnTypeParams { length?: number; unsigned?: boolean; zeroFill?: boolean; } interface BaseFloatNumericParams extends IColumnTypeParams { length?: number; fractionalLength?: number; unsigned?: boolean; zeroFill?: boolean; } export interface TinyIntParams extends BaseNumericParams { type: ColumnType.TINYINT; } export interface SmallIntParams extends BaseNumericParams { type: ColumnType.SMALLINT; } export interface MediumIntParams extends BaseNumericParams { type: ColumnType.MEDIUMINT; } export interface IntParams extends BaseNumericParams { type: ColumnType.INT; } export interface BigIntParams extends BaseNumericParams { type: ColumnType.BIGINT; } export interface DecimalParams extends BaseFloatNumericParams { type: ColumnType.DECIMAL; } export interface FloatParams extends BaseFloatNumericParams { type: ColumnType.FLOAT; } export interface DoubleParams extends BaseFloatNumericParams { type: ColumnType.DOUBLE; } export interface DateParams extends IColumnTypeParams { type: ColumnType.DATE; } export interface DateTimeParams extends IColumnTypeParams { type: ColumnType.DATETIME; precision?: number; autoUpdate?: boolean; } export interface TimestampParams extends IColumnTypeParams { type: ColumnType.TIMESTAMP; precision?: number; autoUpdate?: boolean; } export interface TimeParams extends IColumnTypeParams { type: ColumnType.TIME; precision?: number; } export interface YearParams extends IColumnTypeParams { type: ColumnType.YEAR; } export interface CharParams extends IColumnTypeParams { type: ColumnType.CHAR; length?: number; characterSet?: string; collate?: string; } export interface VarCharParams extends IColumnTypeParams { type: ColumnType.VARCHAR; length: number; characterSet?: string; collate?: string; } export interface BinaryParams extends IColumnTypeParams { type: ColumnType.BINARY; length?: number; } export interface VarBinaryParams extends IColumnTypeParams { type: ColumnType.VARBINARY; length: number; } export interface TinyBlobParams extends IColumnTypeParams { type: ColumnType.TINYBLOB; } export interface TinyTextParams extends IColumnTypeParams { type: ColumnType.TINYTEXT; characterSet?: string; collate?: string; } export interface BlobParams extends IColumnTypeParams { type: ColumnType.BLOB; length?: number; } export interface TextParams extends IColumnTypeParams { type: ColumnType.TEXT; length?: number; characterSet?: string; collate?: string; } export interface MediumBlobParams extends IColumnTypeParams { type: ColumnType.MEDIUMBLOB; } export interface LongBlobParams extends IColumnTypeParams { type: ColumnType.LONGBLOB; } export interface MediumTextParams extends IColumnTypeParams { type: ColumnType.MEDIUMTEXT; characterSet?: string; collate?: string; } export interface LongTextParams extends IColumnTypeParams { type: ColumnType.LONGTEXT; characterSet?: string; collate?: string; } export interface EnumParams extends IColumnTypeParams { type: ColumnType.ENUM; enums: string[]; characterSet?: string; collate?: string; } export interface SetParams extends IColumnTypeParams { type: ColumnType.SET; enums: string[]; characterSet?: string; collate?: string; } export interface JsonParams extends IColumnTypeParams { type: ColumnType.JSON; } export interface BaseSpatialParams extends IColumnTypeParams { SRID?: number; } export interface GeometryParams extends BaseSpatialParams { type: ColumnType.GEOMETRY; } export interface PointParams extends BaseSpatialParams { type: ColumnType.POINT; } export interface LinestringParams extends BaseSpatialParams { type: ColumnType.LINESTRING; } export interface PolygonParams extends BaseSpatialParams { type: ColumnType.POLYGON; } export interface MultiPointParams extends BaseSpatialParams { type: ColumnType.MULTIPOINT; } export interface MultiLinestringParams extends BaseSpatialParams { type: ColumnType.MULTILINESTRING; } export interface MultiPolygonParams extends BaseSpatialParams { type: ColumnType.MULTIPOLYGON; } export interface GeometryCollectionParams extends BaseSpatialParams { type: ColumnType.GEOMETRYCOLLECTION; } export type ColumnTypeParams = BitParams | BoolParams | TinyIntParams | SmallIntParams | MediumIntParams | IntParams | BigIntParams | DecimalParams | FloatParams | DoubleParams | DateParams | DateTimeParams | TimestampParams | TimeParams | YearParams | CharParams | VarCharParams | BinaryParams | VarBinaryParams | TinyBlobParams | TinyTextParams | BlobParams | TextParams | MediumBlobParams | MediumTextParams | LongBlobParams | LongTextParams | EnumParams | SetParams | JsonParams | GeometryParams | PointParams | LinestringParams | PolygonParams | MultiPointParams | MultiLinestringParams | MultiPolygonParams | GeometryCollectionParams; ================================================ FILE: core/types/dal/decorator/DataSourceQualifier.ts ================================================ export const DataSourceQualifierAttribute = Symbol('Qualifier.DataSource'); export const DataSourceInjectName = 'dataSource'; ================================================ FILE: core/types/dal/decorator/Index.ts ================================================ import type { IndexStoreType } from '../enum/IndexStoreType'; import type { IndexType } from '../enum/IndexType'; export interface IndexParams { keys: string[]; name?: string; type?: IndexType, storeType?: IndexStoreType; comment?: string; engineAttribute?: string; secondaryEngineAttribute?: string; parser?: string; } ================================================ FILE: core/types/dal/decorator/Table.ts ================================================ // Create Table https://dev.mysql.com/doc/refman/8.0/en/create-table.html import type { InsertMethod } from '../enum/InsertMethod'; import type { CompressionType } from '../enum/CompressionType'; import type { RowFormat } from '../enum/RowFormat'; export interface TableParams { name?: string; dataSourceName?: string; comment?: string; autoExtendSize?: number; autoIncrement?: number; avgRowLength?: number; characterSet?: string; collate?: string; compression?: CompressionType; encryption?: boolean; engine?: string; engineAttribute?: string; insertMethod?: InsertMethod; keyBlockSize?: number; maxRows?: number; minRows?: number; rowFormat?: RowFormat; secondaryEngineAttribute?: string; } ================================================ FILE: core/types/dal/enum/ColumnFormat.ts ================================================ export enum ColumnFormat { FIXED = 'FIXED', DYNAMIC = 'DYNAMIC', DEFAULT = 'DEFAULT', } ================================================ FILE: core/types/dal/enum/ColumnType.ts ================================================ // Data Types https://dev.mysql.com/doc/refman/8.0/en/data-types.html export enum ColumnType { // Numeric BIT = 'BIT', TINYINT = 'TINYINT', BOOL = 'BOOL', SMALLINT = 'SMALLINT', MEDIUMINT = 'MEDIUMINT', INT = 'INT', BIGINT = 'BIGINT', DECIMAL = 'DECIMAL', FLOAT = 'FLOAT', DOUBLE = 'DOUBLE', // Date DATE = 'DATE', DATETIME = 'DATETIME', TIMESTAMP = 'TIMESTAMP', TIME = 'TIME', YEAR = 'YEAR', // String CHAR = 'CHAR', VARCHAR = 'VARCHAR', BINARY = 'BINARY', VARBINARY = 'VARBINARY', TINYBLOB = 'TINYBLOB', TINYTEXT = 'TINYTEXT', BLOB = 'BLOB', TEXT = 'TEXT', MEDIUMBLOB = 'MEDIUMBLOB', MEDIUMTEXT = 'MEDIUMTEXT', LONGBLOB = 'LONGBLOB', LONGTEXT = 'LONGTEXT', ENUM = 'ENUM', SET = 'SET', // JSON JSON = 'JSON', // Spatial GEOMETRY = 'GEOMETRY', POINT = 'POINT', LINESTRING = 'LINESTRING', POLYGON = 'POLYGON', MULTIPOINT = 'MULTIPOINT', MULTILINESTRING = 'MULTILINESTRING', MULTIPOLYGON = 'MULTIPOLYGON', GEOMETRYCOLLECTION = 'GEOMETRYCOLLECTION', } ================================================ FILE: core/types/dal/enum/CompressionType.ts ================================================ export enum CompressionType { ZLIB = 'ZLIB', LZ4 = 'LZ4', NONE = 'NONE', } ================================================ FILE: core/types/dal/enum/IndexStoreType.ts ================================================ export enum IndexStoreType { BTREE = 'BTREE', HASH = 'HASH', } ================================================ FILE: core/types/dal/enum/IndexType.ts ================================================ export enum IndexType { PRIMARY = 'PRIMARY', UNIQUE = 'UNIQUE', INDEX = 'INDEX', FULLTEXT = 'FULLTEXT', SPATIAL = 'SPATIAL', } ================================================ FILE: core/types/dal/enum/InsertMethod.ts ================================================ export enum InsertMethod { NO = 'NO', FIRST = 'FIRST', LAST = 'LAST', } ================================================ FILE: core/types/dal/enum/RowFormat.ts ================================================ export enum RowFormat { DEFAULT = 'DEFAULT', DYNAMIC = 'DYNAMIC', FIXED = 'FIXED', COMPRESSED = 'COMPRESSED', REDUNDANT = 'REDUNDANT', COMPACT = 'COMPACT', } ================================================ FILE: core/types/dal/enum/SqlType.ts ================================================ export enum SqlType { BLOCK = 'BLOCK', INSERT = 'INSERT', SELECT = 'SELECT', UPDATE = 'UPDATE', DELETE = 'DELETE', } ================================================ FILE: core/types/dal/enum/Templates.ts ================================================ export enum Templates { BASE_DAO = 'base_dao', DAO = 'dao', EXTENSION = 'extension', } ================================================ FILE: core/types/dal/index.ts ================================================ export * from './enum/ColumnFormat'; export * from './enum/ColumnType'; export * from './enum/CompressionType'; export * from './enum/IndexStoreType'; export * from './enum/IndexType'; export * from './enum/InsertMethod'; export * from './enum/RowFormat'; export * from './enum/SqlType'; export * from './enum/Templates'; export * from './type/CodeGenerator'; export * from './type/ColumnTsType'; export * from './type/DateSource'; export * from './type/Spatial'; export * from './type/SqlMap'; export * from './type/BaseDao'; export * from './decorator/Column'; export * from './decorator/DataSourceQualifier'; export * from './decorator/Index'; export * from './decorator/Table'; export * from './Qualifier'; ================================================ FILE: core/types/dal/type/BaseDao.ts ================================================ import { EggProtoImplClass } from '../../core-decorator'; import { SqlMap } from './SqlMap'; export interface BaseDaoType { new(...args: any[]): object; clazzModel: EggProtoImplClass; clazzExtension: Record; // todo: typed structure tableStature: object; tableSql: string; } ================================================ FILE: core/types/dal/type/CodeGenerator.ts ================================================ export interface CodeGeneratorOptions { moduleDir: string; moduleName: string; teggPkg?: string; dalPkg?: string; } ================================================ FILE: core/types/dal/type/ColumnTsType.ts ================================================ import { Geometry, GeometryCollection, Line, MultiLine, MultiPoint, MultiPolygon, Point, Polygon } from './Spatial'; export interface ColumnTsType { BIT: Buffer, TINYINT: number, BOOL: 0 | 1, SMALLINT: number, MEDIUMINT: number, INT: number, BIGINT: string, DECIMAL: string, FLOAT: number, DOUBLE: number, DATE: Date, DATETIME: Date, TIMESTAMP: Date, TIME: string, YEAR: number, CHAR: string, VARCHAR: string, BINARY: Buffer, VARBINARY: Buffer, TINYBLOB: Buffer, TINYTEXT: string, BLOB: Buffer, TEXT: string, MEDIUMBLOB: Buffer, MEDIUMTEXT: string, LONGBLOB: Buffer, LONGTEXT: string, ENUM: string, SET: string, JSON: object, GEOMETRY: Geometry, POINT: Point, LINESTRING: Line, POLYGON: Polygon, MULTIPOINT: MultiPoint, MULTILINESTRING: MultiLine, MULTIPOLYGON: MultiPolygon, GEOMETRYCOLLECTION: GeometryCollection, } ================================================ FILE: core/types/dal/type/DateSource.ts ================================================ export interface PaginateData { total: number; pageNum: number; rows: Array; } export interface DataSource { execute(sqlName: string, data?: any): Promise>; executeScalar(sqlName: string, data?: any): Promise; executeRaw(sqlName: string, data?: any): Promise>; executeRawScalar(sqlName: string, data?: any): Promise; paginate(sqlName: string, data: any, currentPage: number, perPageCount: number): Promise>; count(sqlName: string, data?: any): Promise; } ================================================ FILE: core/types/dal/type/Spatial.ts ================================================ export interface Point { x: number; y: number; } export type Line = Array; export type Polygon = Array; export type Geometry = Point | Line | Polygon; export type MultiPoint = Array; export type MultiLine = Array; export type MultiPolygon = Array; export type GeometryCollection = Array; ================================================ FILE: core/types/dal/type/SqlMap.ts ================================================ import type { SqlType } from '../enum/SqlType'; export interface BaseSqlMap { type?: SqlType; } export interface FullSqlMap extends BaseSqlMap { type: SqlType.DELETE | SqlType.INSERT | SqlType.UPDATE | SqlType.SELECT; sql: string; } export interface BlockSqlMap extends BaseSqlMap { type: SqlType.BLOCK; content: string; } export type SqlMap = FullSqlMap | BlockSqlMap; export interface GenerateSqlMap { name: string; type: SqlType.DELETE | SqlType.UPDATE | SqlType.INSERT | SqlType.SELECT; sql: string; } ================================================ FILE: core/types/dynamic-inject.ts ================================================ import { EggProtoImplClass, QualifierValue } from './core-decorator'; export type EggAbstractClazz = Function & {prototype: T}; export type ImplTypeEnum = { [id: string]: QualifierValue; }; export type ImplDecorator = (type: Enum[keyof Enum]) => ((clazz: EggProtoImplClass) => void); export interface EggObjectFactory { getEggObject(abstractClazz: EggAbstractClazz, qualifierValue: QualifierValue): Promise; getEggObjects(abstractClazz: EggAbstractClazz): Promise>; } export const QUALIFIER_IMPL_MAP = Symbol.for('EggPrototype#qualifierImplMap'); ================================================ FILE: core/types/index.ts ================================================ export * from './aop'; export * from './common'; export * from './controller-decorator'; export * from './core-decorator'; export * from './dal'; export * from './dynamic-inject'; export * from './lifecycle'; export * from './metadata'; export * from './orm'; export * from './runtime'; export * from './schedule'; export * from './transaction'; export * from './agent-runtime'; ================================================ FILE: core/types/lifecycle/EggObjectLifecycle.ts ================================================ import type { EggObject, EggObjectLifeCycleContext } from '@eggjs/tegg-runtime'; /** * lifecycle hook interface for egg object */ export interface EggObjectLifecycle { /** * call before project load */ preLoad?(ctx: EggObjectLifeCycleContext): Promise; /** * call after construct */ postConstruct?(ctx: EggObjectLifeCycleContext, eggObj: EggObject): Promise; /** * call before inject deps */ preInject?(ctx: EggObjectLifeCycleContext, eggObj: EggObject): Promise; /** * call after inject deps */ postInject?(ctx: EggObjectLifeCycleContext, eggObj: EggObject): Promise; /** * before object is ready */ init?(ctx: EggObjectLifeCycleContext, eggObj: EggObject): Promise; /** * call before destroy */ preDestroy?(ctx: EggObjectLifeCycleContext, eggObj: EggObject): Promise; /** * destroy the object */ destroy?(ctx: EggObjectLifeCycleContext, eggObj: EggObject): Promise; } export type LifecycleHookName = keyof EggObjectLifecycle; ================================================ FILE: core/types/lifecycle/IdenticalObject.ts ================================================ export type Id = string; export interface IdenticalObject { id: Id; } ================================================ FILE: core/types/lifecycle/LifecycleHook.ts ================================================ import { IdenticalObject } from './IdenticalObject'; export interface LifecycleContext { } export interface LifecycleObject extends IdenticalObject { preLoad?(): Promise; init?(ctx: T): Promise; destroy?(ctx: T): Promise; } export interface LifecycleHook> { // called after obj constructor preCreate?(ctx: T, obj: R): Promise; // called after all preCreate done and properties injected postCreate?(ctx: T, obj: R): Promise; // call before destroy obj preDestroy?(ctx: T, obj: R): Promise; } ================================================ FILE: core/types/lifecycle/index.ts ================================================ export * from './EggObjectLifecycle'; export * from './IdenticalObject'; export * from './LifecycleHook'; ================================================ FILE: core/types/metadata/enum/ProtoDescriptorType.ts ================================================ export enum ProtoDescriptorType { CLASS = 'CLASS', } ================================================ FILE: core/types/metadata/errors.ts ================================================ export enum ErrorCodes { EGG_PROTO_NOT_FOUND = 'EGG_PROTO_NOT_FOUND', MULTI_PROTO_FOUND = 'MULTI_PROTO_FOUND', INCOMPATIBLE_PROTO_INJECT = 'INCOMPATIBLE_PROTO_INJECT', } ================================================ FILE: core/types/metadata/index.ts ================================================ export * from './model/ProtoDescriptor'; export * from './model/EggPrototype'; export * from './model/Loader'; export * from './model/LoadUnit'; export * from './errors'; ================================================ FILE: core/types/metadata/model/EggPrototype.ts ================================================ import { AccessLevel, EggProtoImplClass, EggPrototypeInfo, EggPrototypeName, InjectType, MetaDataKey, ObjectInitTypeLike, QualifierAttribute, QualifierInfo, QualifierValue, } from '../../core-decorator'; import { LifecycleContext, LifecycleObject } from '../../lifecycle'; import { LoadUnit } from './LoadUnit'; export interface InjectObjectProto { /** * property name obj inject to */ refName: PropertyKey; /** * obj's name will be injected */ objName: PropertyKey; /** * inject qualifiers */ qualifiers: QualifierInfo[]; /** * optional inject */ optional?: boolean; /** * inject prototype */ proto: EggPrototype; } export interface InjectConstructorProto { /** * inject args index */ refIndex: number; /** * property name obj inject to */ refName: PropertyKey; /** * obj's name will be injected */ objName: PropertyKey; /** * inject qualifiers */ qualifiers: QualifierInfo[]; /** * optional inject */ optional?: boolean; /** * inject prototype */ proto: EggPrototype; } export interface InjectObject { /** * property name obj inject to */ refName: PropertyKey; /** * obj's name will be injected */ objName: PropertyKey; /** * obj's initType will be injected * if null same as current obj */ initType?: ObjectInitTypeLike; /** * optional inject */ optional?: boolean; } export interface InjectConstructor { /** * property name obj inject to */ refIndex: number; /** * property name obj inject to */ refName: PropertyKey; /** * obj's name will be injected */ objName: PropertyKey; /** * obj's initType will be injected * if null same as current obj */ initType?: ObjectInitTypeLike; /** * optional inject */ optional?: boolean; } export type EggPrototypeClass = new (...args: any[]) => EggPrototype; export interface EggPrototypeLifecycleContext extends LifecycleContext { clazz: EggProtoImplClass; prototypeInfo: EggPrototypeInfo; loadUnit: LoadUnit; } export interface EggPrototype extends LifecycleObject { // TODO // 1. proto name // 1. default obj name readonly name: EggPrototypeName; readonly initType: ObjectInitTypeLike; readonly accessLevel: AccessLevel; readonly loadUnitId: string; readonly injectObjects: Array; readonly injectType?: InjectType; readonly className?: string; readonly multiInstanceConstructorIndex?: number; readonly multiInstanceConstructorAttributes?: QualifierAttribute[]; /** * get metedata for key * @param {MetaDataKey} metadataKey */ getMetaData(metadataKey: MetaDataKey): T | undefined; /** * verify proto is satisfied with qualifier * * default qualifier: * - load unit name * - init type * * @param qualifier */ verifyQualifier(qualifier: QualifierInfo): boolean; verifyQualifiers(qualifiers: QualifierInfo[]): boolean; getQualifier(attribute: QualifierAttribute): QualifierValue | undefined /** * construct egg object, not trigger lifecycle method/hook */ constructEggObject(...args: any): object; } export interface EggPrototypeWithClazz extends EggPrototype { clazz?: EggProtoImplClass; } export type EggPrototypeCreator = (ctx: EggPrototypeLifecycleContext) => EggPrototype; ================================================ FILE: core/types/metadata/model/LoadUnit.ts ================================================ import { EggPrototypeName, QualifierInfo } from '../../core-decorator'; import { LifecycleContext, LifecycleObject } from '../../lifecycle'; import { EggPrototype } from './EggPrototype'; import { Loader } from './Loader'; export enum EggLoadUnitType { MODULE = 'MODULE', PLUGIN = 'PLUGIN', APP = 'APP', } export type EggLoadUnitTypeLike = EggLoadUnitType | string; export interface LoadUnitLifecycleContext extends LifecycleContext { unitPath: string; loader: Loader; } export interface LoadUnit extends LifecycleObject { readonly name: string; readonly unitPath: string; readonly type: EggLoadUnitTypeLike; iterateEggPrototype(): IterableIterator; registerEggPrototype(proto: EggPrototype): void; deletePrototype(proto: EggPrototype); getEggPrototype(name: EggPrototypeName, qualifiers: QualifierInfo[]): EggPrototype[]; containPrototype(proto: EggPrototype): boolean; } export interface LoadUnitPair { loadUnit: LoadUnit; ctx: LoadUnitLifecycleContext; } export type LoadUnitCreator = (ctx: LoadUnitLifecycleContext) => LoadUnit; ================================================ FILE: core/types/metadata/model/Loader.ts ================================================ import { EggProtoImplClass } from '../../core-decorator'; /** * Loader to load class list in module */ export interface Loader { load(): EggProtoImplClass[]; // TODO impl loadProto // loadProto(): ProtoDescriptor[]; } ================================================ FILE: core/types/metadata/model/ProtoDescriptor.ts ================================================ import { AccessLevel, EggPrototypeInfo, ObjectInitTypeLike, QualifierInfo } from '../../core-decorator'; import { ProtoDescriptorType } from '../enum/ProtoDescriptorType'; export type ProtoDescriptorTypeLike = ProtoDescriptorType | string; export interface InjectObjectDescriptor { refName: PropertyKey; objName: PropertyKey; qualifiers: QualifierInfo[]; } export interface ProtoDescriptor extends EggPrototypeInfo { // base properties name: PropertyKey; accessLevel: AccessLevel; initType: ObjectInitTypeLike; qualifiers: QualifierInfo[]; injectObjects: InjectObjectDescriptor[]; protoImplType: string; properQualifiers: Record; // module info defineModuleName: string; defineUnitPath: string; // multi instance proto may be used in other module instanceModuleName: string; instanceDefineUnitPath: string; // test is the same proto equal(protoDescriptor: ProtoDescriptor): boolean; } ================================================ FILE: core/types/orm.ts ================================================ export interface AttributeOptions { // field name, default is property name name?: string; // allow null, default is true allowNull?: boolean; // auto increment, default is false autoIncrement?: boolean; // primary field, default is false primary?: boolean; // unique field, default is false unique?: boolean; } export interface IndexOptions { unique?: boolean; primary?: boolean; name?: string; } export interface ModelParams { tableName?: string; dataSource?: string; } export interface ModelIndexInfo { fields: string[]; options?: IndexOptions; } export interface ModelAttributeInfo { dataType: string; options?: AttributeOptions; } export const MODEL_PROTO_IMPL_TYPE = 'MODEL_PROTO'; export const IS_MODEL = Symbol.for('EggPrototype#model#isModel'); export const MODEL_DATA_SOURCE = Symbol.for('EggPrototype#model#dataSource'); export const MODEL_DATA_TABLE_NAME = Symbol.for('EggPrototype#model#tableName'); export const MODEL_DATA_INDICES = Symbol.for('EggPrototype#model#indices'); export const MODEL_DATA_ATTRIBUTES = Symbol.for('EggPrototype#model#attributes'); ================================================ FILE: core/types/package.json ================================================ { "name": "@eggjs/tegg-types", "version": "3.78.15", "description": "tegg types", "keywords": [ "egg", "typescript", "tegg", "types" ], "main": "./index.js", "files": [ "**/*.js", "**/*.d.ts" ], "typings": "index.d.ts", "scripts": { "clean": "tsc -b --clean", "tsc:pub": "ut run clean && tsc -p ./tsconfig.pub.json", "prepublishOnly": "ut tsc:pub" }, "author": "gxkl ", "license": "MIT", "homepage": "https://github.com/eggjs/tegg", "bugs": { "url": "https://github.com/eggjs/tegg/issues" }, "repository": { "type": "git", "url": "git@github.com:eggjs/tegg.git", "directory": "core/types" }, "engines": { "node": ">=14.0.0" }, "publishConfig": { "access": "public" }, "devDependencies": { "@modelcontextprotocol/sdk": "^1.23.0", "@types/mocha": "^10.0.1", "@types/node": "^20.2.4", "cross-env": "^7.0.3", "mocha": "^10.2.0", "ts-node": "^10.9.1", "typescript": "^5.0.4" }, "gitHead": "240a3e9f40fda65ebb7589727d197db5ce17916c" } ================================================ FILE: core/types/runtime/Factory.ts ================================================ import type { EggObjectName } from '../core-decorator'; import type { LifecycleContext } from '../lifecycle'; import type { EggPrototype } from '../metadata'; import type { EggContainer } from './model/EggContainer'; import type { EggObject, EggObjectLifeCycleContext } from './model/EggObject'; export type ContainerGetMethod = (proto: EggPrototype) => EggContainer; export type CreateObjectMethod = (name: EggObjectName, proto: EggPrototype, lifecycleContext: EggObjectLifeCycleContext) => Promise; ================================================ FILE: core/types/runtime/index.ts ================================================ export * from './model/EggContainer'; export * from './model/EggContext'; export * from './model/EggObject'; export * from './model/LoadUnitInstance'; export * from './Factory'; ================================================ FILE: core/types/runtime/model/EggContainer.ts ================================================ import { EggObjectName, EggPrototypeName } from '../../core-decorator'; import { LifecycleContext, LifecycleObject } from '../../lifecycle'; import { EggPrototype } from '../../metadata'; import { EggObject } from './EggObject'; export interface EggContainer extends LifecycleObject { // Call this method in LifecycleHook.preCreate // To help container decide which proto should be create iterateProtoToCreate(): IterableIterator<[ EggObjectName, EggPrototype ]>; addProtoToCreate(name: EggPrototypeName, proto: EggPrototype); deleteProtoToCreate(name: EggPrototypeName); // async method for get or create object getOrCreateEggObject(name: EggPrototypeName, proto: EggPrototype): Promise; // sync method for get object // object should be created before get, or throw Error getEggObject(name: EggPrototypeName, proto: EggPrototype): EggObject; } ================================================ FILE: core/types/runtime/model/EggContext.ts ================================================ import { EggContainer } from './EggContainer'; export interface EggContextLifecycleContext { } export interface EggRuntimeContext extends EggContainer { // ctx get/set method get(key: string | symbol): any | undefined; set(key: string | symbol, val: any); } ================================================ FILE: core/types/runtime/model/EggObject.ts ================================================ import { EggPrototypeName } from '../../core-decorator'; import { LifecycleContext, LifecycleObject } from '../../lifecycle'; import { EggPrototype, LoadUnit } from '../../metadata'; import { EggRuntimeContext } from './EggContext'; import { LoadUnitInstance } from './LoadUnitInstance'; export enum EggObjectStatus { PENDING = 'PENDING', READY = 'READY', ERROR = 'ERROR', DESTROYING = 'DESTROYING', DESTROYED = 'DESTROYED', } export interface EggObjectLifeCycleContext extends LifecycleContext { readonly loadUnit: LoadUnit; readonly loadUnitInstance: LoadUnitInstance; } export interface EggObject extends LifecycleObject { readonly isReady: boolean; readonly obj: object; readonly proto: EggPrototype; readonly name: EggPrototypeName; readonly ctx?: EggRuntimeContext; injectProperty(name: string, descriptor: PropertyDescriptor); } ================================================ FILE: core/types/runtime/model/LoadUnitInstance.ts ================================================ import { LoadUnit } from '../../metadata'; import { EggContainer } from './EggContainer'; export interface LoadUnitInstanceLifecycleContext { loadUnit: LoadUnit; } export interface LoadUnitInstance extends EggContainer { readonly name: string; readonly loadUnit: LoadUnit; } ================================================ FILE: core/types/schedule.ts ================================================ export enum ScheduleType { WORKER = 'worker', ALL = 'all', } export const IS_SCHEDULE = Symbol.for('EggPrototype#isSchedule'); export const SCHEDULE_PARAMS = Symbol.for('EggPrototype#schedule#params'); export const SCHEDULE_OPTIONS = Symbol.for('EggPrototype#schedule#options'); export const SCHEDULE_METADATA = Symbol.for('EggPrototype#schedule#metadata'); export type ScheduleTypeLike = ScheduleType | string; export interface ScheduleParams { type: ScheduleTypeLike; scheduleData: T; } export interface CronParams { cron: string; cronOptions?: any; } export interface IntervalParams { interval: string | number; } export type CronScheduleParams = ScheduleParams; export type IntervalScheduleParams = ScheduleParams; export interface ScheduleOptions { // default is false immediate?: boolean; // default is false disable?: boolean; // if env has value, only run in this envs env?: Array; } export interface ScheduleSubscriber { subscribe(data?: any): Promise; } ================================================ FILE: core/types/standalone/ServiceWorkerContext.ts ================================================ import { FetchEvent } from './fetch'; export interface ServiceWorkerContextInit { event: T; } export interface ServiceWorkerContext { event: Event; get response(): Response | undefined; set response(response: Response); get body(): any | undefined; set body(body: any); } export type ServiceWorkerFetchContext = ServiceWorkerContext; ================================================ FILE: core/types/standalone/fetch.ts ================================================ export interface FetchEvent extends Event { request: Request; waitUntil(f: Promise): void; respondWith(r: Response | PromiseLike): void; } ================================================ FILE: core/types/standalone/index.ts ================================================ export * from './fetch'; export * from './ServiceWorkerContext'; ================================================ FILE: core/types/transaction.ts ================================================ export enum PropagationType { /** 不管是当前调用栈是否存在事务,始终让当前函数在新的事务中执行 */ ALWAYS_NEW = 'ALWAYS_NEW', /** 如果当前调用栈存在事务则复用,否则创建一个 */ REQUIRED = 'REQUIRED', } export interface TransactionalParams { /** 事务传播方式,默认 REQUIRED */ propagation?: PropagationType; /** * 数据源,默认使用 module 的数据源,非 module 时将使用 default 数据源 * 需要注意的是数据源之间的连接是隔离的,回滚也是独立的 * 比如函数 B 在函数 A 中执行,A 执行异常时,不会回滚 B 中执行的 sql * */ datasourceName?: string; } export interface TransactionMetadata { propagation: PropagationType; method: PropertyKey; datasourceName?: string; } export const TRANSACTION_META_DATA = Symbol.for('EggPrototype#transaction#metaData'); export const IS_TRANSACTION_CLAZZ = Symbol.for('EggPrototype#IS_TRANSACTION_CLAZZ'); ================================================ FILE: core/types/tsconfig.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "baseUrl": "./" }, "exclude": [ "dist", "node_modules", "test" ] } ================================================ FILE: core/types/tsconfig.pub.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "baseUrl": "./" }, "exclude": [ "dist", "node_modules", "test" ] } ================================================ FILE: core/vitest/CHANGELOG.md ================================================ # Change Log All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. ## [3.78.15](https://github.com/eggjs/tegg/compare/v3.78.14...v3.78.15) (2026-04-23) **Note:** Version bump only for package @eggjs/tegg-vitest ## [3.78.14](https://github.com/eggjs/tegg/compare/v3.78.13...v3.78.14) (2026-04-21) **Note:** Version bump only for package @eggjs/tegg-vitest ## [3.78.13](https://github.com/eggjs/tegg/compare/v3.78.12...v3.78.13) (2026-04-21) **Note:** Version bump only for package @eggjs/tegg-vitest ## [3.78.12](https://github.com/eggjs/tegg/compare/v3.78.11...v3.78.12) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-vitest ## [3.78.11](https://github.com/eggjs/tegg/compare/v3.78.10...v3.78.11) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-vitest ## [3.78.10](https://github.com/eggjs/tegg/compare/v3.78.9...v3.78.10) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-vitest ## [3.78.9](https://github.com/eggjs/tegg/compare/v3.78.8...v3.78.9) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-vitest ## [3.78.8](https://github.com/eggjs/tegg/compare/v3.78.7...v3.78.8) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-vitest ## [3.78.7](https://github.com/eggjs/tegg/compare/v3.78.6...v3.78.7) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-vitest ## [3.78.6](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.6) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-vitest ## [3.78.5](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.5) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-vitest ## [3.78.4](https://github.com/eggjs/tegg/compare/v3.78.3...v3.78.4) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-vitest ## [3.78.3](https://github.com/eggjs/tegg/compare/v3.78.2...v3.78.3) (2026-04-15) **Note:** Version bump only for package @eggjs/tegg-vitest ## [3.78.2](https://github.com/eggjs/tegg/compare/v3.78.1...v3.78.2) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-vitest ## [3.78.1](https://github.com/eggjs/tegg/compare/v3.78.0...v3.78.1) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-vitest # [3.78.0](https://github.com/eggjs/tegg/compare/v3.77.2...v3.78.0) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-vitest ## [3.77.2](https://github.com/eggjs/tegg/compare/v3.77.1...v3.77.2) (2026-04-07) **Note:** Version bump only for package @eggjs/tegg-vitest ## [3.77.1](https://github.com/eggjs/tegg/compare/v3.77.0...v3.77.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-vitest # [3.77.0](https://github.com/eggjs/tegg/compare/v3.76.1...v3.77.0) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-vitest ## [3.76.1](https://github.com/eggjs/tegg/compare/v3.76.0...v3.76.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-vitest # [3.76.0](https://github.com/eggjs/tegg/compare/v3.75.1...v3.76.0) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-vitest ## [3.75.1](https://github.com/eggjs/tegg/compare/v3.75.0...v3.75.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-vitest # [3.75.0](https://github.com/eggjs/tegg/compare/v3.74.0...v3.75.0) (2026-03-30) **Note:** Version bump only for package @eggjs/tegg-vitest # [3.74.0](https://github.com/eggjs/tegg/compare/v3.73.0...v3.74.0) (2026-03-30) **Note:** Version bump only for package @eggjs/tegg-vitest # [3.73.0](https://github.com/eggjs/tegg/compare/v3.72.0...v3.73.0) (2026-03-25) ### Features * add tegg vitest workspace ([#401](https://github.com/eggjs/tegg/issues/401)) ([c853090](https://github.com/eggjs/tegg/commit/c853090c8da22c158b684d4e0ccabf0cba4c17b8)) ================================================ FILE: core/vitest/README.md ================================================ # @eggjs/tegg-vitest Vitest adapter that provides tegg context injection and lifecycle handling via a custom Vitest runner. ## Install This package lives in the tegg monorepo workspace. ## Usage 1. Create a Vitest setup file that calls `configureTeggRunner`: ```ts // vitest.setup.ts import path from 'path'; import mm from 'egg-mock'; import { configureTeggRunner } from '@eggjs/tegg-vitest'; const app = mm.app({ baseDir: path.join(__dirname, 'fixtures/apps/my-app'), framework: require.resolve('egg'), }); configureTeggRunner({ getApp: () => app, restoreMocks: true, parallel: process.env.VITEST_WORKER_ID != null, }); ``` 2. Wire it in `vitest.config.ts` with the custom runner: ```ts import { defineConfig } from 'vitest/config'; export default defineConfig({ test: { environment: 'node', setupFiles: ['./vitest.setup.ts'], runner: '@eggjs/tegg-vitest/runner', }, }); ``` ## Options - `getApp`: Provide a custom app getter. Default: `require('egg-mock/bootstrap').app`. - `parallel`: Skip auto `app.close()` when running in parallel mode. Default: auto-detected from `VITEST_WORKER_ID`. - `restoreMocks`: Restore mocks after each test (defaults to true). ## Lifecycle & Context Injection The custom runner extends Vitest's `VitestTestRunner` and manages tegg context at the runner level: - **`importFile` (collection phase)**: Captures per-file config from `configureTeggRunner()` and calls `app.ready()`. - **`onBeforeRunSuite` (file suite)**: Creates a suite-scoped `ctx` via `app.mockContext()`, overrides `ctxStorage.getStore()`, and opens a held `beginModuleScope` that stays alive for the entire file. - **`onBeforeRunTask` (per test)**: Creates a per-test `ctx` and opens a held `beginModuleScope` for the test. - **`onAfterRunTask`**: Releases the test scope, restores mocks, and restores `ctxStorage.getStore()` back to the suite `ctx`. - **`onAfterRunSuite`**: Releases the suite scope, restores original `getStore()`, and calls `app.close()` unless `parallel` is true. ## Limitations - Context is managed at the **file suite** level. `egg-mock`'s Mocha runner patch can switch context at the **`describe` suite** level. If your tests rely on describe-scoped suite context, you must manage that manually. - If `getApp` throws or returns `undefined`, the adapter will run tests without context injection. ================================================ FILE: core/vitest/index.ts ================================================ export * from './src'; ================================================ FILE: core/vitest/package.json ================================================ { "name": "@eggjs/tegg-vitest", "version": "3.78.15", "description": "Vitest adapter for tegg context injection", "keywords": [ "egg", "tegg", "vitest", "test", "adapter" ], "main": "dist/index.js", "exports": { ".": { "types": "./dist/index.d.ts", "default": "./dist/index.js" }, "./runner": { "types": "./dist/runner.d.ts", "default": "./dist/runner.js" } }, "files": [ "dist/**/*.js", "dist/**/*.d.ts" ], "typings": "dist/index.d.ts", "scripts": { "clean": "tsc -b --clean", "tsc:pub": "ut run clean && tsc -p ./tsconfig.pub.json", "test": "node --eval \"process.exit(parseInt(process.versions.node) < 18 ? 0 : 1)\" || vitest run" }, "author": "killagu ", "license": "MIT", "homepage": "https://github.com/eggjs/tegg", "bugs": { "url": "https://github.com/eggjs/tegg/issues" }, "repository": { "type": "git", "url": "https://github.com/eggjs/tegg.git", "directory": "core/vitest" }, "engines": { "node": ">=18.0.0" }, "dependencies": { "egg-mock": "^5.5.0" }, "peerDependencies": { "vitest": "^1.6.0 || ^2.0.0 || ^3.0.0" }, "devDependencies": { "@types/node": "^20.2.4", "egg": "^3.9.1", "egg-tracer": "^2.0.0", "typescript": "^5.0.4", "vitest": "^1.6.0" } } ================================================ FILE: core/vitest/runner.ts ================================================ export { default } from './src/runner'; ================================================ FILE: core/vitest/src/index.ts ================================================ import { defaultGetApp, } from './shared'; import type { TeggVitestAdapterOptions } from './shared'; export type { EggMockApp, TeggVitestAdapterOptions } from './shared'; /** * Configure the custom Vitest runner (used via globalThis.__teggVitestConfig). * Call this in a setupFile and set `runner` in vitest.config.ts to use the runner approach. */ export function configureTeggRunner(options: TeggVitestAdapterOptions = {}) { (globalThis as any).__teggVitestConfig = { restoreMocks: options.restoreMocks ?? true, getApp: options.getApp ?? defaultGetApp, }; } ================================================ FILE: core/vitest/src/runner.ts ================================================ import { VitestTestRunner } from 'vitest/runners'; import type { Suite, Task, File } from 'vitest'; import { debugLog, defaultGetApp, restoreEggMocksIfNeeded, } from './shared'; import type { EggMockApp } from './shared'; interface TeggRunnerConfig { restoreMocks: boolean; getApp: () => Promise | EggMockApp | undefined; } interface HeldScope { scopePromise: Promise; endScope: () => void; } interface FileAppState { app: EggMockApp; config: TeggRunnerConfig; } interface FileScopeState { app: EggMockApp; config: TeggRunnerConfig; suiteCtx: any; suiteScope: HeldScope | null; } interface TaskScopeState { testScope: HeldScope | null; filepath: string; } /** * Create a held beginModuleScope: starts the scope and waits until init() is * complete (the inner fn starts executing), then returns the held scope. * The scope stays alive until endScope() is called. */ async function createHeldScope(ctx: any): Promise { let endScope!: () => void; const gate = new Promise(resolve => { endScope = resolve; }); let scopeReady!: () => void; const readyPromise = new Promise(resolve => { scopeReady = resolve; }); const scopePromise = ctx.beginModuleScope(async () => { // init() has completed at this point, signal readiness scopeReady(); await gate; }); // Race readyPromise against scopePromise: if beginModuleScope rejects // before invoking the callback (so scopeReady is never called), the error // propagates immediately instead of hanging forever on readyPromise. // Promise.race attaches a rejection handler to scopePromise, so there are // no unhandled rejections. scopePromise itself is preserved as-is for // gate/endScope behavior in releaseHeldScope. await Promise.race([ readyPromise, scopePromise ]); return { scopePromise, endScope }; } async function releaseHeldScope(scope: HeldScope | null) { if (!scope) return; scope.endScope(); await scope.scopePromise; } function isFileSuite(suite: Suite): suite is File { return !suite.suite && !!suite.filepath; } function getTaskFilepath(task: Task): string | undefined { return (task as any).file?.filepath; } export default class TeggVitestRunner extends VitestTestRunner { private fileScopeMap = new Map(); private taskScopeMap = new Map(); private fileAppMap = new Map(); private warned = false; /** * Override importFile to capture per-file config set by configureTeggRunner() * and await app.ready() during collection phase. */ async importFile(filepath: string, source: Parameters[1]): Promise { // Clear stale state for this file before re-collection in watch mode if (source === 'collect') { this.fileAppMap.delete(filepath); this.warned = false; } // Clear any stale config before importing delete (globalThis as any).__teggVitestConfig; const result = await super.importFile(filepath, source); if (source === 'collect') { const rawConfig = (globalThis as any).__teggVitestConfig; if (rawConfig) { delete (globalThis as any).__teggVitestConfig; const config: TeggRunnerConfig = { restoreMocks: rawConfig.restoreMocks ?? true, getApp: rawConfig.getApp ?? defaultGetApp, }; debugLog(`captured config for ${filepath}`); // Resolve app and await ready during collection try { const app = await config.getApp(); if (app) { await app.ready(); this.fileAppMap.set(filepath, { app, config }); debugLog(`app ready for ${filepath}`); } } catch (err) { if (!this.warned) { this.warned = true; // eslint-disable-next-line no-console console.warn('[tegg-vitest] getApp failed, skip context injection.', err); } } } } return result; } async onBeforeRunSuite(suite: Suite): Promise { if (isFileSuite(suite)) { const filepath = suite.filepath!; debugLog(`onBeforeRunSuite (file): ${filepath}`); const fileApp = this.fileAppMap.get(filepath); if (fileApp) { const { app, config } = fileApp; if (typeof app.mockContext === 'function' && app.ctxStorage) { const suiteCtx = app.mockContext(undefined, { mockCtxStorage: false, reuseCtxStorage: false, }); app.ctxStorage.enterWith(suiteCtx); let suiteScope: HeldScope | null = null; if (typeof suiteCtx.beginModuleScope === 'function') { suiteScope = await createHeldScope(suiteCtx); debugLog('suite held scope created'); } this.fileScopeMap.set(filepath, { app, config, suiteCtx, suiteScope }); debugLog('file suite scope created'); } } } await super.onBeforeRunSuite(suite); } async onAfterRunSuite(suite: Suite): Promise { if (isFileSuite(suite)) { const filepath = suite.filepath!; debugLog(`onAfterRunSuite (file): ${filepath}`); const fileState = this.fileScopeMap.get(filepath); if (fileState) { await releaseHeldScope(fileState.suiteScope); this.fileScopeMap.delete(filepath); } this.fileAppMap.delete(filepath); } await super.onAfterRunSuite(suite); } async onBeforeTryTask(test: Task, options?: { retry: number; repeats: number }): Promise { const filepath = getTaskFilepath(test); if (filepath) { const fileState = this.fileScopeMap.get(filepath); if (fileState) { // Release previous scope on retry to avoid leaks const existing = this.taskScopeMap.get(test.id); if (existing) { await releaseHeldScope(existing.testScope); } debugLog(`onBeforeTryTask: ${test.name} (retry=${options?.retry})`); const testCtx = fileState.app.mockContext!(undefined, { mockCtxStorage: false, reuseCtxStorage: false, }); fileState.app.ctxStorage!.enterWith(testCtx); let testScope: HeldScope | null = null; if (typeof testCtx.beginModuleScope === 'function') { testScope = await createHeldScope(testCtx); debugLog('test held scope created'); } this.taskScopeMap.set(test.id, { testScope, filepath }); } } await super.onBeforeTryTask(test); } async onAfterRunTask(test: Task): Promise { const taskState = this.taskScopeMap.get(test.id); if (taskState) { debugLog(`onAfterRunTask: ${test.name}`); await releaseHeldScope(taskState.testScope); this.taskScopeMap.delete(test.id); const fileState = this.fileScopeMap.get(taskState.filepath); if (fileState) { await restoreEggMocksIfNeeded(fileState.config.restoreMocks); // Restore suite context fileState.app.ctxStorage!.enterWith(fileState.suiteCtx); debugLog('restored suite context'); } } await super.onAfterRunTask(test); } } ================================================ FILE: core/vitest/src/shared.ts ================================================ import type { AsyncLocalStorage } from 'node:async_hooks'; import type { Application } from 'egg'; export type EggMockApp = Application & { // egg-mock ctx API ctxStorage?: { getStore?: () => any; enterWith?: (store: any) => void; } & AsyncLocalStorage; mockContext?: (data?: any, options?: any) => any; }; export interface TeggVitestAdapterOptions { /** * Resolve app instance. * Default: require('egg-mock/bootstrap').app */ getApp?: () => Promise | EggMockApp | undefined; /** * Restore mocks after each test. * Default: true (calls egg-mock.restore()) */ restoreMocks?: boolean; } export const DEBUG_ENABLED = process.env.DEBUG_TEGG_VITEST === '1'; export function debugLog(message: string, extra?: unknown) { if (!DEBUG_ENABLED) return; if (extra === undefined) { // eslint-disable-next-line no-console console.log(`[tegg-vitest] ${message}`); return; } // eslint-disable-next-line no-console console.log(`[tegg-vitest] ${message}`, extra); } export async function defaultGetApp(): Promise { // eslint-disable-next-line @typescript-eslint/no-var-requires const bootstrap = require('egg-mock/bootstrap'); return bootstrap?.app; } export async function restoreEggMocksIfNeeded(restoreMocks: boolean) { if (!restoreMocks) return; // eslint-disable-next-line @typescript-eslint/no-var-requires const eggMock = require('egg-mock'); const mm = eggMock?.default || eggMock; if (mm?.restore) { await mm.restore(); } } ================================================ FILE: core/vitest/test/fixture_app.test.ts ================================================ import assert from 'assert'; import path from 'path'; import mm from 'egg-mock'; import { createRequire } from 'module'; import { describe, beforeAll, afterAll, it } from 'vitest'; import { configureTeggRunner } from '../src'; const require = createRequire(import.meta.url); const { HelloService } = require( path.join(__dirname, 'fixtures/apps/demo-app/modules/demo-module/HelloService'), ); const app = mm.app({ baseDir: path.join(__dirname, 'fixtures/apps/demo-app'), framework: require.resolve('egg'), }); configureTeggRunner({ getApp() { return app as any; }, restoreMocks: false, }); describe('fixture demo app', () => { beforeAll(async () => { await app.ready(); }); afterAll(async () => { await app.close(); await mm.restore(); }); it('injects ctx and service', () => { const ctx = app.ctxStorage.getStore(); assert(ctx); assert.strictEqual(ctx.service.hello.sayHi('Ada'), 'hi Ada'); }); it('supports ctx.getEggObject()', async () => { const ctx = app.ctxStorage.getStore(); assert(ctx); const helloService = await ctx.getEggObject(HelloService); assert.strictEqual(helloService.sayHi('Ada'), 'hi Ada'); }); }); ================================================ FILE: core/vitest/test/fixtures/apps/demo-app/config/module.json ================================================ [ { "path": "../modules/demo-module" } ] ================================================ FILE: core/vitest/test/fixtures/apps/demo-app/modules/demo-module/HelloService.ts ================================================ import { AccessLevel, ContextProto } from '@eggjs/core-decorator'; @ContextProto({ accessLevel: AccessLevel.PUBLIC, }) export class HelloService { sayHi(name: string) { return `hi ${name}`; } } ================================================ FILE: core/vitest/test/fixtures/apps/demo-app/modules/demo-module/package.json ================================================ { "name": "demo-module", "eggModule": { "name": "demoModule" } } ================================================ FILE: core/vitest/test/fixtures/apps/demo-app/package.json ================================================ { "name": "demo-app", "version": "0.0.1", "private": true, "description": "tegg-vitest fixture app" } ================================================ FILE: core/vitest/test/get_app_throw.test.ts ================================================ import assert from 'assert'; import { describe, it, afterAll, vi } from 'vitest'; import { configureTeggRunner } from '../src'; let getAppCalls = 0; const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => { /* noop */ }); configureTeggRunner({ getApp() { getAppCalls += 1; throw new Error('boom'); }, restoreMocks: false, }); describe('getApp throw handling', () => { it('should not crash suite when getApp throws', () => { // The runner calls getApp in onBeforeRunSuite, so it should have been called assert(getAppCalls > 0); assert(warnSpy.mock.calls.length > 0); }); afterAll(() => { warnSpy.mockRestore(); }); }); ================================================ FILE: core/vitest/test/get_store_restore.test.ts ================================================ import assert from 'assert'; import path from 'path'; import mm from 'egg-mock'; import { describe, beforeAll, beforeEach, afterEach, afterAll, it } from 'vitest'; import { configureTeggRunner } from '../src'; const app = mm.app({ baseDir: path.join(__dirname, '../../..', 'plugin/tegg/test/fixtures/apps/egg-app'), framework: require.resolve('egg'), }); configureTeggRunner({ getApp() { return app as any; }, restoreMocks: false, }); describe('ctxStorage.getStore restore', () => { const getCtx = () => app.ctxStorage.getStore(); let suiteCtx: any; const testCtxList: any[] = []; const afterEachCtxList: any[] = []; beforeAll(() => { suiteCtx = getCtx(); assert(suiteCtx); }); beforeEach(() => { const current = getCtx(); testCtxList.push(current); assert(current); assert.notStrictEqual(current, suiteCtx); }); it('should have test context (1)', () => { const ctx = getCtx(); assert(ctx); assert.notStrictEqual(ctx, suiteCtx); }); it('should have test context (2)', () => { const ctx = getCtx(); assert(ctx); assert.notStrictEqual(ctx, suiteCtx); }); afterEach(() => { const current = getCtx(); afterEachCtxList.push(current); assert.strictEqual(current, testCtxList[afterEachCtxList.length - 1]); }); it('should not conflict with nested ctxStorage.run()', async () => { const outerCtx = getCtx(); assert(outerCtx); assert.notStrictEqual(outerCtx, suiteCtx); // Nested ctxStorage.run() should see its own store const nestedCtx = app.mockContext(undefined, { mockCtxStorage: false, reuseCtxStorage: false, }); await app.ctxStorage.run(nestedCtx, async () => { assert.strictEqual(getCtx(), nestedCtx); assert.notStrictEqual(getCtx(), outerCtx); }); // After nested run() returns, outer context is restored assert.strictEqual(getCtx(), outerCtx); }); it('should not conflict with concurrent ctxStorage.run()', async () => { const outerCtx = getCtx(); assert(outerCtx); await Promise.all([ app.ctxStorage.run( app.mockContext(undefined, { mockCtxStorage: false, reuseCtxStorage: false }), async () => { const innerCtx = getCtx(); assert(innerCtx); assert.notStrictEqual(innerCtx, outerCtx); }, ), app.ctxStorage.run( app.mockContext(undefined, { mockCtxStorage: false, reuseCtxStorage: false }), async () => { const innerCtx = getCtx(); assert(innerCtx); assert.notStrictEqual(innerCtx, outerCtx); }, ), ]); // After concurrent runs, outer context is restored assert.strictEqual(getCtx(), outerCtx); }); afterAll(async () => { try { // After all tests, suite context is restored assert.strictEqual(getCtx(), suiteCtx); assert.strictEqual(testCtxList.length, afterEachCtxList.length); testCtxList.forEach((ctx, index) => { assert.notStrictEqual(ctx, suiteCtx); assert.strictEqual(ctx, afterEachCtxList[index]); }); assert.notStrictEqual(testCtxList[0], testCtxList[1]); } finally { await app.close(); await mm.restore(); } }); }); ================================================ FILE: core/vitest/test/hooks.test.ts ================================================ import assert from 'assert'; import path from 'path'; import mm from 'egg-mock'; import { describe, beforeAll, afterAll, beforeEach, afterEach, it } from 'vitest'; import { configureTeggRunner } from '../src'; const app = mm.app({ baseDir: path.join(__dirname, '../../..', 'plugin/tegg/test/fixtures/apps/egg-app'), framework: require.resolve('egg'), }); configureTeggRunner({ getApp() { return app as any; }, restoreMocks: false, }); describe('vitest adapter ctx semantics', () => { const getCtx = () => app.ctxStorage.getStore(); let beforeCtx: any; let afterCtx: any; const beforeEachCtxList: Record = {}; const afterEachCtxList: Record = {}; const itCtxList: Record = {}; beforeAll(() => { beforeCtx = getCtx(); }); afterAll(async () => { try { afterCtx = getCtx(); assert(beforeCtx); assert(beforeCtx !== itCtxList.foo); assert(itCtxList.foo !== itCtxList.bar); assert.strictEqual(afterCtx, beforeCtx); assert.strictEqual(beforeEachCtxList.foo, afterEachCtxList.foo); assert.strictEqual(beforeEachCtxList.foo, itCtxList.foo); } finally { await app.close(); await mm.restore(); } }); describe('foo', () => { beforeEach(() => { beforeEachCtxList.foo = getCtx(); }); it('should work', () => { itCtxList.foo = getCtx(); }); afterEach(() => { afterEachCtxList.foo = getCtx(); }); }); describe('bar', () => { beforeEach(() => { beforeEachCtxList.bar = getCtx(); }); it('should work', () => { itCtxList.bar = getCtx(); }); afterEach(() => { afterEachCtxList.bar = getCtx(); }); }); describe('multi it', () => { const multiItCtxList: any[] = []; it('should work 1', () => { multiItCtxList.push(getCtx()); }); it('should work 2', () => { multiItCtxList.push(getCtx()); }); afterAll(() => { assert(multiItCtxList[0] !== multiItCtxList[1]); }); }); }); ================================================ FILE: core/vitest/test/setup.ts ================================================ import { createRequire } from 'module'; const require = createRequire(import.meta.url); const tsNodeRegister = 'ts-node/register/transpile-only'; const tsNodeRegisterPath = require.resolve(tsNodeRegister); if (!process.env.EGG_TYPESCRIPT) { process.env.EGG_TYPESCRIPT = 'true'; } const nodeOptions = process.env.NODE_OPTIONS ?? ''; const hasNodeOptions = nodeOptions.includes('ts-node/register') || nodeOptions.includes(tsNodeRegisterPath); if (!hasNodeOptions) { process.env.NODE_OPTIONS = `${nodeOptions} --require ${tsNodeRegister}`.trim(); } const execArgv = process.execArgv; const hasExecArgv = execArgv.includes(tsNodeRegister) || execArgv.includes(tsNodeRegisterPath); if (!hasExecArgv) { execArgv.push('--require', tsNodeRegister); } require(tsNodeRegisterPath); ================================================ FILE: core/vitest/tsconfig.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "dist", "baseUrl": "./" }, "exclude": [ "dist", "node_modules" ] } ================================================ FILE: core/vitest/tsconfig.pub.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "dist", "baseUrl": "./" }, "exclude": [ "dist", "node_modules", "test", "vitest.config.ts" ] } ================================================ FILE: core/vitest/vitest.config.ts ================================================ import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { defineConfig } from 'vitest/config'; const workspacePath = (rel: string) => path.resolve( fileURLToPath(new URL('.', import.meta.url)), rel, ); export default defineConfig({ resolve: { // Use array form so more specific subpath aliases win over package root aliases. alias: [ // In the tegg monorepo, many workspace packages point "main" to dist/ which doesn't exist in-source. // Alias to source entrypoints so Vitest/Vite can resolve them. // Important: subpath imports like "@eggjs/tegg-types/common" must resolve too. { find: /^@eggjs\/tegg-types\/(.*)$/, replacement: workspacePath('../types/$1') }, { find: '@eggjs/tegg-types', replacement: workspacePath('../types/index.ts') }, { find: '@eggjs/core-decorator', replacement: workspacePath('../core-decorator/index.ts') }, { find: '@eggjs/tegg-common-util', replacement: workspacePath('../common-util/index.ts') }, ], }, test: { environment: 'node', include: [ 'test/**/*.test.ts' ], // Register TS loader (ts-node) before tests so Egg can load .ts via Module._extensions. setupFiles: [ 'test/setup.ts' ], // Custom runner for tegg context injection via enterWith + held beginModuleScope. runner: './src/runner.ts', }, }); ================================================ FILE: lerna.json ================================================ { "useWorkspaces": true, "version": "3.78.15", "npmClientArgs": [ "--package-lock=false" ], "lerna": "1.6.4" } ================================================ FILE: package.json ================================================ { "name": "tegg", "version": "0.0.1", "description": "", "private": true, "homepage": "https://github.com/eggjs/tegg", "bugs": { "url": "https://github.com/eggjs/tegg/issues" }, "repository": { "type": "git", "url": "git@github.com:eggjs/tegg.git" }, "engines": { "node": ">=14.0.0" }, "scripts": { "lint:fix": "eslint . --ext .ts --fix", "lint": "eslint . --ext .ts", "test": "ut run test --workspaces --if-present", "bump": "lerna version --conventional-commits --sign-git-commit --sign-git-tag --force-publish", "pub": "lerna publish from-git", "pub-canary": "lerna publish --canary", "clean": "ut run clean --workspaces --if-present", "prepare-test": "ut run prepare-test --workspaces --if-present", "tsc": "ut run tsc --workspaces --if-present", "tsc:pub": "ut run tsc:pub --workspaces --if-present", "ci": "ut run prepare-test --if-present && ut run lint --if-present && ut run test --if-present" }, "author": "killagu ", "license": "MIT", "devDependencies": { "@eggjs/tsconfig": "^1.0.0", "@istanbuljs/nyc-config-typescript": "^1.0.1", "ajv": "^8.12.0", "eslint": "^8.0.0", "eslint-config-egg": "^12.0.0", "eslint-plugin-import": "^2.24.2", "lerna": "^6.1.0", "nyc": "^15.1.0", "source-map-support": "^0.5.16", "test-exclude": "^6.0.0" }, "workspaces": [ "core/*", "plugin/*", "standalone/*" ] } ================================================ FILE: plugin/ajv/CHANGELOG.md ================================================ # Change Log All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. ## [3.78.15](https://github.com/eggjs/tegg/compare/v3.78.14...v3.78.15) (2026-04-23) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.78.14](https://github.com/eggjs/tegg/compare/v3.78.13...v3.78.14) (2026-04-21) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.78.13](https://github.com/eggjs/tegg/compare/v3.78.12...v3.78.13) (2026-04-21) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.78.12](https://github.com/eggjs/tegg/compare/v3.78.11...v3.78.12) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.78.11](https://github.com/eggjs/tegg/compare/v3.78.10...v3.78.11) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.78.10](https://github.com/eggjs/tegg/compare/v3.78.9...v3.78.10) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.78.9](https://github.com/eggjs/tegg/compare/v3.78.8...v3.78.9) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.78.8](https://github.com/eggjs/tegg/compare/v3.78.7...v3.78.8) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.78.7](https://github.com/eggjs/tegg/compare/v3.78.6...v3.78.7) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.78.6](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.6) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.78.5](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.5) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.78.4](https://github.com/eggjs/tegg/compare/v3.78.3...v3.78.4) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.78.3](https://github.com/eggjs/tegg/compare/v3.78.2...v3.78.3) (2026-04-15) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.78.2](https://github.com/eggjs/tegg/compare/v3.78.1...v3.78.2) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.78.1](https://github.com/eggjs/tegg/compare/v3.78.0...v3.78.1) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin # [3.78.0](https://github.com/eggjs/tegg/compare/v3.77.2...v3.78.0) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.77.2](https://github.com/eggjs/tegg/compare/v3.77.1...v3.77.2) (2026-04-07) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.77.1](https://github.com/eggjs/tegg/compare/v3.77.0...v3.77.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin # [3.77.0](https://github.com/eggjs/tegg/compare/v3.76.1...v3.77.0) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.76.1](https://github.com/eggjs/tegg/compare/v3.76.0...v3.76.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin # [3.76.0](https://github.com/eggjs/tegg/compare/v3.75.1...v3.76.0) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.75.1](https://github.com/eggjs/tegg/compare/v3.75.0...v3.75.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin # [3.75.0](https://github.com/eggjs/tegg/compare/v3.74.0...v3.75.0) (2026-03-30) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin # [3.74.0](https://github.com/eggjs/tegg/compare/v3.73.0...v3.74.0) (2026-03-30) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin # [3.73.0](https://github.com/eggjs/tegg/compare/v3.72.0...v3.73.0) (2026-03-25) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin # [3.72.0](https://github.com/eggjs/tegg/compare/v3.71.2...v3.72.0) (2026-02-04) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.71.2](https://github.com/eggjs/tegg/compare/v3.71.1...v3.71.2) (2026-01-30) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.71.1](https://github.com/eggjs/tegg/compare/v3.71.0...v3.71.1) (2026-01-28) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin # [3.71.0](https://github.com/eggjs/tegg/compare/v3.70.1...v3.71.0) (2026-01-26) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.70.1](https://github.com/eggjs/tegg/compare/v3.70.0...v3.70.1) (2026-01-14) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin # [3.70.0](https://github.com/eggjs/tegg/compare/v3.69.0...v3.70.0) (2026-01-07) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin # [3.69.0](https://github.com/eggjs/tegg/compare/v3.68.0...v3.69.0) (2026-01-06) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin # [3.68.0](https://github.com/eggjs/tegg/compare/v3.67.2...v3.68.0) (2025-12-29) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.67.2](https://github.com/eggjs/tegg/compare/v3.67.1...v3.67.2) (2025-12-24) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.67.1](https://github.com/eggjs/tegg/compare/v3.67.0...v3.67.1) (2025-12-23) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin # [3.67.0](https://github.com/eggjs/tegg/compare/v3.66.0...v3.67.0) (2025-12-18) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin # [3.66.0](https://github.com/eggjs/tegg/compare/v3.65.3...v3.66.0) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.65.3](https://github.com/eggjs/tegg/compare/v3.65.2...v3.65.3) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.65.2](https://github.com/eggjs/tegg/compare/v3.65.1...v3.65.2) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.65.1](https://github.com/eggjs/tegg/compare/v3.65.0...v3.65.1) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin # [3.65.0](https://github.com/eggjs/tegg/compare/v3.64.5...v3.65.0) (2025-12-10) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.64.5](https://github.com/eggjs/tegg/compare/v3.64.4...v3.64.5) (2025-12-08) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.64.4](https://github.com/eggjs/tegg/compare/v3.64.3...v3.64.4) (2025-12-06) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.64.3](https://github.com/eggjs/tegg/compare/v3.64.2...v3.64.3) (2025-12-06) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.64.2](https://github.com/eggjs/tegg/compare/v3.64.1...v3.64.2) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.64.1](https://github.com/eggjs/tegg/compare/v3.64.0...v3.64.1) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin # [3.64.0](https://github.com/eggjs/tegg/compare/v3.63.2...v3.64.0) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.63.2](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.2) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.63.1](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.1) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin # [3.63.0](https://github.com/eggjs/tegg/compare/v3.62.3...v3.63.0) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.62.3](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.3) (2025-11-05) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.62.2](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.2) (2025-11-05) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.62.1](https://github.com/eggjs/tegg/compare/v3.62.0...v3.62.1) (2025-11-03) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin # [3.62.0](https://github.com/eggjs/tegg/compare/v3.61.0...v3.62.0) (2025-09-24) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin # [3.61.0](https://github.com/eggjs/tegg/compare/v3.60.3...v3.61.0) (2025-08-15) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.60.3](https://github.com/eggjs/tegg/compare/v3.60.2...v3.60.3) (2025-08-06) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.60.2](https://github.com/eggjs/tegg/compare/v3.60.1...v3.60.2) (2025-08-06) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.60.1](https://github.com/eggjs/tegg/compare/v3.60.0...v3.60.1) (2025-07-28) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin # [3.60.0](https://github.com/eggjs/tegg/compare/v3.59.1...v3.60.0) (2025-07-28) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.59.1](https://github.com/eggjs/tegg/compare/v3.59.0...v3.59.1) (2025-07-16) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin # [3.59.0](https://github.com/eggjs/tegg/compare/v3.58.0...v3.59.0) (2025-07-07) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin # [3.58.0](https://github.com/eggjs/tegg/compare/v3.57.14...v3.58.0) (2025-07-01) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.57.14](https://github.com/eggjs/tegg/compare/v3.57.13...v3.57.14) (2025-06-18) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.57.13](https://github.com/eggjs/tegg/compare/v3.57.12...v3.57.13) (2025-06-18) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.57.12](https://github.com/eggjs/tegg/compare/v3.57.11...v3.57.12) (2025-06-16) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.57.11](https://github.com/eggjs/tegg/compare/v3.57.10...v3.57.11) (2025-06-16) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.57.10](https://github.com/eggjs/tegg/compare/v3.57.9...v3.57.10) (2025-06-12) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.57.9](https://github.com/eggjs/tegg/compare/v3.57.8...v3.57.9) (2025-05-29) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.57.8](https://github.com/eggjs/tegg/compare/v3.57.7...v3.57.8) (2025-05-29) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.57.7](https://github.com/eggjs/tegg/compare/v3.57.6...v3.57.7) (2025-05-28) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.57.6](https://github.com/eggjs/tegg/compare/v3.57.5...v3.57.6) (2025-05-27) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.57.5](https://github.com/eggjs/tegg/compare/v3.57.4...v3.57.5) (2025-05-15) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.57.4](https://github.com/eggjs/tegg/compare/v3.57.3...v3.57.4) (2025-05-14) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.57.3](https://github.com/eggjs/tegg/compare/v3.57.2...v3.57.3) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.57.2](https://github.com/eggjs/tegg/compare/v3.57.1...v3.57.2) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.57.1](https://github.com/eggjs/tegg/compare/v3.57.0...v3.57.1) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin # [3.57.0](https://github.com/eggjs/tegg/compare/v3.56.3...v3.57.0) (2025-05-09) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.56.3](https://github.com/eggjs/tegg/compare/v3.56.2...v3.56.3) (2025-05-07) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.56.2](https://github.com/eggjs/tegg/compare/v3.56.1...v3.56.2) (2025-05-01) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.56.1](https://github.com/eggjs/tegg/compare/v3.56.0...v3.56.1) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin # [3.56.0](https://github.com/eggjs/tegg/compare/v3.55.0...v3.56.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin # [3.55.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.55.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin # [3.54.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.54.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin # [3.53.0](https://github.com/eggjs/tegg/compare/v3.52.1...v3.53.0) (2025-04-21) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.52.1](https://github.com/eggjs/tegg/compare/v3.52.0...v3.52.1) (2025-03-14) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin # [3.52.0](https://github.com/eggjs/tegg/compare/v3.51.2...v3.52.0) (2024-12-30) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.51.2](https://github.com/eggjs/tegg/compare/v3.51.1...v3.51.2) (2024-12-09) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.51.1](https://github.com/eggjs/tegg/compare/v3.51.0...v3.51.1) (2024-11-06) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin # [3.51.0](https://github.com/eggjs/tegg/compare/v3.50.1...v3.51.0) (2024-10-30) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.50.1](https://github.com/eggjs/tegg/compare/v3.50.0...v3.50.1) (2024-10-23) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin # [3.50.0](https://github.com/eggjs/tegg/compare/v3.49.0...v3.50.0) (2024-10-22) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin # [3.49.0](https://github.com/eggjs/tegg/compare/v3.48.1...v3.49.0) (2024-10-21) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.48.1](https://github.com/eggjs/tegg/compare/v3.48.0...v3.48.1) (2024-10-14) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin # [3.48.0](https://github.com/eggjs/tegg/compare/v3.47.2...v3.48.0) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.47.2](https://github.com/eggjs/tegg/compare/v3.47.1...v3.47.2) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.47.1](https://github.com/eggjs/tegg/compare/v3.47.0...v3.47.1) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin # [3.47.0](https://github.com/eggjs/tegg/compare/v3.46.4...v3.47.0) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.46.4](https://github.com/eggjs/tegg/compare/v3.46.3...v3.46.4) (2024-10-09) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.46.3](https://github.com/eggjs/tegg/compare/v3.46.2...v3.46.3) (2024-10-08) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.46.2](https://github.com/eggjs/tegg/compare/v3.46.1...v3.46.2) (2024-10-07) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.46.1](https://github.com/eggjs/tegg/compare/v3.46.0...v3.46.1) (2024-09-30) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin # [3.46.0](https://github.com/eggjs/tegg/compare/v3.45.0...v3.46.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin # [3.45.0](https://github.com/eggjs/tegg/compare/v3.44.1...v3.45.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.44.1](https://github.com/eggjs/tegg/compare/v3.44.0...v3.44.1) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin # [3.44.0](https://github.com/eggjs/tegg/compare/v3.43.2...v3.44.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.43.2](https://github.com/eggjs/tegg/compare/v3.43.1...v3.43.2) (2024-09-14) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.43.1](https://github.com/eggjs/tegg/compare/v3.43.0...v3.43.1) (2024-09-14) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin # [3.43.0](https://github.com/eggjs/tegg/compare/v3.42.0...v3.43.0) (2024-09-13) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin # [3.42.0](https://github.com/eggjs/tegg/compare/v3.41.0...v3.42.0) (2024-09-10) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin # [3.41.0](https://github.com/eggjs/tegg/compare/v3.40.1...v3.41.0) (2024-08-26) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.40.1](https://github.com/eggjs/tegg/compare/v3.40.0...v3.40.1) (2024-08-23) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin # [3.40.0](https://github.com/eggjs/tegg/compare/v3.39.5...v3.40.0) (2024-08-22) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.39.5](https://github.com/eggjs/tegg/compare/v3.39.4...v3.39.5) (2024-08-09) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.39.4](https://github.com/eggjs/tegg/compare/v3.39.3...v3.39.4) (2024-07-09) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.39.3](https://github.com/eggjs/tegg/compare/v3.39.2...v3.39.3) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.39.2](https://github.com/eggjs/tegg/compare/v3.39.1...v3.39.2) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.39.1](https://github.com/eggjs/tegg/compare/v3.39.0...v3.39.1) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin # [3.39.0](https://github.com/eggjs/tegg/compare/v3.38.0...v3.39.0) (2024-04-19) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin # [3.38.0](https://github.com/eggjs/tegg/compare/v3.37.3...v3.38.0) (2024-04-18) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.37.3](https://github.com/eggjs/tegg/compare/v3.37.2...v3.37.3) (2024-04-17) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.37.2](https://github.com/eggjs/tegg/compare/v3.37.1...v3.37.2) (2024-04-17) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.37.1](https://github.com/eggjs/tegg/compare/v3.37.0...v3.37.1) (2024-04-16) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin # [3.37.0](https://github.com/eggjs/tegg/compare/v3.36.3...v3.37.0) (2024-04-16) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.36.3](https://github.com/eggjs/tegg/compare/v3.36.2...v3.36.3) (2024-04-10) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.36.2](https://github.com/eggjs/tegg/compare/v3.36.1...v3.36.2) (2024-04-08) **Note:** Version bump only for package @eggjs/tegg-ajv-plugin ## [3.36.1](https://github.com/eggjs/tegg/compare/v3.36.0...v3.36.1) (2024-04-07) ### Bug Fixes * use @eggjs/ajv-keywords and @eggjs/ajv-formats ([#204](https://github.com/eggjs/tegg/issues/204)) ([31b02a0](https://github.com/eggjs/tegg/commit/31b02a08dac8bf27212fdb213a7d93b5b3a685ba)) # [3.36.0](https://github.com/eggjs/tegg/compare/v3.35.1...v3.36.0) (2024-04-02) ### Features * impl ajv + typebox Validator ([#201](https://github.com/eggjs/tegg/issues/201)) ([9fd585d](https://github.com/eggjs/tegg/commit/9fd585de9b613466c96b73494a08a494db34ea57)) ================================================ FILE: plugin/ajv/README.md ================================================ # @eggjs/tegg-ajv-plugin 参考 [egg-typebox-validate](https://github.com/xiekw2010/egg-typebox-validate) 的最佳实践,结合 ajv + typebox,只需要定义一次参数类型和规则,就能同时拥有参数校验和类型定义(完整的 ts 类型提示)。 ## egg 模式 ### Install ```shell # tegg 注解 npm i --save @eggjs/tegg # tegg 插件 npm i --save @eggjs/tegg-plugin # tegg ajv 插件 npm i --save @eggjs/tegg-ajv-plugin ``` ### Prepare ```json // tsconfig.json { "extends": "@eggjs/tsconfig" } ``` ### Config ```js // config/plugin.js exports.tegg = { package: '@eggjs/tegg-plugin', enable: true, }; exports.teggAjv = { package: '@eggjs/tegg-ajv-plugin', enable: true, }; ``` ## standalone 模式 ### Install ```shell # tegg 注解 npm i --save @eggjs/tegg # tegg ajv 插件 npm i --save @eggjs/tegg-ajv-plugin ``` ### Prepare ```json // tsconfig.json { "extends": "@eggjs/tsconfig" } ``` ## Usage 1、定义入参校验 Schema 使用 typebox 定义,会内置到 tegg 导出 ```ts import { Type, TransformEnum } from '@eggjs/tegg/ajv'; const SyncPackageTaskSchema = Type.Object({ fullname: Type.String({ transform: [ TransformEnum.trim ], maxLength: 100, }), tips: Type.String({ transform: [ TransformEnum.trim ], maxLength: 1024, }), skipDependencies: Type.Boolean(), syncDownloadData: Type.Boolean(), // force sync immediately, only allow by admin force: Type.Boolean(), // sync history version forceSyncHistory: Type.Boolean(), // source registry registryName: Type.Optional(Type.String()), }); ``` 2、从校验 Schema 生成静态的入参类型 ```ts import { Static } from '@eggjs/tegg/ajv'; type SyncPackageTaskType = Static; ``` 3、在 Controller 中使用入参类型和校验 Schema 注入全局单例 ajv,调用 `ajv.validate(XxxSchema, params)` 进行参数校验,参数校验失败会直接抛出 `AjvInvalidParamError` 异常, tegg 会自动返回相应的错误响应给客户端。 ```ts import { Inject, HTTPController, HTTPMethod, HTTPMethodEnum, HTTPBody } from '@eggjs/tegg'; import { Ajv, Type, Static, TransformEnum } from '@eggjs/tegg/ajv'; const SyncPackageTaskSchema = Type.Object({ fullname: Type.String({ transform: [ TransformEnum.trim ], maxLength: 100, }), tips: Type.String({ transform: [ TransformEnum.trim ], maxLength: 1024, }), skipDependencies: Type.Boolean(), syncDownloadData: Type.Boolean(), // force sync immediately, only allow by admin force: Type.Boolean(), // sync history version forceSyncHistory: Type.Boolean(), // source registry registryName: Type.Optional(Type.String()), }); type SyncPackageTaskType = Static; @HTTPController() export class HelloController { private readonly ajv: Ajv; @HTTPMethod({ method: HTTPMethodEnum.POST, path: '/sync', }) async sync(@HTTPBody() task: SyncPackageTaskType) { this.ajv.validate(SyncPackageTaskSchema, task); return { task, }; } } ``` ================================================ FILE: plugin/ajv/lib/Ajv.ts ================================================ import Ajv2019, { type Schema } from 'ajv/dist/2019'; import addFormats from '@eggjs/ajv-formats'; import keyWords from '@eggjs/ajv-keywords'; import { type Ajv as IAjv, AjvInvalidParamError } from '@eggjs/tegg/ajv'; import { SingletonProto, AccessLevel, LifecycleInit } from '@eggjs/tegg'; @SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) export class Ajv implements IAjv { static InvalidParamErrorClass = AjvInvalidParamError; #ajvInstance: Ajv2019; @LifecycleInit() protected _init() { this.#ajvInstance = new Ajv2019(); keyWords(this.#ajvInstance, 'transform'); addFormats(this.#ajvInstance, [ 'date-time', 'time', 'date', 'email', 'hostname', 'ipv4', 'ipv6', 'uri', 'uri-reference', 'uuid', 'uri-template', 'json-pointer', 'relative-json-pointer', 'regex', ]) .addKeyword('kind') .addKeyword('modifier'); } /** * Validate data with typebox Schema. * * If validate fail, with throw `Ajv.InvalidParamErrorClass` */ validate(schema: Schema, data: unknown): void { const result = this.#ajvInstance.validate(schema, data); if (!result) { throw new Ajv.InvalidParamErrorClass('Validation Failed', { errorData: data, currentSchema: JSON.stringify(schema), errors: this.#ajvInstance.errors!, }); } } } ================================================ FILE: plugin/ajv/package.json ================================================ { "name": "@eggjs/tegg-ajv-plugin", "eggPlugin": { "name": "teggAjv", "strict": false, "dependencies": [ "tegg" ] }, "eggModule": { "name": "teggAjv" }, "version": "3.78.15", "description": "ajv plugin for egg and tegg", "keywords": [ "egg", "plugin", "typescript", "module", "tegg", "ajv" ], "files": [ "lib/**/*.js", "lib/**/*.d.ts", "typings/*.d.ts" ], "types": "typings/index.d.ts", "scripts": { "test": "cross-env NODE_ENV=test NODE_OPTIONS='--no-deprecation' mocha", "clean": "tsc -b --clean", "tsc": "ut run clean && tsc -p ./tsconfig.json", "tsc:pub": "ut run clean && tsc -p ./tsconfig.pub.json", "prepublishOnly": "ut tsc:pub" }, "homepage": "https://github.com/eggjs/tegg", "bugs": { "url": "https://github.com/eggjs/tegg/issues" }, "repository": { "type": "git", "url": "git@github.com:eggjs/tegg.git", "directory": "plugin/ajv" }, "engines": { "node": ">=16.0.0" }, "dependencies": { "@eggjs/ajv-formats": "^3.0.1", "@eggjs/ajv-keywords": "^5.1.0", "@eggjs/tegg": "^3.78.15", "@sinclair/typebox": "^0.32.20", "ajv": "^8.12.0" }, "devDependencies": { "@eggjs/tegg-config": "^3.78.15", "@eggjs/tegg-controller-plugin": "^3.78.15", "@eggjs/tegg-plugin": "^3.78.15", "@types/mocha": "^10.0.1", "@types/node": "^20.2.4", "cross-env": "^7.0.3", "egg": "^3.9.1", "egg-mock": "^5.5.0", "mocha": "^10.2.0", "ts-node": "^10.9.1", "typescript": "^5.0.4" }, "publishConfig": { "access": "public" }, "gitHead": "240a3e9f40fda65ebb7589727d197db5ce17916c" } ================================================ FILE: plugin/ajv/test/ajv.test.ts ================================================ import assert from 'node:assert'; import path from 'node:path'; import mm, { MockApplication } from 'egg-mock'; describe('plugin/ajv/test/ajv.test.ts', () => { let app: MockApplication; afterEach(async () => { return mm.restore(); }); before(async () => { mm(process.env, 'EGG_TYPESCRIPT', true); mm(process, 'cwd', () => { return path.join(__dirname, '../'); }); app = mm.app({ baseDir: path.join(__dirname, './fixtures/apps/ajv-app'), framework: require.resolve('egg'), }); await app.ready(); }); after(() => { return app.close(); }); it('should throw AjvInvalidParamError', async () => { app.mockCsrf(); const res = await app.httpRequest() .post('/foo') .send({}); assert.equal(res.status, 500); assert.match(res.text, /AjvInvalidParamError: Validation Failed/); }); it('should pass', async () => { app.mockCsrf(); const res = await app.httpRequest() .post('/foo') .send({ fullname: 'fullname ', skipDependencies: false, }); assert.equal(res.status, 200); assert.deepEqual(res.body, { body: { fullname: 'fullname', skipDependencies: false, }, }); }); }); ================================================ FILE: plugin/ajv/test/fixtures/apps/ajv-app/config/config.default.js ================================================ module.exports = () => { const config = { keys: 'test key', security: { csrf: { ignoreJSON: false, }, }, }; return config; }; ================================================ FILE: plugin/ajv/test/fixtures/apps/ajv-app/config/module.json ================================================ [ { "path": "../modules/demo" }, { "package": "../../../../" } ] ================================================ FILE: plugin/ajv/test/fixtures/apps/ajv-app/config/plugin.js ================================================ exports.tracer = { package: 'egg-tracer', enable: true, }; exports.tegg = { package: '@eggjs/tegg-plugin', enable: true, }; exports.teggConfig = { package: '@eggjs/tegg-config', enable: true, }; exports.teggController = { package: '@eggjs/tegg-controller-plugin', enable: true, }; ================================================ FILE: plugin/ajv/test/fixtures/apps/ajv-app/modules/demo/FooController.ts ================================================ import { HTTPController, HTTPMethod, HTTPMethodEnum, Inject, HTTPBody, } from '@eggjs/tegg'; import { Ajv, Static, Type, TransformEnum } from '@eggjs/tegg/ajv'; const RequestBodySchema = Type.Object({ fullname: Type.String({ transform: [ TransformEnum.trim ], maxLength: 100, }), skipDependencies: Type.Boolean(), registryName: Type.Optional(Type.String()), }); type RequestBody = Static; @HTTPController() export class FooController { @Inject() private readonly ajv: Ajv; @HTTPMethod({ method: HTTPMethodEnum.POST, path: '/foo', }) async echo(@HTTPBody() body: RequestBody) { this.ajv.validate(RequestBodySchema, body); return { body, }; } } ================================================ FILE: plugin/ajv/test/fixtures/apps/ajv-app/modules/demo/module.yml ================================================ ================================================ FILE: plugin/ajv/test/fixtures/apps/ajv-app/modules/demo/package.json ================================================ { "name": "demo", "eggModule": { "name": "demo" } } ================================================ FILE: plugin/ajv/test/fixtures/apps/ajv-app/package.json ================================================ { "name": "ajv-app" } ================================================ FILE: plugin/ajv/tsconfig.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "baseUrl": "./" }, "exclude": [ "node_modules" ] } ================================================ FILE: plugin/ajv/tsconfig.pub.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "baseUrl": "./" }, "exclude": [ "node_modules", "test" ] } ================================================ FILE: plugin/ajv/typings/index.d.ts ================================================ import 'egg'; import '@eggjs/tegg-plugin'; import '@eggjs/tegg-config'; import '@eggjs/tegg-controller-plugin'; ================================================ FILE: plugin/aop/CHANGELOG.md ================================================ # Change Log All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. ## [3.78.15](https://github.com/eggjs/tegg/compare/v3.78.14...v3.78.15) (2026-04-23) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.78.14](https://github.com/eggjs/tegg/compare/v3.78.13...v3.78.14) (2026-04-21) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.78.13](https://github.com/eggjs/tegg/compare/v3.78.12...v3.78.13) (2026-04-21) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.78.12](https://github.com/eggjs/tegg/compare/v3.78.11...v3.78.12) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.78.11](https://github.com/eggjs/tegg/compare/v3.78.10...v3.78.11) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.78.10](https://github.com/eggjs/tegg/compare/v3.78.9...v3.78.10) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.78.9](https://github.com/eggjs/tegg/compare/v3.78.8...v3.78.9) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.78.8](https://github.com/eggjs/tegg/compare/v3.78.7...v3.78.8) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.78.7](https://github.com/eggjs/tegg/compare/v3.78.6...v3.78.7) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.78.6](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.6) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.78.5](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.5) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.78.4](https://github.com/eggjs/tegg/compare/v3.78.3...v3.78.4) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.78.3](https://github.com/eggjs/tegg/compare/v3.78.2...v3.78.3) (2026-04-15) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.78.2](https://github.com/eggjs/tegg/compare/v3.78.1...v3.78.2) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.78.1](https://github.com/eggjs/tegg/compare/v3.78.0...v3.78.1) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.78.0](https://github.com/eggjs/tegg/compare/v3.77.2...v3.78.0) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.77.2](https://github.com/eggjs/tegg/compare/v3.77.1...v3.77.2) (2026-04-07) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.77.1](https://github.com/eggjs/tegg/compare/v3.77.0...v3.77.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.77.0](https://github.com/eggjs/tegg/compare/v3.76.1...v3.77.0) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.76.1](https://github.com/eggjs/tegg/compare/v3.76.0...v3.76.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.76.0](https://github.com/eggjs/tegg/compare/v3.75.1...v3.76.0) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.75.1](https://github.com/eggjs/tegg/compare/v3.75.0...v3.75.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.75.0](https://github.com/eggjs/tegg/compare/v3.74.0...v3.75.0) (2026-03-30) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.74.0](https://github.com/eggjs/tegg/compare/v3.73.0...v3.74.0) (2026-03-30) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.73.0](https://github.com/eggjs/tegg/compare/v3.72.0...v3.73.0) (2026-03-25) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.72.0](https://github.com/eggjs/tegg/compare/v3.71.2...v3.72.0) (2026-02-04) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.71.2](https://github.com/eggjs/tegg/compare/v3.71.1...v3.71.2) (2026-01-30) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.71.1](https://github.com/eggjs/tegg/compare/v3.71.0...v3.71.1) (2026-01-28) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.71.0](https://github.com/eggjs/tegg/compare/v3.70.1...v3.71.0) (2026-01-26) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.70.1](https://github.com/eggjs/tegg/compare/v3.70.0...v3.70.1) (2026-01-14) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.70.0](https://github.com/eggjs/tegg/compare/v3.69.0...v3.70.0) (2026-01-07) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.69.0](https://github.com/eggjs/tegg/compare/v3.68.0...v3.69.0) (2026-01-06) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.68.0](https://github.com/eggjs/tegg/compare/v3.67.2...v3.68.0) (2025-12-29) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.67.2](https://github.com/eggjs/tegg/compare/v3.67.1...v3.67.2) (2025-12-24) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.67.1](https://github.com/eggjs/tegg/compare/v3.67.0...v3.67.1) (2025-12-23) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.67.0](https://github.com/eggjs/tegg/compare/v3.66.0...v3.67.0) (2025-12-18) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.66.0](https://github.com/eggjs/tegg/compare/v3.65.3...v3.66.0) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.65.3](https://github.com/eggjs/tegg/compare/v3.65.2...v3.65.3) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.65.2](https://github.com/eggjs/tegg/compare/v3.65.1...v3.65.2) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.65.1](https://github.com/eggjs/tegg/compare/v3.65.0...v3.65.1) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.65.0](https://github.com/eggjs/tegg/compare/v3.64.5...v3.65.0) (2025-12-10) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.64.5](https://github.com/eggjs/tegg/compare/v3.64.4...v3.64.5) (2025-12-08) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.64.4](https://github.com/eggjs/tegg/compare/v3.64.3...v3.64.4) (2025-12-06) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.64.3](https://github.com/eggjs/tegg/compare/v3.64.2...v3.64.3) (2025-12-06) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.64.2](https://github.com/eggjs/tegg/compare/v3.64.1...v3.64.2) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.64.1](https://github.com/eggjs/tegg/compare/v3.64.0...v3.64.1) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.64.0](https://github.com/eggjs/tegg/compare/v3.63.2...v3.64.0) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.63.2](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.2) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.63.1](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.1) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.63.0](https://github.com/eggjs/tegg/compare/v3.62.3...v3.63.0) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.62.3](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.3) (2025-11-05) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.62.2](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.2) (2025-11-05) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.62.1](https://github.com/eggjs/tegg/compare/v3.62.0...v3.62.1) (2025-11-03) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.62.0](https://github.com/eggjs/tegg/compare/v3.61.0...v3.62.0) (2025-09-24) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.61.0](https://github.com/eggjs/tegg/compare/v3.60.3...v3.61.0) (2025-08-15) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.60.3](https://github.com/eggjs/tegg/compare/v3.60.2...v3.60.3) (2025-08-06) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.60.2](https://github.com/eggjs/tegg/compare/v3.60.1...v3.60.2) (2025-08-06) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.60.1](https://github.com/eggjs/tegg/compare/v3.60.0...v3.60.1) (2025-07-28) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.60.0](https://github.com/eggjs/tegg/compare/v3.59.1...v3.60.0) (2025-07-28) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.59.1](https://github.com/eggjs/tegg/compare/v3.59.0...v3.59.1) (2025-07-16) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.59.0](https://github.com/eggjs/tegg/compare/v3.58.0...v3.59.0) (2025-07-07) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.58.0](https://github.com/eggjs/tegg/compare/v3.57.14...v3.58.0) (2025-07-01) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.57.14](https://github.com/eggjs/tegg/compare/v3.57.13...v3.57.14) (2025-06-18) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.57.13](https://github.com/eggjs/tegg/compare/v3.57.12...v3.57.13) (2025-06-18) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.57.12](https://github.com/eggjs/tegg/compare/v3.57.11...v3.57.12) (2025-06-16) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.57.11](https://github.com/eggjs/tegg/compare/v3.57.10...v3.57.11) (2025-06-16) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.57.10](https://github.com/eggjs/tegg/compare/v3.57.9...v3.57.10) (2025-06-12) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.57.9](https://github.com/eggjs/tegg/compare/v3.57.8...v3.57.9) (2025-05-29) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.57.8](https://github.com/eggjs/tegg/compare/v3.57.7...v3.57.8) (2025-05-29) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.57.7](https://github.com/eggjs/tegg/compare/v3.57.6...v3.57.7) (2025-05-28) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.57.6](https://github.com/eggjs/tegg/compare/v3.57.5...v3.57.6) (2025-05-27) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.57.5](https://github.com/eggjs/tegg/compare/v3.57.4...v3.57.5) (2025-05-15) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.57.4](https://github.com/eggjs/tegg/compare/v3.57.3...v3.57.4) (2025-05-14) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.57.3](https://github.com/eggjs/tegg/compare/v3.57.2...v3.57.3) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.57.2](https://github.com/eggjs/tegg/compare/v3.57.1...v3.57.2) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.57.1](https://github.com/eggjs/tegg/compare/v3.57.0...v3.57.1) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.57.0](https://github.com/eggjs/tegg/compare/v3.56.3...v3.57.0) (2025-05-09) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.56.3](https://github.com/eggjs/tegg/compare/v3.56.2...v3.56.3) (2025-05-07) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.56.2](https://github.com/eggjs/tegg/compare/v3.56.1...v3.56.2) (2025-05-01) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.56.1](https://github.com/eggjs/tegg/compare/v3.56.0...v3.56.1) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.56.0](https://github.com/eggjs/tegg/compare/v3.55.0...v3.56.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.55.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.55.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.54.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.54.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.53.0](https://github.com/eggjs/tegg/compare/v3.52.1...v3.53.0) (2025-04-21) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.52.1](https://github.com/eggjs/tegg/compare/v3.52.0...v3.52.1) (2025-03-14) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.52.0](https://github.com/eggjs/tegg/compare/v3.51.2...v3.52.0) (2024-12-30) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.51.2](https://github.com/eggjs/tegg/compare/v3.51.1...v3.51.2) (2024-12-09) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.51.1](https://github.com/eggjs/tegg/compare/v3.51.0...v3.51.1) (2024-11-06) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.51.0](https://github.com/eggjs/tegg/compare/v3.50.1...v3.51.0) (2024-10-30) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.50.1](https://github.com/eggjs/tegg/compare/v3.50.0...v3.50.1) (2024-10-23) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.50.0](https://github.com/eggjs/tegg/compare/v3.49.0...v3.50.0) (2024-10-22) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.49.0](https://github.com/eggjs/tegg/compare/v3.48.1...v3.49.0) (2024-10-21) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.48.1](https://github.com/eggjs/tegg/compare/v3.48.0...v3.48.1) (2024-10-14) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.48.0](https://github.com/eggjs/tegg/compare/v3.47.2...v3.48.0) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.47.2](https://github.com/eggjs/tegg/compare/v3.47.1...v3.47.2) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.47.1](https://github.com/eggjs/tegg/compare/v3.47.0...v3.47.1) (2024-10-10) ### Bug Fixes * fix aop in constructor inject type ([#247](https://github.com/eggjs/tegg/issues/247)) ([d169bb2](https://github.com/eggjs/tegg/commit/d169bb2fbbc86335315619866b4134a25296f552)) # [3.47.0](https://github.com/eggjs/tegg/compare/v3.46.4...v3.47.0) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.46.4](https://github.com/eggjs/tegg/compare/v3.46.3...v3.46.4) (2024-10-09) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.46.3](https://github.com/eggjs/tegg/compare/v3.46.2...v3.46.3) (2024-10-08) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.46.2](https://github.com/eggjs/tegg/compare/v3.46.1...v3.46.2) (2024-10-07) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.46.1](https://github.com/eggjs/tegg/compare/v3.46.0...v3.46.1) (2024-09-30) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.46.0](https://github.com/eggjs/tegg/compare/v3.45.0...v3.46.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.45.0](https://github.com/eggjs/tegg/compare/v3.44.1...v3.45.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.44.1](https://github.com/eggjs/tegg/compare/v3.44.0...v3.44.1) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.44.0](https://github.com/eggjs/tegg/compare/v3.43.2...v3.44.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.43.2](https://github.com/eggjs/tegg/compare/v3.43.1...v3.43.2) (2024-09-14) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.43.1](https://github.com/eggjs/tegg/compare/v3.43.0...v3.43.1) (2024-09-14) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.43.0](https://github.com/eggjs/tegg/compare/v3.42.0...v3.43.0) (2024-09-13) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.42.0](https://github.com/eggjs/tegg/compare/v3.41.0...v3.42.0) (2024-09-10) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.41.0](https://github.com/eggjs/tegg/compare/v3.40.1...v3.41.0) (2024-08-26) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.40.1](https://github.com/eggjs/tegg/compare/v3.40.0...v3.40.1) (2024-08-23) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.40.0](https://github.com/eggjs/tegg/compare/v3.39.5...v3.40.0) (2024-08-22) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.39.5](https://github.com/eggjs/tegg/compare/v3.39.4...v3.39.5) (2024-08-09) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.39.4](https://github.com/eggjs/tegg/compare/v3.39.3...v3.39.4) (2024-07-09) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.39.3](https://github.com/eggjs/tegg/compare/v3.39.2...v3.39.3) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.39.2](https://github.com/eggjs/tegg/compare/v3.39.1...v3.39.2) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.39.1](https://github.com/eggjs/tegg/compare/v3.39.0...v3.39.1) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.39.0](https://github.com/eggjs/tegg/compare/v3.38.0...v3.39.0) (2024-04-19) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.38.0](https://github.com/eggjs/tegg/compare/v3.37.3...v3.38.0) (2024-04-18) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.37.3](https://github.com/eggjs/tegg/compare/v3.37.2...v3.37.3) (2024-04-17) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.37.2](https://github.com/eggjs/tegg/compare/v3.37.1...v3.37.2) (2024-04-17) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.37.1](https://github.com/eggjs/tegg/compare/v3.37.0...v3.37.1) (2024-04-16) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.37.0](https://github.com/eggjs/tegg/compare/v3.36.3...v3.37.0) (2024-04-16) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.36.3](https://github.com/eggjs/tegg/compare/v3.36.2...v3.36.3) (2024-04-10) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.36.2](https://github.com/eggjs/tegg/compare/v3.36.1...v3.36.2) (2024-04-08) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.36.1](https://github.com/eggjs/tegg/compare/v3.36.0...v3.36.1) (2024-04-07) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.36.0](https://github.com/eggjs/tegg/compare/v3.35.1...v3.36.0) (2024-04-02) ### Features * impl ajv + typebox Validator ([#201](https://github.com/eggjs/tegg/issues/201)) ([9fd585d](https://github.com/eggjs/tegg/commit/9fd585de9b613466c96b73494a08a494db34ea57)) ## [3.35.1](https://github.com/eggjs/tegg/compare/v3.35.0...v3.35.1) (2024-03-26) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.35.0](https://github.com/eggjs/tegg/compare/v3.34.0...v3.35.0) (2024-03-26) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.34.0](https://github.com/eggjs/tegg/compare/v3.33.1...v3.34.0) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.33.1](https://github.com/eggjs/tegg/compare/v3.33.0...v3.33.1) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.33.0](https://github.com/eggjs/tegg/compare/v3.32.0...v3.33.0) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.32.0](https://github.com/eggjs/tegg/compare/v3.31.0...v3.32.0) (2024-02-19) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.31.0](https://github.com/eggjs/tegg/compare/v3.30.1...v3.31.0) (2024-01-31) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.30.1](https://github.com/eggjs/tegg/compare/v3.30.0...v3.30.1) (2024-01-25) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.30.0](https://github.com/eggjs/tegg/compare/v3.29.0...v3.30.0) (2024-01-17) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.29.0](https://github.com/eggjs/tegg/compare/v3.28.2...v3.29.0) (2023-12-26) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.28.2](https://github.com/eggjs/tegg/compare/v3.28.1...v3.28.2) (2023-12-12) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.28.1](https://github.com/eggjs/tegg/compare/v3.28.0...v3.28.1) (2023-12-11) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.28.0](https://github.com/eggjs/tegg/compare/v3.27.0...v3.28.0) (2023-12-10) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.27.0](https://github.com/eggjs/tegg/compare/v3.26.0...v3.27.0) (2023-11-23) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.26.0](https://github.com/eggjs/tegg/compare/v3.25.2...v3.26.0) (2023-11-17) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.25.2](https://github.com/eggjs/tegg/compare/v3.25.1...v3.25.2) (2023-11-06) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.25.1](https://github.com/eggjs/tegg/compare/v3.25.0...v3.25.1) (2023-11-03) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.25.0](https://github.com/eggjs/tegg/compare/v3.24.0...v3.25.0) (2023-11-03) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.24.0](https://github.com/eggjs/tegg/compare/v3.23.0...v3.24.0) (2023-10-26) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.23.0](https://github.com/eggjs/tegg/compare/v3.22.0...v3.23.0) (2023-09-20) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.20.0](https://github.com/eggjs/tegg/compare/v3.19.0...v3.20.0) (2023-09-07) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.19.0](https://github.com/eggjs/tegg/compare/v3.18.1...v3.19.0) (2023-08-31) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.18.1](https://github.com/eggjs/tegg/compare/v3.18.0...v3.18.1) (2023-08-29) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.18.0](https://github.com/eggjs/tegg/compare/v3.17.0...v3.18.0) (2023-08-29) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.17.0](https://github.com/eggjs/tegg/compare/v3.16.0...v3.17.0) (2023-08-24) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.16.0](https://github.com/eggjs/tegg/compare/v3.15.0...v3.16.0) (2023-08-24) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.14.3](https://github.com/eggjs/tegg/compare/v3.14.2...v3.14.3) (2023-08-14) ### Bug Fixes * fix aop plugin files ([#140](https://github.com/eggjs/tegg/issues/140)) ([f47eef6](https://github.com/eggjs/tegg/commit/f47eef634efd442ac5a8f68567e36c940247e48b)) ## [3.14.2](https://github.com/eggjs/tegg/compare/v3.14.1...v3.14.2) (2023-08-14) ### Bug Fixes * init all context advice if root proto miss ([#139](https://github.com/eggjs/tegg/issues/139)) ([0602ea8](https://github.com/eggjs/tegg/commit/0602ea81578bf717ee4b4c490ace8c1c133478c5)) ## [3.14.1](https://github.com/eggjs/tegg/compare/v3.14.0...v3.14.1) (2023-08-11) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.14.0](https://github.com/eggjs/tegg/compare/v3.13.0...v3.14.0) (2023-07-31) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.13.0](https://github.com/eggjs/tegg/compare/v3.12.0...v3.13.0) (2023-07-25) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.12.0](https://github.com/eggjs/tegg/compare/v3.11.1...v3.12.0) (2023-07-13) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.11.1](https://github.com/eggjs/tegg/compare/v3.11.0...v3.11.1) (2023-06-29) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.11.0](https://github.com/eggjs/tegg/compare/v3.10.0...v3.11.0) (2023-06-29) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.10.0](https://github.com/eggjs/tegg/compare/v3.9.0...v3.10.0) (2023-06-28) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.9.0](https://github.com/eggjs/tegg/compare/v3.8.0...v3.9.0) (2023-06-20) ### Features * implement advice params ([76ec8ad](https://github.com/eggjs/tegg/commit/76ec8ad7b7170a637e59d74d49c1f00d8a201321)) # [3.8.0](https://github.com/eggjs/tegg/compare/v3.7.0...v3.8.0) (2023-05-30) ### Features * impl EggObjectLifecycle hook in decorator ([#119](https://github.com/eggjs/tegg/issues/119)) ([cced8a2](https://github.com/eggjs/tegg/commit/cced8a2e009c33d5040fa21d00409fddef471b0e)) # [3.7.0](https://github.com/eggjs/tegg/compare/v3.6.3...v3.7.0) (2023-04-03) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.6.3](https://github.com/eggjs/tegg/compare/v3.6.2...v3.6.3) (2023-03-02) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.6.2](https://github.com/eggjs/tegg/compare/v3.6.1...v3.6.2) (2023-02-16) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.6.1](https://github.com/eggjs/tegg/compare/v3.6.0...v3.6.1) (2023-02-14) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.6.0](https://github.com/eggjs/tegg/compare/v3.5.2...v3.6.0) (2023-02-13) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.5.2](https://github.com/eggjs/tegg/compare/v3.5.1...v3.5.2) (2023-02-10) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.5.1](https://github.com/eggjs/tegg/compare/v3.5.0...v3.5.1) (2023-02-10) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.5.0](https://github.com/eggjs/tegg/compare/v3.4.1...v3.5.0) (2023-02-10) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.4.1](https://github.com/eggjs/tegg/compare/v3.4.0...v3.4.1) (2023-02-02) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.4.0](https://github.com/eggjs/tegg/compare/v3.3.4...v3.4.0) (2023-02-01) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.3.4](https://github.com/eggjs/tegg/compare/v3.3.3...v3.3.4) (2023-01-29) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.3.3](https://github.com/eggjs/tegg/compare/v3.3.2...v3.3.3) (2023-01-29) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.3.2](https://github.com/eggjs/tegg/compare/v3.3.1...v3.3.2) (2023-01-29) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.3.1](https://github.com/eggjs/tegg/compare/v3.3.0...v3.3.1) (2023-01-28) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.3.0](https://github.com/eggjs/tegg/compare/v3.2.4...v3.3.0) (2023-01-28) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.2.3](https://github.com/eggjs/tegg/compare/v3.2.2...v3.2.3) (2023-01-16) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.2.2](https://github.com/eggjs/tegg/compare/v3.2.1...v3.2.2) (2023-01-06) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.2.1](https://github.com/eggjs/tegg/compare/v3.2.0...v3.2.1) (2022-12-28) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.2.0](https://github.com/eggjs/tegg/compare/v3.1.0...v3.2.0) (2022-12-28) ### Features * impl mockModuleContextScope ([#73](https://github.com/eggjs/tegg/issues/73)) ([041881c](https://github.com/eggjs/tegg/commit/041881ca317ad81366172a35ac56b7b2dc0a0488)) # [3.1.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.1.0) (2022-12-27) ### Features * **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) # [3.0.0](https://github.com/eggjs/tegg/compare/v3.0.0-alpha.0...v3.0.0) (2022-12-26) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.0.0-alpha.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.0.0-alpha.0) (2022-12-22) ### Features * **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) ## [1.3.9](https://github.com/eggjs/tegg/compare/@eggjs/tegg-aop-plugin@1.3.8...@eggjs/tegg-aop-plugin@1.3.9) (2022-09-05) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [1.3.8](https://github.com/eggjs/tegg/compare/@eggjs/tegg-aop-plugin@1.3.7...@eggjs/tegg-aop-plugin@1.3.8) (2022-09-04) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [1.3.7](https://github.com/eggjs/tegg/compare/@eggjs/tegg-aop-plugin@1.3.6...@eggjs/tegg-aop-plugin@1.3.7) (2022-08-24) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [1.3.6](https://github.com/eggjs/tegg/compare/@eggjs/tegg-aop-plugin@1.3.5...@eggjs/tegg-aop-plugin@1.3.6) (2022-08-16) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [1.3.4](https://github.com/eggjs/tegg/compare/@eggjs/tegg-aop-plugin@1.3.3...@eggjs/tegg-aop-plugin@1.3.4) (2022-07-28) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [1.3.3](https://github.com/eggjs/tegg/compare/@eggjs/tegg-aop-plugin@1.3.2...@eggjs/tegg-aop-plugin@1.3.3) (2022-07-20) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [1.3.2](https://github.com/eggjs/tegg/compare/@eggjs/tegg-aop-plugin@1.3.1...@eggjs/tegg-aop-plugin@1.3.2) (2022-07-20) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [1.3.0](https://github.com/eggjs/tegg/compare/v1.2.0...v1.3.0) (2022-07-01) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [0.2.0](https://github.com/eggjs/tegg/compare/v0.1.19...v0.2.0) (2022-01-20) ### Features * impl aop ([c53df00](https://github.com/eggjs/tegg/commit/c53df001d1455a0a105689694775d880541d9d2f)) ================================================ FILE: plugin/aop/README.md ================================================ # @eggjs/tegg-aop-plugin ## Usage ```js // plugin.js export.aopModule = { enable: true, package: '@eggjs/tegg-aop-plugin', }; ``` ## Advice 使用 `@Advice` 注解来申明一个实现,可以用来监听、拦截方法执行。 **注意:Advice 也是一种 Prototype,可以通过 initType 来指定不同的生命周期。** ```ts import { Advice, IAdvice } from '@eggjs/tegg/aop'; @Advice() export class AdviceExample implements IAdvice { // Advice 中可以正常的注入其他的对象 @Inject() private readonly callTrace: CallTrace; // 在函数执行前执行 async beforeCall(ctx: AdviceContext): Promise { // ... } // 在函数成功后执行 async afterReturn(ctx: AdviceContext, result: any): Promise { // ... } // 在函数成功后执行 async afterThrow(ctx: AdviceContext, error: Error): Promise { // ... } // 在函数退出时执行 async afterFinally(ctx: AdviceContext): Promise { } // 类似 koa 中间件的模式 // block = next async around(ctx: AdviceContext, next: () => Promise): Promise { } } ``` ## Pointcut 使用 `@Pointcut` 在某个类特定的方法上申明一个 `Advice` ```ts import { Pointcut } from '@eggjs/tegg/aop'; import { ContextProto } from '@eggjs/tegg'; @ContextProto() export class Hello { // 创建 Hello.hello 的切面 AdviceExample,并传递 adviceParams 给 AdviceExample // AdviceExample 的切面函数可以通过 ctx.adviceParams 拿到注解传入的参数 @Pointcut(AdviceExample, { adviceParams: { foo: 'bar' } }) async hello(name: string) { return `hello ${name}`; } } ``` ## Crosscut 使用 `@Crosscut` 来声明一个通用的 `Advice`,有三种模式 - 指定类和方法 - 通过正则指定类和方法 - 通过回调来指定类和方法 **注意:egg 中的对象无法被 Crosscut 指定到。** ```ts import { Crosscut, Advice, IAdvice } from '@eggjs/tegg/aop'; // 通过类型来指定 // 创建 CrosscutClassAdviceExample.hello 的切面 CrosscutExample,并传递 adviceParams 给 CrosscutExample // CrosscutExample 的切面函数可以通过 ctx.adviceParams 拿到注解传入的参数 @Crosscut({ type: PointcutType.CLASS, clazz: CrosscutExample, methodName: 'hello', }, { adviceParams: { foo: 'bar' } }) @Advice() export class CrosscutClassAdviceExample implements IAdvice { } // 通过正则来指定 @Crosscut({ type: PointcutType.NAME, className: /crosscut.*/i, methodName: /hello/, }) @Advice() export class CrosscutNameAdviceExample implements IAdvice { } // 通过回调来指定 @Crosscut({ type: PointcutType.CUSTOM, callback: (clazz: EggProtoImplClass, method: PropertyKey) => { return clazz === CrosscutExample && method === 'hello'; } }) @Advice() ****export class CrosscutCustomAdviceExample implements IAdvice { } ``` ================================================ FILE: plugin/aop/app.ts ================================================ import { Application } from 'egg'; import { CrosscutAdviceFactory } from '@eggjs/tegg/aop'; import { crossCutGraphHook, EggObjectAopHook, EggPrototypeCrossCutHook, LoadUnitAopHook, pointCutGraphHook, } from '@eggjs/tegg-aop-runtime'; import { AopContextHook } from './lib/AopContextHook'; import { GlobalGraph } from '@eggjs/tegg-metadata'; export default class AopAppHook { private readonly app: Application; private readonly crosscutAdviceFactory: CrosscutAdviceFactory; private readonly loadUnitAopHook: LoadUnitAopHook; private readonly eggPrototypeCrossCutHook: EggPrototypeCrossCutHook; private readonly eggObjectAopHook: EggObjectAopHook; private aopContextHook: AopContextHook; constructor(app: Application) { this.app = app; this.crosscutAdviceFactory = new CrosscutAdviceFactory(); this.loadUnitAopHook = new LoadUnitAopHook(this.crosscutAdviceFactory); this.eggPrototypeCrossCutHook = new EggPrototypeCrossCutHook(this.crosscutAdviceFactory); this.eggObjectAopHook = new EggObjectAopHook(); } configDidLoad() { this.app.eggPrototypeLifecycleUtil.registerLifecycle(this.eggPrototypeCrossCutHook); this.app.loadUnitLifecycleUtil.registerLifecycle(this.loadUnitAopHook); this.app.eggObjectLifecycleUtil.registerLifecycle(this.eggObjectAopHook); GlobalGraph.instance!.registerBuildHook(crossCutGraphHook); GlobalGraph.instance!.registerBuildHook(pointCutGraphHook); } async didLoad() { await this.app.moduleHandler.ready(); this.aopContextHook = new AopContextHook(this.app.moduleHandler); this.app.eggContextLifecycleUtil.registerLifecycle(this.aopContextHook); } beforeClose() { this.app.eggPrototypeLifecycleUtil.deleteLifecycle(this.eggPrototypeCrossCutHook); this.app.loadUnitLifecycleUtil.deleteLifecycle(this.loadUnitAopHook); this.app.eggObjectLifecycleUtil.deleteLifecycle(this.eggObjectAopHook); this.app.eggContextLifecycleUtil.deleteLifecycle(this.aopContextHook); } } ================================================ FILE: plugin/aop/lib/AopContextHook.ts ================================================ import type { Application } from 'egg'; import type { EggContext, EggContextLifecycleContext } from '@eggjs/tegg-runtime'; import type { EggProtoImplClass, LifecycleHook } from '@eggjs/tegg'; import { PrototypeUtil, ObjectInitType } from '@eggjs/tegg'; import { AspectInfoUtil } from '@eggjs/aop-decorator'; import { EggPrototype, TeggError } from '@eggjs/tegg-metadata'; import { ROOT_PROTO } from '@eggjs/egg-module-common'; export interface EggPrototypeWithClazz extends EggPrototype { clazz?: EggProtoImplClass; } export interface ProtoToCreate { name: string; proto: EggPrototype; } export class AopContextHook implements LifecycleHook { private readonly moduleHandler: Application['moduleHandler']; private requestProtoList: Array = []; constructor(moduleHandler: Application['moduleHandler']) { this.moduleHandler = moduleHandler; for (const loadUnitInstance of this.moduleHandler.loadUnitInstances) { const iterator = loadUnitInstance.loadUnit.iterateEggPrototype(); for (const proto of iterator) { const protoWithClazz = proto as EggPrototypeWithClazz; const clazz = protoWithClazz.clazz; if (!clazz) continue; const aspects = AspectInfoUtil.getAspectList(clazz); for (const aspect of aspects) { for (const advice of aspect.adviceList) { const adviceProto = PrototypeUtil.getClazzProto(advice.clazz) as EggPrototype | undefined; if (!adviceProto) { throw TeggError.create(`Aop Advice(${advice.clazz.name}) not found in loadUnits`, 'advice_not_found'); } if (adviceProto.initType === ObjectInitType.CONTEXT) { this.requestProtoList.push({ name: advice.name, proto: adviceProto, }); } } } } } } async preCreate(_, ctx: EggContext): Promise { // compatible with egg controller // add context aspect to ctx if (!ctx.get(ROOT_PROTO)) { for (const proto of this.requestProtoList) { ctx.addProtoToCreate(proto.name, proto.proto); } } } } ================================================ FILE: plugin/aop/package.json ================================================ { "name": "@eggjs/tegg-aop-plugin", "version": "3.78.15", "eggPlugin": { "name": "aopModule", "dependencies": [ "tegg" ] }, "types": "typings/index.d.ts", "description": "tegg aop plugin", "keywords": [ "egg", "typescript", "decorator", "aop", "tegg" ], "files": [ "app.js", "app.d.ts", "lib/**/*.js", "lib/**/*.d.ts" ], "scripts": { "test": "cross-env NODE_ENV=test NODE_OPTIONS='--no-deprecation' mocha", "clean": "tsc -b --clean", "tsc": "ut run clean && tsc -p ./tsconfig.json", "tsc:pub": "ut run clean && tsc -p ./tsconfig.pub.json", "prepublishOnly": "ut tsc:pub" }, "homepage": "https://github.com/eggjs/tegg", "bugs": { "url": "https://github.com/eggjs/tegg/issues" }, "repository": { "type": "git", "url": "git@github.com:eggjs/tegg.git", "directory": "plugin/aop" }, "engines": { "node": ">=14.0.0" }, "dependencies": { "@eggjs/aop-decorator": "^3.78.15", "@eggjs/egg-module-common": "^3.78.15", "@eggjs/tegg": "^3.78.15", "@eggjs/tegg-aop-runtime": "^3.78.15", "@eggjs/tegg-metadata": "^3.78.15" }, "devDependencies": { "@eggjs/tegg-config": "^3.78.15", "@eggjs/tegg-plugin": "^3.78.15", "@types/mocha": "^10.0.1", "@types/node": "^20.2.4", "cross-env": "^7.0.3", "egg": "^3.9.1", "egg-mock": "^5.5.0", "mocha": "^10.2.0", "ts-node": "^10.9.1", "typescript": "^5.0.4" }, "publishConfig": { "access": "public" }, "gitHead": "240a3e9f40fda65ebb7589727d197db5ce17916c" } ================================================ FILE: plugin/aop/test/aop.test.ts ================================================ import mm from 'egg-mock'; import path from 'path'; import assert from 'assert'; describe('plugin/aop/test/aop.test.ts', () => { let app; after(async () => { await app.close(); }); afterEach(() => { mm.restore(); }); before(async () => { mm(process.env, 'EGG_TYPESCRIPT', true); mm(process, 'cwd', () => { return path.join(__dirname, '..'); }); app = mm.app({ baseDir: path.join(__dirname, 'fixtures/apps/aop-app'), framework: require.resolve('egg'), }); await app.ready(); }); it('module aop should work', async () => { app.mockCsrf(); const res = await app.httpRequest() .get('/aop') .expect(200); assert.deepStrictEqual(res.body, { msg: 'withCrossAroundResult(withPointAroundResult(hello withPointAroundParam(withCrosscutAroundParam(foo))))', }); }); it('module aop should work', async () => { app.mockCsrf(); const res = await app.httpRequest() .get('/singletonAop') .expect(200); assert.deepStrictEqual(res.body, { msg: 'withContextPointAroundResult(hello withContextPointAroundParam(foo))', }); }); }); ================================================ FILE: plugin/aop/test/fixtures/apps/aop-app/app/controller/app.ts ================================================ import { Controller } from 'egg'; import { Hello } from '../../modules/aop-module/Hello'; export default class App extends Controller { async aop() { const hello: Hello = await (this.ctx.module as any).aopModule.hello; const msg = await hello.hello('foo'); this.ctx.status = 200; this.ctx.body = { msg }; } async contextAdviceWithSingleton() { const hello: Hello = await (this.app.module as any).aopModule.singletonHello; const msg = await hello.hello('foo'); this.ctx.status = 200; this.ctx.body = { msg }; } } ================================================ FILE: plugin/aop/test/fixtures/apps/aop-app/app/router.ts ================================================ import { Application } from 'egg'; module.exports = (app: Application) => { app.router.get('/aop', app.controller.app.aop); app.router.get('/singletonAop', app.controller.app.contextAdviceWithSingleton); }; ================================================ FILE: plugin/aop/test/fixtures/apps/aop-app/app/typings/index.d.ts ================================================ import 'egg'; import TraceService from '../../modules/multi-module-service/TraceService'; import AppService from '../../modules/multi-module-service/AppService'; declare module 'egg' { export interface EggModule { multiModuleService: { traceService: TraceService; appService: AppService; } } } ================================================ FILE: plugin/aop/test/fixtures/apps/aop-app/config/config.default.js ================================================ 'use strict'; const path = require('path'); module.exports = function(appInfo) { const config = { keys: 'test key', customLogger: { xxLogger: { file: path.join(appInfo.root, 'logs/xx.log'), }, }, security: { csrf: { ignoreJSON: false, } }, }; return config; }; ================================================ FILE: plugin/aop/test/fixtures/apps/aop-app/config/module.json ================================================ [ { "path": "../modules/aop-module" } ] ================================================ FILE: plugin/aop/test/fixtures/apps/aop-app/config/plugin.js ================================================ 'use strict'; exports.tegg = { package: '@eggjs/tegg-plugin', enable: true, }; exports.teggConfig = { package: '@eggjs/tegg-config', enable: true, }; exports.watcher = false; ================================================ FILE: plugin/aop/test/fixtures/apps/aop-app/modules/aop-module/Hello.ts ================================================ import { AccessLevel, ContextProto, Inject, SingletonProto } from '@eggjs/tegg'; import { Advice, AdviceContext, Crosscut, IAdvice, Pointcut, PointcutType } from '@eggjs/tegg/aop'; import { EggLogger } from 'egg'; @Advice() export class PointcutAdvice implements IAdvice { async around(ctx: AdviceContext, next: () => Promise): Promise { ctx.args[0] = `withPointAroundParam(${ctx.args[0]})`; const result = await next(); return `withPointAroundResult(${result})`; } } @ContextProto({ accessLevel: AccessLevel.PUBLIC, }) export class Hello { id = 233; @Inject() logger: EggLogger; @Pointcut(PointcutAdvice) async hello(name: string) { return `hello ${name}`; } async helloEggObjectAop() { this.logger.info('foo'); } } @Crosscut({ type: PointcutType.CLASS, clazz: Hello, methodName: 'hello', }) @Advice() export class CrosscutAdvice implements IAdvice { async around(ctx: AdviceContext, block: () => Promise): Promise { ctx.args[0] = `withCrosscutAroundParam(${ctx.args[0]})`; const result = await block(); return `withCrossAroundResult(${result})`; } } @Advice() export class ContextPointcutAdvice implements IAdvice { async around(ctx: AdviceContext, next: () => Promise): Promise { ctx.args[0] = `withContextPointAroundParam(${ctx.args[0]})`; const result = await next(); return `withContextPointAroundResult(${result})`; } } @SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) export class SingletonHello { id = 233; @Inject() logger: EggLogger; @Pointcut(ContextPointcutAdvice) async hello(name: string) { return `hello ${name}`; } async helloEggObjectAop() { this.logger.info('foo'); } } ================================================ FILE: plugin/aop/test/fixtures/apps/aop-app/modules/aop-module/package.json ================================================ { "name": "aop-module", "eggModule": { "name": "aopModule" } } ================================================ FILE: plugin/aop/test/fixtures/apps/aop-app/package.json ================================================ { "name": "egg-app" } ================================================ FILE: plugin/aop/tsconfig.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "baseUrl": "./" }, "exclude": [ "node_modules", "test" ] } ================================================ FILE: plugin/aop/tsconfig.pub.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "baseUrl": "./" }, "exclude": [ "node_modules", "test" ] } ================================================ FILE: plugin/aop/typings/index.d.ts ================================================ import 'egg'; import '@eggjs/tegg-plugin'; ================================================ FILE: plugin/common/CHANGELOG.md ================================================ # Change Log All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. ## [3.78.15](https://github.com/eggjs/tegg/compare/v3.78.14...v3.78.15) (2026-04-23) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.78.14](https://github.com/eggjs/tegg/compare/v3.78.13...v3.78.14) (2026-04-21) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.78.13](https://github.com/eggjs/tegg/compare/v3.78.12...v3.78.13) (2026-04-21) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.78.12](https://github.com/eggjs/tegg/compare/v3.78.11...v3.78.12) (2026-04-18) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.78.11](https://github.com/eggjs/tegg/compare/v3.78.10...v3.78.11) (2026-04-18) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.78.10](https://github.com/eggjs/tegg/compare/v3.78.9...v3.78.10) (2026-04-18) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.78.9](https://github.com/eggjs/tegg/compare/v3.78.8...v3.78.9) (2026-04-18) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.78.8](https://github.com/eggjs/tegg/compare/v3.78.7...v3.78.8) (2026-04-18) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.78.7](https://github.com/eggjs/tegg/compare/v3.78.6...v3.78.7) (2026-04-18) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.78.6](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.6) (2026-04-18) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.78.5](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.5) (2026-04-18) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.78.4](https://github.com/eggjs/tegg/compare/v3.78.3...v3.78.4) (2026-04-18) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.78.3](https://github.com/eggjs/tegg/compare/v3.78.2...v3.78.3) (2026-04-15) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.78.2](https://github.com/eggjs/tegg/compare/v3.78.1...v3.78.2) (2026-04-09) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.78.1](https://github.com/eggjs/tegg/compare/v3.78.0...v3.78.1) (2026-04-09) **Note:** Version bump only for package @eggjs/egg-module-common # [3.78.0](https://github.com/eggjs/tegg/compare/v3.77.2...v3.78.0) (2026-04-09) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.77.2](https://github.com/eggjs/tegg/compare/v3.77.1...v3.77.2) (2026-04-07) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.77.1](https://github.com/eggjs/tegg/compare/v3.77.0...v3.77.1) (2026-04-01) **Note:** Version bump only for package @eggjs/egg-module-common # [3.77.0](https://github.com/eggjs/tegg/compare/v3.76.1...v3.77.0) (2026-04-01) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.76.1](https://github.com/eggjs/tegg/compare/v3.76.0...v3.76.1) (2026-04-01) **Note:** Version bump only for package @eggjs/egg-module-common # [3.76.0](https://github.com/eggjs/tegg/compare/v3.75.1...v3.76.0) (2026-04-01) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.75.1](https://github.com/eggjs/tegg/compare/v3.75.0...v3.75.1) (2026-04-01) **Note:** Version bump only for package @eggjs/egg-module-common # [3.75.0](https://github.com/eggjs/tegg/compare/v3.74.0...v3.75.0) (2026-03-30) **Note:** Version bump only for package @eggjs/egg-module-common # [3.74.0](https://github.com/eggjs/tegg/compare/v3.73.0...v3.74.0) (2026-03-30) **Note:** Version bump only for package @eggjs/egg-module-common # [3.73.0](https://github.com/eggjs/tegg/compare/v3.72.0...v3.73.0) (2026-03-25) **Note:** Version bump only for package @eggjs/egg-module-common # [3.72.0](https://github.com/eggjs/tegg/compare/v3.71.2...v3.72.0) (2026-02-04) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.71.2](https://github.com/eggjs/tegg/compare/v3.71.1...v3.71.2) (2026-01-30) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.71.1](https://github.com/eggjs/tegg/compare/v3.71.0...v3.71.1) (2026-01-28) **Note:** Version bump only for package @eggjs/egg-module-common # [3.71.0](https://github.com/eggjs/tegg/compare/v3.70.1...v3.71.0) (2026-01-26) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.70.1](https://github.com/eggjs/tegg/compare/v3.70.0...v3.70.1) (2026-01-14) **Note:** Version bump only for package @eggjs/egg-module-common # [3.70.0](https://github.com/eggjs/tegg/compare/v3.69.0...v3.70.0) (2026-01-07) **Note:** Version bump only for package @eggjs/egg-module-common # [3.69.0](https://github.com/eggjs/tegg/compare/v3.68.0...v3.69.0) (2026-01-06) **Note:** Version bump only for package @eggjs/egg-module-common # [3.68.0](https://github.com/eggjs/tegg/compare/v3.67.2...v3.68.0) (2025-12-29) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.67.2](https://github.com/eggjs/tegg/compare/v3.67.1...v3.67.2) (2025-12-24) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.67.1](https://github.com/eggjs/tegg/compare/v3.67.0...v3.67.1) (2025-12-23) **Note:** Version bump only for package @eggjs/egg-module-common # [3.67.0](https://github.com/eggjs/tegg/compare/v3.66.0...v3.67.0) (2025-12-18) **Note:** Version bump only for package @eggjs/egg-module-common # [3.66.0](https://github.com/eggjs/tegg/compare/v3.65.3...v3.66.0) (2025-12-16) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.65.3](https://github.com/eggjs/tegg/compare/v3.65.2...v3.65.3) (2025-12-16) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.65.2](https://github.com/eggjs/tegg/compare/v3.65.1...v3.65.2) (2025-12-16) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.65.1](https://github.com/eggjs/tegg/compare/v3.65.0...v3.65.1) (2025-12-16) **Note:** Version bump only for package @eggjs/egg-module-common # [3.65.0](https://github.com/eggjs/tegg/compare/v3.64.5...v3.65.0) (2025-12-10) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.64.5](https://github.com/eggjs/tegg/compare/v3.64.4...v3.64.5) (2025-12-08) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.64.4](https://github.com/eggjs/tegg/compare/v3.64.3...v3.64.4) (2025-12-06) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.64.3](https://github.com/eggjs/tegg/compare/v3.64.2...v3.64.3) (2025-12-06) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.64.2](https://github.com/eggjs/tegg/compare/v3.64.1...v3.64.2) (2025-11-27) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.64.1](https://github.com/eggjs/tegg/compare/v3.64.0...v3.64.1) (2025-11-27) **Note:** Version bump only for package @eggjs/egg-module-common # [3.64.0](https://github.com/eggjs/tegg/compare/v3.63.2...v3.64.0) (2025-11-27) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.63.2](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.2) (2025-11-18) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.63.1](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.1) (2025-11-18) **Note:** Version bump only for package @eggjs/egg-module-common # [3.63.0](https://github.com/eggjs/tegg/compare/v3.62.3...v3.63.0) (2025-11-18) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.62.3](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.3) (2025-11-05) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.62.2](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.2) (2025-11-05) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.62.1](https://github.com/eggjs/tegg/compare/v3.62.0...v3.62.1) (2025-11-03) **Note:** Version bump only for package @eggjs/egg-module-common # [3.62.0](https://github.com/eggjs/tegg/compare/v3.61.0...v3.62.0) (2025-09-24) **Note:** Version bump only for package @eggjs/egg-module-common # [3.61.0](https://github.com/eggjs/tegg/compare/v3.60.3...v3.61.0) (2025-08-15) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.60.3](https://github.com/eggjs/tegg/compare/v3.60.2...v3.60.3) (2025-08-06) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.60.2](https://github.com/eggjs/tegg/compare/v3.60.1...v3.60.2) (2025-08-06) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.60.1](https://github.com/eggjs/tegg/compare/v3.60.0...v3.60.1) (2025-07-28) **Note:** Version bump only for package @eggjs/egg-module-common # [3.60.0](https://github.com/eggjs/tegg/compare/v3.59.1...v3.60.0) (2025-07-28) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.59.1](https://github.com/eggjs/tegg/compare/v3.59.0...v3.59.1) (2025-07-16) **Note:** Version bump only for package @eggjs/egg-module-common # [3.59.0](https://github.com/eggjs/tegg/compare/v3.58.0...v3.59.0) (2025-07-07) **Note:** Version bump only for package @eggjs/egg-module-common # [3.58.0](https://github.com/eggjs/tegg/compare/v3.57.14...v3.58.0) (2025-07-01) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.57.14](https://github.com/eggjs/tegg/compare/v3.57.13...v3.57.14) (2025-06-18) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.57.13](https://github.com/eggjs/tegg/compare/v3.57.12...v3.57.13) (2025-06-18) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.57.12](https://github.com/eggjs/tegg/compare/v3.57.11...v3.57.12) (2025-06-16) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.57.11](https://github.com/eggjs/tegg/compare/v3.57.10...v3.57.11) (2025-06-16) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.57.10](https://github.com/eggjs/tegg/compare/v3.57.9...v3.57.10) (2025-06-12) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.57.9](https://github.com/eggjs/tegg/compare/v3.57.8...v3.57.9) (2025-05-29) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.57.8](https://github.com/eggjs/tegg/compare/v3.57.7...v3.57.8) (2025-05-29) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.57.7](https://github.com/eggjs/tegg/compare/v3.57.6...v3.57.7) (2025-05-28) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.57.6](https://github.com/eggjs/tegg/compare/v3.57.5...v3.57.6) (2025-05-27) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.57.5](https://github.com/eggjs/tegg/compare/v3.57.4...v3.57.5) (2025-05-15) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.57.4](https://github.com/eggjs/tegg/compare/v3.57.3...v3.57.4) (2025-05-14) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.57.3](https://github.com/eggjs/tegg/compare/v3.57.2...v3.57.3) (2025-05-13) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.57.2](https://github.com/eggjs/tegg/compare/v3.57.1...v3.57.2) (2025-05-13) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.57.1](https://github.com/eggjs/tegg/compare/v3.57.0...v3.57.1) (2025-05-13) **Note:** Version bump only for package @eggjs/egg-module-common # [3.57.0](https://github.com/eggjs/tegg/compare/v3.56.3...v3.57.0) (2025-05-09) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.56.3](https://github.com/eggjs/tegg/compare/v3.56.2...v3.56.3) (2025-05-07) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.56.2](https://github.com/eggjs/tegg/compare/v3.56.1...v3.56.2) (2025-05-01) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.56.1](https://github.com/eggjs/tegg/compare/v3.56.0...v3.56.1) (2025-04-29) **Note:** Version bump only for package @eggjs/egg-module-common # [3.56.0](https://github.com/eggjs/tegg/compare/v3.55.0...v3.56.0) (2025-04-29) **Note:** Version bump only for package @eggjs/egg-module-common # [3.55.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.55.0) (2025-04-29) **Note:** Version bump only for package @eggjs/egg-module-common # [3.54.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.54.0) (2025-04-29) **Note:** Version bump only for package @eggjs/egg-module-common # [3.53.0](https://github.com/eggjs/tegg/compare/v3.52.1...v3.53.0) (2025-04-21) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.52.1](https://github.com/eggjs/tegg/compare/v3.52.0...v3.52.1) (2025-03-14) **Note:** Version bump only for package @eggjs/egg-module-common # [3.52.0](https://github.com/eggjs/tegg/compare/v3.51.2...v3.52.0) (2024-12-30) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.51.2](https://github.com/eggjs/tegg/compare/v3.51.1...v3.51.2) (2024-12-09) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.51.1](https://github.com/eggjs/tegg/compare/v3.51.0...v3.51.1) (2024-11-06) **Note:** Version bump only for package @eggjs/egg-module-common # [3.51.0](https://github.com/eggjs/tegg/compare/v3.50.1...v3.51.0) (2024-10-30) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.50.1](https://github.com/eggjs/tegg/compare/v3.50.0...v3.50.1) (2024-10-23) **Note:** Version bump only for package @eggjs/egg-module-common # [3.50.0](https://github.com/eggjs/tegg/compare/v3.49.0...v3.50.0) (2024-10-22) **Note:** Version bump only for package @eggjs/egg-module-common # [3.49.0](https://github.com/eggjs/tegg/compare/v3.48.1...v3.49.0) (2024-10-21) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.48.1](https://github.com/eggjs/tegg/compare/v3.48.0...v3.48.1) (2024-10-14) **Note:** Version bump only for package @eggjs/egg-module-common # [3.48.0](https://github.com/eggjs/tegg/compare/v3.47.2...v3.48.0) (2024-10-10) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.47.2](https://github.com/eggjs/tegg/compare/v3.47.1...v3.47.2) (2024-10-10) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.47.1](https://github.com/eggjs/tegg/compare/v3.47.0...v3.47.1) (2024-10-10) **Note:** Version bump only for package @eggjs/egg-module-common # [3.47.0](https://github.com/eggjs/tegg/compare/v3.46.4...v3.47.0) (2024-10-10) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.46.4](https://github.com/eggjs/tegg/compare/v3.46.3...v3.46.4) (2024-10-09) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.46.3](https://github.com/eggjs/tegg/compare/v3.46.2...v3.46.3) (2024-10-08) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.46.2](https://github.com/eggjs/tegg/compare/v3.46.1...v3.46.2) (2024-10-07) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.46.1](https://github.com/eggjs/tegg/compare/v3.46.0...v3.46.1) (2024-09-30) **Note:** Version bump only for package @eggjs/egg-module-common # [3.46.0](https://github.com/eggjs/tegg/compare/v3.45.0...v3.46.0) (2024-09-29) **Note:** Version bump only for package @eggjs/egg-module-common # [3.45.0](https://github.com/eggjs/tegg/compare/v3.44.1...v3.45.0) (2024-09-29) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.44.1](https://github.com/eggjs/tegg/compare/v3.44.0...v3.44.1) (2024-09-29) **Note:** Version bump only for package @eggjs/egg-module-common # [3.44.0](https://github.com/eggjs/tegg/compare/v3.43.2...v3.44.0) (2024-09-29) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.43.2](https://github.com/eggjs/tegg/compare/v3.43.1...v3.43.2) (2024-09-14) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.43.1](https://github.com/eggjs/tegg/compare/v3.43.0...v3.43.1) (2024-09-14) **Note:** Version bump only for package @eggjs/egg-module-common # [3.43.0](https://github.com/eggjs/tegg/compare/v3.42.0...v3.43.0) (2024-09-13) **Note:** Version bump only for package @eggjs/egg-module-common # [3.42.0](https://github.com/eggjs/tegg/compare/v3.41.0...v3.42.0) (2024-09-10) **Note:** Version bump only for package @eggjs/egg-module-common # [3.41.0](https://github.com/eggjs/tegg/compare/v3.40.1...v3.41.0) (2024-08-26) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.40.1](https://github.com/eggjs/tegg/compare/v3.40.0...v3.40.1) (2024-08-23) **Note:** Version bump only for package @eggjs/egg-module-common # [3.40.0](https://github.com/eggjs/tegg/compare/v3.39.5...v3.40.0) (2024-08-22) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.39.5](https://github.com/eggjs/tegg/compare/v3.39.4...v3.39.5) (2024-08-09) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.39.4](https://github.com/eggjs/tegg/compare/v3.39.3...v3.39.4) (2024-07-09) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.39.3](https://github.com/eggjs/tegg/compare/v3.39.2...v3.39.3) (2024-04-28) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.39.2](https://github.com/eggjs/tegg/compare/v3.39.1...v3.39.2) (2024-04-28) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.39.1](https://github.com/eggjs/tegg/compare/v3.39.0...v3.39.1) (2024-04-28) **Note:** Version bump only for package @eggjs/egg-module-common # [3.39.0](https://github.com/eggjs/tegg/compare/v3.38.0...v3.39.0) (2024-04-19) **Note:** Version bump only for package @eggjs/egg-module-common # [3.38.0](https://github.com/eggjs/tegg/compare/v3.37.3...v3.38.0) (2024-04-18) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.37.3](https://github.com/eggjs/tegg/compare/v3.37.2...v3.37.3) (2024-04-17) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.37.2](https://github.com/eggjs/tegg/compare/v3.37.1...v3.37.2) (2024-04-17) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.37.1](https://github.com/eggjs/tegg/compare/v3.37.0...v3.37.1) (2024-04-16) **Note:** Version bump only for package @eggjs/egg-module-common # [3.37.0](https://github.com/eggjs/tegg/compare/v3.36.3...v3.37.0) (2024-04-16) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.36.3](https://github.com/eggjs/tegg/compare/v3.36.2...v3.36.3) (2024-04-10) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.36.2](https://github.com/eggjs/tegg/compare/v3.36.1...v3.36.2) (2024-04-08) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.36.1](https://github.com/eggjs/tegg/compare/v3.36.0...v3.36.1) (2024-04-07) **Note:** Version bump only for package @eggjs/egg-module-common # [3.36.0](https://github.com/eggjs/tegg/compare/v3.35.1...v3.36.0) (2024-04-02) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.35.1](https://github.com/eggjs/tegg/compare/v3.35.0...v3.35.1) (2024-03-26) **Note:** Version bump only for package @eggjs/egg-module-common # [3.35.0](https://github.com/eggjs/tegg/compare/v3.34.0...v3.35.0) (2024-03-26) **Note:** Version bump only for package @eggjs/egg-module-common # [3.34.0](https://github.com/eggjs/tegg/compare/v3.33.1...v3.34.0) (2024-03-22) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.33.1](https://github.com/eggjs/tegg/compare/v3.33.0...v3.33.1) (2024-03-22) **Note:** Version bump only for package @eggjs/egg-module-common # [3.33.0](https://github.com/eggjs/tegg/compare/v3.32.0...v3.33.0) (2024-03-22) **Note:** Version bump only for package @eggjs/egg-module-common # [3.32.0](https://github.com/eggjs/tegg/compare/v3.31.0...v3.32.0) (2024-02-19) **Note:** Version bump only for package @eggjs/egg-module-common # [3.31.0](https://github.com/eggjs/tegg/compare/v3.30.1...v3.31.0) (2024-01-31) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.30.1](https://github.com/eggjs/tegg/compare/v3.30.0...v3.30.1) (2024-01-25) **Note:** Version bump only for package @eggjs/egg-module-common # [3.30.0](https://github.com/eggjs/tegg/compare/v3.29.0...v3.30.0) (2024-01-17) **Note:** Version bump only for package @eggjs/egg-module-common # [3.29.0](https://github.com/eggjs/tegg/compare/v3.28.2...v3.29.0) (2023-12-26) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.28.2](https://github.com/eggjs/tegg/compare/v3.28.1...v3.28.2) (2023-12-12) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.28.1](https://github.com/eggjs/tegg/compare/v3.28.0...v3.28.1) (2023-12-11) **Note:** Version bump only for package @eggjs/egg-module-common # [3.28.0](https://github.com/eggjs/tegg/compare/v3.27.0...v3.28.0) (2023-12-10) **Note:** Version bump only for package @eggjs/egg-module-common # [3.27.0](https://github.com/eggjs/tegg/compare/v3.26.0...v3.27.0) (2023-11-23) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.25.2](https://github.com/eggjs/tegg/compare/v3.25.1...v3.25.2) (2023-11-06) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.25.1](https://github.com/eggjs/tegg/compare/v3.25.0...v3.25.1) (2023-11-03) **Note:** Version bump only for package @eggjs/egg-module-common # [3.25.0](https://github.com/eggjs/tegg/compare/v3.24.0...v3.25.0) (2023-11-03) **Note:** Version bump only for package @eggjs/egg-module-common # [3.24.0](https://github.com/eggjs/tegg/compare/v3.23.0...v3.24.0) (2023-10-26) **Note:** Version bump only for package @eggjs/egg-module-common # [3.23.0](https://github.com/eggjs/tegg/compare/v3.22.0...v3.23.0) (2023-09-20) **Note:** Version bump only for package @eggjs/egg-module-common # [3.20.0](https://github.com/eggjs/tegg/compare/v3.19.0...v3.20.0) (2023-09-07) **Note:** Version bump only for package @eggjs/egg-module-common # [3.19.0](https://github.com/eggjs/tegg/compare/v3.18.1...v3.19.0) (2023-08-31) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.18.1](https://github.com/eggjs/tegg/compare/v3.18.0...v3.18.1) (2023-08-29) **Note:** Version bump only for package @eggjs/egg-module-common # [3.18.0](https://github.com/eggjs/tegg/compare/v3.17.0...v3.18.0) (2023-08-29) **Note:** Version bump only for package @eggjs/egg-module-common # [3.17.0](https://github.com/eggjs/tegg/compare/v3.16.0...v3.17.0) (2023-08-24) **Note:** Version bump only for package @eggjs/egg-module-common # [3.16.0](https://github.com/eggjs/tegg/compare/v3.15.0...v3.16.0) (2023-08-24) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.14.3](https://github.com/eggjs/tegg/compare/v3.14.2...v3.14.3) (2023-08-14) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.14.2](https://github.com/eggjs/tegg/compare/v3.14.1...v3.14.2) (2023-08-14) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.14.1](https://github.com/eggjs/tegg/compare/v3.14.0...v3.14.1) (2023-08-11) **Note:** Version bump only for package @eggjs/egg-module-common # [3.14.0](https://github.com/eggjs/tegg/compare/v3.13.0...v3.14.0) (2023-07-31) **Note:** Version bump only for package @eggjs/egg-module-common # [3.13.0](https://github.com/eggjs/tegg/compare/v3.12.0...v3.13.0) (2023-07-25) **Note:** Version bump only for package @eggjs/egg-module-common # [3.12.0](https://github.com/eggjs/tegg/compare/v3.11.1...v3.12.0) (2023-07-13) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.11.1](https://github.com/eggjs/tegg/compare/v3.11.0...v3.11.1) (2023-06-29) **Note:** Version bump only for package @eggjs/egg-module-common # [3.11.0](https://github.com/eggjs/tegg/compare/v3.10.0...v3.11.0) (2023-06-29) **Note:** Version bump only for package @eggjs/egg-module-common # [3.10.0](https://github.com/eggjs/tegg/compare/v3.9.0...v3.10.0) (2023-06-28) **Note:** Version bump only for package @eggjs/egg-module-common # [3.8.0](https://github.com/eggjs/tegg/compare/v3.7.0...v3.8.0) (2023-05-30) ### Features * impl EggObjectLifecycle hook in decorator ([#119](https://github.com/eggjs/tegg/issues/119)) ([cced8a2](https://github.com/eggjs/tegg/commit/cced8a2e009c33d5040fa21d00409fddef471b0e)) ## [3.2.3](https://github.com/eggjs/tegg/compare/v3.2.2...v3.2.3) (2023-01-16) **Note:** Version bump only for package @eggjs/egg-module-common ## [3.2.2](https://github.com/eggjs/tegg/compare/v3.2.1...v3.2.2) (2023-01-06) **Note:** Version bump only for package @eggjs/egg-module-common # [0.2.0](https://github.com/eggjs/tegg/compare/v0.1.19...v0.2.0) (2022-01-20) **Note:** Version bump only for package @eggjs/egg-module-common ================================================ FILE: plugin/common/README.md ================================================ # `@eggjs/egg-module-common` # Usage This is an internal tegg library, you probably shouldn't use it directly. ================================================ FILE: plugin/common/index.ts ================================================ export const TEGG_CONTEXT = Symbol.for('context#teggContext'); export const EGG_CONTEXT = Symbol.for('context#eggContext'); export const ROOT_PROTO = Symbol.for('context#rootProto'); ================================================ FILE: plugin/common/package.json ================================================ { "name": "@eggjs/egg-module-common", "version": "3.78.15", "description": "common module", "keywords": [ "egg", "typescript", "module", "common", "tegg" ], "files": [ "index.js", "index.d.ts" ], "types": "index.d.ts", "scripts": { "clean": "tsc -b --clean", "tsc": "ut run clean && tsc -p ./tsconfig.json", "tsc:pub": "ut run clean && tsc -p ./tsconfig.pub.json", "prepublishOnly": "ut tsc:pub" }, "author": "killagu ", "license": "MIT", "homepage": "https://github.com/eggjs/tegg", "bugs": { "url": "https://github.com/eggjs/tegg/issues" }, "repository": { "type": "git", "url": "git@github.com:eggjs/tegg.git", "directory": "plugin/common" }, "engines": { "node": ">=14.0.0" }, "publishConfig": { "access": "public" }, "devDependencies": { "@types/mocha": "^10.0.1", "@types/node": "^20.2.4", "cross-env": "^7.0.3", "mocha": "^10.2.0", "ts-node": "^10.9.1", "typescript": "^5.0.4" }, "gitHead": "240a3e9f40fda65ebb7589727d197db5ce17916c" } ================================================ FILE: plugin/common/tsconfig.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "baseUrl": "./" }, "exclude": [ "node_modules", "test" ] } ================================================ FILE: plugin/common/tsconfig.pub.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "baseUrl": "./" }, "exclude": [ "node_modules", "test" ] } ================================================ FILE: plugin/config/CHANGELOG.md ================================================ # Change Log All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. ## [3.78.15](https://github.com/eggjs/tegg/compare/v3.78.14...v3.78.15) (2026-04-23) **Note:** Version bump only for package @eggjs/tegg-config ## [3.78.14](https://github.com/eggjs/tegg/compare/v3.78.13...v3.78.14) (2026-04-21) **Note:** Version bump only for package @eggjs/tegg-config ## [3.78.13](https://github.com/eggjs/tegg/compare/v3.78.12...v3.78.13) (2026-04-21) **Note:** Version bump only for package @eggjs/tegg-config ## [3.78.12](https://github.com/eggjs/tegg/compare/v3.78.11...v3.78.12) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-config ## [3.78.11](https://github.com/eggjs/tegg/compare/v3.78.10...v3.78.11) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-config ## [3.78.10](https://github.com/eggjs/tegg/compare/v3.78.9...v3.78.10) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-config ## [3.78.9](https://github.com/eggjs/tegg/compare/v3.78.8...v3.78.9) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-config ## [3.78.8](https://github.com/eggjs/tegg/compare/v3.78.7...v3.78.8) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-config ## [3.78.7](https://github.com/eggjs/tegg/compare/v3.78.6...v3.78.7) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-config ## [3.78.6](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.6) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-config ## [3.78.5](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.5) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-config ## [3.78.4](https://github.com/eggjs/tegg/compare/v3.78.3...v3.78.4) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-config ## [3.78.3](https://github.com/eggjs/tegg/compare/v3.78.2...v3.78.3) (2026-04-15) **Note:** Version bump only for package @eggjs/tegg-config ## [3.78.2](https://github.com/eggjs/tegg/compare/v3.78.1...v3.78.2) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-config ## [3.78.1](https://github.com/eggjs/tegg/compare/v3.78.0...v3.78.1) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-config # [3.78.0](https://github.com/eggjs/tegg/compare/v3.77.2...v3.78.0) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-config ## [3.77.2](https://github.com/eggjs/tegg/compare/v3.77.1...v3.77.2) (2026-04-07) **Note:** Version bump only for package @eggjs/tegg-config ## [3.77.1](https://github.com/eggjs/tegg/compare/v3.77.0...v3.77.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-config # [3.77.0](https://github.com/eggjs/tegg/compare/v3.76.1...v3.77.0) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-config ## [3.76.1](https://github.com/eggjs/tegg/compare/v3.76.0...v3.76.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-config # [3.76.0](https://github.com/eggjs/tegg/compare/v3.75.1...v3.76.0) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-config ## [3.75.1](https://github.com/eggjs/tegg/compare/v3.75.0...v3.75.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-config # [3.75.0](https://github.com/eggjs/tegg/compare/v3.74.0...v3.75.0) (2026-03-30) **Note:** Version bump only for package @eggjs/tegg-config # [3.74.0](https://github.com/eggjs/tegg/compare/v3.73.0...v3.74.0) (2026-03-30) **Note:** Version bump only for package @eggjs/tegg-config # [3.73.0](https://github.com/eggjs/tegg/compare/v3.72.0...v3.73.0) (2026-03-25) **Note:** Version bump only for package @eggjs/tegg-config # [3.72.0](https://github.com/eggjs/tegg/compare/v3.71.2...v3.72.0) (2026-02-04) **Note:** Version bump only for package @eggjs/tegg-config ## [3.71.2](https://github.com/eggjs/tegg/compare/v3.71.1...v3.71.2) (2026-01-30) **Note:** Version bump only for package @eggjs/tegg-config ## [3.71.1](https://github.com/eggjs/tegg/compare/v3.71.0...v3.71.1) (2026-01-28) **Note:** Version bump only for package @eggjs/tegg-config # [3.71.0](https://github.com/eggjs/tegg/compare/v3.70.1...v3.71.0) (2026-01-26) **Note:** Version bump only for package @eggjs/tegg-config ## [3.70.1](https://github.com/eggjs/tegg/compare/v3.70.0...v3.70.1) (2026-01-14) **Note:** Version bump only for package @eggjs/tegg-config # [3.70.0](https://github.com/eggjs/tegg/compare/v3.69.0...v3.70.0) (2026-01-07) **Note:** Version bump only for package @eggjs/tegg-config # [3.69.0](https://github.com/eggjs/tegg/compare/v3.68.0...v3.69.0) (2026-01-06) **Note:** Version bump only for package @eggjs/tegg-config # [3.68.0](https://github.com/eggjs/tegg/compare/v3.67.2...v3.68.0) (2025-12-29) **Note:** Version bump only for package @eggjs/tegg-config ## [3.67.2](https://github.com/eggjs/tegg/compare/v3.67.1...v3.67.2) (2025-12-24) **Note:** Version bump only for package @eggjs/tegg-config ## [3.67.1](https://github.com/eggjs/tegg/compare/v3.67.0...v3.67.1) (2025-12-23) **Note:** Version bump only for package @eggjs/tegg-config # [3.67.0](https://github.com/eggjs/tegg/compare/v3.66.0...v3.67.0) (2025-12-18) **Note:** Version bump only for package @eggjs/tegg-config # [3.66.0](https://github.com/eggjs/tegg/compare/v3.65.3...v3.66.0) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-config ## [3.65.3](https://github.com/eggjs/tegg/compare/v3.65.2...v3.65.3) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-config ## [3.65.2](https://github.com/eggjs/tegg/compare/v3.65.1...v3.65.2) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-config ## [3.65.1](https://github.com/eggjs/tegg/compare/v3.65.0...v3.65.1) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-config # [3.65.0](https://github.com/eggjs/tegg/compare/v3.64.5...v3.65.0) (2025-12-10) **Note:** Version bump only for package @eggjs/tegg-config ## [3.64.5](https://github.com/eggjs/tegg/compare/v3.64.4...v3.64.5) (2025-12-08) **Note:** Version bump only for package @eggjs/tegg-config ## [3.64.4](https://github.com/eggjs/tegg/compare/v3.64.3...v3.64.4) (2025-12-06) **Note:** Version bump only for package @eggjs/tegg-config ## [3.64.3](https://github.com/eggjs/tegg/compare/v3.64.2...v3.64.3) (2025-12-06) **Note:** Version bump only for package @eggjs/tegg-config ## [3.64.2](https://github.com/eggjs/tegg/compare/v3.64.1...v3.64.2) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-config ## [3.64.1](https://github.com/eggjs/tegg/compare/v3.64.0...v3.64.1) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-config # [3.64.0](https://github.com/eggjs/tegg/compare/v3.63.2...v3.64.0) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-config ## [3.63.2](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.2) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg-config ## [3.63.1](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.1) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg-config # [3.63.0](https://github.com/eggjs/tegg/compare/v3.62.3...v3.63.0) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg-config ## [3.62.3](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.3) (2025-11-05) **Note:** Version bump only for package @eggjs/tegg-config ## [3.62.2](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.2) (2025-11-05) **Note:** Version bump only for package @eggjs/tegg-config ## [3.62.1](https://github.com/eggjs/tegg/compare/v3.62.0...v3.62.1) (2025-11-03) **Note:** Version bump only for package @eggjs/tegg-config # [3.62.0](https://github.com/eggjs/tegg/compare/v3.61.0...v3.62.0) (2025-09-24) ### Bug Fixes * deduplicate modules reference ([#343](https://github.com/eggjs/tegg/issues/343)) ([aa5daf7](https://github.com/eggjs/tegg/commit/aa5daf7e8db49c8b273ba2102c127fd14a2de044)) # [3.61.0](https://github.com/eggjs/tegg/compare/v3.60.3...v3.61.0) (2025-08-15) **Note:** Version bump only for package @eggjs/tegg-config ## [3.60.3](https://github.com/eggjs/tegg/compare/v3.60.2...v3.60.3) (2025-08-06) **Note:** Version bump only for package @eggjs/tegg-config ## [3.60.2](https://github.com/eggjs/tegg/compare/v3.60.1...v3.60.2) (2025-08-06) **Note:** Version bump only for package @eggjs/tegg-config ## [3.60.1](https://github.com/eggjs/tegg/compare/v3.60.0...v3.60.1) (2025-07-28) **Note:** Version bump only for package @eggjs/tegg-config # [3.60.0](https://github.com/eggjs/tegg/compare/v3.59.1...v3.60.0) (2025-07-28) **Note:** Version bump only for package @eggjs/tegg-config ## [3.59.1](https://github.com/eggjs/tegg/compare/v3.59.0...v3.59.1) (2025-07-16) **Note:** Version bump only for package @eggjs/tegg-config # [3.59.0](https://github.com/eggjs/tegg/compare/v3.58.0...v3.59.0) (2025-07-07) **Note:** Version bump only for package @eggjs/tegg-config # [3.58.0](https://github.com/eggjs/tegg/compare/v3.57.14...v3.58.0) (2025-07-01) **Note:** Version bump only for package @eggjs/tegg-config ## [3.57.14](https://github.com/eggjs/tegg/compare/v3.57.13...v3.57.14) (2025-06-18) **Note:** Version bump only for package @eggjs/tegg-config ## [3.57.13](https://github.com/eggjs/tegg/compare/v3.57.12...v3.57.13) (2025-06-18) **Note:** Version bump only for package @eggjs/tegg-config ## [3.57.12](https://github.com/eggjs/tegg/compare/v3.57.11...v3.57.12) (2025-06-16) **Note:** Version bump only for package @eggjs/tegg-config ## [3.57.11](https://github.com/eggjs/tegg/compare/v3.57.10...v3.57.11) (2025-06-16) **Note:** Version bump only for package @eggjs/tegg-config ## [3.57.10](https://github.com/eggjs/tegg/compare/v3.57.9...v3.57.10) (2025-06-12) **Note:** Version bump only for package @eggjs/tegg-config ## [3.57.9](https://github.com/eggjs/tegg/compare/v3.57.8...v3.57.9) (2025-05-29) **Note:** Version bump only for package @eggjs/tegg-config ## [3.57.8](https://github.com/eggjs/tegg/compare/v3.57.7...v3.57.8) (2025-05-29) **Note:** Version bump only for package @eggjs/tegg-config ## [3.57.7](https://github.com/eggjs/tegg/compare/v3.57.6...v3.57.7) (2025-05-28) **Note:** Version bump only for package @eggjs/tegg-config ## [3.57.6](https://github.com/eggjs/tegg/compare/v3.57.5...v3.57.6) (2025-05-27) **Note:** Version bump only for package @eggjs/tegg-config ## [3.57.5](https://github.com/eggjs/tegg/compare/v3.57.4...v3.57.5) (2025-05-15) **Note:** Version bump only for package @eggjs/tegg-config ## [3.57.4](https://github.com/eggjs/tegg/compare/v3.57.3...v3.57.4) (2025-05-14) **Note:** Version bump only for package @eggjs/tegg-config ## [3.57.3](https://github.com/eggjs/tegg/compare/v3.57.2...v3.57.3) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-config ## [3.57.2](https://github.com/eggjs/tegg/compare/v3.57.1...v3.57.2) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-config ## [3.57.1](https://github.com/eggjs/tegg/compare/v3.57.0...v3.57.1) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-config # [3.57.0](https://github.com/eggjs/tegg/compare/v3.56.3...v3.57.0) (2025-05-09) **Note:** Version bump only for package @eggjs/tegg-config ## [3.56.3](https://github.com/eggjs/tegg/compare/v3.56.2...v3.56.3) (2025-05-07) **Note:** Version bump only for package @eggjs/tegg-config ## [3.56.2](https://github.com/eggjs/tegg/compare/v3.56.1...v3.56.2) (2025-05-01) **Note:** Version bump only for package @eggjs/tegg-config ## [3.56.1](https://github.com/eggjs/tegg/compare/v3.56.0...v3.56.1) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-config # [3.56.0](https://github.com/eggjs/tegg/compare/v3.55.0...v3.56.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-config # [3.55.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.55.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-config # [3.54.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.54.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-config # [3.53.0](https://github.com/eggjs/tegg/compare/v3.52.1...v3.53.0) (2025-04-21) **Note:** Version bump only for package @eggjs/tegg-config ## [3.52.1](https://github.com/eggjs/tegg/compare/v3.52.0...v3.52.1) (2025-03-14) **Note:** Version bump only for package @eggjs/tegg-config # [3.52.0](https://github.com/eggjs/tegg/compare/v3.51.2...v3.52.0) (2024-12-30) **Note:** Version bump only for package @eggjs/tegg-config ## [3.51.2](https://github.com/eggjs/tegg/compare/v3.51.1...v3.51.2) (2024-12-09) **Note:** Version bump only for package @eggjs/tegg-config ## [3.51.1](https://github.com/eggjs/tegg/compare/v3.51.0...v3.51.1) (2024-11-06) **Note:** Version bump only for package @eggjs/tegg-config # [3.51.0](https://github.com/eggjs/tegg/compare/v3.50.1...v3.51.0) (2024-10-30) **Note:** Version bump only for package @eggjs/tegg-config ## [3.50.1](https://github.com/eggjs/tegg/compare/v3.50.0...v3.50.1) (2024-10-23) **Note:** Version bump only for package @eggjs/tegg-config # [3.50.0](https://github.com/eggjs/tegg/compare/v3.49.0...v3.50.0) (2024-10-22) **Note:** Version bump only for package @eggjs/tegg-config # [3.49.0](https://github.com/eggjs/tegg/compare/v3.48.1...v3.49.0) (2024-10-21) **Note:** Version bump only for package @eggjs/tegg-config ## [3.48.1](https://github.com/eggjs/tegg/compare/v3.48.0...v3.48.1) (2024-10-14) **Note:** Version bump only for package @eggjs/tegg-config # [3.48.0](https://github.com/eggjs/tegg/compare/v3.47.2...v3.48.0) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-config ## [3.47.2](https://github.com/eggjs/tegg/compare/v3.47.1...v3.47.2) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-config ## [3.47.1](https://github.com/eggjs/tegg/compare/v3.47.0...v3.47.1) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-config # [3.47.0](https://github.com/eggjs/tegg/compare/v3.46.4...v3.47.0) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-config ## [3.46.4](https://github.com/eggjs/tegg/compare/v3.46.3...v3.46.4) (2024-10-09) **Note:** Version bump only for package @eggjs/tegg-config ## [3.46.3](https://github.com/eggjs/tegg/compare/v3.46.2...v3.46.3) (2024-10-08) **Note:** Version bump only for package @eggjs/tegg-config ## [3.46.2](https://github.com/eggjs/tegg/compare/v3.46.1...v3.46.2) (2024-10-07) **Note:** Version bump only for package @eggjs/tegg-config ## [3.46.1](https://github.com/eggjs/tegg/compare/v3.46.0...v3.46.1) (2024-09-30) **Note:** Version bump only for package @eggjs/tegg-config # [3.46.0](https://github.com/eggjs/tegg/compare/v3.45.0...v3.46.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-config # [3.45.0](https://github.com/eggjs/tegg/compare/v3.44.1...v3.45.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-config ## [3.44.1](https://github.com/eggjs/tegg/compare/v3.44.0...v3.44.1) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-config # [3.44.0](https://github.com/eggjs/tegg/compare/v3.43.2...v3.44.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-config ## [3.43.2](https://github.com/eggjs/tegg/compare/v3.43.1...v3.43.2) (2024-09-14) **Note:** Version bump only for package @eggjs/tegg-config ## [3.43.1](https://github.com/eggjs/tegg/compare/v3.43.0...v3.43.1) (2024-09-14) **Note:** Version bump only for package @eggjs/tegg-config # [3.43.0](https://github.com/eggjs/tegg/compare/v3.42.0...v3.43.0) (2024-09-13) **Note:** Version bump only for package @eggjs/tegg-config # [3.42.0](https://github.com/eggjs/tegg/compare/v3.41.0...v3.42.0) (2024-09-10) **Note:** Version bump only for package @eggjs/tegg-config # [3.41.0](https://github.com/eggjs/tegg/compare/v3.40.1...v3.41.0) (2024-08-26) **Note:** Version bump only for package @eggjs/tegg-config ## [3.40.1](https://github.com/eggjs/tegg/compare/v3.40.0...v3.40.1) (2024-08-23) **Note:** Version bump only for package @eggjs/tegg-config # [3.40.0](https://github.com/eggjs/tegg/compare/v3.39.5...v3.40.0) (2024-08-22) **Note:** Version bump only for package @eggjs/tegg-config ## [3.39.5](https://github.com/eggjs/tegg/compare/v3.39.4...v3.39.5) (2024-08-09) **Note:** Version bump only for package @eggjs/tegg-config ## [3.39.4](https://github.com/eggjs/tegg/compare/v3.39.3...v3.39.4) (2024-07-09) **Note:** Version bump only for package @eggjs/tegg-config ## [3.39.3](https://github.com/eggjs/tegg/compare/v3.39.2...v3.39.3) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-config ## [3.39.2](https://github.com/eggjs/tegg/compare/v3.39.1...v3.39.2) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-config ## [3.39.1](https://github.com/eggjs/tegg/compare/v3.39.0...v3.39.1) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-config # [3.39.0](https://github.com/eggjs/tegg/compare/v3.38.0...v3.39.0) (2024-04-19) ### Features * use app.loader.getTypeFiles to generate module config file names ([#213](https://github.com/eggjs/tegg/issues/213)) ([e0656a4](https://github.com/eggjs/tegg/commit/e0656a4d59beef103a5627461d9b9c87996928e3)) # [3.38.0](https://github.com/eggjs/tegg/compare/v3.37.3...v3.38.0) (2024-04-18) **Note:** Version bump only for package @eggjs/tegg-config ## [3.37.3](https://github.com/eggjs/tegg/compare/v3.37.2...v3.37.3) (2024-04-17) **Note:** Version bump only for package @eggjs/tegg-config ## [3.37.2](https://github.com/eggjs/tegg/compare/v3.37.1...v3.37.2) (2024-04-17) **Note:** Version bump only for package @eggjs/tegg-config ## [3.37.1](https://github.com/eggjs/tegg/compare/v3.37.0...v3.37.1) (2024-04-16) **Note:** Version bump only for package @eggjs/tegg-config # [3.37.0](https://github.com/eggjs/tegg/compare/v3.36.3...v3.37.0) (2024-04-16) **Note:** Version bump only for package @eggjs/tegg-config ## [3.36.3](https://github.com/eggjs/tegg/compare/v3.36.2...v3.36.3) (2024-04-10) **Note:** Version bump only for package @eggjs/tegg-config ## [3.36.2](https://github.com/eggjs/tegg/compare/v3.36.1...v3.36.2) (2024-04-08) **Note:** Version bump only for package @eggjs/tegg-config ## [3.36.1](https://github.com/eggjs/tegg/compare/v3.36.0...v3.36.1) (2024-04-07) **Note:** Version bump only for package @eggjs/tegg-config # [3.36.0](https://github.com/eggjs/tegg/compare/v3.35.1...v3.36.0) (2024-04-02) ### Features * impl ajv + typebox Validator ([#201](https://github.com/eggjs/tegg/issues/201)) ([9fd585d](https://github.com/eggjs/tegg/commit/9fd585de9b613466c96b73494a08a494db34ea57)) ## [3.35.1](https://github.com/eggjs/tegg/compare/v3.35.0...v3.35.1) (2024-03-26) **Note:** Version bump only for package @eggjs/tegg-config # [3.35.0](https://github.com/eggjs/tegg/compare/v3.34.0...v3.35.0) (2024-03-26) **Note:** Version bump only for package @eggjs/tegg-config # [3.34.0](https://github.com/eggjs/tegg/compare/v3.33.1...v3.34.0) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-config ## [3.33.1](https://github.com/eggjs/tegg/compare/v3.33.0...v3.33.1) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-config # [3.33.0](https://github.com/eggjs/tegg/compare/v3.32.0...v3.33.0) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-config # [3.32.0](https://github.com/eggjs/tegg/compare/v3.31.0...v3.32.0) (2024-02-19) ### Bug Fixes * ignore duplicated module ([#191](https://github.com/eggjs/tegg/issues/191)) ([263467f](https://github.com/eggjs/tegg/commit/263467fc43a25eb5a1670de4778de127662a201b)) # [3.31.0](https://github.com/eggjs/tegg/compare/v3.30.1...v3.31.0) (2024-01-31) **Note:** Version bump only for package @eggjs/tegg-config ## [3.30.1](https://github.com/eggjs/tegg/compare/v3.30.0...v3.30.1) (2024-01-25) **Note:** Version bump only for package @eggjs/tegg-config # [3.30.0](https://github.com/eggjs/tegg/compare/v3.29.0...v3.30.0) (2024-01-17) ### Features * scan framework dependencies as optional module ([#184](https://github.com/eggjs/tegg/issues/184)) ([a4908c6](https://github.com/eggjs/tegg/commit/a4908c6c640000c7068def57d32052cca15adf47)) # [3.29.0](https://github.com/eggjs/tegg/compare/v3.28.2...v3.29.0) (2023-12-26) **Note:** Version bump only for package @eggjs/tegg-config ## [3.28.2](https://github.com/eggjs/tegg/compare/v3.28.1...v3.28.2) (2023-12-12) **Note:** Version bump only for package @eggjs/tegg-config ## [3.28.1](https://github.com/eggjs/tegg/compare/v3.28.0...v3.28.1) (2023-12-11) **Note:** Version bump only for package @eggjs/tegg-config # [3.28.0](https://github.com/eggjs/tegg/compare/v3.27.0...v3.28.0) (2023-12-10) **Note:** Version bump only for package @eggjs/tegg-config # [3.27.0](https://github.com/eggjs/tegg/compare/v3.26.0...v3.27.0) (2023-11-23) **Note:** Version bump only for package @eggjs/tegg-config ## [3.25.2](https://github.com/eggjs/tegg/compare/v3.25.1...v3.25.2) (2023-11-06) **Note:** Version bump only for package @eggjs/tegg-config ## [3.25.1](https://github.com/eggjs/tegg/compare/v3.25.0...v3.25.1) (2023-11-03) **Note:** Version bump only for package @eggjs/tegg-config # [3.25.0](https://github.com/eggjs/tegg/compare/v3.24.0...v3.25.0) (2023-11-03) **Note:** Version bump only for package @eggjs/tegg-config # [3.24.0](https://github.com/eggjs/tegg/compare/v3.23.0...v3.24.0) (2023-10-26) **Note:** Version bump only for package @eggjs/tegg-config # [3.23.0](https://github.com/eggjs/tegg/compare/v3.22.0...v3.23.0) (2023-09-20) **Note:** Version bump only for package @eggjs/tegg-config # [3.20.0](https://github.com/eggjs/tegg/compare/v3.19.0...v3.20.0) (2023-09-07) ### Features * support load module config with env ([#151](https://github.com/eggjs/tegg/issues/151)) ([c087226](https://github.com/eggjs/tegg/commit/c087226bd7764242fadce5622fccd9e9fee56322)) # [3.19.0](https://github.com/eggjs/tegg/compare/v3.18.1...v3.19.0) (2023-08-31) **Note:** Version bump only for package @eggjs/tegg-config ## [3.18.1](https://github.com/eggjs/tegg/compare/v3.18.0...v3.18.1) (2023-08-29) **Note:** Version bump only for package @eggjs/tegg-config # [3.18.0](https://github.com/eggjs/tegg/compare/v3.17.0...v3.18.0) (2023-08-29) **Note:** Version bump only for package @eggjs/tegg-config # [3.17.0](https://github.com/eggjs/tegg/compare/v3.16.0...v3.17.0) (2023-08-24) **Note:** Version bump only for package @eggjs/tegg-config # [3.16.0](https://github.com/eggjs/tegg/compare/v3.15.0...v3.16.0) (2023-08-24) **Note:** Version bump only for package @eggjs/tegg-config ## [3.14.3](https://github.com/eggjs/tegg/compare/v3.14.2...v3.14.3) (2023-08-14) **Note:** Version bump only for package @eggjs/tegg-config ## [3.14.2](https://github.com/eggjs/tegg/compare/v3.14.1...v3.14.2) (2023-08-14) **Note:** Version bump only for package @eggjs/tegg-config ## [3.14.1](https://github.com/eggjs/tegg/compare/v3.14.0...v3.14.1) (2023-08-11) **Note:** Version bump only for package @eggjs/tegg-config # [3.14.0](https://github.com/eggjs/tegg/compare/v3.13.0...v3.14.0) (2023-07-31) **Note:** Version bump only for package @eggjs/tegg-config # [3.13.0](https://github.com/eggjs/tegg/compare/v3.12.0...v3.13.0) (2023-07-25) **Note:** Version bump only for package @eggjs/tegg-config # [3.12.0](https://github.com/eggjs/tegg/compare/v3.11.1...v3.12.0) (2023-07-13) **Note:** Version bump only for package @eggjs/tegg-config ## [3.11.1](https://github.com/eggjs/tegg/compare/v3.11.0...v3.11.1) (2023-06-29) **Note:** Version bump only for package @eggjs/tegg-config # [3.11.0](https://github.com/eggjs/tegg/compare/v3.10.0...v3.11.0) (2023-06-29) **Note:** Version bump only for package @eggjs/tegg-config # [3.10.0](https://github.com/eggjs/tegg/compare/v3.9.0...v3.10.0) (2023-06-28) **Note:** Version bump only for package @eggjs/tegg-config # [3.8.0](https://github.com/eggjs/tegg/compare/v3.7.0...v3.8.0) (2023-05-30) ### Features * impl EggObjectLifecycle hook in decorator ([#119](https://github.com/eggjs/tegg/issues/119)) ([cced8a2](https://github.com/eggjs/tegg/commit/cced8a2e009c33d5040fa21d00409fddef471b0e)) # [3.7.0](https://github.com/eggjs/tegg/compare/v3.6.3...v3.7.0) (2023-04-03) ### Features * The exposed module reads the options. ([#112](https://github.com/eggjs/tegg/issues/112)) ([a52b44b](https://github.com/eggjs/tegg/commit/a52b44b753463bfdef6fbbc39f920be8eccf1567)) ## [3.2.3](https://github.com/eggjs/tegg/compare/v3.2.2...v3.2.3) (2023-01-16) **Note:** Version bump only for package @eggjs/tegg-config ## [3.2.2](https://github.com/eggjs/tegg/compare/v3.2.1...v3.2.2) (2023-01-06) **Note:** Version bump only for package @eggjs/tegg-config # [3.1.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.1.0) (2022-12-27) ### Bug Fixes * fix miss agent file ([0fa496b](https://github.com/eggjs/tegg/commit/0fa496bdbb4ffa4e911fffa3e176fa7bdf03fb12)) ### Features * **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) * impl Schedule decorator ([#52](https://github.com/eggjs/tegg/issues/52)) ([7f95005](https://github.com/eggjs/tegg/commit/7f950050b548ca542addbd7b466675da4e81ce3f)) # [3.0.0](https://github.com/eggjs/tegg/compare/v3.0.0-alpha.0...v3.0.0) (2022-12-26) **Note:** Version bump only for package @eggjs/tegg-config # [3.0.0-alpha.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.0.0-alpha.0) (2022-12-22) ### Bug Fixes * fix miss agent file ([0fa496b](https://github.com/eggjs/tegg/commit/0fa496bdbb4ffa4e911fffa3e176fa7bdf03fb12)) ### Features * **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) * impl Schedule decorator ([#52](https://github.com/eggjs/tegg/issues/52)) ([7f95005](https://github.com/eggjs/tegg/commit/7f950050b548ca542addbd7b466675da4e81ce3f)) ## [1.2.1](https://github.com/eggjs/tegg/compare/@eggjs/tegg-config@1.2.0...@eggjs/tegg-config@1.2.1) (2022-09-05) ### Bug Fixes * fix miss agent file ([0fa496b](https://github.com/eggjs/tegg/commit/0fa496bdbb4ffa4e911fffa3e176fa7bdf03fb12)) # 1.2.0 (2022-09-04) ### Features * impl Schedule decorator ([#52](https://github.com/eggjs/tegg/issues/52)) ([7f95005](https://github.com/eggjs/tegg/commit/7f950050b548ca542addbd7b466675da4e81ce3f)) ## 1.1.1 (2022-06-21) # 1.1.0 (2022-06-15) # 1.0.0 (2022-02-08) # 0.2.0 (2022-01-20) ## 0.1.19 (2022-01-05) ## 0.1.18 (2021-12-31) ## 0.1.13 (2021-11-14) ### Features * impl standalone tegg ([af9f682](https://github.com/eggjs/tegg/commit/af9f6826ef882ef7206e80ee25433a2b19012995)) ## 0.1.5 (2021-09-08) ## 0.1.2 (2021-09-01) # 0.1.0 (2021-07-22) ### Bug Fixes * set publishConfig.access to public ([527f1fa](https://github.com/eggjs/tegg/commit/527f1fa8e3bcaf45ff5b3a63d90473d4a6a2e2b0)) # [0.2.0](https://github.com/eggjs/tegg/compare/v0.1.19...v0.2.0) (2022-01-20) **Note:** Version bump only for package @eggjs/tegg-config ================================================ FILE: plugin/config/README.md ================================================ # `@eggjs/tegg-config` Egg plugin to load module config(module.yml/module.json) # Usage This is an internal tegg library, you probably shouldn't use it directly. ================================================ FILE: plugin/config/agent.ts ================================================ import TeggConfigAppHook from './app'; export default TeggConfigAppHook; ================================================ FILE: plugin/config/app.ts ================================================ import type { Application, IBoot } from 'egg'; import { ModuleConfigUtil } from '@eggjs/tegg-common-util'; import type { ModuleReference } from '@eggjs/tegg-common-util'; import { ModuleScanner } from './lib/ModuleScanner'; export default class App implements IBoot { private readonly app: Application; constructor(app: Application) { this.app = app; const configNames = this.app.loader.getTypeFiles('module'); ModuleConfigUtil.setConfigNames(configNames); } configWillLoad() { const { readModuleOptions } = this.app.config.tegg || {}; const moduleScanner = new ModuleScanner(this.app.baseDir, readModuleOptions); this.app.moduleReferences = moduleScanner.loadModuleReferences(); this.app.moduleConfigs = {}; for (const reference of this.app.moduleReferences) { const absoluteRef: ModuleReference = { path: ModuleConfigUtil.resolveModuleDir(reference.path, this.app.baseDir), name: reference.name, optional: reference.optional, }; const moduleName = ModuleConfigUtil.readModuleNameSync(absoluteRef.path); this.app.moduleConfigs[moduleName] = { name: moduleName, reference: absoluteRef, config: ModuleConfigUtil.loadModuleConfigSync(absoluteRef.path), }; } } async beforeClose() { ModuleConfigUtil.setConfigNames(undefined); } } ================================================ FILE: plugin/config/lib/ModuleScanner.ts ================================================ import { ModuleConfigUtil, ModuleReference, ReadModuleReferenceOptions } from '@eggjs/tegg-common-util'; import path from 'path'; export class ModuleScanner { private readonly baseDir: string; private readonly readModuleOptions: ReadModuleReferenceOptions; constructor(baseDir: string, readModuleOptions: ReadModuleReferenceOptions) { this.baseDir = baseDir; this.readModuleOptions = readModuleOptions; } /** * - load module references from config or scan from baseDir * - load framework module as optional module reference */ loadModuleReferences(): readonly ModuleReference[] { const moduleReferences = ModuleConfigUtil.readModuleReference(this.baseDir, this.readModuleOptions || {}); // eslint-disable-next-line @typescript-eslint/no-var-requires const appPkg = require(path.join(this.baseDir, 'package.json')); const framework = appPkg.egg?.framework; if (!framework) { return ModuleConfigUtil.deduplicateModules(moduleReferences); } // eslint-disable-next-line @typescript-eslint/no-var-requires const frameworkPkg = require.resolve(`${framework}/package.json`, { paths: [ this.baseDir ], }); const frameworkDir = path.dirname(frameworkPkg); const optionalModuleReferences = ModuleConfigUtil.readModuleReference(frameworkDir, this.readModuleOptions || {}); // 合并所有模块引用并去重 const allModuleReferences = [ ...moduleReferences, ...optionalModuleReferences.map(ref => ({ ...ref, optional: true })), ]; return ModuleConfigUtil.deduplicateModules(allModuleReferences); } } ================================================ FILE: plugin/config/package.json ================================================ { "name": "@eggjs/tegg-config", "eggPlugin": { "name": "teggConfig" }, "version": "3.78.15", "description": "module config plugin for egg", "keywords": [ "egg", "plugin", "typescript", "module", "config", "tegg" ], "files": [ "app.js", "app.d.ts", "typings/index.d.ts", "lib/**/*.js", "lib/**/*.d.ts", "agent.js", "agent.d.ts" ], "types": "typings/index.d.ts", "scripts": { "test": "cross-env NODE_ENV=test NODE_OPTIONS='--no-deprecation' mocha", "clean": "tsc -b --clean", "tsc": "ut run clean && tsc -p ./tsconfig.json", "tsc:pub": "ut run clean && tsc -p ./tsconfig.pub.json", "prepublishOnly": "ut tsc:pub" }, "homepage": "https://github.com/eggjs/tegg", "bugs": { "url": "https://github.com/eggjs/tegg/issues" }, "repository": { "type": "git", "url": "git@github.com:eggjs/tegg.git", "directory": "plugin/config" }, "engines": { "node": ">=14.0.0" }, "dependencies": { "@eggjs/tegg-common-util": "^3.78.15" }, "devDependencies": { "@types/mocha": "^10.0.1", "@types/node": "^20.2.4", "cross-env": "^7.0.3", "egg": "^3.9.1", "mocha": "^10.2.0", "ts-node": "^10.9.1", "typescript": "^5.0.4" }, "publishConfig": { "access": "public" }, "gitHead": "240a3e9f40fda65ebb7589727d197db5ce17916c" } ================================================ FILE: plugin/config/test/DuplicateOptionalModule.test.ts ================================================ import mm from 'egg-mock'; import assert from 'assert'; import path from 'path'; describe('plugin/config/test/DuplicateOptionalModule.test.ts', () => { let app; const fixturesPath = path.join(__dirname, './fixtures/apps/duplicate-optional-module'); after(async () => { await app.close(); }); afterEach(() => { mm.restore(); }); before(async () => { mm(process.env, 'EGG_TYPESCRIPT', true); mm(process, 'cwd', () => { return path.join(__dirname, '..'); }); app = mm.app({ baseDir: fixturesPath, framework: require.resolve('egg'), }); await app.ready(); }); it('should work', async () => { assert.equal(app.moduleReferences.length, 2); }); }); ================================================ FILE: plugin/config/test/ReadModule.test.ts ================================================ import mm from 'egg-mock'; import assert from 'assert'; import path from 'path'; describe('plugin/config/test/ReadModule.test.ts', () => { let app; const fixturesPath = path.join(__dirname, './fixtures/apps/app-with-modules'); after(async () => { await app.close(); }); afterEach(() => { mm.restore(); }); before(async () => { mm(process.env, 'EGG_TYPESCRIPT', true); mm(process, 'cwd', () => { return path.join(__dirname, '..'); }); app = mm.app({ baseDir: fixturesPath, framework: require.resolve('egg'), }); await app.ready(); }); it('should work', async () => { assert.deepStrictEqual(app.moduleConfigs, { moduleA: { config: {}, name: 'moduleA', reference: { optional: undefined, name: 'moduleA', path: path.join(fixturesPath, 'app/module-a'), }, }, }); }); }); ================================================ FILE: plugin/config/test/fixtures/apps/app-with-modules/app/module-a/package.json ================================================ { "name": "module-a", "eggModule": { "name": "moduleA" } } ================================================ FILE: plugin/config/test/fixtures/apps/app-with-modules/config/config.default.ts ================================================ export default () => { return { tegg: { readModuleOptions: { extraFilePattern: [ '!**/dist' ], }, }, }; }; ================================================ FILE: plugin/config/test/fixtures/apps/app-with-modules/package.json ================================================ { "name": "foo" } ================================================ FILE: plugin/config/test/fixtures/apps/duplicate-optional-module/config/config.default.js ================================================ 'use strict'; const path = require('path'); module.exports = function(appInfo) { const config = { keys: 'test key', customLogger: { xxLogger: { file: path.join(appInfo.root, 'logs/xx.log'), }, }, security: { csrf: { ignoreJSON: false, } }, }; return config; }; ================================================ FILE: plugin/config/test/fixtures/apps/duplicate-optional-module/config/plugin.js ================================================ 'use strict'; exports.tracer = { package: 'egg-tracer', enable: true, }; exports.watcher = false; ================================================ FILE: plugin/config/test/fixtures/apps/duplicate-optional-module/node_modules/foo/package.json ================================================ { "name": "foo", "dependencies": { "used": "*", "unused": "*" } } ================================================ FILE: plugin/config/test/fixtures/apps/duplicate-optional-module/node_modules/unused/Unused.js ================================================ "use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.UnusedProto = void 0; const tegg_core_decorator_1 = require('../../../../../../../../core/core-decorator'); let UnusedProto = class UnusedProto { }; exports.UnusedProto = UnusedProto; exports.UnusedProto = UnusedProto = __decorate([ (0, tegg_core_decorator_1.SingletonProto)() ], UnusedProto); ================================================ FILE: plugin/config/test/fixtures/apps/duplicate-optional-module/node_modules/unused/package.json ================================================ { "name": "unused", "eggModule": { "name": "unused" } } ================================================ FILE: plugin/config/test/fixtures/apps/duplicate-optional-module/node_modules/used/Used.js ================================================ "use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.UsedProto = void 0; const tegg_core_decorator_1 = require('../../../../../../../../core/core-decorator'); let UsedProto = class UsedProto { }; exports.UsedProto = UsedProto; exports.UsedProto = UsedProto = __decorate([ (0, tegg_core_decorator_1.SingletonProto)({ accessLevel: tegg_core_decorator_1.AccessLevel.PUBLIC, }) ], UsedProto); ================================================ FILE: plugin/config/test/fixtures/apps/duplicate-optional-module/node_modules/used/package.json ================================================ { "name": "used", "eggModule": { "name": "used" } } ================================================ FILE: plugin/config/test/fixtures/apps/duplicate-optional-module/package.json ================================================ { "name": "egg-app", "egg": { "framework": "foo" }, "dependencies": { "used": "*" } } ================================================ FILE: plugin/config/tsconfig.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "baseUrl": "./" }, "exclude": [ "node_modules", "test" ] } ================================================ FILE: plugin/config/tsconfig.pub.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "baseUrl": "./" }, "exclude": [ "node_modules", "test" ] } ================================================ FILE: plugin/config/typings/index.d.ts ================================================ import 'egg'; import { ModuleReference as ModuleReferenceAlias } from '@eggjs/tegg-common-util'; declare module 'egg' { export type ModuleReference = ModuleReferenceAlias; export interface ModuleConfig { } export interface ModuleConfigHolder { name: string; config: ModuleConfig; reference: ModuleReference; } export interface ModuleConfigApplication { moduleReferences: readonly ModuleReference[]; moduleConfigs: Record; } export interface Application extends ModuleConfigApplication { } } ================================================ FILE: plugin/controller/CHANGELOG.md ================================================ # Change Log All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. ## [3.78.15](https://github.com/eggjs/tegg/compare/v3.78.14...v3.78.15) (2026-04-23) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.78.14](https://github.com/eggjs/tegg/compare/v3.78.13...v3.78.14) (2026-04-21) ### Bug Fixes * mcp sse connect clear ([#440](https://github.com/eggjs/tegg/issues/440)) ([4c59d6a](https://github.com/eggjs/tegg/commit/4c59d6a13cf066dec70173494bd09c30a5052712)) ## [3.78.13](https://github.com/eggjs/tegg/compare/v3.78.12...v3.78.13) (2026-04-21) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.78.12](https://github.com/eggjs/tegg/compare/v3.78.11...v3.78.12) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.78.11](https://github.com/eggjs/tegg/compare/v3.78.10...v3.78.11) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.78.10](https://github.com/eggjs/tegg/compare/v3.78.9...v3.78.10) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.78.9](https://github.com/eggjs/tegg/compare/v3.78.8...v3.78.9) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.78.8](https://github.com/eggjs/tegg/compare/v3.78.7...v3.78.8) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.78.7](https://github.com/eggjs/tegg/compare/v3.78.6...v3.78.7) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.78.6](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.6) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.78.5](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.5) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.78.4](https://github.com/eggjs/tegg/compare/v3.78.3...v3.78.4) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.78.3](https://github.com/eggjs/tegg/compare/v3.78.2...v3.78.3) (2026-04-15) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.78.2](https://github.com/eggjs/tegg/compare/v3.78.1...v3.78.2) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.78.1](https://github.com/eggjs/tegg/compare/v3.78.0...v3.78.1) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-controller-plugin # [3.78.0](https://github.com/eggjs/tegg/compare/v3.77.2...v3.78.0) (2026-04-09) ### Features * **agent-runtime:** rewrite streamRun with StreamEvent format and reconnection ([#432](https://github.com/eggjs/tegg/issues/432)) ([d03dac2](https://github.com/eggjs/tegg/commit/d03dac2ddd78641acb47e19275488ad9fbfcda2a)) ## [3.77.2](https://github.com/eggjs/tegg/compare/v3.77.1...v3.77.2) (2026-04-07) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.77.1](https://github.com/eggjs/tegg/compare/v3.77.0...v3.77.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-controller-plugin # [3.77.0](https://github.com/eggjs/tegg/compare/v3.76.1...v3.77.0) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.76.1](https://github.com/eggjs/tegg/compare/v3.76.0...v3.76.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-controller-plugin # [3.76.0](https://github.com/eggjs/tegg/compare/v3.75.1...v3.76.0) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.75.1](https://github.com/eggjs/tegg/compare/v3.75.0...v3.75.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-controller-plugin # [3.75.0](https://github.com/eggjs/tegg/compare/v3.74.0...v3.75.0) (2026-03-30) **Note:** Version bump only for package @eggjs/tegg-controller-plugin # [3.74.0](https://github.com/eggjs/tegg/compare/v3.73.0...v3.74.0) (2026-03-30) **Note:** Version bump only for package @eggjs/tegg-controller-plugin # [3.73.0](https://github.com/eggjs/tegg/compare/v3.72.0...v3.73.0) (2026-03-25) ### Bug Fixes * compatibility with @modelcontextprotocol/sdk 1.26 ([#404](https://github.com/eggjs/tegg/issues/404)) ([dbda39a](https://github.com/eggjs/tegg/commit/dbda39ad2f4be5879f1e0540cf26a242cecd05e3)) * mcp helper init ([#415](https://github.com/eggjs/tegg/issues/415)) ([58f15e1](https://github.com/eggjs/tegg/commit/58f15e176edf3e8229d2bcddcf034fd7d96d5f14)) ### Features * add agent-runtime package with @AgentController decorator ([#411](https://github.com/eggjs/tegg/issues/411)) ([d4d0006](https://github.com/eggjs/tegg/commit/d4d00061e90230f82c0958bcf5268f8a511395db)) # [3.72.0](https://github.com/eggjs/tegg/compare/v3.71.2...v3.72.0) (2026-02-04) ### Features * add structured tool ([#387](https://github.com/eggjs/tegg/issues/387)) ([56c23ad](https://github.com/eggjs/tegg/commit/56c23adb0af25ce0fd3624491eaf7af3fb1570cf)) ## [3.71.2](https://github.com/eggjs/tegg/compare/v3.71.1...v3.71.2) (2026-01-30) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.71.1](https://github.com/eggjs/tegg/compare/v3.71.0...v3.71.1) (2026-01-28) ### Bug Fixes * mcp proxy header ([#397](https://github.com/eggjs/tegg/issues/397)) ([d79cf73](https://github.com/eggjs/tegg/commit/d79cf735e535fe41756a4a349e1b04a556f81ce9)) # [3.71.0](https://github.com/eggjs/tegg/compare/v3.70.1...v3.71.0) (2026-01-26) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.70.1](https://github.com/eggjs/tegg/compare/v3.70.0...v3.70.1) (2026-01-14) **Note:** Version bump only for package @eggjs/tegg-controller-plugin # [3.70.0](https://github.com/eggjs/tegg/compare/v3.69.0...v3.70.0) (2026-01-07) **Note:** Version bump only for package @eggjs/tegg-controller-plugin # [3.69.0](https://github.com/eggjs/tegg/compare/v3.68.0...v3.69.0) (2026-01-06) **Note:** Version bump only for package @eggjs/tegg-controller-plugin # [3.68.0](https://github.com/eggjs/tegg/compare/v3.67.2...v3.68.0) (2025-12-29) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.67.2](https://github.com/eggjs/tegg/compare/v3.67.1...v3.67.2) (2025-12-24) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.67.1](https://github.com/eggjs/tegg/compare/v3.67.0...v3.67.1) (2025-12-23) ### Bug Fixes * zod v4 ([#381](https://github.com/eggjs/tegg/issues/381)) ([43614c8](https://github.com/eggjs/tegg/commit/43614c8734084a98b1a25c6e907c9c12ff41cb8f)) # [3.67.0](https://github.com/eggjs/tegg/compare/v3.66.0...v3.67.0) (2025-12-18) **Note:** Version bump only for package @eggjs/tegg-controller-plugin # [3.66.0](https://github.com/eggjs/tegg/compare/v3.65.3...v3.66.0) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.65.3](https://github.com/eggjs/tegg/compare/v3.65.2...v3.65.3) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.65.2](https://github.com/eggjs/tegg/compare/v3.65.1...v3.65.2) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.65.1](https://github.com/eggjs/tegg/compare/v3.65.0...v3.65.1) (2025-12-16) ### Bug Fixes * hono node v16 ([#374](https://github.com/eggjs/tegg/issues/374)) ([870b5e3](https://github.com/eggjs/tegg/commit/870b5e34f41399a44023756614b8bb5c59efc6ee)) # [3.65.0](https://github.com/eggjs/tegg/compare/v3.64.5...v3.65.0) (2025-12-10) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.64.5](https://github.com/eggjs/tegg/compare/v3.64.4...v3.64.5) (2025-12-08) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.64.4](https://github.com/eggjs/tegg/compare/v3.64.3...v3.64.4) (2025-12-06) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.64.3](https://github.com/eggjs/tegg/compare/v3.64.2...v3.64.3) (2025-12-06) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.64.2](https://github.com/eggjs/tegg/compare/v3.64.1...v3.64.2) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.64.1](https://github.com/eggjs/tegg/compare/v3.64.0...v3.64.1) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-controller-plugin # [3.64.0](https://github.com/eggjs/tegg/compare/v3.63.2...v3.64.0) (2025-11-27) ### Bug Fixes * add ping config ([#364](https://github.com/eggjs/tegg/issues/364)) ([c2757fc](https://github.com/eggjs/tegg/commit/c2757fc568599f7da9a65b2b8fd2ebf3f4522f9d)) * mcp zod type and langchain test version ([#369](https://github.com/eggjs/tegg/issues/369)) ([8178168](https://github.com/eggjs/tegg/commit/81781685c392346d21c56b649bfe8bb7a99bc9fb)) ### Features * add langchain decorator ([#356](https://github.com/eggjs/tegg/issues/356)) ([b176c73](https://github.com/eggjs/tegg/commit/b176c7325009c372ce9d17f348b4fc1f1b6d7fb1)) ## [3.63.2](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.2) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.63.1](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.1) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg-controller-plugin # [3.63.0](https://github.com/eggjs/tegg/compare/v3.62.3...v3.63.0) (2025-11-18) ### Features * add MiddlewareGraphHook to handle controller middleware depende… ([#361](https://github.com/eggjs/tegg/issues/361)) ([7ab3eae](https://github.com/eggjs/tegg/commit/7ab3eae1af20e14101e1df63628a426cb5f6d3db)) ## [3.62.3](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.3) (2025-11-05) ### Bug Fixes * mcp args chinese ([61dad90](https://github.com/eggjs/tegg/commit/61dad903b2ba6140dd52a5b5600a36caef26373f)) ## [3.62.2](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.2) (2025-11-05) ### Bug Fixes * mcp args chinese ([61dad90](https://github.com/eggjs/tegg/commit/61dad903b2ba6140dd52a5b5600a36caef26373f)) ## [3.62.1](https://github.com/eggjs/tegg/compare/v3.62.0...v3.62.1) (2025-11-03) ### Bug Fixes * multi mcp client ([#357](https://github.com/eggjs/tegg/issues/357)) ([f9e4728](https://github.com/eggjs/tegg/commit/f9e47289160dffc5b47e93b60128a25ffd94fb4e)) # [3.62.0](https://github.com/eggjs/tegg/compare/v3.61.0...v3.62.0) (2025-09-24) ### Features * add mcp middleware hook ([#344](https://github.com/eggjs/tegg/issues/344)) ([7215645](https://github.com/eggjs/tegg/commit/72156452a2d69ff8f31b4fe76324dd4164761698)) # [3.61.0](https://github.com/eggjs/tegg/compare/v3.60.3...v3.61.0) (2025-08-15) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.60.3](https://github.com/eggjs/tegg/compare/v3.60.2...v3.60.3) (2025-08-06) ### Bug Fixes * mcp middleware ([#340](https://github.com/eggjs/tegg/issues/340)) ([a47db22](https://github.com/eggjs/tegg/commit/a47db2295a899113aad46d7f4ca0857d91d44774)) ## [3.60.2](https://github.com/eggjs/tegg/compare/v3.60.1...v3.60.2) (2025-08-06) ### Bug Fixes * csrf type is bool bug ([#338](https://github.com/eggjs/tegg/issues/338)) ([67f69c9](https://github.com/eggjs/tegg/commit/67f69c90f8550f56d4bbf336986a0feabd1d192c)) ## [3.60.1](https://github.com/eggjs/tegg/compare/v3.60.0...v3.60.1) (2025-07-28) **Note:** Version bump only for package @eggjs/tegg-controller-plugin # [3.60.0](https://github.com/eggjs/tegg/compare/v3.59.1...v3.60.0) (2025-07-28) ### Features * add multiple mcp server ([#337](https://github.com/eggjs/tegg/issues/337)) ([5b5e233](https://github.com/eggjs/tegg/commit/5b5e233510111b63bbcba14da1703becccebbd2f)) ## [3.59.1](https://github.com/eggjs/tegg/compare/v3.59.0...v3.59.1) (2025-07-16) **Note:** Version bump only for package @eggjs/tegg-controller-plugin # [3.59.0](https://github.com/eggjs/tegg/compare/v3.58.0...v3.59.0) (2025-07-07) ### Features * add mcp global middleware ([#335](https://github.com/eggjs/tegg/issues/335)) ([7722102](https://github.com/eggjs/tegg/commit/772210298a937b7fbae9fd4fd1e1bc318b754cef)) # [3.58.0](https://github.com/eggjs/tegg/compare/v3.57.14...v3.58.0) (2025-07-01) ### Features * add timeout ([#334](https://github.com/eggjs/tegg/issues/334)) ([6d5d94b](https://github.com/eggjs/tegg/commit/6d5d94b6f319388a94b4adf4d427b95d2b851c17)) ## [3.57.14](https://github.com/eggjs/tegg/compare/v3.57.13...v3.57.14) (2025-06-18) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.57.13](https://github.com/eggjs/tegg/compare/v3.57.12...v3.57.13) (2025-06-18) ### Bug Fixes * add mcp clearInterval ([#332](https://github.com/eggjs/tegg/issues/332)) ([b29d68b](https://github.com/eggjs/tegg/commit/b29d68bd4ec4cdbef5b8246426fa0391208e8ded)) ## [3.57.12](https://github.com/eggjs/tegg/compare/v3.57.11...v3.57.12) (2025-06-16) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.57.11](https://github.com/eggjs/tegg/compare/v3.57.10...v3.57.11) (2025-06-16) ### Bug Fixes * mcp mem leak ([#329](https://github.com/eggjs/tegg/issues/329)) ([bddf8ed](https://github.com/eggjs/tegg/commit/bddf8ed45ea477f59c4d3b7d63bb81ca89484e56)) ## [3.57.10](https://github.com/eggjs/tegg/compare/v3.57.9...v3.57.10) (2025-06-12) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.57.9](https://github.com/eggjs/tegg/compare/v3.57.8...v3.57.9) (2025-05-29) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.57.8](https://github.com/eggjs/tegg/compare/v3.57.7...v3.57.8) (2025-05-29) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.57.7](https://github.com/eggjs/tegg/compare/v3.57.6...v3.57.7) (2025-05-28) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.57.6](https://github.com/eggjs/tegg/compare/v3.57.5...v3.57.6) (2025-05-27) ### Bug Fixes * sse new ctx ([#323](https://github.com/eggjs/tegg/issues/323)) ([5716e0a](https://github.com/eggjs/tegg/commit/5716e0a33f8f58249ebdc1a3b6fb7959394ef4ee)) ## [3.57.5](https://github.com/eggjs/tegg/compare/v3.57.4...v3.57.5) (2025-05-15) ### Bug Fixes * zod phantom dependency ([#322](https://github.com/eggjs/tegg/issues/322)) ([e92372e](https://github.com/eggjs/tegg/commit/e92372eb884d0f5d8227d340a3d7db01b51267cf)) ## [3.57.4](https://github.com/eggjs/tegg/compare/v3.57.3...v3.57.4) (2025-05-14) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.57.3](https://github.com/eggjs/tegg/compare/v3.57.2...v3.57.3) (2025-05-13) ### Bug Fixes * update sessionIdGenerator ([#320](https://github.com/eggjs/tegg/issues/320)) ([ffc1e19](https://github.com/eggjs/tegg/commit/ffc1e19f69c30aa21a19c8f7ecc920fece21a947)) ## [3.57.2](https://github.com/eggjs/tegg/compare/v3.57.1...v3.57.2) (2025-05-13) ### Bug Fixes * stream mcp wait ([#319](https://github.com/eggjs/tegg/issues/319)) ([47ef28b](https://github.com/eggjs/tegg/commit/47ef28b1fae06c57857a7b340d0703403a907859)) ## [3.57.1](https://github.com/eggjs/tegg/compare/v3.57.0...v3.57.1) (2025-05-13) ### Bug Fixes * mcp context proto ([#318](https://github.com/eggjs/tegg/issues/318)) ([4d8e107](https://github.com/eggjs/tegg/commit/4d8e107fad4414da9593dd07ab2ae888dfd6a335)) # [3.57.0](https://github.com/eggjs/tegg/compare/v3.56.3...v3.57.0) (2025-05-09) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.56.3](https://github.com/eggjs/tegg/compare/v3.56.2...v3.56.3) (2025-05-07) ### Bug Fixes * dep ([#313](https://github.com/eggjs/tegg/issues/313)) ([791feea](https://github.com/eggjs/tegg/commit/791feead91ad48adaa2dee4c0746bea382e61d34)) * mcp teggCtxLifecycleMiddleware ([#312](https://github.com/eggjs/tegg/issues/312)) ([5304384](https://github.com/eggjs/tegg/commit/53043840c3aaab0e485db50b7a2d9362266eef8c)) ## [3.56.2](https://github.com/eggjs/tegg/compare/v3.56.1...v3.56.2) (2025-05-01) ### Bug Fixes * mcp version check ([#311](https://github.com/eggjs/tegg/issues/311)) ([7f16c9c](https://github.com/eggjs/tegg/commit/7f16c9c361cfec8b5ffd4075b23cff317cc5207a)) ## [3.56.1](https://github.com/eggjs/tegg/compare/v3.56.0...v3.56.1) (2025-04-29) ### Bug Fixes * mock init to ready ([#310](https://github.com/eggjs/tegg/issues/310)) ([2bd6fb6](https://github.com/eggjs/tegg/commit/2bd6fb6d5e60945637358140c8d669f78ea923f7)) # [3.56.0](https://github.com/eggjs/tegg/compare/v3.55.0...v3.56.0) (2025-04-29) ### Features * add mcp stateless ([#309](https://github.com/eggjs/tegg/issues/309)) ([b79d313](https://github.com/eggjs/tegg/commit/b79d313aa8f24adc91f88ea0732f2d98c0a8ead9)) # [3.55.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.55.0) (2025-04-29) ### Features * add mcp ([#307](https://github.com/eggjs/tegg/issues/307)) ([a9a57b4](https://github.com/eggjs/tegg/commit/a9a57b4d7102dd552e09d33c3f82fc15a245790a)) # [3.54.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.54.0) (2025-04-29) ### Features * add mcp ([#307](https://github.com/eggjs/tegg/issues/307)) ([a9a57b4](https://github.com/eggjs/tegg/commit/a9a57b4d7102dd552e09d33c3f82fc15a245790a)) # [3.53.0](https://github.com/eggjs/tegg/compare/v3.52.1...v3.53.0) (2025-04-21) ### Bug Fixes * stream end ([#302](https://github.com/eggjs/tegg/issues/302)) ([7f1f4b3](https://github.com/eggjs/tegg/commit/7f1f4b396294af5609c9454f6882d213dc237512)) ### Features * add timeout metadata for http controller ([#301](https://github.com/eggjs/tegg/issues/301)) ([68980c2](https://github.com/eggjs/tegg/commit/68980c23de81dbc9bd86c1d8df7b3952f52aa5ce)) ## [3.52.1](https://github.com/eggjs/tegg/compare/v3.52.0...v3.52.1) (2025-03-14) **Note:** Version bump only for package @eggjs/tegg-controller-plugin # [3.52.0](https://github.com/eggjs/tegg/compare/v3.51.2...v3.52.0) (2024-12-30) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.51.2](https://github.com/eggjs/tegg/compare/v3.51.1...v3.51.2) (2024-12-09) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.51.1](https://github.com/eggjs/tegg/compare/v3.51.0...v3.51.1) (2024-11-06) **Note:** Version bump only for package @eggjs/tegg-controller-plugin # [3.51.0](https://github.com/eggjs/tegg/compare/v3.50.1...v3.51.0) (2024-10-30) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.50.1](https://github.com/eggjs/tegg/compare/v3.50.0...v3.50.1) (2024-10-23) **Note:** Version bump only for package @eggjs/tegg-controller-plugin # [3.50.0](https://github.com/eggjs/tegg/compare/v3.49.0...v3.50.0) (2024-10-22) **Note:** Version bump only for package @eggjs/tegg-controller-plugin # [3.49.0](https://github.com/eggjs/tegg/compare/v3.48.1...v3.49.0) (2024-10-21) ### Features * expand register add loadUnit ([#251](https://github.com/eggjs/tegg/issues/251)) ([8a1649d](https://github.com/eggjs/tegg/commit/8a1649d5ea539d22c7cfd8881595247a07e3fbd7)) ## [3.48.1](https://github.com/eggjs/tegg/compare/v3.48.0...v3.48.1) (2024-10-14) **Note:** Version bump only for package @eggjs/tegg-controller-plugin # [3.48.0](https://github.com/eggjs/tegg/compare/v3.47.2...v3.48.0) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.47.2](https://github.com/eggjs/tegg/compare/v3.47.1...v3.47.2) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.47.1](https://github.com/eggjs/tegg/compare/v3.47.0...v3.47.1) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-controller-plugin # [3.47.0](https://github.com/eggjs/tegg/compare/v3.46.4...v3.47.0) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.46.4](https://github.com/eggjs/tegg/compare/v3.46.3...v3.46.4) (2024-10-09) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.46.3](https://github.com/eggjs/tegg/compare/v3.46.2...v3.46.3) (2024-10-08) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.46.2](https://github.com/eggjs/tegg/compare/v3.46.1...v3.46.2) (2024-10-07) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.46.1](https://github.com/eggjs/tegg/compare/v3.46.0...v3.46.1) (2024-09-30) **Note:** Version bump only for package @eggjs/tegg-controller-plugin # [3.46.0](https://github.com/eggjs/tegg/compare/v3.45.0...v3.46.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-controller-plugin # [3.45.0](https://github.com/eggjs/tegg/compare/v3.44.1...v3.45.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.44.1](https://github.com/eggjs/tegg/compare/v3.44.0...v3.44.1) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-controller-plugin # [3.44.0](https://github.com/eggjs/tegg/compare/v3.43.2...v3.44.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.43.2](https://github.com/eggjs/tegg/compare/v3.43.1...v3.43.2) (2024-09-14) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.43.1](https://github.com/eggjs/tegg/compare/v3.43.0...v3.43.1) (2024-09-14) **Note:** Version bump only for package @eggjs/tegg-controller-plugin # [3.43.0](https://github.com/eggjs/tegg/compare/v3.42.0...v3.43.0) (2024-09-13) ### Features * add http cookies ([#235](https://github.com/eggjs/tegg/issues/235)) ([f46efa5](https://github.com/eggjs/tegg/commit/f46efa54b03bad41504bf76f6ed2baa8c48858ce)) # [3.42.0](https://github.com/eggjs/tegg/compare/v3.41.0...v3.42.0) (2024-09-10) **Note:** Version bump only for package @eggjs/tegg-controller-plugin # [3.41.0](https://github.com/eggjs/tegg/compare/v3.40.1...v3.41.0) (2024-08-26) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.40.1](https://github.com/eggjs/tegg/compare/v3.40.0...v3.40.1) (2024-08-23) **Note:** Version bump only for package @eggjs/tegg-controller-plugin # [3.40.0](https://github.com/eggjs/tegg/compare/v3.39.5...v3.40.0) (2024-08-22) ### Features * @Middleware support Advice ([#231](https://github.com/eggjs/tegg/issues/231)) ([613a89d](https://github.com/eggjs/tegg/commit/613a89da7ea6dd70d50e34aa9f4152358a622625)) ## [3.39.5](https://github.com/eggjs/tegg/compare/v3.39.4...v3.39.5) (2024-08-09) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.39.4](https://github.com/eggjs/tegg/compare/v3.39.3...v3.39.4) (2024-07-09) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.39.3](https://github.com/eggjs/tegg/compare/v3.39.2...v3.39.3) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.39.2](https://github.com/eggjs/tegg/compare/v3.39.1...v3.39.2) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.39.1](https://github.com/eggjs/tegg/compare/v3.39.0...v3.39.1) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-controller-plugin # [3.39.0](https://github.com/eggjs/tegg/compare/v3.38.0...v3.39.0) (2024-04-19) **Note:** Version bump only for package @eggjs/tegg-controller-plugin # [3.38.0](https://github.com/eggjs/tegg/compare/v3.37.3...v3.38.0) (2024-04-18) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.37.3](https://github.com/eggjs/tegg/compare/v3.37.2...v3.37.3) (2024-04-17) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.37.2](https://github.com/eggjs/tegg/compare/v3.37.1...v3.37.2) (2024-04-17) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.37.1](https://github.com/eggjs/tegg/compare/v3.37.0...v3.37.1) (2024-04-16) **Note:** Version bump only for package @eggjs/tegg-controller-plugin # [3.37.0](https://github.com/eggjs/tegg/compare/v3.36.3...v3.37.0) (2024-04-16) ### Features * add HTTPHeaders decorator ([#208](https://github.com/eggjs/tegg/issues/208)) ([4678c45](https://github.com/eggjs/tegg/commit/4678c450d8b3c632bbdbe2b49b9c02e99f16733c)) ## [3.36.3](https://github.com/eggjs/tegg/compare/v3.36.2...v3.36.3) (2024-04-10) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.36.2](https://github.com/eggjs/tegg/compare/v3.36.1...v3.36.2) (2024-04-08) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.36.1](https://github.com/eggjs/tegg/compare/v3.36.0...v3.36.1) (2024-04-07) **Note:** Version bump only for package @eggjs/tegg-controller-plugin # [3.36.0](https://github.com/eggjs/tegg/compare/v3.35.1...v3.36.0) (2024-04-02) ### Features * impl ajv + typebox Validator ([#201](https://github.com/eggjs/tegg/issues/201)) ([9fd585d](https://github.com/eggjs/tegg/commit/9fd585de9b613466c96b73494a08a494db34ea57)) ## [3.35.1](https://github.com/eggjs/tegg/compare/v3.35.0...v3.35.1) (2024-03-26) **Note:** Version bump only for package @eggjs/tegg-controller-plugin # [3.35.0](https://github.com/eggjs/tegg/compare/v3.34.0...v3.35.0) (2024-03-26) **Note:** Version bump only for package @eggjs/tegg-controller-plugin # [3.34.0](https://github.com/eggjs/tegg/compare/v3.33.1...v3.34.0) (2024-03-22) ### Features * impl dal for standalone tegg ([#197](https://github.com/eggjs/tegg/issues/197)) ([56b259d](https://github.com/eggjs/tegg/commit/56b259d7215a9d9542b36e421996623819369846)) ## [3.33.1](https://github.com/eggjs/tegg/compare/v3.33.0...v3.33.1) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-controller-plugin # [3.33.0](https://github.com/eggjs/tegg/compare/v3.32.0...v3.33.0) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-controller-plugin # [3.32.0](https://github.com/eggjs/tegg/compare/v3.31.0...v3.32.0) (2024-02-19) **Note:** Version bump only for package @eggjs/tegg-controller-plugin # [3.31.0](https://github.com/eggjs/tegg/compare/v3.30.1...v3.31.0) (2024-01-31) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.30.1](https://github.com/eggjs/tegg/compare/v3.30.0...v3.30.1) (2024-01-25) **Note:** Version bump only for package @eggjs/tegg-controller-plugin # [3.30.0](https://github.com/eggjs/tegg/compare/v3.29.0...v3.30.0) (2024-01-17) **Note:** Version bump only for package @eggjs/tegg-controller-plugin # [3.29.0](https://github.com/eggjs/tegg/compare/v3.28.2...v3.29.0) (2023-12-26) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.28.2](https://github.com/eggjs/tegg/compare/v3.28.1...v3.28.2) (2023-12-12) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.28.1](https://github.com/eggjs/tegg/compare/v3.28.0...v3.28.1) (2023-12-11) **Note:** Version bump only for package @eggjs/tegg-controller-plugin # [3.28.0](https://github.com/eggjs/tegg/compare/v3.27.0...v3.28.0) (2023-12-10) **Note:** Version bump only for package @eggjs/tegg-controller-plugin # [3.27.0](https://github.com/eggjs/tegg/compare/v3.26.0...v3.27.0) (2023-11-23) **Note:** Version bump only for package @eggjs/tegg-controller-plugin # [3.26.0](https://github.com/eggjs/tegg/compare/v3.25.2...v3.26.0) (2023-11-17) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.25.2](https://github.com/eggjs/tegg/compare/v3.25.1...v3.25.2) (2023-11-06) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.25.1](https://github.com/eggjs/tegg/compare/v3.25.0...v3.25.1) (2023-11-03) **Note:** Version bump only for package @eggjs/tegg-controller-plugin # [3.25.0](https://github.com/eggjs/tegg/compare/v3.24.0...v3.25.0) (2023-11-03) **Note:** Version bump only for package @eggjs/tegg-controller-plugin # [3.24.0](https://github.com/eggjs/tegg/compare/v3.23.0...v3.24.0) (2023-10-26) ### Features * support Request decorators for HTTPController ([#159](https://github.com/eggjs/tegg/issues/159)) ([945e1eb](https://github.com/eggjs/tegg/commit/945e1eb18237f40879acdd2e43cd53dd2e8272a9)) # [3.23.0](https://github.com/eggjs/tegg/compare/v3.22.0...v3.23.0) (2023-09-20) **Note:** Version bump only for package @eggjs/tegg-controller-plugin # [3.20.0](https://github.com/eggjs/tegg/compare/v3.19.0...v3.20.0) (2023-09-07) **Note:** Version bump only for package @eggjs/tegg-controller-plugin # [3.19.0](https://github.com/eggjs/tegg/compare/v3.18.1...v3.19.0) (2023-08-31) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.18.1](https://github.com/eggjs/tegg/compare/v3.18.0...v3.18.1) (2023-08-29) **Note:** Version bump only for package @eggjs/tegg-controller-plugin # [3.18.0](https://github.com/eggjs/tegg/compare/v3.17.0...v3.18.0) (2023-08-29) **Note:** Version bump only for package @eggjs/tegg-controller-plugin # [3.17.0](https://github.com/eggjs/tegg/compare/v3.16.0...v3.17.0) (2023-08-24) ### Features * impl MultiInstanceProto ([#145](https://github.com/eggjs/tegg/issues/145)) ([12fd5cf](https://github.com/eggjs/tegg/commit/12fd5cff4004578bcc737dcdf4f7e9d1159f5633)) # [3.16.0](https://github.com/eggjs/tegg/compare/v3.15.0...v3.16.0) (2023-08-24) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.14.3](https://github.com/eggjs/tegg/compare/v3.14.2...v3.14.3) (2023-08-14) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.14.2](https://github.com/eggjs/tegg/compare/v3.14.1...v3.14.2) (2023-08-14) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.14.1](https://github.com/eggjs/tegg/compare/v3.14.0...v3.14.1) (2023-08-11) ### Bug Fixes * ensure ContextInitiator be called after ctx ready ([#138](https://github.com/eggjs/tegg/issues/138)) ([79e16da](https://github.com/eggjs/tegg/commit/79e16dae913b6114ac8d13bde8de60164d57dab3)) # [3.14.0](https://github.com/eggjs/tegg/compare/v3.13.0...v3.14.0) (2023-07-31) **Note:** Version bump only for package @eggjs/tegg-controller-plugin # [3.13.0](https://github.com/eggjs/tegg/compare/v3.12.0...v3.13.0) (2023-07-25) **Note:** Version bump only for package @eggjs/tegg-controller-plugin # [3.12.0](https://github.com/eggjs/tegg/compare/v3.11.1...v3.12.0) (2023-07-13) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.11.1](https://github.com/eggjs/tegg/compare/v3.11.0...v3.11.1) (2023-06-29) **Note:** Version bump only for package @eggjs/tegg-controller-plugin # [3.11.0](https://github.com/eggjs/tegg/compare/v3.10.0...v3.11.0) (2023-06-29) **Note:** Version bump only for package @eggjs/tegg-controller-plugin # [3.10.0](https://github.com/eggjs/tegg/compare/v3.9.0...v3.10.0) (2023-06-28) **Note:** Version bump only for package @eggjs/tegg-controller-plugin # [3.9.0](https://github.com/eggjs/tegg/compare/v3.8.0...v3.9.0) (2023-06-20) **Note:** Version bump only for package @eggjs/tegg-controller-plugin # [3.8.0](https://github.com/eggjs/tegg/compare/v3.7.0...v3.8.0) (2023-05-30) ### Features * impl EggObjectLifecycle hook in decorator ([#119](https://github.com/eggjs/tegg/issues/119)) ([cced8a2](https://github.com/eggjs/tegg/commit/cced8a2e009c33d5040fa21d00409fddef471b0e)) # [3.7.0](https://github.com/eggjs/tegg/compare/v3.6.3...v3.7.0) (2023-04-03) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.6.3](https://github.com/eggjs/tegg/compare/v3.6.2...v3.6.3) (2023-03-02) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.6.2](https://github.com/eggjs/tegg/compare/v3.6.1...v3.6.2) (2023-02-16) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.6.1](https://github.com/eggjs/tegg/compare/v3.6.0...v3.6.1) (2023-02-14) **Note:** Version bump only for package @eggjs/tegg-controller-plugin # [3.6.0](https://github.com/eggjs/tegg/compare/v3.5.2...v3.6.0) (2023-02-13) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.5.2](https://github.com/eggjs/tegg/compare/v3.5.1...v3.5.2) (2023-02-10) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.5.1](https://github.com/eggjs/tegg/compare/v3.5.0...v3.5.1) (2023-02-10) **Note:** Version bump only for package @eggjs/tegg-controller-plugin # [3.5.0](https://github.com/eggjs/tegg/compare/v3.4.1...v3.5.0) (2023-02-10) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.4.1](https://github.com/eggjs/tegg/compare/v3.4.0...v3.4.1) (2023-02-02) **Note:** Version bump only for package @eggjs/tegg-controller-plugin # [3.4.0](https://github.com/eggjs/tegg/compare/v3.3.4...v3.4.0) (2023-02-01) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.3.4](https://github.com/eggjs/tegg/compare/v3.3.3...v3.3.4) (2023-01-29) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.3.3](https://github.com/eggjs/tegg/compare/v3.3.2...v3.3.3) (2023-01-29) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.3.2](https://github.com/eggjs/tegg/compare/v3.3.1...v3.3.2) (2023-01-29) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.3.1](https://github.com/eggjs/tegg/compare/v3.3.0...v3.3.1) (2023-01-28) **Note:** Version bump only for package @eggjs/tegg-controller-plugin # [3.3.0](https://github.com/eggjs/tegg/compare/v3.2.4...v3.3.0) (2023-01-28) ### Bug Fixes * router type ([#83](https://github.com/eggjs/tegg/issues/83)) ([b32d9b8](https://github.com/eggjs/tegg/commit/b32d9b8e94552d27dc0249c9f38e7223b24beff0)) ## [3.2.3](https://github.com/eggjs/tegg/compare/v3.2.2...v3.2.3) (2023-01-16) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.2.2](https://github.com/eggjs/tegg/compare/v3.2.1...v3.2.2) (2023-01-06) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [3.2.1](https://github.com/eggjs/tegg/compare/v3.2.0...v3.2.1) (2022-12-28) **Note:** Version bump only for package @eggjs/tegg-controller-plugin # [3.2.0](https://github.com/eggjs/tegg/compare/v3.1.0...v3.2.0) (2022-12-28) ### Features * impl mockModuleContextScope ([#73](https://github.com/eggjs/tegg/issues/73)) ([041881c](https://github.com/eggjs/tegg/commit/041881ca317ad81366172a35ac56b7b2dc0a0488)) # [3.1.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.1.0) (2022-12-27) ### Bug Fixes * add 'globby' as dependencies ([#71](https://github.com/eggjs/tegg/issues/71)) ([76d85d9](https://github.com/eggjs/tegg/commit/76d85d9948527028f926ae0ff5a61111eb1cbd04)) * fix rootProtoManager.registerRootProto ([f416ed7](https://github.com/eggjs/tegg/commit/f416ed70af1c46d31ebf712b208205d67337d958)) ### Features * **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) * impl Host decorator ([#48](https://github.com/eggjs/tegg/issues/48)) ([65dc7a8](https://github.com/eggjs/tegg/commit/65dc7a899ba72dd0851c35046562766d7f2b71b6)) * middleware decorator allow multi middleware function ([#46](https://github.com/eggjs/tegg/issues/46)) ([a4b55f7](https://github.com/eggjs/tegg/commit/a4b55f7065c3d78e2c98c4b05f01871f666542ef)) * multi host decorator ([#68](https://github.com/eggjs/tegg/issues/68)) ([f6679de](https://github.com/eggjs/tegg/commit/f6679de1495024ecb9182168843300aa91288508)) # [3.0.0](https://github.com/eggjs/tegg/compare/v3.0.0-alpha.0...v3.0.0) (2022-12-26) ### Features * delete controller root hook ([bbb68f4](https://github.com/eggjs/tegg/commit/bbb68f43a1a9fcfd86c05581b10c56eeb77d4053)) # [3.0.0-alpha.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.0.0-alpha.0) (2022-12-22) ### Bug Fixes * fix rootProtoManager.registerRootProto ([f416ed7](https://github.com/eggjs/tegg/commit/f416ed70af1c46d31ebf712b208205d67337d958)) ### Features * **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) * impl Host decorator ([#48](https://github.com/eggjs/tegg/issues/48)) ([65dc7a8](https://github.com/eggjs/tegg/commit/65dc7a899ba72dd0851c35046562766d7f2b71b6)) * middleware decorator allow multi middleware function ([#46](https://github.com/eggjs/tegg/issues/46)) ([a4b55f7](https://github.com/eggjs/tegg/commit/a4b55f7065c3d78e2c98c4b05f01871f666542ef)) * multi host decorator ([#68](https://github.com/eggjs/tegg/issues/68)) ([f6679de](https://github.com/eggjs/tegg/commit/f6679de1495024ecb9182168843300aa91288508)) ## [1.5.3](https://github.com/eggjs/tegg/compare/@eggjs/tegg-controller-plugin@1.5.2...@eggjs/tegg-controller-plugin@1.5.3) (2022-09-05) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [1.5.2](https://github.com/eggjs/tegg/compare/@eggjs/tegg-controller-plugin@1.5.1...@eggjs/tegg-controller-plugin@1.5.2) (2022-09-04) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [1.5.1](https://github.com/eggjs/tegg/compare/@eggjs/tegg-controller-plugin@1.5.0...@eggjs/tegg-controller-plugin@1.5.1) (2022-08-17) ### Bug Fixes * fix rootProtoManager.registerRootProto ([f416ed7](https://github.com/eggjs/tegg/commit/f416ed70af1c46d31ebf712b208205d67337d958)) # [1.5.0](https://github.com/eggjs/tegg/compare/@eggjs/tegg-controller-plugin@1.4.1...@eggjs/tegg-controller-plugin@1.5.0) (2022-08-16) ### Features * impl Host decorator ([#48](https://github.com/eggjs/tegg/issues/48)) ([65dc7a8](https://github.com/eggjs/tegg/commit/65dc7a899ba72dd0851c35046562766d7f2b71b6)) # [1.4.0](https://github.com/eggjs/tegg/compare/@eggjs/tegg-controller-plugin@1.3.3...@eggjs/tegg-controller-plugin@1.4.0) (2022-07-28) ### Features * middleware decorator allow multi middleware function ([#46](https://github.com/eggjs/tegg/issues/46)) ([a4b55f7](https://github.com/eggjs/tegg/commit/a4b55f7065c3d78e2c98c4b05f01871f666542ef)) ## [1.3.3](https://github.com/eggjs/tegg/compare/@eggjs/tegg-controller-plugin@1.3.2...@eggjs/tegg-controller-plugin@1.3.3) (2022-07-20) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ## [1.3.2](https://github.com/eggjs/tegg/compare/@eggjs/tegg-controller-plugin@1.3.1...@eggjs/tegg-controller-plugin@1.3.2) (2022-07-20) **Note:** Version bump only for package @eggjs/tegg-controller-plugin # [1.3.0](https://github.com/eggjs/tegg/compare/v1.2.0...v1.3.0) (2022-07-01) **Note:** Version bump only for package @eggjs/tegg-controller-plugin # [0.2.0](https://github.com/eggjs/tegg/compare/v0.1.19...v0.2.0) (2022-01-20) **Note:** Version bump only for package @eggjs/tegg-controller-plugin ================================================ FILE: plugin/controller/README.md ================================================ # @eggjs/tegg-controller-plugin 使用注解的方式来开发 egg 中的 Controller ## Install ```shell # tegg 注解 npm i --save @eggjs/tegg # tegg 插件 npm i --save @eggjs/tegg-plugin # tegg controller 插件 npm i --save @eggjs/tegg-controller-plugin ``` ## Prepare ```json // tsconfig.json { "extends": "@eggjs/tsconfig" } ``` ## Config ```js // config/plugin.js exports.tegg = { package: '@eggjs/tegg-plugin', enable: true, }; exports.teggController = { package: '@eggjs/tegg-controller-plugin', enable: true, }; ``` ## Usage ### Middleware Middleware 支持多个入参,依次传入要生效的中间件 中间件注解,可以添加在类/方法上。添加在类上时,对类上所有方法生效,添加在方法上时,只对当前方法生效。 ```ts // app/middleware/global_log.ts import { Context } from 'egg'; import type { Next } from '@eggjs/controller-decorator'; export default async function globalLog(ctx: Context, next: Next) { ctx.logger.info('have a request'); return next(); } export default async function globalLog2(ctx: Context, next: Next) { ctx.logger.info('have a request2'); return next(); } // app/controller/FooController.ts import { Middleware } from '@eggjs/tegg'; @Middleware(globalLog,globalLog2) export class FooController { @Middleware(methodCount) async hello() { } } ``` ### Context 当需要 egg context 时,可以使用 `@Context` 注解来声明。 ```ts // app/controller/FooController.ts import { Context, EggContext } from '@eggjs/tegg'; export class FooController { @Middleware(methodCount) async hello(@Context() ctx: EggContext) { } } ``` ### HTTP 注解 #### HTTPController/HTTPMethod `@HTTPController` 注解用来声明当前类是一个 HTTP controller,可以配置路径前缀。 `@HTTPMethod` 注解用来声明当前方法是一个 HTTP method,只有带了这个注解,HTTP 方法才会被暴露出去,可以配置方法路径, ```ts // app/controller/FooController.ts import { Context, EggContext, HTTPController, HTTPMethod, HTTPMethodEnum } from '@eggjs/tegg'; @HTTPController({ path: '/foo', }) export class FooController { @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/hello', }) async hello() { } } ``` #### Param HTTP 协议中有各种各样的传参方式,比如 query,path,body 等等。 ##### HTTPBody 接收 body 参数 ```ts // app/controller/FooController.ts import { Context, EggContext, HTTPController, HTTPMethod, HTTPMethodEnum, HTTPBody } from '@eggjs/tegg'; @HTTPController({ path: '/foo', }) export class FooController { @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/hello', }) async hello(@HTTPBody() name: string) { return `hello, ${name}`; } } ``` ##### HTTPQuery/HTTPQueries 两者的区别在于参数是否为数组, HTTPQuery 只会取第一个参数,HTTPQueries 只提供数组形式。 HTTPQuery 的参数类型只能是 string, HTTPQueries 的参数类型只能是 string[]。 ```ts // app/controller/FooController.ts import { Context, EggContext, HTTPController, HTTPMethod, HTTPMethodEnum, HTTPQuery, HTTPQueries } from '@eggjs/tegg'; @HTTPController({ path: '/foo', }) export class FooController { @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/hello', }) async hello( // /foo/hello?name=bar // HTTPQuery: name=bar // HTTPQueries: name=[bar] @HTTPQuery() name: string, @HTTPQueries() names: string[], ) { return `hello, ${name}`; } } ``` 如果需要使用别名,比如说 query 中的 name 不能在 js 中声明时,如 foo[bar] 这类的。可以通过以下形式 ```ts @HTTPQuery({ name: 'foo[bar]' }) fooBar: string, ``` ##### HTTPParam 接收 path 中的参数,类型只能为 string ```ts // app/controller/FooController.ts import { Context, EggContext, HTTPController, HTTPMethod, HTTPMethodEnum, HTTPBody } from '@eggjs/tegg'; @HTTPController({ path: '/foo', }) export class FooController { @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/:id', }) async hello(@HTTPParam() id: string) { return `hello, ${name}`; } } ``` 如果需要使用别名,比如说 path 中使用正则声明 `/foo/(.*)`, 可以通过以下形式 ```ts // 具体 name 值可以查看 path-to-regexp @HTTPParam({ name: '0' }) id: string ``` ### Host Host 注解,用于指定 HTTP 方法仅在 host 匹配时执行。 可以添加在类/方法上。添加在类上时,对类上所有方法生效,添加在方法上时,只对当前方法生效。方法上的注解可以覆盖类上的注解 ```ts // app/controller/FooController.ts import { Host } from '@eggjs/tegg'; @Host('foo.eggjs.com') export class FooController { // 仅能通过 foo.eggjs.com 访问 async hello() { } // 仅能通过 bar.eggjs.com 访问 @Host('bar.eggjs.com') async bar() { } } ``` ### MCP 注解 #### MCPController/MCPPrompt/MCPTool/MCPResource `@MCPController` 注解用来声明当前类是一个 MCP controller。 通过使用装饰器 `@MCPPrompt` `@MCPTool` `@MCPResource` 来声明对应的 MCP 类型。 使用 `@ToolArgsSchema` `@PromptArgsSchema` `@Extra` 来 schema 和 extra。 ```ts import { MCPController, PromptArgs, ToolArgs, MCPPromptResponse, MCPToolResponse, MCPResourceResponse, MCPPrompt, MCPTool, MCPResource, PromptArgsSchema, Logger, Inject, ToolArgsSchema, } from '@eggjs/tegg'; import * as z from 'zod/v4'; export const PromptType = { name: z.string(), }; export const ToolType = { name: z.string().describe('npm package name'), }; @MCPController() export class McpController { @Inject() logger: Logger; @MCPPrompt() async foo(@PromptArgsSchema(PromptType) args: PromptArgs): Promise { this.logger.info('hello world'); return { messages: [ { role: 'user', content: { type: 'text', text: `Generate a concise but descriptive commit message for these changes:\n\n${args.name}`, }, }, ], }; } @MCPTool() async bar(@ToolArgsSchema(ToolType) args: ToolArgs): Promise { return { content: [ { type: 'text', text: `npm package: ${args.name} not found`, }, ], }; } @MCPResource({ template: [ 'mcp://npm/{name}{?version}', { list: () => { return { resources: [ { name: 'egg', uri: 'mcp://npm/egg?version=4.10.0' }, { name: 'mcp', uri: 'mcp://npm/mcp?version=0.10.0' }, ], }; }, }, ], }) async car(uri: URL): Promise { return { contents: [{ uri: uri.toString(), text: 'MOCK TEXT', }], }; } } ``` #### MCP 地址 MCP controller 完整的实现了 SSE / streamable HTTP / streamable stateless HTTP 三种模式,默认情况下,他们的路径分别是 `/mcp/init` `/mcp/stream` `/mcp/stateless/stream`, 你可以根据你所需要的场景,灵活使用对应的接口。 ``` ts // config.{env}.ts import { randomUUID } from 'node:crypto'; export default () => { const config = { mcp: { sseInitPath: '/mcp/init', // SSE path sseMessagePath: '/mcp/message', // SSE message path streamPath: '/mcp/stream', // streamable path statelessStreamPath: '/mcp/stateless/stream', // stateless streamable path sessionIdGenerator: randomUUID, }, }; return config; }; ``` ================================================ FILE: plugin/controller/app/middleware/mcp_body_middleware.ts ================================================ import { EggContext, Next } from '@eggjs/tegg'; import pathToRegexp from 'path-to-regexp'; export default () => { return async function mcpBodyMiddleware(ctx: EggContext, next: Next) { const arr = [ ctx.app.config.mcp.sseInitPath, ctx.app.config.mcp.sseMessagePath, ctx.app.config.mcp.streamPath, ctx.app.config.mcp.statelessStreamPath ]; for (const name of Object.keys(ctx.app.config.mcp.multipleServer || {})) { arr.push(ctx.app.config.mcp.multipleServer![name].sseInitPath); arr.push(ctx.app.config.mcp.multipleServer![name].sseMessagePath); arr.push(ctx.app.config.mcp.multipleServer![name].streamPath); arr.push(ctx.app.config.mcp.multipleServer![name].statelessStreamPath); } const res = arr.some(igPath => { const match = pathToRegexp(igPath, [], { end: false, }); return match.test(ctx.path); }); if (res) { ctx.disableBodyParser = true; try { for (const hook of ctx.app.config.mcp.hooks) { await hook.middlewareStart?.(ctx); } await next(); if (!ctx.mcpArg) { ctx.mcpArg = JSON.parse(decodeURIComponent(ctx.response.header['mcp-proxy-arg'] as string ?? '{}')); } for (const hook of ctx.app.config.mcp.hooks) { await hook.middlewareEnd?.(ctx); } } catch (e) { for (const hook of ctx.app.config.mcp.hooks) { await hook.middlewareError?.(ctx, e); } } } else { await next(); } }; }; ================================================ FILE: plugin/controller/app/middleware/tegg_root_proto.ts ================================================ import { Application } from 'egg'; import { Next, EggContext } from '@eggjs/tegg'; import { ROOT_PROTO } from '@eggjs/egg-module-common'; export default function(_, app: Application) { return async function teggRootProto(ctx: EggContext, next: Next) { (ctx as any)[ROOT_PROTO] = app.rootProtoManager.getRootProto(ctx); return next(); }; } ================================================ FILE: plugin/controller/app.ts ================================================ import { Application } from 'egg'; import { CONTROLLER_LOAD_UNIT, ControllerLoadUnit } from './lib/ControllerLoadUnit'; import { AppLoadUnitControllerHook } from './lib/AppLoadUnitControllerHook'; import { GlobalGraph, LoadUnitLifecycleContext } from '@eggjs/tegg-metadata'; import { ControllerMetaBuilderFactory, ControllerType } from '@eggjs/tegg'; import { HTTPControllerRegister } from './lib/impl/http/HTTPControllerRegister'; import { ControllerRegisterFactory } from './lib/ControllerRegisterFactory'; import { ControllerLoadUnitHandler } from './lib/ControllerLoadUnitHandler'; import { LoadUnitInstanceLifecycleContext, ModuleLoadUnitInstance } from '@eggjs/tegg-runtime'; import { ControllerMetadataManager } from './lib/ControllerMetadataManager'; import { EggControllerPrototypeHook } from './lib/EggControllerPrototypeHook'; import { RootProtoManager } from './lib/RootProtoManager'; import { EggControllerLoader } from './lib/EggControllerLoader'; import { middlewareGraphHook } from './lib/MiddlewareGraphHook'; import { AGENT_CONTROLLER_PROTO_IMPL_TYPE } from '@eggjs/tegg-types'; import { AgentControllerProto } from './lib/AgentControllerProto'; import { AgentControllerObject } from './lib/AgentControllerObject'; import assert from 'node:assert'; // Load Controller process // 1. await add load unit is ready, controller may depend other load unit // 2. load ${app_base_dir}app/controller file // 3. ControllerRegister register controller implement const majorVersion = parseInt(process.versions.node.split('.')[0], 10); export default class ControllerAppBootHook { private readonly app: Application; private readonly loadUnitHook: AppLoadUnitControllerHook; private readonly controllerRegisterFactory: ControllerRegisterFactory; private controllerLoadUnitHandler: ControllerLoadUnitHandler; private readonly controllerPrototypeHook: EggControllerPrototypeHook; private mcpControllerRegister?: typeof import('./lib/impl/mcp/MCPControllerRegister').MCPControllerRegister; constructor(app: Application) { this.app = app; this.controllerRegisterFactory = new ControllerRegisterFactory(this.app); this.app.rootProtoManager = new RootProtoManager(); this.app.controllerRegisterFactory = this.controllerRegisterFactory; this.app.controllerMetaBuilderFactory = ControllerMetaBuilderFactory; this.loadUnitHook = new AppLoadUnitControllerHook(this.controllerRegisterFactory, this.app.rootProtoManager); this.controllerPrototypeHook = new EggControllerPrototypeHook(); this.app.eggPrototypeCreatorFactory.registerPrototypeCreator( AGENT_CONTROLLER_PROTO_IMPL_TYPE, AgentControllerProto.createProto, ); AgentControllerObject.setLogger(this.app.logger); if (parseInt(process.versions.node.split('.')[0], 10) >= 18) { // eslint-disable-next-line @typescript-eslint/no-var-requires this.mcpControllerRegister = require('./lib/impl/mcp/MCPControllerRegister').MCPControllerRegister; } } configWillLoad() { this.app.loadUnitLifecycleUtil.registerLifecycle(this.loadUnitHook); this.app.eggPrototypeLifecycleUtil.registerLifecycle(this.controllerPrototypeHook); this.app.eggObjectFactory.registerEggObjectCreateMethod(AgentControllerProto, AgentControllerObject.createObject); this.app.loaderFactory.registerLoader(CONTROLLER_LOAD_UNIT, unitPath => { return new EggControllerLoader(unitPath); }); this.controllerRegisterFactory.registerControllerRegister(ControllerType.HTTP, HTTPControllerRegister.create); this.app.loadUnitFactory.registerLoadUnitCreator( CONTROLLER_LOAD_UNIT, (ctx: LoadUnitLifecycleContext): ControllerLoadUnit => { return new ControllerLoadUnit( `tegg-app-controller:${ctx.unitPath}`, ctx.unitPath, ctx.loader, this.app.eggPrototypeFactory, this.app.eggPrototypeCreatorFactory, ); }); this.app.loadUnitInstanceFactory.registerLoadUnitInstanceClass( CONTROLLER_LOAD_UNIT, (ctx: LoadUnitInstanceLifecycleContext): ModuleLoadUnitInstance => { return new ModuleLoadUnitInstance(ctx.loadUnit); }, ); if (this.app.config.security?.csrf !== void 0) { assert(typeof this.app.config.security.csrf === 'boolean' || typeof this.app.config.security.csrf === 'object', 'csrf must be boolean or object'); if (typeof this.app.config.security.csrf === 'boolean') { this.app.config.security.csrf = { enable: this.app.config.security.csrf, }; } } // init http root proto middleware this.prepareMiddleware(this.app.config.coreMiddleware); if (this.mcpEnable() && this.mcpControllerRegister) { this.controllerRegisterFactory.registerControllerRegister(ControllerType.MCP, this.mcpControllerRegister.create); // Don't let the mcp's body be consumed this.app.config.coreMiddleware.unshift('mcpBodyMiddleware'); if (this.app.config.security.csrf.ignore) { if (Array.isArray(this.app.config.security.csrf.ignore)) { this.app.config.security.csrf.ignore = [ /^\/mcp\//, this.app.config.mcp.sseInitPath, this.app.config.mcp.sseMessagePath, this.app.config.mcp.streamPath, this.app.config.mcp.statelessStreamPath, ...(Array.isArray(this.app.config.security.csrf.ignore) ? this.app.config.security.csrf.ignore : [ this.app.config.security.csrf.ignore ]), ]; } } else { this.app.config.security.csrf.ignore = [ /^\/mcp\//, this.app.config.mcp.sseInitPath, this.app.config.mcp.sseMessagePath, this.app.config.mcp.streamPath, this.app.config.mcp.statelessStreamPath, ]; } if (this.app.config.mcp.multipleServer) { for (const name of Object.keys(this.app.config.mcp.multipleServer)) { [ 'sseInitPath', 'sseMessagePath', 'streamPath', 'statelessStreamPath' ].forEach(key => { if (this.app.config.mcp.multipleServer[name][key]) this.app.config.security.csrf.ignore.push(this.app.config.mcp.multipleServer[name][key]); }); } } } } prepareMiddleware(middlewareNames: string[]) { if (!middlewareNames.includes('teggCtxLifecycleMiddleware')) { middlewareNames.unshift('teggCtxLifecycleMiddleware'); } const index = middlewareNames.indexOf('teggCtxLifecycleMiddleware'); middlewareNames.splice(index, 0, 'teggRootProto'); return middlewareNames; } async didLoad() { await this.app.moduleHandler.ready(); this.controllerLoadUnitHandler = new ControllerLoadUnitHandler(this.app); await this.controllerLoadUnitHandler.ready(); // The real register HTTP controller/method. // HTTP method should sort by priority // The HTTPControllerRegister will collect all the methods // and register methods after collect is done. HTTPControllerRegister.instance?.doRegister(this.app.rootProtoManager); this.app.config.mcp.hooks = this.mcpControllerRegister?.hooks; } configDidLoad() { GlobalGraph.instance!.registerBuildHook(middlewareGraphHook); } async willReady() { if (this.mcpEnable() && this.mcpControllerRegister) { await this.mcpControllerRegister.connectStatelessStreamTransport(); const names = this.mcpControllerRegister.instance?.mcpConfig.getMultipleServerNames(); if (names && names.length > 0) { for (const name of names) { await this.mcpControllerRegister.connectStatelessStreamTransport(name); } } } } mcpEnable() { return majorVersion >= 18 && !!this.app.plugins.mcpProxy?.enable; } async beforeClose() { if (this.controllerLoadUnitHandler) { await this.controllerLoadUnitHandler.destroy(); } this.app.loadUnitLifecycleUtil.deleteLifecycle(this.loadUnitHook); this.app.eggPrototypeLifecycleUtil.deleteLifecycle(this.controllerPrototypeHook); ControllerMetadataManager.instance.clear(); HTTPControllerRegister.clean(); this.mcpControllerRegister?.clean(); } } ================================================ FILE: plugin/controller/config/config.default.ts ================================================ import { randomUUID } from 'node:crypto'; export default () => { const config = { mcp: { sseInitPath: '/mcp/sse', sseMessagePath: '/mcp/message', streamPath: '/mcp/stream', statelessStreamPath: '/mcp/stateless/stream', sessionIdGenerator: randomUUID, }, }; return config; }; ================================================ FILE: plugin/controller/lib/AgentControllerObject.ts ================================================ import { AgentRuntime, type AgentExecutor, AGENT_RUNTIME, HttpSSEWriter } from '@eggjs/agent-runtime'; import { AgentInfoUtil, IdenticalUtil } from '@eggjs/tegg'; import { LoadUnitFactory } from '@eggjs/tegg-metadata'; import { EGG_CONTEXT } from '@eggjs/egg-module-common'; import { ContextHandler, EggContainerFactory, EggObjectLifecycleUtil, EggObjectUtil } from '@eggjs/tegg-runtime'; import type { EggObject, EggObjectLifeCycleContext, EggObjectLifecycle, EggObjectName, EggPrototype, EggPrototypeName, } from '@eggjs/tegg-types'; import { EggObjectStatus, ObjectInitType } from '@eggjs/tegg-types'; import type { AgentStore, CreateRunInput } from '@eggjs/tegg-types/agent-runtime'; import type { EggLogger } from 'egg'; import { AgentControllerProto } from './AgentControllerProto'; /** Method names that can be delegated to AgentRuntime. */ type AgentMethodName = 'createThread' | 'getThread' | 'asyncRun' | 'syncRun' | 'getRun' | 'cancelRun'; const AGENT_METHOD_NAMES: AgentMethodName[] = [ 'createThread', 'getThread', 'asyncRun', 'syncRun', 'getRun', 'cancelRun', ]; /** * Custom EggObject for @AgentController classes. * * Replicates the full EggObjectImpl.initWithInjectProperty lifecycle and * inserts AgentRuntime delegate installation between postInject and init * hooks — exactly where the user's `init()` expects runtime to be ready. */ export class AgentControllerObject implements EggObject { private static logger: EggLogger; private _obj!: object; private status: EggObjectStatus = EggObjectStatus.PENDING; private runtime: AgentRuntime | undefined; readonly id: string; readonly name: EggPrototypeName; readonly proto: AgentControllerProto; /** Inject a logger to be used by all AgentRuntime instances. */ static setLogger(logger: EggLogger): void { AgentControllerObject.logger = logger; } constructor(name: EggObjectName, proto: AgentControllerProto) { this.name = name; this.proto = proto; const ctx = ContextHandler.getContext(); this.id = IdenticalUtil.createObjectId(this.proto.id, ctx?.id); } get obj(): object { return this._obj; } get isReady(): boolean { return this.status === EggObjectStatus.READY; } injectProperty(name: EggObjectName, descriptor: PropertyDescriptor): void { Reflect.defineProperty(this._obj, name, descriptor); } /** * Full lifecycle sequence mirroring EggObjectImpl.initWithInjectProperty, * with AgentRuntime installation inserted between postInject and init. */ async init(ctx: EggObjectLifeCycleContext): Promise { try { // 1. Construct object this._obj = this.proto.constructEggObject(); const objLifecycleHook = this._obj as EggObjectLifecycle; // 2. Global preCreate hook await EggObjectLifecycleUtil.objectPreCreate(ctx, this); // 3. Self postConstruct hook const postConstructMethod = EggObjectLifecycleUtil.getLifecycleHook('postConstruct', this.proto) ?? 'postConstruct'; if (objLifecycleHook[postConstructMethod]) { await objLifecycleHook[postConstructMethod](ctx, this); } // 4. Self preInject hook const preInjectMethod = EggObjectLifecycleUtil.getLifecycleHook('preInject', this.proto) ?? 'preInject'; if (objLifecycleHook[preInjectMethod]) { await objLifecycleHook[preInjectMethod](ctx, this); } // 5. Inject dependencies await Promise.all( this.proto.injectObjects.map(async injectObject => { const proto = injectObject.proto; const loadUnit = LoadUnitFactory.getLoadUnitById(proto.loadUnitId); if (!loadUnit) { throw new Error(`can not find load unit: ${proto.loadUnitId}`); } if ( this.proto.initType !== ObjectInitType.CONTEXT && injectObject.proto.initType === ObjectInitType.CONTEXT ) { this.injectProperty( injectObject.refName, EggObjectUtil.contextEggObjectGetProperty(proto, injectObject.objName), ); } else { const injectObj = await EggContainerFactory.getOrCreateEggObject(proto, injectObject.objName); this.injectProperty(injectObject.refName, EggObjectUtil.eggObjectGetProperty(injectObj)); } }), ); // 6. Global postCreate hook await EggObjectLifecycleUtil.objectPostCreate(ctx, this); // 7. Self postInject hook const postInjectMethod = EggObjectLifecycleUtil.getLifecycleHook('postInject', this.proto) ?? 'postInject'; if (objLifecycleHook[postInjectMethod]) { await objLifecycleHook[postInjectMethod](ctx, this); } // === AgentRuntime installation (before user init) === await this.installAgentRuntime(); // 8. Self init hook (user's init()) const initMethod = EggObjectLifecycleUtil.getLifecycleHook('init', this.proto) ?? 'init'; if (objLifecycleHook[initMethod]) { await objLifecycleHook[initMethod](ctx, this); } // 9. Ready this.status = EggObjectStatus.READY; } catch (e) { // Clean up runtime if it was created but init failed if (this.runtime) { try { await this.runtime.destroy(); } catch { // Swallow cleanup errors to preserve the original exception } this.runtime = undefined; } this.status = EggObjectStatus.ERROR; throw e; } } async destroy(ctx: EggObjectLifeCycleContext): Promise { if (this.status === EggObjectStatus.READY) { this.status = EggObjectStatus.DESTROYING; // Destroy AgentRuntime first (waits for in-flight tasks) if (this.runtime) { await this.runtime.destroy(); } // Global preDestroy hook await EggObjectLifecycleUtil.objectPreDestroy(ctx, this); // Self lifecycle hooks const objLifecycleHook = this._obj as EggObjectLifecycle; const preDestroyMethod = EggObjectLifecycleUtil.getLifecycleHook('preDestroy', this.proto) ?? 'preDestroy'; if (objLifecycleHook[preDestroyMethod]) { await objLifecycleHook[preDestroyMethod](ctx, this); } const destroyMethod = EggObjectLifecycleUtil.getLifecycleHook('destroy', this.proto) ?? 'destroy'; if (objLifecycleHook[destroyMethod]) { await objLifecycleHook[destroyMethod](ctx, this); } this.status = EggObjectStatus.DESTROYED; } } /** * Create AgentRuntime and install delegate methods on the instance. * Logic ported from the removed enhanceAgentController.ts. */ private async installAgentRuntime(): Promise { const instance = this._obj as Record; // Determine which methods are stubs vs user-defined const stubMethods = new Set(); for (const name of AGENT_METHOD_NAMES) { const method = instance[name]; if (typeof method !== 'function' || AgentInfoUtil.isNotImplemented(method)) { stubMethods.add(name); } } const streamRunFn = instance.streamRun; const streamRunIsStub = typeof streamRunFn !== 'function' || AgentInfoUtil.isNotImplemented(streamRunFn); // Create store — user must implement createStore() let store: AgentStore; const createStoreFn = instance.createStore; if (typeof createStoreFn === 'function') { store = (await Reflect.apply(createStoreFn, this._obj, [])) as AgentStore; } else { throw new Error( '@AgentController requires a createStore() method. ' + 'Implement createStore() in your controller to provide an AgentStore instance.', ); } if (store.init) { await store.init(); } // Create runtime with AgentRuntime.create factory const runtime = AgentRuntime.create({ executor: this._obj as AgentExecutor, store, logger: AgentControllerObject.logger, }); this.runtime = runtime; instance[AGENT_RUNTIME] = runtime; // Install delegate methods for stubs (type-safe: all names are keys of AgentRuntime) for (const methodName of stubMethods) { const runtimeMethod = runtime[methodName].bind(runtime); instance[methodName] = runtimeMethod; } // streamRun needs special handling: create HttpSSEWriter from request context if (streamRunIsStub) { instance.streamRun = async (input: CreateRunInput): Promise => { const runtimeCtx = ContextHandler.getContext(); if (!runtimeCtx) { throw new Error('streamRun must be called within a request context'); } const eggCtx = runtimeCtx.get(EGG_CONTEXT); eggCtx.respond = false; const writer = new HttpSSEWriter(eggCtx.res); return runtime.streamRun(input, writer); }; } // getRunStream: always delegate to runtime (no user override needed) // lastSeq comes from query string as a string, needs parseInt instance.getRunStream = async (runId: string, lastSeq?: string): Promise => { const runtimeCtx = ContextHandler.getContext(); if (!runtimeCtx) { throw new Error('getRunStream must be called within a request context'); } const eggCtx = runtimeCtx.get(EGG_CONTEXT); eggCtx.respond = false; const writer = new HttpSSEWriter(eggCtx.res); const seq = parseInt(lastSeq as string, 10) || 0; return runtime.getRunStream(runId, writer, seq); }; } static async createObject( name: EggObjectName, proto: EggPrototype, lifecycleContext: EggObjectLifeCycleContext, ): Promise { const obj = new AgentControllerObject(name, proto as AgentControllerProto); await obj.init(lifecycleContext); return obj; } } ================================================ FILE: plugin/controller/lib/AgentControllerProto.ts ================================================ import { EggPrototypeCreatorFactory } from '@eggjs/tegg-metadata'; import type { AccessLevel, EggPrototype, EggPrototypeCreator, EggPrototypeLifecycleContext, EggPrototypeName, InjectConstructorProto, InjectObjectProto, InjectType, MetaDataKey, ObjectInitTypeLike, QualifierAttribute, QualifierInfo, QualifierValue, } from '@eggjs/tegg-types'; import { DEFAULT_PROTO_IMPL_TYPE } from '@eggjs/tegg-types'; /** * Wraps a standard EggPrototypeImpl (created by the DEFAULT creator) to * provide a distinct class identity so that EggObjectFactory can dispatch * to AgentControllerObject.createObject. * * All EggPrototype interface members are delegated to the inner proto. * Symbol-keyed properties (qualifier descriptors set by the runtime) are * copied from the delegate in the constructor via Object.defineProperty. */ export class AgentControllerProto implements EggPrototype { [key: symbol]: PropertyDescriptor; private readonly delegate: EggPrototype; constructor(delegate: EggPrototype) { this.delegate = delegate; // Copy symbol-keyed properties from delegate (qualifier descriptors, etc.) for (const sym of Object.getOwnPropertySymbols(delegate)) { const desc = Object.getOwnPropertyDescriptor(delegate, sym); if (desc) { Object.defineProperty(this, sym, desc); } } } get id(): string { return this.delegate.id; } get name(): EggPrototypeName { return this.delegate.name; } get initType(): ObjectInitTypeLike { return this.delegate.initType; } get accessLevel(): AccessLevel { return this.delegate.accessLevel; } get loadUnitId(): string { return this.delegate.loadUnitId; } get injectObjects(): Array { return this.delegate.injectObjects; } get injectType(): InjectType | undefined { return this.delegate.injectType; } get className(): string | undefined { return this.delegate.className; } get multiInstanceConstructorIndex(): number | undefined { return this.delegate.multiInstanceConstructorIndex; } get multiInstanceConstructorAttributes(): QualifierAttribute[] | undefined { return this.delegate.multiInstanceConstructorAttributes; } getMetaData(metadataKey: MetaDataKey): T | undefined { return this.delegate.getMetaData(metadataKey); } verifyQualifier(qualifier: QualifierInfo): boolean { return this.delegate.verifyQualifier(qualifier); } verifyQualifiers(qualifiers: QualifierInfo[]): boolean { return this.delegate.verifyQualifiers(qualifiers); } getQualifier(attribute: QualifierAttribute): QualifierValue | undefined { return this.delegate.getQualifier(attribute); } constructEggObject(...args: any): object { return this.delegate.constructEggObject(...args); } static createProto(ctx: EggPrototypeLifecycleContext): AgentControllerProto { const defaultCreator: EggPrototypeCreator | undefined = EggPrototypeCreatorFactory.getPrototypeCreator(DEFAULT_PROTO_IMPL_TYPE); if (!defaultCreator) { throw new Error(`Default prototype creator (${DEFAULT_PROTO_IMPL_TYPE}) not registered`); } const delegate = defaultCreator(ctx); return new AgentControllerProto(delegate); } } ================================================ FILE: plugin/controller/lib/AppLoadUnitControllerHook.ts ================================================ import { LoadUnit, LoadUnitLifecycleContext } from '@eggjs/tegg-metadata'; import { CONTROLLER_META_DATA, ControllerMetadata, LifecycleHook } from '@eggjs/tegg'; import { ControllerRegisterFactory } from './ControllerRegisterFactory'; import { ControllerMetadataManager } from './ControllerMetadataManager'; import { RootProtoManager } from './RootProtoManager'; export class AppLoadUnitControllerHook implements LifecycleHook { private readonly controllerRegisterFactory: ControllerRegisterFactory; private readonly rootProtoManager: RootProtoManager; constructor(controllerRegisterFactory: ControllerRegisterFactory, rootProtoManager: RootProtoManager) { this.controllerRegisterFactory = controllerRegisterFactory; this.rootProtoManager = rootProtoManager; } async postCreate(_: LoadUnitLifecycleContext, obj: LoadUnit): Promise { const iterator = obj.iterateEggPrototype(); for (const proto of iterator) { const metadata: ControllerMetadata | undefined = proto.getMetaData(CONTROLLER_META_DATA); if (!metadata) { continue; } const register = this.controllerRegisterFactory.getControllerRegister(proto, metadata); if (!register) { throw new Error(`not find controller implement for ${String(proto.name)} which type is ${metadata.type}`); } ControllerMetadataManager.instance.addController(metadata); await register.register(this.rootProtoManager, obj); } } } ================================================ FILE: plugin/controller/lib/ControllerLoadUnit.ts ================================================ import { EggPrototype, EggPrototypeFactory, Loader, LoadUnit, EggPrototypeCreatorFactory, } from '@eggjs/tegg-metadata'; import { EggPrototypeName, QualifierInfo, Id, IdenticalUtil, } from '@eggjs/tegg'; import { MapUtil } from '@eggjs/tegg-common-util'; export const CONTROLLER_LOAD_UNIT = 'app#controller'; // ControllerLoadUnit is responsible for manage controller proto export class ControllerLoadUnit implements LoadUnit { private readonly loader: Loader; id: Id; readonly name: string; readonly type = CONTROLLER_LOAD_UNIT; readonly unitPath: string; private eggPrototypeFactory: EggPrototypeFactory; private eggPrototypeCreatorFactory: typeof EggPrototypeCreatorFactory; private protoMap: Map = new Map(); constructor( name: string, unitPath: string, loader: Loader, eggPrototypeFactory: EggPrototypeFactory, eggPrototypeCreatorFactory: typeof EggPrototypeCreatorFactory, ) { this.id = IdenticalUtil.createLoadUnitId(name); this.name = name; this.unitPath = unitPath; this.loader = loader; this.eggPrototypeFactory = eggPrototypeFactory; this.eggPrototypeCreatorFactory = eggPrototypeCreatorFactory; } async init() { const clazzList = this.loader.load(); for (const clazz of clazzList) { const protos = await this.eggPrototypeCreatorFactory.createProto(clazz, this); for (const proto of protos) { this.eggPrototypeFactory.registerPrototype(proto, this); } } } containPrototype(proto: EggPrototype): boolean { return !!(this.protoMap.get(proto.name)?.find(t => t === proto)); } getEggPrototype(name: string, qualifiers: QualifierInfo[]): EggPrototype[] { const protos = this.protoMap.get(name); return protos?.filter(proto => proto.verifyQualifiers(qualifiers)) || []; } registerEggPrototype(proto: EggPrototype) { const protoList = MapUtil.getOrStore(this.protoMap, proto.name, []); protoList.push(proto); } deletePrototype(proto: EggPrototype) { const protos = this.protoMap.get(proto.name); if (protos) { const index = protos.indexOf(proto); if (index !== -1) { protos.splice(index, 1); } } } async destroy() { for (const namedProtos of this.protoMap.values()) { // Delete prototype will delete item // array iterator is not safe const protos = namedProtos.slice(); for (const proto of protos) { EggPrototypeFactory.instance.deletePrototype(proto, this); } } this.protoMap.clear(); } iterateEggPrototype(): IterableIterator { const protos: EggPrototype[] = Array.from(this.protoMap.values()) .reduce((p, c) => { p = p.concat(c); return p; }, []); return protos.values(); } } ================================================ FILE: plugin/controller/lib/ControllerLoadUnitHandler.ts ================================================ import Base from 'sdk-base'; import path from 'path'; import { Application } from 'egg'; import { EggLoadUnitType, LoadUnit } from '@eggjs/tegg-metadata'; import { LoadUnitInstance } from '@eggjs/tegg-runtime'; import { CONTROLLER_LOAD_UNIT } from './ControllerLoadUnit'; export class ControllerLoadUnitHandler extends Base { private readonly app: Application; controllerLoadUnit: LoadUnit; controllerLoadUnitInstance: LoadUnitInstance; constructor(app: Application) { super({ initMethod: '_init' }); this.app = app; } async _init() { const controllerDir = path.join(this.app.config.baseDir, 'app/controller'); const loader = this.app.loaderFactory.createLoader(controllerDir, CONTROLLER_LOAD_UNIT as EggLoadUnitType); this.controllerLoadUnit = await this.app.loadUnitFactory.createLoadUnit(controllerDir, CONTROLLER_LOAD_UNIT, loader); this.controllerLoadUnitInstance = await this.app.loadUnitInstanceFactory.createLoadUnitInstance(this.controllerLoadUnit); } async destroy() { if (this.controllerLoadUnit) { await this.app.loadUnitFactory.destroyLoadUnit(this.controllerLoadUnit); } if (this.controllerLoadUnitInstance) { await this.app.loadUnitInstanceFactory.destroyLoadUnitInstance(this.controllerLoadUnitInstance); } } } ================================================ FILE: plugin/controller/lib/ControllerLoadUnitInstance.ts ================================================ import { EggPrototype, LoadUnit } from '@eggjs/tegg-metadata'; import { EggObjectName, EggPrototypeName, IdenticalUtil } from '@eggjs/tegg'; import { EggObject, LoadUnitInstance, LoadUnitInstanceLifecycleContext, LoadUnitInstanceLifecycleUtil, } from '@eggjs/tegg-runtime'; export class ControllerLoadUnitInstance implements LoadUnitInstance { readonly loadUnit: LoadUnit; readonly id: string; readonly name: string; private protoToCreateMap: Map = new Map(); private loadUnitInstanceLifecycleUtil: typeof LoadUnitInstanceLifecycleUtil; constructor(loadUnit: LoadUnit, loadUnitInstanceLifecycleUtil: typeof LoadUnitInstanceLifecycleUtil) { this.loadUnit = loadUnit; this.name = loadUnit.name; this.id = IdenticalUtil.createLoadUnitInstanceId(loadUnit.id); this.loadUnitInstanceLifecycleUtil = loadUnitInstanceLifecycleUtil; } iterateProtoToCreate(): IterableIterator<[ EggObjectName, EggPrototype ]> { return this.protoToCreateMap.entries(); } addProtoToCreate() { throw new Error('controller load unit not allow have singleton proto'); } deleteProtoToCreate() { throw new Error('controller load unit not allow have singleton proto'); } async init(ctx: LoadUnitInstanceLifecycleContext): Promise { await this.loadUnitInstanceLifecycleUtil.objectPreCreate(ctx, this); await this.loadUnitInstanceLifecycleUtil.objectPostCreate(ctx, this); } async getOrCreateEggObject(): Promise { throw new Error('controller load unit not allow have singleton proto'); } getEggObject(): EggObject { throw new Error('controller load unit not allow have singleton proto'); } } ================================================ FILE: plugin/controller/lib/ControllerMetadataManager.ts ================================================ import { ControllerMetadata, ControllerTypeLike } from '@eggjs/tegg'; import { MapUtil } from '@eggjs/tegg-common-util'; export class ControllerMetadataManager { private readonly controllers = new Map(); static instance = new ControllerMetadataManager(); constructor() { this.controllers = new Map(); } addController(metadata: ControllerMetadata) { const typeControllers = MapUtil.getOrStore(this.controllers, metadata.type, []); // 1.check controller name // 2.check proto name const sameNameControllers = typeControllers.filter(c => c.controllerName === metadata.controllerName); if (sameNameControllers.length) { throw new Error(`duplicate controller name ${metadata.controllerName}`); } const sameProtoControllers = typeControllers.filter(c => c.protoName === metadata.protoName); if (sameProtoControllers.length) { throw new Error(`duplicate proto name ${String(metadata.protoName)}`); } typeControllers.push(metadata); } clear() { this.controllers.clear(); } } ================================================ FILE: plugin/controller/lib/ControllerRegister.ts ================================================ import { LoadUnit } from '@eggjs/tegg-metadata'; import { RootProtoManager } from './RootProtoManager'; export interface ControllerRegister { register(rootProtoManager: RootProtoManager, loadUnit?: LoadUnit): Promise; } ================================================ FILE: plugin/controller/lib/ControllerRegisterFactory.ts ================================================ import { Application } from 'egg'; import { ControllerMetadata, ControllerTypeLike } from '@eggjs/tegg'; import { EggPrototype } from '@eggjs/tegg-metadata'; import { ControllerRegister } from './ControllerRegister'; export type RegisterCreator = (proto: EggPrototype, controllerMeta: ControllerMetadata, app: Application) => ControllerRegister; export class ControllerRegisterFactory { private readonly app: Application; private registerCreatorMap: Map; constructor(app: Application) { this.app = app; this.registerCreatorMap = new Map(); } registerControllerRegister(type: ControllerTypeLike, creator: RegisterCreator) { this.registerCreatorMap.set(type, creator); } getControllerRegister(proto: EggPrototype, metadata: ControllerMetadata): ControllerRegister | undefined { const creator = this.registerCreatorMap.get(metadata.type); if (!creator) { return; } return creator(proto, metadata, this.app); } } ================================================ FILE: plugin/controller/lib/EggControllerLoader.ts ================================================ import globby from 'globby'; import path from 'path'; import { LoaderUtil } from '@eggjs/tegg-loader'; import { EggProtoImplClass } from '@eggjs/tegg'; export class EggControllerLoader { private readonly controllerDir: string; constructor(controllerDir: string) { this.controllerDir = controllerDir; } load(): EggProtoImplClass[] { const filePattern = LoaderUtil.filePattern(); let files: string[]; try { const httpControllers = globby.sync(filePattern, { cwd: this.controllerDir }) .map(file => path.join(this.controllerDir, file)); files = httpControllers; } catch (_) { files = []; // app/controller dir not exists } const protoClassList: EggProtoImplClass[] = []; for (const file of files) { const fileClazzList = LoaderUtil.loadFile(file); for (const clazz of fileClazzList) { protoClassList.push(clazz); } } return protoClassList; } } ================================================ FILE: plugin/controller/lib/EggControllerPrototypeHook.ts ================================================ import { EggPrototype, EggPrototypeLifecycleContext } from '@eggjs/tegg-metadata'; import { ControllerMetaBuilderFactory, ControllerMetadataUtil, LifecycleHook, } from '@eggjs/tegg'; export class EggControllerPrototypeHook implements LifecycleHook { async postCreate(ctx: EggPrototypeLifecycleContext): Promise { const metadata = ControllerMetaBuilderFactory.build(ctx.clazz); if (metadata) { ControllerMetadataUtil.setControllerMetadata(ctx.clazz, metadata); } } } ================================================ FILE: plugin/controller/lib/MiddlewareGraphHook.ts ================================================ import { GraphNode } from '@eggjs/tegg-common-util'; import { ClassProtoDescriptor, GlobalGraph, ProtoDependencyMeta, ProtoNode, } from '@eggjs/tegg-metadata'; import { EggProtoImplClass, IAdvice } from '@eggjs/tegg-types'; import { ControllerInfoUtil, MethodInfoUtil } from '@eggjs/tegg'; export function middlewareGraphHook(globalGraph: GlobalGraph) { for (const moduleNode of globalGraph.moduleGraph.nodes.values()) { for (const controllerProtoNode of moduleNode.val.protos) { const middlewareProtoNodes = findMiddlewareProtoNodes(globalGraph, controllerProtoNode); if (!middlewareProtoNodes) continue; for (const middlewareProtoNode of middlewareProtoNodes) { const middlewareModuleNode = globalGraph.findModuleNode(middlewareProtoNode.val.proto.instanceModuleName); if (!middlewareModuleNode) continue; globalGraph.addInject( moduleNode, controllerProtoNode, middlewareProtoNode, middlewareProtoNode.val.proto.name); } } } } function findMiddlewareProtoNodes(globalGraph: GlobalGraph, protoNode: GraphNode) { const proto = protoNode.val.proto; if (!ClassProtoDescriptor.isClassProtoDescriptor(proto)) { return; } const middlewareClazzSet = new Set>(); // Get AOP middlewares from controller class const controllerAopMiddlewares = ControllerInfoUtil.getControllerAopMiddlewares(proto.clazz); if (controllerAopMiddlewares && controllerAopMiddlewares.length > 0) { for (const middlewareClazz of controllerAopMiddlewares) { middlewareClazzSet.add(middlewareClazz); } } // Get AOP middlewares from controller methods const methods = MethodInfoUtil.getMethods(proto.clazz); for (const methodName of methods) { const methodAopMiddlewares = MethodInfoUtil.getMethodAopMiddlewares(proto.clazz, methodName); if (methodAopMiddlewares && methodAopMiddlewares.length > 0) { for (const middlewareClazz of methodAopMiddlewares) { middlewareClazzSet.add(middlewareClazz); } } } if (middlewareClazzSet.size === 0) { return; } const result: GraphNode[] = []; for (const middlewareClazz of middlewareClazzSet) { // Find the proto node for this middleware class const middlewareProtoNode = findProtoNodeByClass(globalGraph, middlewareClazz); if (middlewareProtoNode) { result.push(middlewareProtoNode); } } return result.length > 0 ? result : undefined; } function findProtoNodeByClass( globalGraph: GlobalGraph, clazz: EggProtoImplClass, ): GraphNode | undefined { for (const protoNode of globalGraph.protoGraph.nodes.values()) { const proto = protoNode.val.proto; if (ClassProtoDescriptor.isClassProtoDescriptor(proto) && proto.clazz === clazz) { return protoNode; } } return undefined; } ================================================ FILE: plugin/controller/lib/RootProtoManager.ts ================================================ import { EggPrototype } from '@eggjs/tegg-metadata'; import { EggContext } from '@eggjs/tegg'; import { MapUtil } from '@eggjs/tegg-common-util'; export type GetRootProtoCallback = (ctx: EggContext) => EggPrototype | undefined; export class RootProtoManager { // protoMap: Map = new Map(); registerRootProto(method: string, cb: GetRootProtoCallback, host: string) { host = host || ''; const cbList = MapUtil.getOrStore(this.protoMap, method + host, []); cbList.push(cb); } getRootProto(ctx: EggContext): EggPrototype | undefined { const hostCbList = this.protoMap.get(ctx.method + ctx.host); if (hostCbList) { for (const cb of hostCbList) { const proto = cb(ctx); if (proto) { return proto; } } } const cbList = this.protoMap.get(ctx.method); if (!cbList) { return; } for (const cb of cbList) { const proto = cb(ctx); if (proto) { return proto; } } } } ================================================ FILE: plugin/controller/lib/errors.ts ================================================ import { TeggError } from '@eggjs/tegg-metadata'; enum ErrorCodes { ROUTER_CONFLICT = 'ROUTER_CONFLICT' } /** 路由冲突错误 */ export class RouterConflictError extends TeggError { constructor(msg: string) { super(msg, ErrorCodes.ROUTER_CONFLICT); } } ================================================ FILE: plugin/controller/lib/impl/http/Acl.ts ================================================ import { Next, EggContext, HTTPControllerMeta, HTTPMethodMeta } from '@eggjs/tegg'; export function aclMiddlewareFactory(controllerMeta: HTTPControllerMeta, methodMeta: HTTPMethodMeta) { if (!controllerMeta.hasMethodAcl(methodMeta)) { return; } const code = controllerMeta.getMethodAcl(methodMeta); return async function aclMiddleware(ctx: EggContext, next: Next) { try { await ctx.acl(code); } catch (e) { const { redirectUrl, status } = e.data || {}; if (!redirectUrl) { throw e; } if (ctx.acceptJSON) { ctx.body = { target: redirectUrl, stat: 'deny', }; return; } if (status) { ctx.realStatus = status; } ctx.redirect(redirectUrl); return; } return next(); }; } ================================================ FILE: plugin/controller/lib/impl/http/HTTPControllerRegister.ts ================================================ import assert from 'assert'; import { Application, Router } from 'egg'; import { CONTROLLER_META_DATA, ControllerMetadata, ControllerType, HTTPControllerMeta, HTTPMethodMeta, } from '@eggjs/tegg'; import { EggPrototype } from '@eggjs/tegg-metadata'; import { ControllerRegister } from '../../ControllerRegister'; import { HTTPMethodRegister } from './HTTPMethodRegister'; import { EggContainerFactory } from '@eggjs/tegg-runtime'; import { RootProtoManager } from '../../RootProtoManager'; export class HTTPControllerRegister implements ControllerRegister { static instance?: HTTPControllerRegister; private readonly router: Router; private readonly checkRouters: Map; private readonly eggContainerFactory: typeof EggContainerFactory; private controllerProtos: EggPrototype[] = []; static create(proto: EggPrototype, controllerMeta: ControllerMetadata, app: Application) { assert(controllerMeta.type === ControllerType.HTTP, 'controller meta type is not HTTP'); if (!HTTPControllerRegister.instance) { HTTPControllerRegister.instance = new HTTPControllerRegister(app.router, app.eggContainerFactory); } HTTPControllerRegister.instance.controllerProtos.push(proto); return HTTPControllerRegister.instance; } constructor(router: Router, eggContainerFactory: typeof EggContainerFactory) { this.router = router; this.checkRouters = new Map(); this.checkRouters.set('default', router); this.eggContainerFactory = eggContainerFactory; } register(): Promise { // do noting return Promise.resolve(); } static clean() { if (this.instance) { this.instance.controllerProtos = []; this.instance.checkRouters.clear(); } this.instance = undefined; } doRegister(rootProtoManager: RootProtoManager) { const methodMap = new Map(); for (const proto of this.controllerProtos) { const metadata = proto.getMetaData(CONTROLLER_META_DATA) as HTTPControllerMeta; for (const method of metadata.methods) { methodMap.set(method, proto); } } const allMethods = Array.from(methodMap.keys()) .sort((a, b) => b.priority - a.priority); for (const method of allMethods) { const controllerProto = methodMap.get(method)!; const controllerMeta = controllerProto.getMetaData(CONTROLLER_META_DATA) as HTTPControllerMeta; const methodRegister = new HTTPMethodRegister( controllerProto, controllerMeta, method, this.router, this.checkRouters, this.eggContainerFactory); methodRegister.checkDuplicate(); } for (const method of allMethods) { const controllerProto = methodMap.get(method)!; const controllerMeta = controllerProto.getMetaData(CONTROLLER_META_DATA) as HTTPControllerMeta; const methodRegister = new HTTPMethodRegister( controllerProto, controllerMeta, method, this.router, this.checkRouters, this.eggContainerFactory); methodRegister.register(rootProtoManager); } } } ================================================ FILE: plugin/controller/lib/impl/http/HTTPMethodRegister.ts ================================================ import assert from 'assert'; import pathToRegexp from 'path-to-regexp'; import { EggRouter } from '@eggjs/router'; import { Context, Router } from 'egg'; import { FrameworkErrorFormater } from 'egg-errors'; import { EggContext, HTTPControllerMeta, HTTPMethodMeta, HTTPParamType, Next, PathParamMeta, QueriesParamMeta, QueryParamMeta, HTTPCookies, } from '@eggjs/tegg'; import { TimerUtil } from '@eggjs/tegg-common-util'; import { EggContainerFactory } from '@eggjs/tegg-runtime'; import { EggPrototype } from '@eggjs/tegg-metadata'; import { RootProtoManager } from '../../RootProtoManager'; import { aclMiddlewareFactory } from './Acl'; import { HTTPRequest } from './Req'; import { RouterConflictError } from '../../errors'; const noop = () => { // ... }; export class HTTPMethodRegister { private readonly router: Router; private readonly checkRouters: Map; private readonly controllerMeta: HTTPControllerMeta; private readonly methodMeta: HTTPMethodMeta; private readonly proto: EggPrototype; private readonly eggContainerFactory: typeof EggContainerFactory; constructor( proto: EggPrototype, controllerMeta: HTTPControllerMeta, methodMeta: HTTPMethodMeta, router: Router, checkRouters: Map, eggContainerFactory: typeof EggContainerFactory, ) { this.proto = proto; this.controllerMeta = controllerMeta; this.router = router; this.methodMeta = methodMeta; this.checkRouters = checkRouters; this.eggContainerFactory = eggContainerFactory; } private createHandler(methodMeta: HTTPMethodMeta, host: string | undefined) { const argsLength = methodMeta.paramMap.size; const hasContext = methodMeta.contextParamIndex !== undefined; const contextIndex = methodMeta.contextParamIndex; const methodArgsLength = argsLength + (hasContext ? 1 : 0); const timeout = this.controllerMeta.getMethodTimeout(methodMeta); const self = this; return async function(ctx: Context, next: Next) { // if hosts is not empty and host is not matched, not execute if (host && host !== ctx.host) { return await next(); } // HTTP decorator core implement // use controller metadata map http request to function arguments const eggObj = await self.eggContainerFactory.getOrCreateEggObject(self.proto, self.proto.name); const realObj = eggObj.obj; const realMethod = realObj[methodMeta.name]; const args: Array = new Array(methodArgsLength); if (hasContext) { args[contextIndex!] = ctx; } for (const [ index, param ] of methodMeta.paramMap) { switch (param.type) { case HTTPParamType.BODY: { args[index] = ctx.request.body; break; } case HTTPParamType.PARAM: { const pathParam: PathParamMeta = param as PathParamMeta; args[index] = ctx.params[pathParam.name]; break; } case HTTPParamType.QUERY: { const queryParam: QueryParamMeta = param as QueryParamMeta; args[index] = ctx.query[queryParam.name]; break; } case HTTPParamType.QUERIES: { const queryParam: QueriesParamMeta = param as QueriesParamMeta; args[index] = ctx.queries[queryParam.name]; break; } case HTTPParamType.HEADERS: { args[index] = ctx.request.headers; break; } case HTTPParamType.REQUEST: { args[index] = new HTTPRequest(ctx); break; } case HTTPParamType.COOKIES: { args[index] = new HTTPCookies(ctx, []); break; } default: assert.fail('never arrive'); } } let body: unknown; try { body = await TimerUtil.timeout(() => Reflect.apply(realMethod, realObj, args), timeout); } catch (e: any) { if (e instanceof TimerUtil.TimeoutError) { ctx.logger.error(`timeout after ${timeout}ms`); ctx.throw(500, 'timeout'); } throw e; } // https://github.com/koajs/koa/blob/master/lib/response.js#L88 // ctx.status is set const explicitStatus = (ctx.response as any)._explicitStatus; if ( // has body body != null || // status is not set and has no body // code should by 204 // https://github.com/koajs/koa/blob/master/lib/response.js#L140 !explicitStatus ) { ctx.body = body; } }; } checkDuplicate() { // 1. check duplicate with egg controller this.checkDuplicateInRouter(this.router); // 2. check duplicate with host tegg controller let hostRouter; const hosts = this.controllerMeta.getMethodHosts(this.methodMeta) || []; hosts.forEach(h => { if (h) { hostRouter = this.checkRouters.get(h); if (!hostRouter) { hostRouter = new EggRouter({ sensitive: true }, {} as any); this.checkRouters.set(h, hostRouter!); } } if (hostRouter) { this.checkDuplicateInRouter(hostRouter); this.registerToRouter(hostRouter); } }); } private registerToRouter(router: Router) { const routerFunc = router[this.methodMeta.method.toLowerCase()]; const methodRealPath = this.controllerMeta.getMethodRealPath(this.methodMeta); const methodName = this.controllerMeta.getMethodName(this.methodMeta); Reflect.apply(routerFunc, router, [ methodName, methodRealPath, noop ]); } private checkDuplicateInRouter(router: Router) { const methodRealPath = this.controllerMeta.getMethodRealPath(this.methodMeta); const matched = router.match(methodRealPath, this.methodMeta.method); const methodName = this.controllerMeta.getMethodName(this.methodMeta); if (matched.route) { const [ layer ] = matched.path; const err = new RouterConflictError(`register http controller ${methodName} failed, ${this.methodMeta.method} ${methodRealPath} is conflict with exists rule ${layer.path}`); throw FrameworkErrorFormater.format(err); } } register(rootProtoManager: RootProtoManager) { const methodRealPath = this.controllerMeta.getMethodRealPath(this.methodMeta); const methodName = this.controllerMeta.getMethodName(this.methodMeta); const routerFunc = this.router[this.methodMeta.method.toLowerCase()]; const methodMiddlewares = this.controllerMeta.getMethodMiddlewares(this.methodMeta); const aclMiddleware = aclMiddlewareFactory(this.controllerMeta, this.methodMeta); if (aclMiddleware) { methodMiddlewares.push(aclMiddleware); } const hosts = this.controllerMeta.getMethodHosts(this.methodMeta) || [ undefined ]; hosts.forEach(h => { const handler = this.createHandler(this.methodMeta, h); Reflect.apply(routerFunc, this.router, [ methodName, methodRealPath, ...methodMiddlewares, handler ]); // https://github.com/eggjs/egg-core/blob/0af6178022e7734c4a8b17bb56d592b315207883/lib/egg.js#L279 const regExp = pathToRegexp(methodRealPath, { sensitive: true, }); rootProtoManager.registerRootProto(this.methodMeta.method, (ctx: EggContext) => { if (regExp.test(ctx.path)) { return this.proto; } }, h || ''); }); } } ================================================ FILE: plugin/controller/lib/impl/http/Req.ts ================================================ import type { Context } from 'egg'; import { HTTPRequest as BaseHTTPRequest } from '@eggjs/tegg'; export class HTTPRequest extends BaseHTTPRequest { constructor(ctx:Context) { const request = ctx.request; // href: https://github.com/eggjs/koa/blob/master/src/request.ts#L90C1-L98C4 super(request.href, { method: request.method, headers: request.headers as Record, body: (ctx.request as any).rawBody, }); } } ================================================ FILE: plugin/controller/lib/impl/mcp/MCPConfig.ts ================================================ import type { Application, Context } from 'egg'; import { randomUUID } from 'node:crypto'; import { EventStore } from '@modelcontextprotocol/sdk/server/streamableHttp.js'; import { InMemoryEventStore } from '@modelcontextprotocol/sdk/examples/shared/inMemoryEventStore.js'; export interface MCPConfigOptions { sseInitPath: string; sseMessagePath: string; streamPath: string; statelessStreamPath: string; ssePingEnabled?: boolean; streamPingEnabled?: boolean; pingElapsed?: number; pingInterval?: number; sessionIdGenerator?: (ctx: Context) => string; eventStore?: EventStore; sseHeartTime?: number; multipleServer?: Record>; } export class MCPConfig { private _sseInitPath: string; private _sseMessagePath: string; private _streamPath: string; private _statelessStreamPath: string; private _sessionIdGenerator: (ctx: Context) => string; private _eventStore: EventStore; private _sseHeartTime: number; private _pingElapsed: number | undefined; private _pingInterval: number; private _ssePingEnabled: boolean; private _streamPingEnabled: boolean; private _multipleServer: Record>; constructor(options: MCPConfigOptions) { this._sessionIdGenerator = options.sessionIdGenerator ?? (() => randomUUID()); this._sseInitPath = options.sseInitPath; this._sseMessagePath = options.sseMessagePath; this._streamPath = options.streamPath; this._statelessStreamPath = options.statelessStreamPath; this._eventStore = options.eventStore ?? new InMemoryEventStore(); this._sseHeartTime = options.sseHeartTime ?? 25000; this._pingElapsed = options.pingElapsed; this._pingInterval = options.pingInterval ?? 5 * 1000; this._ssePingEnabled = options.ssePingEnabled ?? false; this._streamPingEnabled = options.streamPingEnabled ?? false; this._multipleServer = options.multipleServer ?? {}; } getSseInitPath(name?: string) { if (name) { const config = this._multipleServer[name]; if (config?.sseInitPath) { return config.sseInitPath; } return `/mcp/${name}/sse`; } return this._sseInitPath; } getSseMessagePath(name?: string) { if (name) { const config = this._multipleServer[name]; if (config?.sseMessagePath) { return config.sseMessagePath; } return `/mcp/${name}/message`; } return this._sseMessagePath; } getStreamPath(name?: string) { if (name) { const config = this._multipleServer[name]; if (config?.streamPath) { return config.streamPath; } return `/mcp/${name}/stream`; } return this._streamPath; } getStatelessStreamPath(name?: string) { if (name) { const config = this._multipleServer[name]; if (config?.statelessStreamPath) { return config.statelessStreamPath; } return `/mcp/${name}/stateless/stream`; } return this._statelessStreamPath; } getSessionIdGenerator(name?: string) { if (name) { const config = this._multipleServer[name]; if (config?.sessionIdGenerator) { return config.sessionIdGenerator; } return () => randomUUID(); } return this._sessionIdGenerator; } getEventStore(name?: string) { if (name) { const config = this._multipleServer[name]; if (config?.eventStore) { return config.eventStore; } return new InMemoryEventStore(); } return this._eventStore; } getSseHeartTime(name?: string) { if (name) { const config = this._multipleServer[name]; if (config?.sseHeartTime) { return config.sseHeartTime; } return 25000; } return this._sseHeartTime; } getMultipleServerNames() { return Object.keys(this._multipleServer); } getPingElapsed(name?: string) { if (name) { const config = this._multipleServer[name]; if (config?.pingElapsed !== undefined) { return config.pingElapsed; } } return this._pingElapsed; } getPingInterval(name?: string) { if (name) { const config = this._multipleServer[name]; if (config?.pingInterval !== undefined) { return config.pingInterval; } return 5 * 1000; } return this._pingInterval; } getSsePingEnabled(name?: string) { if (name) { const config = this._multipleServer[name]; if (config?.ssePingEnabled !== undefined) { return config.ssePingEnabled; } return false; } return this._ssePingEnabled; } getStreamPingEnabled(name?: string) { if (name) { const config = this._multipleServer[name]; if (config?.streamPingEnabled !== undefined) { return config.streamPingEnabled; } return false; } return this._streamPingEnabled; } setMultipleServerPath(app: Application, name: string) { if (!(app.config.mcp as MCPConfigOptions).multipleServer) { (app.config.mcp as MCPConfigOptions).multipleServer = {}; } (app.config.mcp as MCPConfigOptions).multipleServer![name] = { sseInitPath: `/mcp/${name}/sse`, sseMessagePath: `/mcp/${name}/message`, streamPath: `/mcp/${name}/stream`, statelessStreamPath: `/mcp/${name}/stateless/stream`, ...app.config.mcp.multipleServer?.[name], }; } } ================================================ FILE: plugin/controller/lib/impl/mcp/MCPControllerRegister.ts ================================================ import type { Application, Context, Router } from 'egg'; import assert from 'node:assert'; import http, { IncomingMessage, ServerResponse } from 'node:http'; import { Socket } from 'node:net'; import { ControllerMetadata, MCPControllerMeta, CONTROLLER_META_DATA, MCPPromptMeta, MCPToolMeta, ControllerType, EggContext, EggObjectName, MCPResourceMeta, MCPProtocols, } from '@eggjs/tegg'; import { EggPrototype } from '@eggjs/tegg-metadata'; import { EggContainerFactory, EggObject } from '@eggjs/tegg-runtime'; import { ControllerRegister } from '../../ControllerRegister'; import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js'; import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js'; import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { isInitializeRequest, isJSONRPCRequest, JSONRPCMessage, MessageExtraInfo } from '@modelcontextprotocol/sdk/types.js'; import awaitEvent from 'await-event'; import compose from 'koa-compose'; import getRawBody from 'raw-body'; import contentType from 'content-type'; import { MCPConfig } from './MCPConfig'; import { MCPServerHelper } from './MCPServerHelper'; import { Server } from '@modelcontextprotocol/sdk/server/index.js'; export interface MCPControllerHook { // SSE preSSEInitHandle?: (ctx: Context, transport: SSEServerTransport, register: MCPControllerRegister) => Promise preHandleInitHandle?: (ctx: Context) => Promise // STREAM preHandle?: (ctx: Context) => Promise onStreamSessionInitialized?: (ctx: Context, transport: StreamableHTTPServerTransport, server: McpServer, register: MCPControllerRegister) => Promise // COMMON preProxy?: (ctx: Context, proxyReq: http.IncomingMessage, proxyResp: http.ServerResponse) => Promise schemaLoader?: (controllerMeta: MCPControllerMeta, meta: MCPPromptMeta | MCPToolMeta) => Promise['2'] | Parameters['2']> checkAndRunProxy?: (ctx: Context, type: MCPProtocols, sessionId: string) => Promise; // middleware middlewareStart?: (ctx: Context) => Promise middlewareEnd?: (ctx: Context) => Promise middlewareError?: (ctx: Context, e: Error) => Promise } interface ServerRegisterRecord { getOrCreateEggObject: (proto: EggPrototype, name?: EggObjectName) => Promise; proto: EggPrototype, meta: T, } class InnerSSEServerTransport extends SSEServerTransport { async send(message: JSONRPCMessage) { let res: null | Error = null; try { await super.send(message); } catch (e) { res = e as Error; } finally { // eslint-disable-next-line @typescript-eslint/no-use-before-define const map = MCPControllerRegister.instance?.sseTransportsRequestMap.get(this); if (map && 'id' in message) { const { resolve, reject } = map[message.id!] ?? {}; if (resolve) { res ? reject(res) : resolve(res); delete map[String(message.id)]; } } } } } export class MCPControllerRegister implements ControllerRegister { static instance?: MCPControllerRegister; readonly app: Application; readonly eggContainerFactory: typeof EggContainerFactory; private readonly router: Router; private controllerProtos: EggPrototype[] = []; private registeredControllerProtos: EggPrototype[] = []; transports: Record = {}; sseConnections = new Map< string, { res: ServerResponse; intervalId: NodeJS.Timeout } >(); mcpServerHelperMap: Record MCPServerHelper> = {}; mcpServerMap: Record = {}; private controllerMeta: MCPControllerMeta; mcpConfig: MCPConfig; streamTransports: Record = {}; // eslint-disable-next-line no-spaced-func sseTransportsRequestMap = new Map< InnerSSEServerTransport, Record< string, { resolve: (value: PromiseLike | null) => void; reject: (reason?: any) => void; } > >(); static hooks: MCPControllerHook[] = []; globalMiddlewares: compose.ComposedMiddleware; registerMap: Record[], prompts: ServerRegisterRecord[], resources: ServerRegisterRecord[] }> = {}; pingIntervals: Record = {}; static create(proto: EggPrototype, controllerMeta: ControllerMetadata, app: Application) { assert(controllerMeta.type === ControllerType.MCP, 'controller meta type is not MCP'); if (!MCPControllerRegister.instance) { MCPControllerRegister.instance = new MCPControllerRegister(proto, controllerMeta as MCPControllerMeta, app); } MCPControllerRegister.instance.controllerProtos.push(proto); return MCPControllerRegister.instance; } constructor(_proto: EggPrototype, controllerMeta: MCPControllerMeta, app: Application) { this.app = app; this.eggContainerFactory = app.eggContainerFactory; this.router = app.router; this.controllerMeta = controllerMeta; this.mcpConfig = new MCPConfig(app.config.mcp); } static addHook(hook: MCPControllerHook) { MCPControllerRegister.hooks.push(hook); } // eslint-disable-next-line @typescript-eslint/no-unused-vars static async connectStatelessStreamTransport(_name?: string) { // No-op: MCP SDK >= 1.26 requires stateless transports to be single-use, // so transports are now created per-request in the route handler. } static clean() { if (this.instance) { this.instance.controllerProtos = []; } this.instance = undefined; } mcpStatelessStreamServerInit(name?: string) { const postRouterFunc = this.router.post; const self = this; let mw = self.app.middleware.teggCtxLifecycleMiddleware(); if (self.globalMiddlewares) { mw = compose([ mw, self.globalMiddlewares ]); } const initHandler = async (ctx: Context) => { // Create fresh transport and server per request // MCP SDK >= 1.26 requires stateless transports to be single-use const transport: StreamableHTTPServerTransport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined, }); const mcpServerHelper = self.mcpServerHelperMap[name ?? 'default'](); const registerEntry = self.registerMap[name ?? 'default']; if (registerEntry) { for (const tool of registerEntry.tools) { await mcpServerHelper.mcpToolRegister( tool.getOrCreateEggObject, tool.proto, tool.meta, ); } for (const resource of registerEntry.resources) { await mcpServerHelper.mcpResourceRegister( resource.getOrCreateEggObject, resource.proto, resource.meta, ); } for (const prompt of registerEntry.prompts) { await mcpServerHelper.mcpPromptRegister( prompt.getOrCreateEggObject, prompt.proto, prompt.meta, ); } } await mcpServerHelper.server.connect(transport); const onmessage = transport.onmessage; transport.onmessage = async (message: JSONRPCMessage, extra?: MessageExtraInfo) => { if (self.app.currentContext) { self.app.currentContext.mcpArg = message; } onmessage && await onmessage(message, extra); }; if (MCPControllerRegister.hooks.length > 0) { for (const hook of MCPControllerRegister.hooks) { await hook.preHandle?.(self.app.currentContext); } } ctx.respond = false; ctx.set({ 'content-type': 'text/event-stream', }); await ctx.app.ctxStorage.run(ctx, async () => { await mw(ctx, async () => { await transport.handleRequest(ctx.req, ctx.res); await awaitEvent(ctx.res, 'close'); }); }); return; }; Reflect.apply(postRouterFunc, this.router, [ 'chairMcpStatelessStreamInit', self.mcpConfig.getStatelessStreamPath(name), ...[], initHandler, ]); // stateless 只支持 post const getRouterFunc = this.router.get; const delRouterFunc = this.router.del; const notHandler = async (ctx: Context) => { ctx.status = 405; ctx.body = { jsonrpc: '2.0', error: { code: -32000, message: 'Method not allowed.', }, id: null, }; }; Reflect.apply(getRouterFunc, this.router, [ 'chairMcpStatelessStreamInit', self.mcpConfig.getStatelessStreamPath(name), ...[], notHandler, ]); Reflect.apply(delRouterFunc, this.router, [ 'chairMcpStatelessStreamInit', self.mcpConfig.getStatelessStreamPath(name), ...[], notHandler, ]); } mcpStreamServerInit(name?: string) { const allRouterFunc = this.router.all; const self = this; let mw = self.app.middleware.teggCtxLifecycleMiddleware(); if (self.globalMiddlewares) { mw = compose([ mw, self.globalMiddlewares ]); } const initHandler = async (ctx: Context) => { ctx.respond = false; if (MCPControllerRegister.hooks.length > 0) { for (const hook of MCPControllerRegister.hooks) { await hook.preHandle?.(self.app.currentContext); } } const sessionId = ctx.req.headers['mcp-session-id'] as string | undefined; if (!sessionId) { const ct = contentType.parse(ctx.req.headers['content-type'] || 'application/json'); let body; try { const rawBody = await getRawBody(ctx.req, { limit: '4mb', encoding: ct.parameters.charset ?? 'utf-8', }); body = JSON.parse(rawBody); } catch (e) { ctx.status = 400; ctx.body = { jsonrpc: '2.0', error: { code: -32000, message: `Bad Request: body should is json, ${e.toString()}`, }, id: null, }; return; } if (isInitializeRequest(body)) { ctx.respond = false; const eventStore = this.mcpConfig.getEventStore(); const self = this; const mcpServerHelper = self.mcpServerHelperMap[name ?? 'default'](); for (const tool of self.registerMap[name ?? 'default'].tools) { await mcpServerHelper.mcpToolRegister( tool.getOrCreateEggObject, tool.proto, tool.meta, ); } for (const resource of self.registerMap[name ?? 'default'].resources) { await mcpServerHelper.mcpResourceRegister( resource.getOrCreateEggObject, resource.proto, resource.meta, ); } for (const prompt of self.registerMap[name ?? 'default'].prompts) { await mcpServerHelper.mcpPromptRegister( prompt.getOrCreateEggObject, prompt.proto, prompt.meta, ); } const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: () => this.mcpConfig.getSessionIdGenerator(name)(ctx), eventStore, onsessioninitialized: async sessionId => { if (MCPControllerRegister.hooks.length > 0) { for (const hook of MCPControllerRegister.hooks) { await hook.onStreamSessionInitialized?.( self.app.currentContext, transport, mcpServerHelper.server, self, ); } } if (self.mcpConfig.getStreamPingEnabled(name)) { self.mcpServerPing(mcpServerHelper.server.server, sessionId, name); } }, }); ctx.set({ 'content-type': 'text/event-stream', }); await mcpServerHelper.server.connect(transport); transport.onclose = async () => { if (transport.sessionId && self.pingIntervals[transport.sessionId]) { clearInterval(self.pingIntervals[transport.sessionId]); delete self.pingIntervals[transport.sessionId]; } }; const onmessage = transport.onmessage; transport.onmessage = async (message: JSONRPCMessage, extra?: MessageExtraInfo) => { if (self.app.currentContext) { self.app.currentContext.mcpArg = message; } onmessage && await onmessage(message, extra); }; await ctx.app.ctxStorage.run(ctx, async () => { await mw(ctx, async () => { await transport.handleRequest(ctx.req, ctx.res, body); await awaitEvent(ctx.res, 'close'); }); }); } else { ctx.status = 400; ctx.body = { jsonrpc: '2.0', error: { code: -32000, message: 'Bad Request: No valid session ID provided', }, id: null, }; return; } } else if (sessionId) { const transport = self.streamTransports[sessionId]; if (transport) { if (MCPControllerRegister.hooks.length > 0) { for (const hook of MCPControllerRegister.hooks) { await hook.preHandle?.(self.app.currentContext); } } ctx.respond = false; ctx.set({ 'content-type': 'text/event-stream', }); await ctx.app.ctxStorage.run(ctx, async () => { await mw(ctx, async () => { await transport.handleRequest(ctx.req, ctx.res); await awaitEvent(ctx.res, 'close'); }); }); return; } if (MCPControllerRegister.hooks.length > 0) { for (const hook of MCPControllerRegister.hooks) { const checked = await hook.checkAndRunProxy?.( self.app.currentContext, MCPProtocols.STREAM, sessionId, ); if (checked) { return; } } } } return; }; Reflect.apply(allRouterFunc, this.router, [ 'chairMcpStreamInit', self.mcpConfig.getStreamPath(name), ...[], initHandler, ]); } mcpServerInit(name?: string) { const routerFunc = this.router.get; // const aclMiddleware = aclMiddlewareFactory(this.controllerMeta, this.methodMeta); // if (aclMiddleware) { // methodMiddlewares.push(aclMiddleware); // } const self = this; const initHandler = async (ctx: Context) => { const transport = new InnerSSEServerTransport( self.mcpConfig.getSseMessagePath(name), ctx.res, ); const id = transport.sessionId; self.app.logger.warn('sse init, sessionId: %s, removeIp: %s', id, `${ctx.request.socket.remoteAddress}:${ctx.request.socket.remotePort}`); if (MCPControllerRegister.hooks.length > 0) { for (const hook of MCPControllerRegister.hooks) { await hook.preSSEInitHandle?.( self.app.currentContext, transport, self, ); } } // https://github.com/modelcontextprotocol/typescript-sdk/issues/270#issuecomment-2789526821 const intervalId = setInterval(() => { if (self.sseConnections.has(id) && !ctx.res.writableEnded) { ctx.res.write(': keepalive\n\n'); } else { clearInterval(intervalId); self.sseConnections.delete(id); } }, self.mcpConfig.getSseHeartTime(name)); self.sseConnections.set(id, { res: ctx.res, intervalId }); self.transports[id] = transport; ctx.set({ 'content-type': 'text/event-stream', }); ctx.respond = false; const mcpServerHelper = self.mcpServerHelperMap[name ?? 'default'](); for (const tool of self.registerMap[name ?? 'default'].tools) { await mcpServerHelper.mcpToolRegister( tool.getOrCreateEggObject, tool.proto, tool.meta, ); } for (const resource of self.registerMap[name ?? 'default'].resources) { await mcpServerHelper.mcpResourceRegister( resource.getOrCreateEggObject, resource.proto, resource.meta, ); } for (const prompt of self.registerMap[name ?? 'default'].prompts) { await mcpServerHelper.mcpPromptRegister( prompt.getOrCreateEggObject, prompt.proto, prompt.meta, ); } await mcpServerHelper.server.connect(transport); self.mcpServerMap[id] = mcpServerHelper.server; if (self.mcpConfig.getSsePingEnabled(name)) { self.mcpServerPing(mcpServerHelper.server.server, transport.sessionId, name); } return self.sseCtxStorageRun.bind(self)(ctx, transport, name); }; Reflect.apply(routerFunc, this.router, [ 'chairMcpInit', self.mcpConfig.getSseInitPath(name), ...[], initHandler, ]); } async clearSseMcpServer(transport: SSEServerTransport) { delete this.transports[transport.sessionId]; delete this.mcpServerMap[transport.sessionId]; if (transport.sessionId && this.pingIntervals[transport.sessionId]) { clearInterval(this.pingIntervals[transport.sessionId]); delete this.pingIntervals[transport.sessionId]; } this.sseTransportsRequestMap.delete(transport); const connection = this.sseConnections.get(transport.sessionId); if (connection) { clearInterval(connection.intervalId); this.sseConnections.delete(transport.sessionId); } } sseCtxStorageRun(ctx: Context, transport: SSEServerTransport, name?: string) { const self = this; let mw = this.app.middleware.teggCtxLifecycleMiddleware(); if (self.globalMiddlewares) { mw = compose([ mw, self.globalMiddlewares ]); } const closeFunc = transport.onclose; transport.onclose = (...args) => { closeFunc?.(...args); this.clearSseMcpServer(transport); }; transport.onerror = error => { self.app.logger.error('session %s error %o', transport.sessionId, error); }; const messageFunc = transport.onmessage; self.sseTransportsRequestMap.set(transport, {}); transport.onmessage = async (message: JSONRPCMessage, extra?: MessageExtraInfo) => { const args = [ message, extra ]; // 这里需要 new 一个新的 ctx,否则 ContextProto 会未被初始化 const socket = new Socket(); const req = new IncomingMessage(socket); const res = new ServerResponse(req); req.method = 'POST'; req.url = self.mcpConfig.getSseInitPath(name); req.headers = { ...ctx.req.headers, ...extra?.requestInfo?.headers, accept: 'application/json, text/event-stream', 'content-type': 'application/json', }; const newCtx = self.app.createContext(req, res) as unknown as Context; await ctx.app.ctxStorage.run(newCtx, async () => { await mw(newCtx, async () => { if (MCPControllerRegister.hooks.length > 0) { for (const hook of MCPControllerRegister.hooks) { await hook.preHandle?.(newCtx); } } messageFunc!(message, extra); if (isJSONRPCRequest(args[0])) { const map = self.sseTransportsRequestMap.get(transport)!; const wait = new Promise((resolve, reject) => { if (extra && 'id' in extra) { map[extra.id as string] = { resolve, reject }; } }); await wait; } }); }); }; } mcpServerRegister(name?: string) { const routerFunc = this.router.post; const self = this; // const aclMiddleware = aclMiddlewareFactory(this.controllerMeta, this.methodMeta); // if (aclMiddleware) { // methodMiddlewares.push(aclMiddleware); // } let mw = self.app.middleware.teggCtxLifecycleMiddleware(); if (self.globalMiddlewares) { mw = compose([ mw, self.globalMiddlewares ]); } const messageHander = async (ctx: Context) => { const sessionId = ctx.query.sessionId; if (self.transports[sessionId]) { if (MCPControllerRegister.hooks.length > 0) { for (const hook of MCPControllerRegister.hooks) { await hook.preHandleInitHandle?.(self.app.currentContext); } } self.app.logger.info('message coming', sessionId); try { const ct = contentType.parse(ctx.req.headers['content-type'] ?? ''); const rawBody = await getRawBody(ctx.req, { limit: '4mb', encoding: ct.parameters.charset ?? 'utf-8', }); const body = JSON.parse(rawBody); ctx.mcpArg = body; await self.transports[sessionId].handlePostMessage(ctx.req, ctx.res, body); } catch (error) { self.app.logger.error('Error handling MCP message', error); if (!ctx.res.headersSent) { ctx.status = 500; ctx.body = { jsonrpc: '2.0', error: { code: -32603, message: `Internal error: ${error.message}`, }, id: null, }; } } return; } if (MCPControllerRegister.hooks.length > 0) { for (const hook of MCPControllerRegister.hooks) { const checked = await hook.checkAndRunProxy?.( self.app.currentContext, MCPProtocols.SSE, sessionId, ); if (checked) { return; } } } }; Reflect.apply(routerFunc, this.router, [ 'chairMcpMessage', self.mcpConfig.getSseMessagePath(name), ...[ mw ], messageHander, ]); } getGlobalMiddleware() { const middlewareNames = this.app.config.mcp.middleware || []; const middlewares: compose.Middleware[] = []; for (const name of middlewareNames) { const middlewareFactory = (this.app as unknown as any).middlewares[name]; if (!middlewareFactory) { throw new TypeError(`Middleware ${name} not found`); } const options = (this.app.config as any)[name] || {}; const mw = middlewareFactory(options, this.app); (mw as any)._name = name; middlewares.push(mw); } this.globalMiddlewares = compose(middlewares); } mcpServerPing(server: Server, sessionId: string, name?: string) { const duration = this.mcpConfig.getPingElapsed(name); const interval = this.mcpConfig.getPingInterval(name); const startTime = Date.now(); let errCount = 0; const timerId = setInterval(async () => { const elapsed = Date.now() - startTime; try { await server.ping(); } catch (e) { errCount++; this.app.logger.warn('mcp server ping failed: %s, errCount: %s', e, errCount); } finally { if ((duration && elapsed >= duration) || errCount > 10) { if (this.sseConnections[sessionId]) { this.clearSseMcpServer(this.sseConnections[sessionId]); } else { this.app.logger.warn('mcp server ping clear fail, sessionId: ', sessionId); } } } }, interval); this.pingIntervals[sessionId] = timerId; } async register() { for (const proto of this.controllerProtos) { if (this.registeredControllerProtos.includes(proto)) { continue; } const metadata = proto.getMetaData( CONTROLLER_META_DATA, ) as MCPControllerMeta; if (!this.mcpServerHelperMap[metadata.name ?? 'default']) { this.getGlobalMiddleware(); this.mcpServerHelperMap[metadata.name ?? 'default'] = () => { return new MCPServerHelper({ name: this.controllerMeta.name ?? `chair-mcp-${metadata.name ?? this.app.name}-server`, version: this.controllerMeta.version ?? '1.0.0', hooks: MCPControllerRegister.hooks, }); }; this.mcpStatelessStreamServerInit(metadata.name); this.mcpStreamServerInit(metadata.name); this.mcpServerInit(metadata.name); this.mcpServerRegister(metadata.name); if (metadata.name) { this.mcpConfig.setMultipleServerPath(this.app, metadata.name); } } for (const prompt of metadata.prompts) { if (!this.registerMap[metadata.name ?? 'default']) { this.registerMap[metadata.name ?? 'default'] = { prompts: [], resources: [], tools: [], }; } this.registerMap[metadata.name ?? 'default'].prompts.push({ getOrCreateEggObject: this.eggContainerFactory.getOrCreateEggObject.bind( this.eggContainerFactory, ), proto, meta: prompt, }); } for (const resource of metadata.resources) { if (!this.registerMap[metadata.name ?? 'default']) { this.registerMap[metadata.name ?? 'default'] = { prompts: [], resources: [], tools: [], }; } this.registerMap[metadata.name ?? 'default'].resources.push({ getOrCreateEggObject: this.eggContainerFactory.getOrCreateEggObject.bind( this.eggContainerFactory, ), proto, meta: resource, }); } for (const tool of metadata.tools) { if (!this.registerMap[metadata.name ?? 'default']) { this.registerMap[metadata.name ?? 'default'] = { prompts: [], resources: [], tools: [], }; } this.registerMap[metadata.name ?? 'default'].tools.push({ getOrCreateEggObject: this.eggContainerFactory.getOrCreateEggObject.bind( this.eggContainerFactory, ), proto, meta: tool, }); } this.registeredControllerProtos.push(proto); } } } ================================================ FILE: plugin/controller/lib/impl/mcp/MCPServerHelper.ts ================================================ import { McpServer, ReadResourceCallback, ToolCallback, PromptCallback, } from '@modelcontextprotocol/sdk/server/mcp.js'; import { MCPControllerHook, MCPControllerRegister, } from './MCPControllerRegister'; import { CONTROLLER_META_DATA, EggObject, EggObjectName, EggPrototype } from '@eggjs/tegg-types'; import { MCPControllerMeta, MCPPromptMeta, MCPResourceMeta, MCPToolMeta } from '@eggjs/tegg'; export interface MCPServerHelperOptions { name: string; version: string; hooks: MCPControllerHook[]; } export class MCPServerHelper { server: McpServer; hooks: MCPControllerHook[]; constructor(opts: MCPServerHelperOptions) { this.server = new McpServer( { name: opts.name, version: opts.version, }, { capabilities: { logging: {} } }, ); this.hooks = opts.hooks; } async mcpResourceRegister( getOrCreateEggObject: (proto: EggPrototype, name?: EggObjectName) => Promise, controllerProto: EggPrototype, resourceMeta: MCPResourceMeta, ) { const handler = async (...args) => { const eggObj = await getOrCreateEggObject( controllerProto, controllerProto.name, ); const realObj = eggObj.obj; const realMethod = realObj[resourceMeta.name]; return Reflect.apply( realMethod, realObj, args, ) as ReturnType; }; const name = resourceMeta.mcpName ?? resourceMeta.name; if (resourceMeta.uri) { this.server.registerResource(name, resourceMeta.uri, resourceMeta.metadata ?? {}, handler); } else if (resourceMeta.template) { this.server.registerResource(name, resourceMeta.template, resourceMeta.metadata ?? {}, handler); } else { throw new Error(`MCPResource ${name} must have uri or template`); } } async mcpToolRegister( getOrCreateEggObject: (proto: EggPrototype, name?: EggObjectName) => Promise, controllerProto: EggPrototype, toolMeta: MCPToolMeta, ) { const controllerMeta = controllerProto.getMetaData( CONTROLLER_META_DATA, ) as MCPControllerMeta; const name: string = toolMeta.mcpName ?? toolMeta.name; const description: string | undefined = toolMeta.description; let schema: NonNullable['argsSchema'] | undefined; if (toolMeta.detail?.argsSchema) { schema = toolMeta.detail?.argsSchema; } else if (MCPControllerRegister.hooks.length > 0) { for (const hook of MCPControllerRegister.hooks) { schema = await hook.schemaLoader?.(controllerMeta, toolMeta); if (schema) { break; } } } const handler = async (...args) => { const eggObj = await getOrCreateEggObject( controllerProto, controllerProto.name, ); const realObj = eggObj.obj; const realMethod = realObj[toolMeta.name]; let newArgs: any[] = []; if (schema && toolMeta.detail) { // 如果有 schema 则证明入参第一个就是 schema newArgs[toolMeta.detail.index] = args[0]; // 如果有 schema 则证明入参第二个就是 extra if (toolMeta.extra) { newArgs[toolMeta.extra] = args[1]; } } else if (toolMeta.extra) { // 无 schema, 那么入参第一个就是 extra newArgs[toolMeta.extra] = args[0]; } newArgs = [ ...newArgs, ...args ]; return Reflect.apply(realMethod, realObj, newArgs) as ReturnType; }; this.server.registerTool(name, { description, inputSchema: schema, // TODO: outputSchema }, handler); } async mcpPromptRegister( getOrCreateEggObject: (proto: EggPrototype, name?: EggObjectName) => Promise, controllerProto: EggPrototype, promptMeta: MCPPromptMeta, ) { const controllerMeta = controllerProto.getMetaData(CONTROLLER_META_DATA) as MCPControllerMeta; const name: string = promptMeta.mcpName ?? promptMeta.name; const description: string | undefined = promptMeta.description; let schema: NonNullable['argsSchema'] | undefined; if (promptMeta.detail?.argsSchema) { schema = promptMeta.detail?.argsSchema; } else if (MCPControllerRegister.hooks.length > 0) { for (const hook of MCPControllerRegister.hooks) { schema = await hook.schemaLoader?.(controllerMeta, promptMeta); if (schema) { break; } } } const handler = async (...args) => { const eggObj = await getOrCreateEggObject(controllerProto, controllerProto.name); const realObj = eggObj.obj; const realMethod = realObj[promptMeta.name]; let newArgs: any[] = []; if (schema && promptMeta.detail) { // 如果有 schema 则证明入参第一个就是 schema newArgs[promptMeta.detail.index] = args[0]; // 如果有 schema 则证明入参第二个就是 extra if (promptMeta.extra) { newArgs[promptMeta.extra] = args[1]; } } else if (promptMeta.extra) { // 无 schema, 那么入参第一个就是 extra newArgs[promptMeta.extra] = args[0]; } newArgs = [ ...newArgs, ...args ]; return Reflect.apply(realMethod, realObj, newArgs) as ReturnType; }; this.server.registerPrompt(name, { title: promptMeta.title, description, argsSchema: schema, }, handler); } } ================================================ FILE: plugin/controller/package.json ================================================ { "name": "@eggjs/tegg-controller-plugin", "eggPlugin": { "name": "teggController", "strict": false, "dependencies": [ "tegg" ] }, "version": "3.78.15", "description": "controller decorator for egg", "keywords": [ "egg", "plugin", "typescript", "module", "tegg" ], "files": [ "app.js", "app.d.ts", "config/**/*.js", "config/**/*.d.ts", "lib/**/*.js", "lib/**/*.d.ts", "app/**/*.js", "app/**/*.d.ts", "typings/*.d.ts" ], "types": "typings/index.d.ts", "scripts": { "test": "cross-env NODE_ENV=test mocha", "clean": "tsc -b --clean", "tsc": "ut run clean && tsc -p ./tsconfig.json", "tsc:pub": "ut run clean && tsc -p ./tsconfig.pub.json", "prepublishOnly": "ut tsc:pub" }, "homepage": "https://github.com/eggjs/tegg", "bugs": { "url": "https://github.com/eggjs/tegg/issues" }, "repository": { "type": "git", "url": "git@github.com:eggjs/tegg.git", "directory": "plugin/controller" }, "engines": { "node": ">=14.0.0" }, "dependencies": { "@eggjs/agent-runtime": "^3.78.15", "@eggjs/egg-module-common": "^3.78.15", "@eggjs/router": "^2.0.1", "@eggjs/tegg": "^3.78.15", "@eggjs/tegg-common-util": "^3.78.15", "@eggjs/tegg-loader": "^3.78.15", "@eggjs/tegg-metadata": "^3.78.15", "@eggjs/tegg-runtime": "^3.78.15", "@modelcontextprotocol/sdk": "^1.23.0", "await-event": "^2.1.0", "content-type": "^1.0.5", "egg-errors": "^2.3.0", "globby": "^10.0.2", "koa-compose": "^3.2.1", "path-to-regexp": "^1.8.0", "raw-body": "^2.5.2", "sdk-base": "^4.2.0", "zod": "^4.0.0" }, "devDependencies": { "@eggjs/module-test-util": "^3.78.15", "@eggjs/tegg-config": "^3.78.15", "@eggjs/tegg-plugin": "^3.78.15", "@types/mocha": "^10.0.1", "@types/node": "^20.2.4", "cross-env": "^7.0.3", "egg": "^3.9.1", "egg-mock": "^5.5.0", "egg-tracer": "^2.0.0", "mocha": "^10.2.0", "ts-node": "^10.9.1", "typescript": "^5.0.4" }, "publishConfig": { "access": "public" }, "gitHead": "240a3e9f40fda65ebb7589727d197db5ce17916c" } ================================================ FILE: plugin/controller/test/fixtures/apps/acl-app/app/controller/AclController.ts ================================================ import { Acl, HTTPController, HTTPMethod, HTTPMethodEnum } from '@eggjs/tegg'; @HTTPController() export default class AclController { @Acl() @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/foo', }) async foo() { return 'hello, foo'; } @Acl('mock1') @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/bar', }) async bar() { return 'hello, bar'; } } ================================================ FILE: plugin/controller/test/fixtures/apps/acl-app/app/extend/context.ts ================================================ import { EggContext } from '@eggjs/tegg'; export default { acl(this: EggContext, code?: string) { const authenticated = this.query.pass === 'true'; let authorized = true; if (code && this.query.code !== code) { authorized = false; } if (!authenticated) { const error = new Error('unauthenticated') as any; error.data = { status: '401', redirectUrl: 'http://alipay.com/401', }; throw error; } if (!authorized) { const error = new Error('unauthorized') as any; error.data = { status: '403', redirectUrl: 'http://alipay.com/403', }; throw error; } }, }; ================================================ FILE: plugin/controller/test/fixtures/apps/acl-app/config/config.default.js ================================================ 'use strict'; module.exports = function() { const config = { keys: 'test key', security: { csrf: { ignoreJSON: false, } }, }; return config; }; ================================================ FILE: plugin/controller/test/fixtures/apps/acl-app/config/plugin.js ================================================ 'use strict'; exports.tegg = { package: '@eggjs/tegg-plugin', enable: true, }; exports.teggConfig = { package: '@eggjs/tegg-config', enable: true, }; ================================================ FILE: plugin/controller/test/fixtures/apps/acl-app/package.json ================================================ { "name": "acl-app" } ================================================ FILE: plugin/controller/test/fixtures/apps/controller-app/app/controller/AopMiddlewareController.ts ================================================ import { HTTPController, HTTPMethod, HTTPMethodEnum, Middleware, Inject, } from '@eggjs/tegg'; import AppService from '../../modules/multi-module-service/AppService'; import { CountAdvice } from '../../modules/multi-module-common/advice/CountAdvice'; import { FooControllerAdvice } from '../../modules/multi-module-common/advice/FooControllerAdvice'; import { FooMethodAdvice } from '../../modules/multi-module-common/advice/FooMethodAdvice'; import { BarMethodAdvice } from '../../modules/multi-module-common/advice/BarMethodAdvice'; @HTTPController({ path: '/aop/middleware', }) @Middleware(CountAdvice, FooControllerAdvice) export class AopMiddlewareController { @Inject() appService: AppService; @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/global', }) // CountAdvice, FooControllerAdvice async global() { return { method: 'global', }; } @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/method', }) @Middleware(FooMethodAdvice, BarMethodAdvice) async middleware() { // FooMethodAdvice, BarMethodAdvice, CountAdvice, FooControllerAdvice return { method: 'middleware', }; } } ================================================ FILE: plugin/controller/test/fixtures/apps/controller-app/app/controller/AppController.ts ================================================ import { Context as EggContext } from 'egg'; import { Context, HTTPBody, HTTPController, HTTPMethod, HTTPMethodEnum, HTTPParam, HTTPQuery, HTTPHeaders, IncomingHttpHeaders, Middleware, Inject, } from '@eggjs/tegg'; import AppService from '../../modules/multi-module-service/AppService'; import App from '../../modules/multi-module-common/model/App'; import { countMw } from '../middleware/count_mw'; @HTTPController({ path: '/apps', }) @Middleware(countMw) export class AppController { @Inject() appService: AppService; @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/:id', }) async get(@Context() ctx: EggContext, @HTTPParam() id: string) { const traceId = await ctx.tracer.traceId; const app = await this.appService.findApp(id); return { traceId, app, }; } @HTTPMethod({ method: HTTPMethodEnum.GET, path: '', }) async find(@Context() ctx: EggContext, @HTTPQuery() name: string) { const traceId = await ctx.tracer.traceId; const app = await this.appService.findApp(name); return { traceId, app, }; } @HTTPMethod({ method: HTTPMethodEnum.POST, path: '', }) async save(@Context() ctx: EggContext, @HTTPBody() app: App, @HTTPHeaders() headers: IncomingHttpHeaders) { const traceId = await ctx.tracer.traceId; await this.appService.save(app); return { success: true, traceId, sessionId: headers['x-session-id'], }; } } ================================================ FILE: plugin/controller/test/fixtures/apps/controller-app/app/controller/MiddlewareController.ts ================================================ import { HTTPController, HTTPMethod, HTTPMethodEnum, Middleware, Inject, } from '@eggjs/tegg'; import AppService from '../../modules/multi-module-service/AppService'; import { countMw } from '../middleware/count_mw'; import { logMwFactory } from '../middleware/log_mw'; import { callModuleCtx } from '../middleware/call_module'; @HTTPController({ path: '/middleware', }) @Middleware(countMw) export class MiddlewareController { @Inject() appService: AppService; @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/global', }) async global() { return {}; } @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/method', }) @Middleware(logMwFactory('use middleware')) async middleware() { return {}; } @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/methodCallModule', }) @Middleware(callModuleCtx) async middlewareCallModule() { return {}; } } ================================================ FILE: plugin/controller/test/fixtures/apps/controller-app/app/controller/ParamController.ts ================================================ import { HTTPController, HTTPMethod, HTTPMethodEnum, HTTPParam, } from '@eggjs/tegg'; @HTTPController({ path: '/foo/:fooId', }) export class ParamController { @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/bar/:barId', }) async get(@HTTPParam() barId: string, @HTTPParam() fooId: string) { return { fooId, barId, }; } } ================================================ FILE: plugin/controller/test/fixtures/apps/controller-app/app/controller/PriorityController.ts ================================================ import { HTTPController, HTTPMethod, HTTPMethodEnum, } from '@eggjs/tegg'; @HTTPController({ path: '/users', }) export class PriorityController { @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/*', }) async lowPriority() { return 'low priority'; } @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/group', }) async highPriority() { return 'high priority'; } } ================================================ FILE: plugin/controller/test/fixtures/apps/controller-app/app/controller/RedirectController.ts ================================================ import { Context as EggContext } from 'egg'; import { Context, HTTPController, HTTPMethod, HTTPMethodEnum, } from '@eggjs/tegg'; @HTTPController() export class EdgeCaseController { @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/redirect', }) async redirect(@Context() ctx: EggContext) { ctx.redirect('https://alipay.com'); } @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/empty', }) async empty() { return; } } ================================================ FILE: plugin/controller/test/fixtures/apps/controller-app/app/controller/TimeoutController.ts ================================================ import { HTTPController, HTTPMethod, HTTPMethodEnum } from '@eggjs/tegg'; import { TimerUtil } from '@eggjs/tegg-common-util'; @HTTPController({ timeout: 1000, }) export class TimeoutController { @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/timeout-1', timeout: 500 }) async timeout1() { await TimerUtil.sleep(600); return 'success'; } @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/timeout-2', timeout: 500 }) async timeout2() { await TimerUtil.sleep(300); return 'success'; } @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/timeout-3' }) async timeout3() { await TimerUtil.sleep(1200); return 'success'; } } ================================================ FILE: plugin/controller/test/fixtures/apps/controller-app/app/controller/ViewController.ts ================================================ import { HTTPController, HTTPMethod, HTTPMethodEnum, } from '@eggjs/tegg'; @HTTPController() export class ViewController { @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/*', }) async get() { return 'hello, view'; } } ================================================ FILE: plugin/controller/test/fixtures/apps/controller-app/app/middleware/call_module.ts ================================================ import { Context } from 'egg'; import { Next } from '@eggjs/tegg'; export async function callModuleCtx(ctx: Context, next: Next) { await (ctx.module as any).multiModuleService.appService.findApp('foo'); await next(); } ================================================ FILE: plugin/controller/test/fixtures/apps/controller-app/app/middleware/count_mw.ts ================================================ import { Context } from 'egg'; import { Next } from '@eggjs/tegg'; let index = 0; export async function countMw(ctx: Context, next: Next) { await next(); if (ctx.body) ctx.body.count = index++; } ================================================ FILE: plugin/controller/test/fixtures/apps/controller-app/app/middleware/log_mw.ts ================================================ import { Context } from 'egg'; import { Next } from '@eggjs/tegg'; export function logMwFactory(log: string) { return async function logMw(ctx: Context, next: Next) { await next(); ctx.body.log = log; }; } ================================================ FILE: plugin/controller/test/fixtures/apps/controller-app/config/config.default.js ================================================ 'use strict'; module.exports = function() { const config = { keys: 'test key', security: { csrf: { ignoreJSON: false, }, }, }; return config; }; ================================================ FILE: plugin/controller/test/fixtures/apps/controller-app/config/module.json ================================================ [ { "path": "../modules/multi-module-common" }, { "path": "../modules/multi-module-repo" }, { "path": "../modules/multi-module-service" } ] ================================================ FILE: plugin/controller/test/fixtures/apps/controller-app/config/plugin.js ================================================ 'use strict'; exports.tracer = { package: 'egg-tracer', enable: true, }; exports.tegg = { package: '@eggjs/tegg-plugin', enable: true, }; exports.teggConfig = { package: '@eggjs/tegg-config', enable: true, }; exports.aopModule = { package: '@eggjs/tegg-aop-plugin', enable: true, }; ================================================ FILE: plugin/controller/test/fixtures/apps/controller-app/modules/multi-module-common/advice/BarMethodAdvice.ts ================================================ import { Advice, AdviceContext, IAdvice, } from '@eggjs/tegg/aop'; import { AccessLevel } from '@eggjs/tegg-types'; @Advice({ accessLevel: AccessLevel.PUBLIC, }) export class BarMethodAdvice implements IAdvice { async around(_ctx: AdviceContext, next: () => Promise) { const body = await next(); body.aopList = body.aopList || []; body.aopList.push(BarMethodAdvice.name); return body; } } ================================================ FILE: plugin/controller/test/fixtures/apps/controller-app/modules/multi-module-common/advice/CountAdvice.ts ================================================ import { Advice, AdviceContext, IAdvice, } from '@eggjs/tegg/aop'; import { AccessLevel } from '@eggjs/tegg-types'; @Advice({ accessLevel: AccessLevel.PUBLIC, }) export class CountAdvice implements IAdvice { private index = 0; async around(_ctx: AdviceContext, next: () => Promise) { const body = await next(); if (body) body.count = this.index++; body.aopList = body.aopList || []; body.aopList.push(CountAdvice.name); return body; } } ================================================ FILE: plugin/controller/test/fixtures/apps/controller-app/modules/multi-module-common/advice/FooControllerAdvice.ts ================================================ import { Advice, AdviceContext, IAdvice } from '@eggjs/tegg/aop'; import { AccessLevel } from '@eggjs/tegg-types'; @Advice({ accessLevel: AccessLevel.PUBLIC, }) export class FooControllerAdvice implements IAdvice { async around(_ctx: AdviceContext, next: () => Promise) { const body = await next(); body.aopList = body.aopList || []; body.aopList.push(FooControllerAdvice.name); return body; } } ================================================ FILE: plugin/controller/test/fixtures/apps/controller-app/modules/multi-module-common/advice/FooMethodAdvice.ts ================================================ import { Advice, AdviceContext, IAdvice, } from '@eggjs/tegg/aop'; import { AccessLevel } from '@eggjs/tegg-types'; @Advice({ accessLevel: AccessLevel.PUBLIC, }) export class FooMethodAdvice implements IAdvice { async around(_ctx: AdviceContext, next: () => Promise) { const body = await next(); body.aopList = body.aopList || []; body.aopList.push(FooMethodAdvice.name); return body; } } ================================================ FILE: plugin/controller/test/fixtures/apps/controller-app/modules/multi-module-common/model/App.ts ================================================ export default class App { name: string; desc: string; } ================================================ FILE: plugin/controller/test/fixtures/apps/controller-app/modules/multi-module-common/package.json ================================================ { "name": "multi-module-common", "eggModule": { "name": "multi-module-common" } } ================================================ FILE: plugin/controller/test/fixtures/apps/controller-app/modules/multi-module-repo/AppRepo.ts ================================================ import PersistenceService from './PersistenceService'; import { AccessLevel, ContextProto, Inject } from '@eggjs/tegg'; import App from '../multi-module-common/model/App'; @ContextProto({ accessLevel: AccessLevel.PUBLIC, }) export default class AppRepo { @Inject() persistenceService: PersistenceService; public async findApp(name): Promise { const raw = this.persistenceService.get(name); if (!raw) { return null; } return JSON.parse(raw); } public async insertApp(app: App): Promise { this.persistenceService.set(app.name, JSON.stringify(app)); } } ================================================ FILE: plugin/controller/test/fixtures/apps/controller-app/modules/multi-module-repo/PersistenceService.ts ================================================ import { SingletonProto } from '@eggjs/tegg'; @SingletonProto() export default class PersistenceService { private store: Map = new Map(); public set(key: string, val: string) { this.store.set(key, val); } public get(key: string): string | undefined { return this.store.get(key); } } ================================================ FILE: plugin/controller/test/fixtures/apps/controller-app/modules/multi-module-repo/package.json ================================================ { "name": "multi-module-repo", "eggModule": { "name": "multi-module-repo" } } ================================================ FILE: plugin/controller/test/fixtures/apps/controller-app/modules/multi-module-service/AppService.ts ================================================ import { AccessLevel, ContextProto, Inject } from '@eggjs/tegg'; import AppRepo from '../multi-module-repo/AppRepo'; import App from '../multi-module-common/model/App'; @ContextProto({ accessLevel: AccessLevel.PUBLIC, }) export default class AppService { @Inject() appRepo: AppRepo; findApp(name: string): Promise { return this.appRepo.findApp(name); } save(app: App) { return this.appRepo.insertApp(app); } } ================================================ FILE: plugin/controller/test/fixtures/apps/controller-app/modules/multi-module-service/package.json ================================================ { "name": "multi-module-service", "eggModule": { "name": "multiModuleService" } } ================================================ FILE: plugin/controller/test/fixtures/apps/controller-app/package.json ================================================ { "name": "controller-app" } ================================================ FILE: plugin/controller/test/fixtures/apps/duplicate-controller-name-app/app/controller/AppController.ts ================================================ import { HTTPController, HTTPMethod, HTTPMethodEnum, } from '@eggjs/tegg'; @HTTPController({ controllerName: 'AppController', path: '/apps', }) export class AppController { @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/:id', }) async get() { return 'hello'; } } ================================================ FILE: plugin/controller/test/fixtures/apps/duplicate-controller-name-app/app/controller/AppController2.ts ================================================ import { HTTPController, HTTPMethod, HTTPMethodEnum, } from '@eggjs/tegg'; @HTTPController({ controllerName: 'AppController', path: '/apps', }) export class AppController2 { @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/:id', }) async get() { return 'hello'; } } ================================================ FILE: plugin/controller/test/fixtures/apps/duplicate-controller-name-app/config/config.default.js ================================================ 'use strict'; module.exports = function() { const config = { keys: 'test key', security: { csrf: { ignoreJSON: false, } }, }; return config; }; exports.teggConfig = { package: '@eggjs/tegg-config', enable: true, }; ================================================ FILE: plugin/controller/test/fixtures/apps/duplicate-controller-name-app/config/plugin.js ================================================ 'use strict'; exports.tracer = { package: 'egg-tracer', enable: true, }; exports.tegg = { package: '@eggjs/tegg-plugin', enable: true, }; exports.teggConfig = { package: '@eggjs/tegg-config', enable: true, }; ================================================ FILE: plugin/controller/test/fixtures/apps/duplicate-controller-name-app/package.json ================================================ { "name": "controller-app" } ================================================ FILE: plugin/controller/test/fixtures/apps/duplicate-proto-name-app/app/controller/AppController.ts ================================================ import { HTTPController, HTTPMethod, HTTPMethodEnum, } from '@eggjs/tegg'; @HTTPController({ path: '/apps', }) export class AppController { @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/:id', }) async get() { return 'hello'; } } ================================================ FILE: plugin/controller/test/fixtures/apps/duplicate-proto-name-app/app/controller/AppController2.ts ================================================ import { HTTPController, HTTPMethod, HTTPMethodEnum, } from '@eggjs/tegg'; @HTTPController({ controllerName: 'AppController2', path: '/apps', }) export class AppController { @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/:id', }) async get() { return 'hello'; } } ================================================ FILE: plugin/controller/test/fixtures/apps/duplicate-proto-name-app/config/config.default.js ================================================ 'use strict'; module.exports = function() { const config = { keys: 'test key', security: { csrf: { ignoreJSON: false, }, }, }; return config; }; ================================================ FILE: plugin/controller/test/fixtures/apps/duplicate-proto-name-app/config/plugin.js ================================================ 'use strict'; exports.tracer = { package: 'egg-tracer', enable: true, }; exports.tegg = { package: '@eggjs/tegg-plugin', enable: true, }; exports.teggConfig = { package: '@eggjs/tegg-config', enable: true, }; ================================================ FILE: plugin/controller/test/fixtures/apps/duplicate-proto-name-app/package.json ================================================ { "name": "controller-app" } ================================================ FILE: plugin/controller/test/fixtures/apps/host-controller-app/app/controller/AppController.ts ================================================ import { HTTPController, HTTPMethod, HTTPMethodEnum, Host, } from '@eggjs/tegg'; @Host('foo.eggjs.com') @HTTPController({ controllerName: 'AppController', path: '/apps', }) export class AppController { @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/:id', }) async get() { return 'foo'; } } ================================================ FILE: plugin/controller/test/fixtures/apps/host-controller-app/app/controller/AppController2.ts ================================================ import { HTTPController, HTTPMethod, HTTPMethodEnum, Host, } from '@eggjs/tegg'; @HTTPController({ path: '/apps', }) export class AppController2 { @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/:id', }) @Host('bar.eggjs.com') async get() { return 'bar'; } } ================================================ FILE: plugin/controller/test/fixtures/apps/host-controller-app/app/controller/MultiHostController.ts ================================================ import { HTTPController, HTTPMethod, HTTPMethodEnum, Host, } from '@eggjs/tegg'; @Host([ 'apple.eggjs.com', 'a.eggjs.com' ]) @HTTPController({ controllerName: 'MultiHostController', path: '/apps', }) export class MultiHostController { @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/apple', }) async apple() { return 'apple'; } @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/a', }) async a() { return 'a'; } } ================================================ FILE: plugin/controller/test/fixtures/apps/host-controller-app/app/controller/MultiMethodHostController.ts ================================================ import { HTTPController, HTTPMethod, HTTPMethodEnum, Host, } from '@eggjs/tegg'; @HTTPController({ controllerName: 'MultiMethodHostController', path: '/apps', }) export class MultiMethodHostController { @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/orange', }) @Host([ 'orange.eggjs.com', 'o.eggjs.com' ]) async orange() { return 'orange'; } @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/juice', }) @Host('juice.eggjs.com') async juice() { return 'juice'; } } ================================================ FILE: plugin/controller/test/fixtures/apps/host-controller-app/config/config.default.js ================================================ 'use strict'; module.exports = function() { const config = { keys: 'test key', security: { csrf: { ignoreJSON: false, } }, }; return config; }; exports.teggConfig = { package: '@eggjs/tegg-config', enable: true, }; ================================================ FILE: plugin/controller/test/fixtures/apps/host-controller-app/config/plugin.js ================================================ 'use strict'; exports.tracer = { package: 'egg-tracer', enable: true, }; exports.tegg = { package: '@eggjs/tegg-plugin', enable: true, }; exports.teggConfig = { package: '@eggjs/tegg-config', enable: true, }; ================================================ FILE: plugin/controller/test/fixtures/apps/host-controller-app/package.json ================================================ { "name": "controller-app" } ================================================ FILE: plugin/controller/test/fixtures/apps/http-conflict-app/app/controller/AppController.ts ================================================ import { HTTPController, HTTPMethod, HTTPMethodEnum, } from '@eggjs/tegg'; @HTTPController({ path: '/apps', }) export class AppController { @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/:id', }) async get() { return 'hello'; } } ================================================ FILE: plugin/controller/test/fixtures/apps/http-conflict-app/app/controller/HostController1.ts ================================================ import { HTTPController, HTTPMethod, HTTPMethodEnum, Host, } from '@eggjs/tegg'; @HTTPController({ path: '/foo', }) @Host('foo.eggjs.com') export class AppController1 { @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/:id', }) async get() { return 'hello'; } } ================================================ FILE: plugin/controller/test/fixtures/apps/http-conflict-app/app/controller/HostController2.ts ================================================ import { HTTPController, HTTPMethod, HTTPMethodEnum, Host, } from '@eggjs/tegg'; @HTTPController({ path: '/foo', }) @Host('foo.eggjs.com') export class AppController2 { @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/:id', }) async get() { return 'hello'; } } ================================================ FILE: plugin/controller/test/fixtures/apps/http-conflict-app/config/config.default.js ================================================ 'use strict'; module.exports = function() { const config = { keys: 'test key', security: { csrf: { ignoreJSON: false, }, }, }; return config; }; ================================================ FILE: plugin/controller/test/fixtures/apps/http-conflict-app/config/plugin.js ================================================ 'use strict'; exports.tracer = { package: 'egg-tracer', enable: true, }; exports.tegg = { package: '@eggjs/tegg-plugin', enable: true, }; exports.teggConfig = { package: '@eggjs/tegg-config', enable: true, }; ================================================ FILE: plugin/controller/test/fixtures/apps/http-conflict-app/package.json ================================================ { "name": "controller-app" } ================================================ FILE: plugin/controller/test/fixtures/apps/http-inject-app/app/controller/AppController.ts ================================================ import { Context as EggContext } from 'egg'; import { setTimeout } from 'node:timers/promises'; import { Context, HTTPController, HTTPMethod, HTTPMethodEnum, Middleware, Request, HTTPRequest, Cookies, HTTPCookies, Inject, BackgroundTaskHelper, } from '@eggjs/tegg'; import { countMw } from '../middleware/count_mw'; import { Tracer } from '@eggjs/module-test-util'; import { Readable } from 'node:stream'; @HTTPController({ path: '/apps', }) @Middleware(countMw) export class AppController { @Inject() readonly tracer: Tracer; @Inject() readonly backgroundTaskHelper: BackgroundTaskHelper; @HTTPMethod({ method: HTTPMethodEnum.POST, path: '/testRequest', }) async testRequest(@Context() ctx: EggContext, @Request() request: HTTPRequest, @Cookies() cookies: HTTPCookies) { const traceId = await ctx.tracer.traceId; return { success: true, traceId, headers: Object.fromEntries(request.headers), method: request.method, requestBody: await request.text(), cookies: cookies.get('test', { signed: false }), }; } @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/stream' }) async streamResponse(@Context() ctx: EggContext) { const self = this; async function* generate(count = 5, duration = 500) { yield 'hello stream'; for (let i = 0; i < count; i++) { console.log('generate', i); yield `

流式内容${i + 1},${Date()}

`; await setTimeout(duration); } yield self.backgroundTaskHelper.toString(); yield ''; } ctx.type = 'html'; ctx.set('X-Accel-Buffering', 'no'); return Readable.from(generate()); } @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/error_stream' }) async errorStreamResponse(@Context() ctx: EggContext) { const self = this; async function* generate(count = 5, duration = 500) { yield 'hello stream'; for (let i = 0; i < count; i++) { console.log('generate', i); if (i > 2) { throw new Error('test'); } yield `

流式内容${i + 1},${Date()}

`; await setTimeout(duration); } yield self.backgroundTaskHelper.toString(); yield ''; } ctx.type = 'html'; ctx.set('X-Accel-Buffering', 'no'); return Readable.from(generate()); } @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/test-timeout', timeout: 100, }) async testTimeout() { await setTimeout(200); return 'success'; } } ================================================ FILE: plugin/controller/test/fixtures/apps/http-inject-app/app/middleware/call_module.ts ================================================ import { Context } from 'egg'; import { Next } from '@eggjs/tegg'; export async function callModuleCtx(ctx: Context, next: Next) { await (ctx.module as any).multiModuleService.appService.findApp('foo'); await next(); } ================================================ FILE: plugin/controller/test/fixtures/apps/http-inject-app/app/middleware/count_mw.ts ================================================ import { Context } from 'egg'; import { Next } from '@eggjs/tegg'; let index = 0; export async function countMw(ctx: Context, next: Next) { await next(); if (ctx.body) ctx.body.count = index++; } ================================================ FILE: plugin/controller/test/fixtures/apps/http-inject-app/app/middleware/log_mw.ts ================================================ import { Context } from 'egg'; import { Next } from '@eggjs/tegg'; export function logMwFactory(log: string) { return async function logMw(ctx: Context, next: Next) { await next(); ctx.body.log = log; }; } ================================================ FILE: plugin/controller/test/fixtures/apps/http-inject-app/config/config.default.js ================================================ 'use strict'; module.exports = function() { const config = { keys: 'test key', security: { csrf: { ignoreJSON: false, }, }, }; return config; }; ================================================ FILE: plugin/controller/test/fixtures/apps/http-inject-app/config/module.json ================================================ [] ================================================ FILE: plugin/controller/test/fixtures/apps/http-inject-app/config/plugin.js ================================================ 'use strict'; exports.tracer = { package: 'egg-tracer', enable: true, }; exports.tegg = { package: '@eggjs/tegg-plugin', enable: true, }; exports.teggConfig = { package: '@eggjs/tegg-config', enable: true, }; ================================================ FILE: plugin/controller/test/fixtures/apps/http-inject-app/package.json ================================================ { "name": "http-inject-app" } ================================================ FILE: plugin/controller/test/fixtures/apps/mcp-app/app/controller/AppController.ts ================================================ import { MCPController, ToolArgs, MCPToolResponse, MCPTool, ToolExtra, ToolArgsSchema, Extra, Logger, Inject, } from '@eggjs/tegg'; import * as z from 'zod/v4'; export const NotificationType = { interval: z .number() .describe('Interval in milliseconds between notifications') .default(100), count: z .number() .describe('Number of notifications to send (0 for 100)') .default(50), }; @MCPController() export class AppController { @Inject() logger: Logger; @MCPTool({ name: 'start-notification-stream', description: 'Starts sending periodic notifications for testing resumability', }) async startNotificationStream(@ToolArgsSchema(NotificationType) args: ToolArgs, @Extra() extra :ToolExtra): Promise { const { interval, count } = args; const { sendNotification } = extra; const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); let counter = 0; while (count === 0 || counter < count) { counter++; try { await sendNotification({ method: 'notifications/message', params: { level: 'info', data: `Periodic notification #${counter}`, }, }); } catch (error) { console.error('Error sending notification:', error); } await sleep(interval); } this.logger.info('startNotificationStream finish'); return { content: [ { type: 'text', text: `Started sending periodic notifications every ${interval}ms`, }, ], }; } } ================================================ FILE: plugin/controller/test/fixtures/apps/mcp-app/app/controller/McpController.ts ================================================ import { MCPController, PromptArgs, ToolArgs, MCPPromptResponse, MCPToolResponse, MCPResourceResponse, MCPPrompt, MCPTool, MCPResource, PromptArgsSchema, Logger, Inject, ContextProto, ToolArgsSchema, Extra, ToolExtra, } from '@eggjs/tegg'; import * as z from 'zod/v4'; export const PromptType = { name: z.string(), }; export const ToolType = { name: z.string().describe('npm package name'), }; @ContextProto() export class CommonService { @Inject() logger: Logger; async sayHello(): Promise { this.logger.info('hello world'); return 'hello world'; } } @MCPController() export class McpController { @Inject() logger: Logger; @Inject() commonService: CommonService; @Inject() user: any; @MCPPrompt() async foo(@PromptArgsSchema(PromptType) args: PromptArgs): Promise { this.logger.info('hello world'); return { messages: [ { role: 'user', content: { type: 'text', text: `Generate a concise but descriptive commit message for these changes:\n\n${args.name}`, }, }, ], }; } @MCPTool() async bar(@ToolArgsSchema(ToolType) args: ToolArgs): Promise { await this.commonService.sayHello(); return { content: [ { type: 'text', text: `npm package: ${args.name} not found`, }, ], }; } @MCPTool() async mockError(): Promise { throw new Error('mock error'); } @MCPTool() async echoUser(): Promise { return { content: [ { type: 'text', text: `hello ${this.user}`, }, ], }; } @MCPTool() async traceTest(@Extra() extra: ToolExtra): Promise { return { content: [ { type: 'text', text: `hello ${extra.requestInfo?.headers.trace}`, }, ], }; } @MCPResource({ template: [ 'mcp://npm/{name}{?version}', { list: () => { return { resources: [ { name: 'egg', uri: 'mcp://npm/egg?version=4.10.0' }, { name: 'mcp', uri: 'mcp://npm/mcp?version=0.10.0' }, ], }; }, }, ], }) async car(uri: URL): Promise { return { contents: [{ uri: uri.toString(), text: 'MOCK TEXT', }], }; } } ================================================ FILE: plugin/controller/test/fixtures/apps/mcp-app/app/controller/TestAppController.ts ================================================ import { MCPController, ToolArgs, MCPToolResponse, MCPTool, ToolExtra, ToolArgsSchema, Extra, Logger, Inject, } from '@eggjs/tegg'; import * as z from 'zod/v4'; export const NotificationType = { interval: z .number() .describe('Interval in milliseconds between notifications') .default(100), count: z .number() .describe('Number of notifications to send (0 for 100)') .default(50), }; @MCPController({ name: 'test', }) export class TestAppController { @Inject() logger: Logger; @MCPTool({ name: 'test-start-notification-stream', description: 'Starts sending periodic notifications for testing resumability', }) async startNotificationStream(@ToolArgsSchema(NotificationType) args: ToolArgs, @Extra() extra :ToolExtra): Promise { const { interval, count } = args; const { sendNotification } = extra; const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); let counter = 0; while (count === 0 || counter < count) { counter++; try { await sendNotification({ method: 'notifications/message', params: { level: 'info', data: `Periodic notification #${counter}`, }, }); } catch (error) { console.error('Error sending notification:', error); } await sleep(interval); } this.logger.info('startNotificationStream finish'); return { content: [ { type: 'text', text: `Started sending periodic notifications every ${interval}ms`, }, ], }; } } ================================================ FILE: plugin/controller/test/fixtures/apps/mcp-app/app/controller/TestController.ts ================================================ import { MCPController, PromptArgs, ToolArgs, MCPPromptResponse, MCPToolResponse, MCPResourceResponse, MCPPrompt, MCPTool, MCPResource, PromptArgsSchema, Logger, Inject, ContextProto, ToolArgsSchema, Extra, ToolExtra, } from '@eggjs/tegg'; import * as z from 'zod/v4'; export const PromptType = { name: z.string(), }; export const ToolType = { name: z.string().describe('npm package name'), }; @ContextProto() export class TestCommonService { @Inject() logger: Logger; async sayHello(): Promise { this.logger.info('hello world'); return 'hello world'; } } @MCPController({ name: 'test', }) export class TestMcpController { @Inject() logger: Logger; @Inject() testCommonService: TestCommonService; @Inject() user: any; @MCPPrompt() async testFoo(@PromptArgsSchema(PromptType) args: PromptArgs): Promise { this.logger.info('hello world'); return { messages: [ { role: 'user', content: { type: 'text', text: `Generate a concise but descriptive commit message for these changes:\n\n${args.name}`, }, }, ], }; } @MCPTool() async testBar(@ToolArgsSchema(ToolType) args: ToolArgs): Promise { await this.testCommonService.sayHello(); return { content: [ { type: 'text', text: `npm package: ${args.name} not found`, }, ], }; } @MCPTool() async testEchoUser(): Promise { return { content: [ { type: 'text', text: `hello ${this.user}`, }, ], }; } @MCPTool() async testTraceTest(@Extra() extra: ToolExtra): Promise { return { content: [ { type: 'text', text: `hello ${extra.requestInfo?.headers.trace}`, }, ], }; } @MCPResource({ template: [ 'mcp://npm/{name}{?version}', { list: () => { return { resources: [ { name: 'testEgg', uri: 'mcp://npm/testEgg?version=4.10.0' }, { name: 'testMcp', uri: 'mcp://npm/testMcp?version=0.10.0' }, ], }; }, }, ], }) async testCar(uri: URL): Promise { return { contents: [{ uri: uri.toString(), text: 'MOCK TEXT', }], }; } } ================================================ FILE: plugin/controller/test/fixtures/apps/mcp-app/app/middleware/tracelog.js ================================================ 'use strict'; module.exports = () => { return async function tracelog(ctx, next) { ctx.req.headers.trace = 'middleware'; ctx.req.rawHeaders.push('trace'); ctx.req.rawHeaders.push('middleware'); await next(); }; }; ================================================ FILE: plugin/controller/test/fixtures/apps/mcp-app/config/config.default.js ================================================ 'use strict'; // eslint-disable-next-line @typescript-eslint/no-var-requires const { randomUUID } = require('node:crypto'); // eslint-disable-next-line @typescript-eslint/no-var-requires const path = require('node:path'); module.exports = function(appInfo) { const config = { keys: 'test key', mcp: { ssePingEnabled: true, streamPingEnabled: true, sessionIdGenerator: ctx => { return ctx.request.headers['custom-session-id'] || randomUUID(); }, middleware: [ 'tracelog', ], multipleServer: { test: { sessionIdGenerator: ctx => { return ctx.request.headers['custom-session-id'] || randomUUID(); }, }, }, }, customLogger: { mcpMiddewareStartLogger: { file: path.join(appInfo.root, 'logs', 'tracelog', 'mcpMiddlewareStart.log'), }, mcpMiddewareEndLogger: { file: path.join(appInfo.root, 'logs', 'tracelog', 'mcpMiddlewareEnd.log'), }, mcpMiddewareErrorLogger: { file: path.join(appInfo.root, 'logs', 'tracelog', 'mcpMiddlewareError.log'), }, }, }; return config; }; ================================================ FILE: plugin/controller/test/fixtures/apps/mcp-app/config/plugin.js ================================================ 'use strict'; // eslint-disable-next-line @typescript-eslint/no-var-requires const path = require('path'); exports.tracer = { package: 'egg-tracer', enable: true, }; exports.tegg = { package: '@eggjs/tegg-plugin', enable: true, }; exports.teggConfig = { package: '@eggjs/tegg-config', enable: true, }; exports.aopModule = { package: '@eggjs/tegg-aop-plugin', enable: true, }; exports.mcpProxy = { package: '@eggjs/mcp-proxy', enable: true, }; exports.hookPlugin = { path: path.join(__dirname, '../hook-plugin'), enable: true, }; ================================================ FILE: plugin/controller/test/fixtures/apps/mcp-app/hook-plugin/app.ts ================================================ import type { Application } from 'egg'; import { MCPControllerRegister } from '@eggjs/tegg-controller-plugin/lib/impl/mcp/MCPControllerRegister'; import { GetAlipayTeggHook } from './lib/MCPControllerHook'; export default class ControllerAppBootHook { #app: Application; constructor(app: Application) { this.#app = app; } configWillLoad() { MCPControllerRegister.addHook(GetAlipayTeggHook(this.#app)); } } ================================================ FILE: plugin/controller/test/fixtures/apps/mcp-app/hook-plugin/lib/MCPControllerHook.ts ================================================ import type { Application, Context } from 'egg'; import { MCPControllerHook } from '@eggjs/tegg-controller-plugin/lib/impl/mcp/MCPControllerRegister'; export const GetAlipayTeggHook = (app: Application) => { const setUser = (ctx: Context) => { ctx.set({ 'content-type': 'text/event-stream', 'cache-control': 'no-cache', }); try { const auth = ctx.get('authorization'); const atitString = Buffer.from( auth.substring('Bearer '.length), 'base64', ).toString('utf8'); ctx.user = atitString; } catch (e) { app.logger.warn('get user failed: ', e); } }; const AlipayTeggControllerHook: MCPControllerHook = { async preHandle(ctx) { setUser(ctx); }, async preHandleInitHandle(ctx) { setUser(ctx); }, async preSSEInitHandle(ctx) { setUser(ctx); }, async preProxy(ctx) { setUser(ctx); }, async middlewareStart(ctx) { ctx.mcpStartTime = Date.now(); ctx.getLogger('mcpMiddewareStartLogger').info('mcp middleware start'); }, async middlewareEnd(ctx) { ctx.getLogger('mcpMiddewareEndLogger').info('mcp middleware end, arg: ', JSON.stringify(ctx.mcpArg), `, time: ${Date.now() - ctx.mcpStartTime}`); }, async middlewareError(ctx, e) { ctx.getLogger('mcpMiddewareErrorLogger').info('mcp middleware error: ', e); }, }; return AlipayTeggControllerHook; }; ================================================ FILE: plugin/controller/test/fixtures/apps/mcp-app/hook-plugin/package.json ================================================ { "name": "@eggjs/hook-plugin", "eggPlugin": { "name": "hookPlugin" } } ================================================ FILE: plugin/controller/test/fixtures/apps/mcp-app/package.json ================================================ { "name": "mcp-app" } ================================================ FILE: plugin/controller/test/fixtures/apps/middleware-graph-app/app/router.ts ================================================ export default () => { // This file is required by egg }; ================================================ FILE: plugin/controller/test/fixtures/apps/middleware-graph-app/config/config.default.js ================================================ 'use strict'; module.exports = function() { const config = { keys: 'test key', security: { csrf: { ignoreJSON: false, }, }, }; return config; }; ================================================ FILE: plugin/controller/test/fixtures/apps/middleware-graph-app/config/module.json ================================================ [ { "path": "../modules/advice-module" }, { "path": "../modules/controller-module" } ] ================================================ FILE: plugin/controller/test/fixtures/apps/middleware-graph-app/config/plugin.js ================================================ 'use strict'; exports.tracer = { package: 'egg-tracer', enable: true, }; exports.tegg = { package: '@eggjs/tegg-plugin', enable: true, }; exports.teggConfig = { package: '@eggjs/tegg-config', enable: true, }; exports.aopModule = { package: '@eggjs/tegg-aop-plugin', enable: true, }; ================================================ FILE: plugin/controller/test/fixtures/apps/middleware-graph-app/modules/advice-module/advice/AnotherAdvice.ts ================================================ import { Advice, AdviceContext, IAdvice, } from '@eggjs/tegg/aop'; import { AccessLevel } from '@eggjs/tegg-types'; @Advice({ accessLevel: AccessLevel.PUBLIC, }) export class AnotherAdvice implements IAdvice { async around(_ctx: AdviceContext, next: () => Promise) { const body = await next(); if (body) { body.anotherAdviceApplied = true; } return body; } } ================================================ FILE: plugin/controller/test/fixtures/apps/middleware-graph-app/modules/advice-module/advice/TestAdvice.ts ================================================ import { Advice, AdviceContext, IAdvice, } from '@eggjs/tegg/aop'; import { AccessLevel } from '@eggjs/tegg-types'; @Advice({ accessLevel: AccessLevel.PUBLIC, }) export class TestAdvice implements IAdvice { async around(_ctx: AdviceContext, next: () => Promise) { const body = await next(); if (body) { body.adviceApplied = true; } return body; } } ================================================ FILE: plugin/controller/test/fixtures/apps/middleware-graph-app/modules/advice-module/package.json ================================================ { "name": "advice-module", "eggModule": { "name": "advice-module" } } ================================================ FILE: plugin/controller/test/fixtures/apps/middleware-graph-app/modules/controller-module/TestController.ts ================================================ import { HTTPController, HTTPMethod, HTTPMethodEnum, Middleware, } from '@eggjs/tegg'; import { TestAdvice } from '../advice-module/advice/TestAdvice'; import { AnotherAdvice } from '../advice-module/advice/AnotherAdvice'; @HTTPController({ path: '/test', }) @Middleware(TestAdvice) export class TestController { @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/class-middleware', }) async classMiddleware() { return { message: 'class middleware', }; } @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/method-middleware', }) @Middleware(AnotherAdvice) async methodMiddleware() { return { message: 'method middleware', }; } } ================================================ FILE: plugin/controller/test/fixtures/apps/middleware-graph-app/modules/controller-module/package.json ================================================ { "name": "controller-module", "eggModule": { "name": "controller-module" } } ================================================ FILE: plugin/controller/test/fixtures/apps/middleware-graph-app/package.json ================================================ { "name": "middleware-graph-app" } ================================================ FILE: plugin/controller/test/fixtures/apps/module-app/app/controller/AppController2.ts ================================================ import { Controller } from 'egg'; export default class AppController2 extends Controller { async foo() { const traceId = await this.ctx.tracer.traceId; const id = this.ctx.params.id; this.ctx.body = { traceId, app: 'mock-app:' + id, }; } } ================================================ FILE: plugin/controller/test/fixtures/apps/module-app/app/router.ts ================================================ import { Application } from 'egg'; export default function(app: Application) { app.get('/apps2/:id', app.controller.appController2.foo); } ================================================ FILE: plugin/controller/test/fixtures/apps/module-app/config/config.default.js ================================================ 'use strict'; module.exports = function() { const config = { keys: 'test key', security: { csrf: false, }, controller: { supportParams: true, }, }; return config; }; ================================================ FILE: plugin/controller/test/fixtures/apps/module-app/config/module.json ================================================ [ { "path": "../modules/http-module" }, { "path": "../modules/foo-module" } ] ================================================ FILE: plugin/controller/test/fixtures/apps/module-app/config/plugin.js ================================================ 'use strict'; exports.tracer = { package: 'egg-tracer', enable: true, }; exports.tegg = { package: '@eggjs/tegg-plugin', enable: true, }; exports.teggConfig = { package: '@eggjs/tegg-config', enable: true, }; ================================================ FILE: plugin/controller/test/fixtures/apps/module-app/modules/foo-module/AppService.ts ================================================ import { AccessLevel, ContextProto } from '@eggjs/tegg'; // No one deps it // It should not be construct @ContextProto({ accessLevel: AccessLevel.PUBLIC, }) export class AppService { constructor() { (global as any).constructAppService = true; } } ================================================ FILE: plugin/controller/test/fixtures/apps/module-app/modules/foo-module/package.json ================================================ { "name": "foo-module", "eggModule": { "name": "foo" } } ================================================ FILE: plugin/controller/test/fixtures/apps/module-app/modules/http-module/AppController.ts ================================================ import { Context as EggContext } from 'egg'; import { Context, HTTPController, HTTPMethod, HTTPMethodEnum, HTTPParam, } from '@eggjs/tegg'; @HTTPController() export class AppController { @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/apps/:id', }) async get(@Context() ctx: EggContext, @HTTPParam() id: string) { const traceId = await ctx.tracer.traceId; return { traceId, app: 'mock-app:' + id, }; } } ================================================ FILE: plugin/controller/test/fixtures/apps/module-app/modules/http-module/package.json ================================================ { "name": "http-module", "eggModule": { "name": "http-module" } } ================================================ FILE: plugin/controller/test/fixtures/apps/module-app/package.json ================================================ { "name": "egg-app" } ================================================ FILE: plugin/controller/test/fixtures/apps/proto-poisoning/app/controller/HelloController.ts ================================================ import { HTTPController, HTTPMethod, HTTPMethodEnum, HTTPBody, } from '@eggjs/tegg'; @HTTPController() export class HelloController { @HTTPMethod({ method: HTTPMethodEnum.POST, path: '/hello-proto-poisoning', }) async get(@HTTPBody() body: any) { // console.log(body, body.__proto__); const params1 = Object.assign({}, body); const params2 = { ...body, }; return { params1, params2, body, 'params1.boom': params1.boom, 'params2.boom': params2.boom, 'body.boom': body.boom, }; } } ================================================ FILE: plugin/controller/test/fixtures/apps/proto-poisoning/config/config.default.js ================================================ module.exports = () => { const config = { keys: 'test key', security: { csrf: { ignoreJSON: false, }, }, bodyParser: { onProtoPoisoning: 'remove', }, }; return config; }; ================================================ FILE: plugin/controller/test/fixtures/apps/proto-poisoning/config/plugin.js ================================================ exports.tracer = { package: 'egg-tracer', enable: true, }; exports.tegg = { package: '@eggjs/tegg-plugin', enable: true, }; exports.teggConfig = { package: '@eggjs/tegg-config', enable: true, }; ================================================ FILE: plugin/controller/test/fixtures/apps/proto-poisoning/package.json ================================================ { "name": "proto-poisoning" } ================================================ FILE: plugin/controller/test/http/acl.test.ts ================================================ import mm from 'egg-mock'; import path from 'path'; import assert from 'assert'; describe('plugin/controller/test/http/acl.test.ts', () => { let app; beforeEach(() => { mm(process.env, 'EGG_TYPESCRIPT', true); }); afterEach(() => { mm.restore(); }); before(async () => { mm(process.env, 'EGG_TYPESCRIPT', true); mm(process, 'cwd', () => { return path.join(__dirname, '../..'); }); app = mm.app({ baseDir: path.join(__dirname, '../fixtures/apps/acl-app'), framework: require.resolve('egg'), }); await app.ready(); }); after(() => { return app.close(); }); describe('authenticate', () => { describe('authenticate success', () => { it('should ok', async () => { await app.httpRequest() .get('/foo?pass=true') .set('accept', 'application/json') .expect(res => { assert.deepStrictEqual(res.text, 'hello, foo'); }); }); }); describe('authenticate failed', () => { describe('json', () => { it('should deny', async () => { await app.httpRequest() .get('/foo') .set('accept', 'application/json') .expect(res => { assert.deepStrictEqual(res.body, { target: 'http://alipay.com/401', stat: 'deny', }); }); }); }); describe('not json', () => { it('should 302', async () => { await app.httpRequest() .get('/foo') .expect(302) .expectHeader('location', 'http://alipay.com/401'); }); }); }); }); describe('authorize', () => { describe('authorize success', () => { it('should ok', async () => { await app.httpRequest() .get('/bar?pass=true&code=mock1') .set('accept', 'application/json') .expect(res => { assert.deepStrictEqual(res.text, 'hello, bar'); }); }); }); describe('authorize failed', () => { describe('json', () => { it('should deny', async () => { await app.httpRequest() .get('/bar?pass=true&code=mock2') .set('accept', 'application/json') .expect(res => { assert.deepStrictEqual(res.body, { target: 'http://alipay.com/403', stat: 'deny', }); }); }); }); describe('not json', () => { it('should 302', async () => { await app.httpRequest() .get('/bar?pass=true&code=mock2') .expect(302) .expectHeader('location', 'http://alipay.com/403'); }); }); }); }); }); ================================================ FILE: plugin/controller/test/http/decorator.test.ts ================================================ import path from 'node:path'; import mm from 'egg-mock'; describe('plugin/controller/test/http/decorator.test.ts', () => { let app; beforeEach(() => { mm(process.env, 'EGG_TYPESCRIPT', true); }); afterEach(() => { mm.restore(); }); before(async () => { mm(process.env, 'EGG_TYPESCRIPT', true); mm(process, 'cwd', () => { return path.join(__dirname, '../..'); }); app = mm.app({ baseDir: path.join(__dirname, '../fixtures/apps/controller-app'), framework: require.resolve('egg'), }); await app.ready(); }); after(() => { return app.close(); }); it('should timeout for method timeout', async () => { await app.httpRequest() .get('/timeout-1') .expect(500); }); it('should not timeout', async () => { await app.httpRequest() .get('/timeout-2') .expect(200, 'success'); }); it('should timeout for controller timeout', async () => { await app.httpRequest() .get('/timeout-3') .expect(500); }); }); ================================================ FILE: plugin/controller/test/http/edgecase.test.ts ================================================ import mm from 'egg-mock'; import path from 'path'; import assert from 'assert'; describe('plugin/controller/test/http/edgecase.test.ts', () => { let app; beforeEach(() => { mm(process.env, 'EGG_TYPESCRIPT', true); }); afterEach(() => { mm.restore(); }); before(async () => { mm(process.env, 'EGG_TYPESCRIPT', true); mm(process, 'cwd', () => { return path.join(__dirname, '../..'); }); app = mm.app({ baseDir: path.join(__dirname, '../fixtures/apps/controller-app'), framework: require.resolve('egg'), }); await app.ready(); }); after(() => { return app.close(); }); it('redirect should work', async () => { await app.httpRequest() .get('/redirect') .expectHeader('location') .expect(302); }); it('empty should work', async () => { await app.httpRequest() .get('/empty') .expect(204); }); it('should case sensitive', async () => { app.mockCsrf(); await app.httpRequest() .get('/Middleware/Method') .expect(200) .expect(res => { assert(res.text === 'hello, view'); }); }); }); ================================================ FILE: plugin/controller/test/http/host.test.ts ================================================ import mm from 'egg-mock'; import path from 'path'; import assert from 'assert'; describe('plugin/controller/test/http/host.test.ts', () => { let app; beforeEach(() => { mm(process.env, 'EGG_TYPESCRIPT', true); }); afterEach(() => { mm.restore(); }); before(async () => { mm(process.env, 'EGG_TYPESCRIPT', true); mm(process, 'cwd', () => { return path.join(__dirname, '../..'); }); app = mm.app({ baseDir: path.join(__dirname, '../fixtures/apps/host-controller-app'), framework: require.resolve('egg'), }); await app.ready(); }); after(() => { return app.close(); }); it('global host should work', async () => { app.mockCsrf(); await app.httpRequest() .get('/apps/1') .set('host', 'foo.eggjs.com') .expect(200) .expect(res => { console.log('res: ', res.text, res.body); assert(res.text === 'foo'); }); }); it('method host should work', async () => { app.mockCsrf(); await app.httpRequest() .get('/apps/2') .set('host', 'bar.eggjs.com') .expect(200) .expect(res => { assert(res.text === 'bar'); }); }); it('multi class host should work', async () => { app.mockCsrf(); await app.httpRequest() .get('/apps/apple') .set('host', 'orange.eggjs.com') .expect(404); await app.httpRequest() .get('/apps/apple') .set('host', 'apple.eggjs.com') .expect(200) .expect(res => { assert(res.text === 'apple'); }); await app.httpRequest() .get('/apps/a') .set('host', 'a.eggjs.com') .expect(200) .expect(res => { assert(res.text === 'a'); }); }); it('method class host should work', async () => { app.mockCsrf(); await app.httpRequest() .get('/apps/orange') .set('host', 'o.eggjs.com') .expect(200) .expect(res => { assert(res.text === 'orange'); }); await app.httpRequest() .get('/apps/orange') .set('host', 'orange.eggjs.com') .expect(200) .expect(res => { assert(res.text === 'orange'); }); await app.httpRequest() .get('/apps/juice') .set('host', 'juice.eggjs.com') .expect(200) .expect(res => { assert(res.text === 'juice'); }); await app.httpRequest() .get('/apps/juice') .set('host', 'o.eggjs.com') .expect(404); }); }); ================================================ FILE: plugin/controller/test/http/middleware-aop.test.ts ================================================ import mm from 'egg-mock'; import path from 'path'; import assert from 'assert'; describe('plugin/controller/test/http/middleware-graph-hook.test.ts', () => { let app; beforeEach(() => { mm(process.env, 'EGG_TYPESCRIPT', true); }); afterEach(() => { mm.restore(); }); before(async () => { mm(process.env, 'EGG_TYPESCRIPT', true); mm(process, 'cwd', () => { return path.join(__dirname, '../..'); }); app = mm.app({ baseDir: path.join(__dirname, '../fixtures/apps/middleware-graph-app'), framework: require.resolve('egg'), }); await app.ready(); }); after(() => { return app.close(); }); it('should add module reference for class level middleware', async () => { assert.deepStrictEqual(app.moduleReferences.map(t => t.name), [ 'advice-module', 'controller-module', ]); }); it('class level middleware should work', async () => { app.mockCsrf(); const res = await app.httpRequest() .get('/test/class-middleware') .expect(200); assert.deepStrictEqual(res.body, { message: 'class middleware', adviceApplied: true, }); }); it('method level middleware should work', async () => { app.mockCsrf(); const res = await app.httpRequest() .get('/test/method-middleware') .expect(200); assert.deepStrictEqual(res.body, { message: 'method middleware', adviceApplied: true, anotherAdviceApplied: true, }); }); }); ================================================ FILE: plugin/controller/test/http/middleware.test.ts ================================================ import mm from 'egg-mock'; import path from 'path'; import assert from 'assert'; describe('plugin/controller/test/http/middleware.test.ts', () => { let app; beforeEach(() => { mm(process.env, 'EGG_TYPESCRIPT', true); }); afterEach(() => { mm.restore(); }); before(async () => { mm(process.env, 'EGG_TYPESCRIPT', true); mm(process, 'cwd', () => { return path.join(__dirname, '../..'); }); app = mm.app({ baseDir: path.join(__dirname, '../fixtures/apps/controller-app'), framework: require.resolve('egg'), }); await app.ready(); }); after(() => { return app.close(); }); it('global middleware should work', async () => { app.mockCsrf(); await app.httpRequest() .get('/middleware/global') .expect(200) .expect(res => { assert(res.body.count === 0); }); }); it('method middleware should work', async () => { app.mockCsrf(); await app.httpRequest() .get('/middleware/method') .expect(200) .expect(res => { assert(res.body.log === 'use middleware'); }); }); it('method middleware call module should work', async () => { app.mockCsrf(); await app.httpRequest() .get('/middleware/methodCallModule') .expect(200); }); it('aop controller middleware should work', async () => { app.mockCsrf(); const res = await app.httpRequest() .get('/aop/middleware/global') .expect(200); assert.deepStrictEqual(res.body, { method: 'global', count: 0, aopList: [ 'FooControllerAdvice', 'CountAdvice' ], }); }); it('aop method middleware should work', async () => { app.mockCsrf(); const res = await app.httpRequest() .get('/aop/middleware/method') .expect(200); assert.deepStrictEqual(res.body, { method: 'middleware', aopList: [ 'FooControllerAdvice', 'CountAdvice', 'BarMethodAdvice', 'FooMethodAdvice', ], count: 0, }); }); }); ================================================ FILE: plugin/controller/test/http/module.test.ts ================================================ import mm from 'egg-mock'; import path from 'path'; import assert from 'assert'; describe('plugin/controller/test/http/module.test.ts', () => { let app; beforeEach(() => { mm(process.env, 'EGG_TYPESCRIPT', true); }); afterEach(() => { mm.restore(); delete (global as any).constructAppService; }); before(async () => { mm(process.env, 'EGG_TYPESCRIPT', true); mm(process, 'cwd', () => { return path.join(__dirname, '../..'); }); app = mm.app({ baseDir: path.join(__dirname, '../fixtures/apps/module-app'), framework: require.resolve('egg'), }); await app.ready(); }); after(() => { return app.close(); }); it('controller in module should work', async () => { app.mockCsrf(); await app.httpRequest() .get('/apps/foo') .expect(200) .expect(res => { assert(res.body.app === 'mock-app:foo'); }); }); it('tegg controller should not construct AppService', async () => { app.mockCsrf(); await app.httpRequest() .get('/apps/foo') .expect(200) .expect(res => { assert(res.body.app === 'mock-app:foo'); }); assert(!(global as any).constructAppService); }); it('egg controller should construct AppService', async () => { app.mockCsrf(); await app.httpRequest() .get('/apps2/foo') .expect(200) .expect(res => { assert(res.body.app === 'mock-app:foo'); }); assert((global as any).constructAppService); }); }); ================================================ FILE: plugin/controller/test/http/params.test.ts ================================================ import path from 'node:path'; import { strict as assert } from 'node:assert'; import mm from 'egg-mock'; describe('plugin/controller/test/http/params.test.ts', () => { let app; beforeEach(() => { mm(process.env, 'EGG_TYPESCRIPT', true); }); afterEach(() => { mm.restore(); }); before(async () => { mm(process.env, 'EGG_TYPESCRIPT', true); mm(process, 'cwd', () => { return path.join(__dirname, '../..'); }); app = mm.app({ baseDir: path.join(__dirname, '../fixtures/apps/controller-app'), framework: require.resolve('egg'), }); await app.ready(); }); after(() => { return app.close(); }); it('body param should work', async () => { app.mockCsrf(); await app.httpRequest() .post('/apps') .send({ name: 'foo', desc: 'mock-desc', }) .expect(200) .expect(res => { assert(res.body.success === true); assert(res.body.traceId); }); }); it('headers param should work', async () => { app.mockCsrf(); await app.httpRequest() .post('/apps') .set('x-session-id', 'mock-session-id') .send({ name: 'foo', desc: 'mock-desc', }) .expect(200) .expect(res => { assert.equal(res.body.success, true); assert(res.body.traceId); assert.equal(res.body.sessionId, 'mock-session-id'); }); }); it('query param should work', async () => { app.mockCsrf(); await app.httpRequest() .get('/apps?name=foo') .expect(200) .expect(res => { assert(res.body.traceId); assert.deepStrictEqual(res.body.app, { name: 'foo', desc: 'mock-desc', }); }); }); it('path param should work', async () => { app.mockCsrf(); await app.httpRequest() .get('/apps/foo') .expect(200) .expect(res => { assert(res.body.traceId); assert.deepStrictEqual(res.body.app, { name: 'foo', desc: 'mock-desc', }); }); }); it('global middleware should work', async () => { app.mockCsrf(); await app.httpRequest() .get('/apps/foo') .expect(200) .expect(res => { assert(res.body.traceId); assert.deepStrictEqual(res.body.app, { name: 'foo', desc: 'mock-desc', }); }); }); it('controller path param should work', async () => { app.mockCsrf(); await app.httpRequest() .get('/foo/fooId/bar/barId') .expect(200) .expect(res => { assert.deepStrictEqual(res.body, { fooId: 'fooId', barId: 'barId', }); }); }); }); ================================================ FILE: plugin/controller/test/http/priority.test.ts ================================================ import mm from 'egg-mock'; import path from 'path'; describe('plugin/controller/test/http/priority.test.ts', () => { let app; beforeEach(() => { mm(process.env, 'EGG_TYPESCRIPT', true); }); afterEach(() => { mm.restore(); }); before(async () => { mm(process.env, 'EGG_TYPESCRIPT', true); mm(process, 'cwd', () => { return path.join(__dirname, '../..'); }); app = mm.app({ baseDir: path.join(__dirname, '../fixtures/apps/controller-app'), framework: require.resolve('egg'), }); await app.ready(); }); after(() => { return app.close(); }); it('/* should work', async () => { app.mockCsrf(); await app.httpRequest() .get('/view/foo') .expect(200) .expect('hello, view'); }); it('/users/group', async () => { app.mockCsrf(); await app.httpRequest() .get('/users/group') .expect(200) .expect('high priority'); }); it('/users/* should work', async () => { app.mockCsrf(); await app.httpRequest() .get('/users/foo') .expect(200) .expect('low priority'); }); }); ================================================ FILE: plugin/controller/test/http/proto-poisoning.test.ts ================================================ import path from 'node:path'; import { strict as assert } from 'node:assert'; import mm from 'egg-mock'; describe('plugin/controller/test/http/proto-poisoning.test.ts', () => { let app; beforeEach(() => { mm(process.env, 'EGG_TYPESCRIPT', true); }); afterEach(() => { mm.restore(); }); before(async () => { mm(process.env, 'EGG_TYPESCRIPT', true); mm(process, 'cwd', () => { return path.join(__dirname, '../..'); }); app = mm.app({ baseDir: path.join(__dirname, '../fixtures/apps/proto-poisoning'), framework: require.resolve('egg'), }); await app.ready(); }); after(() => { return app.close(); }); it('should protect proto poisoning', async () => { app.mockCsrf(); const res = await app.httpRequest() .post('/hello-proto-poisoning') .set('content-type', 'application/json') .send(`{ "hello": "world", "__proto__": { "boom": "💣" } }`) .expect(200); console.log(res.body); assert.equal(res.body['body.boom'], undefined, 'body.boom'); assert.equal(res.body['params2.boom'], undefined, 'params2.boom'); assert.equal(res.body['params1.boom'], undefined, 'params1.boom'); }); }); ================================================ FILE: plugin/controller/test/http/request.test.ts ================================================ import mm from 'egg-mock'; import path from 'path'; import assert from 'assert'; describe('plugin/controller/test/http/request.test.ts', () => { let app; beforeEach(() => { mm(process.env, 'EGG_TYPESCRIPT', true); }); afterEach(() => { mm.restore(); }); before(async () => { mm(process.env, 'EGG_TYPESCRIPT', true); mm(process, 'cwd', () => { return path.join(__dirname, '../..'); }); app = mm.app({ baseDir: path.join(__dirname, '../fixtures/apps/http-inject-app'), framework: require.resolve('egg'), }); await app.ready(); }); after(() => { return app.close(); }); const [ nodeMajor ] = process.versions.node.split('.').map(v => Number(v)); if (nodeMajor >= 16) { it('Request should work', async () => { app.mockCsrf(); const param = { name: 'foo', desc: 'mock-desc', }; const headerKey = 'test-header'; await app.httpRequest() .post('/apps/testRequest') .send(param) .set('test', headerKey) .set('cookie', 'test=foo') .expect(200) .expect(res => { assert(res.body.headers.test === headerKey); assert(res.body.method === 'POST'); assert(res.body.requestBody === JSON.stringify(param)); assert(res.body.cookies === 'foo'); }); }); it('stream should work', async () => { await app.httpRequest() .get('/apps/stream') .expect(200) .expect(res => { assert(res.text.includes('流式内容5')); }); }); it('error stream should work', async () => { await assert.rejects( app.httpRequest() .get('/apps/error_stream') .expect(200) , /Error: aborted/); }); } }); ================================================ FILE: plugin/controller/test/lib/AgentControllerProto.test.ts ================================================ import assert from 'node:assert/strict'; import { EggPrototypeCreatorFactory } from '@eggjs/tegg-metadata'; import type { EggPrototype, EggPrototypeLifecycleContext } from '@eggjs/tegg-types'; import { DEFAULT_PROTO_IMPL_TYPE } from '@eggjs/tegg-types'; import { AgentControllerProto } from '../../lib/AgentControllerProto'; function createMockDelegate(): EggPrototype { const sym1 = Symbol('qualifier1'); const sym2 = Symbol('qualifier2'); const delegate = { id: 'mock-id', name: 'mockProto', initType: 'SINGLETON', accessLevel: 'PUBLIC', loadUnitId: 'load-unit-1', injectObjects: [{ refName: 'dep1' }], injectType: 'PROPERTY', className: 'MockClass', multiInstanceConstructorIndex: undefined, multiInstanceConstructorAttributes: undefined, getMetaData(key: string) { if (key === 'testKey') return 'testValue'; return undefined; }, verifyQualifier(q: { attribute: string }) { return q.attribute === 'valid'; }, verifyQualifiers(qs: Array<{ attribute: string }>) { return qs.every(q => q.attribute === 'valid'); }, getQualifier(attr: string) { if (attr === 'env') return 'prod'; return undefined; }, constructEggObject(...args: any[]) { return { constructed: true, args }; }, } as unknown as EggPrototype; // Add symbol-keyed properties to test symbol copying Object.defineProperty(delegate, sym1, { value: 'value1', enumerable: false }); Object.defineProperty(delegate, sym2, { value: 'value2', enumerable: false }); return delegate; } describe('plugin/controller/test/lib/AgentControllerProto.test.ts', () => { describe('constructor delegation', () => { it('should be an instance of AgentControllerProto', () => { const delegate = createMockDelegate(); const proto = new AgentControllerProto(delegate); assert(proto instanceof AgentControllerProto); }); it('should copy symbol-keyed properties from delegate', () => { const delegate = createMockDelegate(); const symbols = Object.getOwnPropertySymbols(delegate); assert(symbols.length >= 2, 'delegate should have symbol properties'); const proto = new AgentControllerProto(delegate); for (const sym of symbols) { assert.strictEqual((proto as any)[sym], (delegate as any)[sym]); } }); }); describe('getter delegation', () => { const delegate = createMockDelegate(); const proto = new AgentControllerProto(delegate); it('should delegate id', () => { assert.strictEqual(proto.id, 'mock-id'); }); it('should delegate name', () => { assert.strictEqual(proto.name, 'mockProto'); }); it('should delegate initType', () => { assert.strictEqual(proto.initType, 'SINGLETON'); }); it('should delegate accessLevel', () => { assert.strictEqual(proto.accessLevel, 'PUBLIC'); }); it('should delegate loadUnitId', () => { assert.strictEqual(proto.loadUnitId, 'load-unit-1'); }); it('should delegate injectObjects', () => { assert.deepStrictEqual(proto.injectObjects, [{ refName: 'dep1' }]); }); it('should delegate injectType', () => { assert.strictEqual(proto.injectType, 'PROPERTY'); }); it('should delegate className', () => { assert.strictEqual(proto.className, 'MockClass'); }); it('should delegate multiInstanceConstructorIndex', () => { assert.strictEqual(proto.multiInstanceConstructorIndex, undefined); }); it('should delegate multiInstanceConstructorAttributes', () => { assert.strictEqual(proto.multiInstanceConstructorAttributes, undefined); }); }); describe('method delegation', () => { const delegate = createMockDelegate(); const proto = new AgentControllerProto(delegate); it('should delegate getMetaData', () => { assert.strictEqual(proto.getMetaData('testKey'), 'testValue'); assert.strictEqual(proto.getMetaData('unknown'), undefined); }); it('should delegate verifyQualifier', () => { assert.strictEqual(proto.verifyQualifier({ attribute: 'valid' } as any), true); assert.strictEqual(proto.verifyQualifier({ attribute: 'invalid' } as any), false); }); it('should delegate verifyQualifiers', () => { assert.strictEqual(proto.verifyQualifiers([{ attribute: 'valid' }] as any), true); assert.strictEqual(proto.verifyQualifiers([{ attribute: 'invalid' }] as any), false); }); it('should delegate getQualifier', () => { assert.strictEqual(proto.getQualifier('env'), 'prod'); assert.strictEqual(proto.getQualifier('missing'), undefined); }); it('should delegate constructEggObject', () => { const result = proto.constructEggObject('a', 'b'); assert.deepStrictEqual(result, { constructed: true, args: [ 'a', 'b' ] }); }); }); describe('static createProto', () => { const mockDelegate = createMockDelegate(); let originalCreator: ReturnType; before(() => { originalCreator = EggPrototypeCreatorFactory.getPrototypeCreator(DEFAULT_PROTO_IMPL_TYPE); EggPrototypeCreatorFactory.registerPrototypeCreator(DEFAULT_PROTO_IMPL_TYPE, () => mockDelegate); }); after(() => { if (originalCreator) { EggPrototypeCreatorFactory.registerPrototypeCreator(DEFAULT_PROTO_IMPL_TYPE, originalCreator); } }); it('should create an AgentControllerProto wrapping the default creator result', () => { const ctx = {} as EggPrototypeLifecycleContext; const proto = AgentControllerProto.createProto(ctx); assert(proto instanceof AgentControllerProto); assert.strictEqual(proto.id, mockDelegate.id); assert.strictEqual(proto.name, mockDelegate.name); }); it('should throw when default creator is not registered', () => { // Temporarily remove the creator const saved = EggPrototypeCreatorFactory.getPrototypeCreator(DEFAULT_PROTO_IMPL_TYPE); EggPrototypeCreatorFactory.registerPrototypeCreator(DEFAULT_PROTO_IMPL_TYPE, undefined as any); // Force the map entry to be deleted so getPrototypeCreator returns undefined (EggPrototypeCreatorFactory as any).creatorMap.delete(DEFAULT_PROTO_IMPL_TYPE); try { assert.throws( () => AgentControllerProto.createProto({} as EggPrototypeLifecycleContext), /Default prototype creator.*not registered/, ); } finally { // Restore if (saved) { EggPrototypeCreatorFactory.registerPrototypeCreator(DEFAULT_PROTO_IMPL_TYPE, saved); } } }); }); }); ================================================ FILE: plugin/controller/test/lib/ControllerMetaManager.test.ts ================================================ import mm from 'egg-mock'; import path from 'path'; import assert from 'assert'; describe('plugin/controller/test/lib/ControllerMetaManager.test.ts', () => { beforeEach(() => { mm(process.env, 'EGG_TYPESCRIPT', true); }); afterEach(() => { mm.restore(); }); describe('controllers have same controller name', () => { it('should throw error', async () => { let app; mm(process, 'cwd', () => { return path.join(__dirname, '../..'); }); await assert.rejects(async () => { app = mm.app({ baseDir: path.join(__dirname, '../fixtures/apps/duplicate-controller-name-app'), framework: require.resolve('egg'), }); await app.ready(); }, /duplicate controller name AppController/); await app.close(); }); }); describe('controllers have same proto name', () => { it('should throw error', async () => { let app; mm(process, 'cwd', () => { return path.join(__dirname, '../..'); }); await assert.rejects(async () => { app = mm.app({ baseDir: path.join(__dirname, '../fixtures/apps/duplicate-proto-name-app'), framework: require.resolve('egg'), }); await app.ready(); }, /duplicate proto name appController/); await app.close(); }); }); }); ================================================ FILE: plugin/controller/test/lib/EggControllerLoader.test.ts ================================================ import mm from 'egg-mock'; import assert from 'assert'; import path from 'path'; import { EggControllerLoader } from '../../lib/EggControllerLoader'; import { ControllerMetadataUtil } from '@eggjs/tegg'; describe('plugin/controller/test/lib/EggModuleLoader.test.ts', () => { beforeEach(() => { mm(process.env, 'EGG_TYPESCRIPT', true); }); afterEach(() => { mm.restore(); }); it('should work', () => { const controllerDir = path.join(__dirname, '../fixtures/apps/controller-app/app/controller'); const loader = new EggControllerLoader(controllerDir); const clazzs = loader.load(); assert.strictEqual(clazzs.length, 8); const AppController = clazzs[0]; const metadata = ControllerMetadataUtil.getControllerMetadata(AppController); assert(metadata); }); }); ================================================ FILE: plugin/controller/test/lib/HTTPMethodRegister.test.ts ================================================ import assert from 'assert'; import KoaRouter from '@eggjs/router'; import path from 'path'; import { EggPrototypeCreatorFactory, EggPrototypeFactory, EggPrototypeLifecycleUtil, LoadUnit, LoadUnitFactory, } from '@eggjs/tegg-metadata'; import { EggControllerLoader } from '../../lib/EggControllerLoader'; import { CONTROLLER_LOAD_UNIT, ControllerLoadUnit } from '../../lib/ControllerLoadUnit'; import { CONTROLLER_META_DATA, HTTPControllerMeta } from '@eggjs/tegg'; import { EggControllerPrototypeHook } from '../../lib/EggControllerPrototypeHook'; import { HTTPMethodRegister } from '../../lib/impl/http/HTTPMethodRegister'; import { EggContainerFactory } from '@eggjs/tegg-runtime'; describe('plugin/controller/test/lib/HTTPControllerRegister.test.ts', () => { describe('method/path is registered', () => { const router = new KoaRouter(); const controllerPrototypeHook = new EggControllerPrototypeHook(); let loadUnit: LoadUnit; before(async () => { EggPrototypeLifecycleUtil.registerLifecycle(controllerPrototypeHook); router.get('mock_controller', '/apps/:id', () => { // ... }); const baseDir = path.join(__dirname, '../fixtures/apps/http-conflict-app'); const controllerDir = path.join(baseDir, 'app/controller'); const loader = new EggControllerLoader(controllerDir); LoadUnitFactory.registerLoadUnitCreator(CONTROLLER_LOAD_UNIT, ctx => { return new ControllerLoadUnit( 'tegg-app-controller', ctx.unitPath, ctx.loader, new EggPrototypeFactory(), EggPrototypeCreatorFactory, ); }); loadUnit = await LoadUnitFactory.createLoadUnit(controllerDir, CONTROLLER_LOAD_UNIT, loader); }); after(async () => { EggPrototypeLifecycleUtil.deleteLifecycle(controllerPrototypeHook); await LoadUnitFactory.destroyLoadUnit(loadUnit); }); it('should throw error with same rule', async () => { const proto = loadUnit.getEggPrototype('appController', [])[0]; const controllerMeta = proto.getMetaData(CONTROLLER_META_DATA)!; await assert.rejects(async () => { for (const methodMeta of controllerMeta.methods) { const register = new HTTPMethodRegister(proto, controllerMeta, methodMeta, router as any, new Map(), EggContainerFactory); await register.checkDuplicate(); } }, /RouterConflictError: register http controller GET AppController.get failed, GET \/apps\/:id is conflict with exists rule \/apps\/:id/); }); it('should throw error with sub rule', async () => { const proto = loadUnit.getEggPrototype('appController', [])[0]; const controllerMeta = proto.getMetaData(CONTROLLER_META_DATA)!; await assert.rejects(async () => { const register = new HTTPMethodRegister(proto, controllerMeta, { name: 'test', method: 'GET', path: '/123', } as any, router as any, new Map(), EggContainerFactory); await register.checkDuplicate(); }, /RouterConflictError: register http controller GET AppController.test failed, GET \/apps\/123 is conflict with exists rule \/apps\/:id/); }); it('should throw error with same rule with host', async () => { const proto1 = loadUnit.getEggPrototype('appController1', [])[0]; const proto2 = loadUnit.getEggPrototype('appController2', [])[0]; const controllerMeta1 = proto1.getMetaData(CONTROLLER_META_DATA)!; const controllerMeta2 = proto2.getMetaData(CONTROLLER_META_DATA)!; await assert.rejects(async () => { const routerMap = new Map(); for (const methodMeta of controllerMeta1.methods) { const register = new HTTPMethodRegister(proto1, controllerMeta1, methodMeta, router as any, routerMap, EggContainerFactory); await register.checkDuplicate(); } for (const methodMeta of controllerMeta2.methods) { const register = new HTTPMethodRegister(proto2, controllerMeta2, methodMeta, router as any, routerMap, EggContainerFactory); await register.checkDuplicate(); } }, /RouterConflictError: register http controller GET AppController2\.get failed, GET \/foo\/:id is conflict with exists rule \/foo\/:id/); }); }); }); ================================================ FILE: plugin/controller/test/mcp/helper.test.ts ================================================ import { MCPResourceMeta, MCPToolMeta, MCPPromptMeta, } from '@eggjs/controller-decorator'; import { InMemoryTransport } from '@modelcontextprotocol/sdk/inMemory.js'; import * as z from 'zod/v4'; import assert from 'assert'; import { Client } from '@modelcontextprotocol/sdk/client/index.js'; describe('plugin/controller/test/mcp/mcp.test.ts', () => { if (parseInt(process.versions.node, 10) < 18) { return; } // eslint-disable-next-line @typescript-eslint/no-var-requires const { MCPServerHelper } = require('../../lib/impl/mcp/MCPServerHelper'); it('MCPServerHelper should work', async () => { const PromptType = { name: z.string(), }; const ToolType = { name: z.string().describe('npm package name'), }; const helper = new MCPServerHelper({ name: 'test', version: '1.0.0', hooks: [], }); const resourceMeta = new MCPResourceMeta({ name: 'testResource', needAcl: false, middlewares: [], uri: 'mcp://npm/egg?version=4.10.0', }); const toolMeta = new MCPToolMeta({ name: 'testTool', needAcl: false, middlewares: [], detail: { argsSchema: ToolType, index: 0, }, }); const promptMeta = new MCPPromptMeta({ name: 'testPrompt', needAcl: false, middlewares: [], description: 'description', title: 'title', detail: { argsSchema: PromptType, index: 0, }, }); const getOrCreateEggObject = () => ({ obj: { [promptMeta.name]: args => { return { messages: [ { role: 'user', content: { type: 'text', text: `Generate a concise but descriptive commit message for these changes:\n\n${args.name}`, }, }, ], }; }, [toolMeta.name]: args => { return { content: [ { type: 'text', text: `npm package: ${args.name} not found`, }, ], }; }, [resourceMeta.name]: uri => { return { contents: [ { uri: uri.toString(), text: 'MOCK TEXT', }, ], }; }, } }); await helper.mcpToolRegister(getOrCreateEggObject as any, { getMetaData: () => ({}) } as any, toolMeta); await helper.mcpResourceRegister(getOrCreateEggObject as any, { getMetaData: () => ({}) } as any, resourceMeta); await helper.mcpPromptRegister(getOrCreateEggObject as any, { getMetaData: () => ({}) } as any, promptMeta); const [ clientTransport, serverTransport ] = InMemoryTransport.createLinkedPair(); const client = new Client( { name: 'test client', version: '1.0', }, { capabilities: { // tools: {}, }, }, ); await Promise.all([ client.connect(clientTransport), helper.server.connect(serverTransport), ]); const tools = await client.listTools(); assert.deepEqual(tools.tools.map(tool => ({ name: tool.name, description: tool.description })), [ { description: undefined, name: 'testTool', }, ]); const toolRes = await client.callTool({ name: 'testTool', arguments: { name: 'aaa', }, }); assert.deepEqual(toolRes, { content: [{ type: 'text', text: 'npm package: aaa not found' }], }); const resources = await client.listResources(); assert.deepEqual(resources, { resources: [ { uri: 'mcp://npm/egg?version=4.10.0', name: 'testResource' }, ], }); const resourceRes = await client.readResource({ uri: 'mcp://npm/egg?version=4.10.0', }); assert.deepEqual(resourceRes, { contents: [{ uri: 'mcp://npm/egg?version=4.10.0', text: 'MOCK TEXT' }], }); const prompts = await client.listPrompts(); assert.deepEqual(prompts, { prompts: [ { name: 'testPrompt', arguments: [{ name: 'name', required: true, description: undefined }], description: 'description', title: 'title' }, ], }); const promptRes = await client.getPrompt({ name: 'testPrompt', arguments: { name: 'bbb', }, }); assert.deepEqual(promptRes, { messages: [ { role: 'user', content: { type: 'text', text: 'Generate a concise but descriptive commit message for these changes:\n\nbbb', }, }, ], }); await clientTransport.close(); }); }); ================================================ FILE: plugin/controller/test/mcp/mcp.test.ts ================================================ import mm from 'egg-mock'; import path from 'path'; import fs from 'fs/promises'; import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js'; import { Client } from '@modelcontextprotocol/sdk/client/index.js'; import { CallToolRequest, CallToolResultSchema, ListToolsRequest, ListToolsResultSchema, LoggingMessageNotificationSchema, JSONRPCMessage } from '@modelcontextprotocol/sdk/types.js'; import assert from 'assert'; import { MCPControllerRegister } from '../../lib/impl/mcp/MCPControllerRegister'; async function listTools(client: Client) { const toolsRequest: ListToolsRequest = { method: 'tools/list', params: {}, }; const toolsResult = await client.request(toolsRequest, ListToolsResultSchema); const tools: { name: string; description?: string; }[] = []; for (const tool of toolsResult.tools) { tools.push({ name: tool.name, description: tool.description, }); } return tools; } async function startNotificationTool(client: Client, name?: string) { // Call the notification tool using reasonable defaults const request: CallToolRequest = { method: 'tools/call', params: { name: name ?? 'start-notification-stream', arguments: { interval: 1000, // 1 second between notifications count: 5, // Send 5 notifications }, }, }; const result = await client.request(request, CallToolResultSchema); const notifications: { text: string }[] = []; result.content.forEach(item => { if (item.type === 'text') { notifications.push({ text: item.text, }); } else { notifications.push({ text: (item as any).data!.toString(), }); } }); return notifications; } describe('plugin/controller/test/mcp/mcp.test.ts', () => { if (parseInt(process.version.slice(1, 3)) > 17) { // eslint-disable-next-line @typescript-eslint/no-var-requires const { StreamableHTTPClientTransport } = require('@modelcontextprotocol/sdk/client/streamableHttp.js'); let app; after(async () => { await app.close(); }); afterEach(() => { // mm.restore(); }); before(async () => { mm(process.env, 'EGG_TYPESCRIPT', true); mm(process, 'cwd', () => { return path.join(__dirname, '../..'); }); app = mm.app({ baseDir: path.join(__dirname, '../fixtures/apps/mcp-app'), framework: path.dirname(require.resolve('egg')), }); await app.ready(); }); after(() => { return app.close(); }); it('sse should work', async () => { const sseClient = new Client({ name: 'sse-demo-client', version: '1.0.0', }); const baseUrl = await app.httpRequest() .get('/mcp/sse').url; const sseTransport = new SSEClientTransport( new URL(baseUrl), { authProvider: { get redirectUrl() { return 'http://localhost/callback'; }, get clientMetadata() { return { redirect_uris: [ 'http://localhost/callback' ] }; }, clientInformation: () => ({ client_id: 'test-client-id', client_secret: 'test-client-secret' }), tokens: () => { return { access_token: Buffer.from('akita').toString('base64'), token_type: 'Bearer', }; }, // eslint-disable-next-line @typescript-eslint/no-empty-function saveTokens: () => {}, // eslint-disable-next-line @typescript-eslint/no-empty-function redirectToAuthorization: () => {}, // eslint-disable-next-line @typescript-eslint/no-empty-function saveCodeVerifier: () => {}, codeVerifier: () => '', }, }, ); const pingMessages: JSONRPCMessage[] = []; const sseNotifications: { level: string, data: string }[] = []; sseClient.setNotificationHandler(LoggingMessageNotificationSchema, notification => { sseNotifications.push({ level: notification.params.level, data: notification.params.data as string }); }); sseTransport.onmessage = message => { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore if (message?.method === 'ping') { pingMessages.push(message); } }; await sseClient.connect(sseTransport); // tool const tools = await listTools(sseClient); assert.deepEqual(tools, [ { name: 'start-notification-stream', description: 'Starts sending periodic notifications for testing resumability', }, { description: undefined, name: 'bar', }, { description: undefined, name: 'mockError', }, { description: undefined, name: 'echoUser', }, { description: undefined, name: 'traceTest', }, ]); const toolRes = await sseClient.callTool({ name: 'bar', arguments: { name: 'aaa', }, }); assert.deepEqual(toolRes, { content: [{ type: 'text', text: 'npm package: aaa not found' }], }); const userRes = await sseClient.callTool({ name: 'echoUser', arguments: {}, }); assert.deepEqual(userRes, { content: [{ type: 'text', text: 'hello akita' }], }); const traceRes = await sseClient.callTool({ name: 'traceTest', arguments: {}, }); assert.deepEqual(traceRes, { content: [{ type: 'text', text: 'hello middleware' }], }); // notification const notificationResp = await startNotificationTool(sseClient); await new Promise(resolve => setTimeout(resolve, 5000)); assert.deepEqual(notificationResp, [{ text: 'Started sending periodic notifications every 1000ms' }]); assert.deepEqual(sseNotifications, [ { level: 'info', data: 'Periodic notification #1' }, { level: 'info', data: 'Periodic notification #2' }, { level: 'info', data: 'Periodic notification #3' }, { level: 'info', data: 'Periodic notification #4' }, { level: 'info', data: 'Periodic notification #5' }, ]); // resources const resources = await sseClient.listResources(); assert.deepEqual(resources, { resources: [ { uri: 'mcp://npm/egg?version=4.10.0', name: 'egg' }, { uri: 'mcp://npm/mcp?version=0.10.0', name: 'mcp' }, ], }); const resourceRes = await sseClient.readResource({ uri: 'mcp://npm/egg?version=4.10.0', }); assert.deepEqual(resourceRes, { contents: [{ uri: 'mcp://npm/egg?version=4.10.0', text: 'MOCK TEXT' }], }); // prompts const prompts = await sseClient.listPrompts(); assert.deepEqual(prompts, { prompts: [ { name: 'foo', arguments: [{ name: 'name', required: true }] }, ], }); const promptRes = await sseClient.getPrompt({ name: 'foo', arguments: { name: 'bbb', }, }); assert.deepEqual(promptRes, { messages: [ { role: 'user', content: { type: 'text', text: 'Generate a concise but descriptive commit message for these changes:\n\nbbb', }, }, ], }); await sseTransport.close(); const middlewareStartTracelog = await fs.readFile(path.join(__dirname, '../fixtures/apps/mcp-app/logs/tracelog/mcpMiddlewareStart.log'), 'utf-8'); const middlewareEndTracelog = await fs.readFile(path.join(__dirname, '../fixtures/apps/mcp-app/logs/tracelog/mcpMiddlewareEnd.log'), 'utf-8'); assert.ok(middlewareStartTracelog.includes('mcp middleware start')); assert.ok(middlewareEndTracelog.includes('mcp middleware end, arg: {')); assert.ok(pingMessages.length > 0); }); it('streamable should work', async () => { const streamableClient = new Client({ name: 'streamable-demo-client', version: '1.0.0', }); const baseUrl = await app.httpRequest() .post('/mcp/stream').url; const streamableTransport = new StreamableHTTPClientTransport( new URL(baseUrl), { authProvider: { get redirectUrl() { return 'http://localhost/callback'; }, get clientMetadata() { return { redirect_uris: [ 'http://localhost/callback' ] }; }, clientInformation: () => ({ client_id: 'test-client-id', client_secret: 'test-client-secret' }), tokens: () => { return { access_token: Buffer.from('akita').toString('base64'), token_type: 'Bearer', }; }, // eslint-disable-next-line @typescript-eslint/no-empty-function saveTokens: () => {}, // eslint-disable-next-line @typescript-eslint/no-empty-function redirectToAuthorization: () => {}, // eslint-disable-next-line @typescript-eslint/no-empty-function saveCodeVerifier: () => {}, codeVerifier: () => '', }, requestInit: { headers: { 'custom-session-id': 'custom-session-id' } }, }, ); const pingMessages: JSONRPCMessage[] = []; const streamableNotifications: { level: string, data: string }[] = []; streamableClient.setNotificationHandler(LoggingMessageNotificationSchema, notification => { streamableNotifications.push({ level: notification.params.level, data: notification.params.data as string }); }); streamableTransport.onmessage = (...args) => { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore if (args[0]?.method === 'ping') { pingMessages.push(args[0]); } }; await streamableClient.connect(streamableTransport); // tool const tools = await listTools(streamableClient); assert.deepEqual(streamableTransport.sessionId, 'custom-session-id'); assert.deepEqual(tools, [ { name: 'start-notification-stream', description: 'Starts sending periodic notifications for testing resumability', }, { description: undefined, name: 'bar', }, { description: undefined, name: 'mockError', }, { description: undefined, name: 'echoUser', }, { description: undefined, name: 'traceTest', }, ]); const toolRes = await streamableClient.callTool({ name: 'bar', arguments: { name: 'aaa', }, }); assert.deepEqual(toolRes, { content: [{ type: 'text', text: 'npm package: aaa not found' }], }); const userRes = await streamableClient.callTool({ name: 'echoUser', arguments: {}, }); assert.deepEqual(userRes, { content: [{ type: 'text', text: 'hello akita' }], }); const traceRes = await streamableClient.callTool({ name: 'traceTest', arguments: {}, }); assert.deepEqual(traceRes, { content: [{ type: 'text', text: 'hello middleware' }], }); // notification const notificationResp = await startNotificationTool(streamableClient); await new Promise(resolve => setTimeout(resolve, 5000)); assert.deepEqual(notificationResp, [{ text: 'Started sending periodic notifications every 1000ms' }]); assert.deepEqual(streamableNotifications, [ { level: 'info', data: 'Periodic notification #1' }, { level: 'info', data: 'Periodic notification #2' }, { level: 'info', data: 'Periodic notification #3' }, { level: 'info', data: 'Periodic notification #4' }, { level: 'info', data: 'Periodic notification #5' }, ]); // resources const resources = await streamableClient.listResources(); assert.deepEqual(resources, { resources: [ { uri: 'mcp://npm/egg?version=4.10.0', name: 'egg' }, { uri: 'mcp://npm/mcp?version=0.10.0', name: 'mcp' }, ], }); const resourceRes = await streamableClient.readResource({ uri: 'mcp://npm/egg?version=4.10.0', }); assert.deepEqual(resourceRes, { contents: [{ uri: 'mcp://npm/egg?version=4.10.0', text: 'MOCK TEXT' }], }); // prompts const prompts = await streamableClient.listPrompts(); assert.deepEqual(prompts, { prompts: [ { name: 'foo', arguments: [{ name: 'name', required: true }] }, ], }); const promptRes = await streamableClient.getPrompt({ name: 'foo', arguments: { name: 'bbb', }, }); assert.deepEqual(promptRes, { messages: [ { role: 'user', content: { type: 'text', text: 'Generate a concise but descriptive commit message for these changes:\n\nbbb', }, }, ], }); await streamableTransport.terminateSession(); await streamableClient.close(); const logContent = await fs.readFile(path.join(__dirname, '../fixtures/apps/mcp-app/logs/mcp-app/mcp-app-web.log')); assert.ok(logContent.includes('startNotificationStream finish')); const middlewareStartTracelog = await fs.readFile(path.join(__dirname, '../fixtures/apps/mcp-app/logs/tracelog/mcpMiddlewareStart.log'), 'utf-8'); const middlewareEndTracelog = await fs.readFile(path.join(__dirname, '../fixtures/apps/mcp-app/logs/tracelog/mcpMiddlewareEnd.log'), 'utf-8'); assert.ok(middlewareStartTracelog.includes(' POST /mcp/stream] mcp middleware start')); assert.ok(middlewareEndTracelog.includes(' POST /mcp/stream] mcp middleware end, arg: ')); assert.ok(pingMessages.length > 0); }); it('stateless streamable should work', async () => { const streamableClient = new Client({ name: 'streamable-demo-client', version: '1.0.0', }); const baseUrl = await app.httpRequest() .post('/mcp/stateless/stream').url; const streamableTransport = new StreamableHTTPClientTransport( new URL(baseUrl), { authProvider: { get redirectUrl() { return 'http://localhost/callback'; }, get clientMetadata() { return { redirect_uris: [ 'http://localhost/callback' ] }; }, clientInformation: () => ({ client_id: 'test-client-id', client_secret: 'test-client-secret' }), tokens: () => { return { access_token: Buffer.from('akita').toString('base64'), token_type: 'Bearer', }; }, // eslint-disable-next-line @typescript-eslint/no-empty-function saveTokens: () => {}, // eslint-disable-next-line @typescript-eslint/no-empty-function redirectToAuthorization: () => {}, // eslint-disable-next-line @typescript-eslint/no-empty-function saveCodeVerifier: () => {}, codeVerifier: () => '', }, }, ); const streamableNotifications: { level: string, data: string }[] = []; streamableClient.setNotificationHandler(LoggingMessageNotificationSchema, notification => { streamableNotifications.push({ level: notification.params.level, data: notification.params.data as string }); }); await streamableClient.connect(streamableTransport); // tool const tools = await listTools(streamableClient); assert.deepEqual(tools, [ { name: 'start-notification-stream', description: 'Starts sending periodic notifications for testing resumability', }, { description: undefined, name: 'bar', }, { description: undefined, name: 'mockError', }, { description: undefined, name: 'echoUser', }, { description: undefined, name: 'traceTest', }, ]); const toolRes = await streamableClient.callTool({ name: 'bar', arguments: { name: 'aaa', }, }); assert.deepEqual(toolRes, { content: [{ type: 'text', text: 'npm package: aaa not found' }], }); const userRes = await streamableClient.callTool({ name: 'echoUser', arguments: {}, }); assert.deepEqual(userRes, { content: [{ type: 'text', text: 'hello akita' }], }); const traceRes = await streamableClient.callTool({ name: 'traceTest', arguments: {}, }); assert.deepEqual(traceRes, { content: [{ type: 'text', text: 'hello middleware' }], }); // notification const notificationResp = await startNotificationTool(streamableClient); await new Promise(resolve => setTimeout(resolve, 5000)); assert.deepEqual(notificationResp, [{ text: 'Started sending periodic notifications every 1000ms' }]); assert.deepEqual(streamableNotifications, [ { level: 'info', data: 'Periodic notification #1' }, { level: 'info', data: 'Periodic notification #2' }, { level: 'info', data: 'Periodic notification #3' }, { level: 'info', data: 'Periodic notification #4' }, { level: 'info', data: 'Periodic notification #5' }, ]); // resources const resources = await streamableClient.listResources(); assert.deepEqual(resources, { resources: [ { uri: 'mcp://npm/egg?version=4.10.0', name: 'egg' }, { uri: 'mcp://npm/mcp?version=0.10.0', name: 'mcp' }, ], }); const resourceRes = await streamableClient.readResource({ uri: 'mcp://npm/egg?version=4.10.0', }); assert.deepEqual(resourceRes, { contents: [{ uri: 'mcp://npm/egg?version=4.10.0', text: 'MOCK TEXT' }], }); // prompts const prompts = await streamableClient.listPrompts(); assert.deepEqual(prompts, { prompts: [ { name: 'foo', arguments: [{ name: 'name', required: true }] }, ], }); const promptRes = await streamableClient.getPrompt({ name: 'foo', arguments: { name: 'bbb', }, }); assert.deepEqual(promptRes, { messages: [ { role: 'user', content: { type: 'text', text: 'Generate a concise but descriptive commit message for these changes:\n\nbbb', }, }, ], }); await streamableTransport.terminateSession(); await streamableClient.close(); const middlewareStartTracelog = await fs.readFile(path.join(__dirname, '../fixtures/apps/mcp-app/logs/tracelog/mcpMiddlewareStart.log'), 'utf-8'); const middlewareEndTracelog = await fs.readFile(path.join(__dirname, '../fixtures/apps/mcp-app/logs/tracelog/mcpMiddlewareEnd.log'), 'utf-8'); assert.ok(middlewareStartTracelog.includes(' POST /mcp/stateless/stream] mcp middleware start')); assert.ok(middlewareEndTracelog.includes(' POST /mcp/stateless/stream] mcp middleware end, arg: {')); }); it('multiple sse should work', async () => { const sseClient = new Client({ name: 'sse-demo-client', version: '1.0.0', }); const baseUrl = await app.httpRequest() .get('/mcp/test/sse').url; const sseTransport = new SSEClientTransport( new URL(baseUrl), { authProvider: { get redirectUrl() { return 'http://localhost/callback'; }, get clientMetadata() { return { redirect_uris: [ 'http://localhost/callback' ] }; }, clientInformation: () => ({ client_id: 'test-client-id', client_secret: 'test-client-secret' }), tokens: () => { return { access_token: Buffer.from('akita').toString('base64'), token_type: 'Bearer', }; }, // eslint-disable-next-line @typescript-eslint/no-empty-function saveTokens: () => {}, // eslint-disable-next-line @typescript-eslint/no-empty-function redirectToAuthorization: () => {}, // eslint-disable-next-line @typescript-eslint/no-empty-function saveCodeVerifier: () => {}, codeVerifier: () => '', }, }, ); const sseNotifications: { level: string, data: string }[] = []; sseClient.setNotificationHandler(LoggingMessageNotificationSchema, notification => { sseNotifications.push({ level: notification.params.level, data: notification.params.data as string }); }); await sseClient.connect(sseTransport); // tool const tools = await listTools(sseClient); assert.deepEqual(tools, [ { name: 'test-start-notification-stream', description: 'Starts sending periodic notifications for testing resumability', }, { description: undefined, name: 'testBar', }, { description: undefined, name: 'testEchoUser', }, { description: undefined, name: 'testTraceTest', }, ]); const toolRes = await sseClient.callTool({ name: 'testBar', arguments: { name: 'aaa', }, }); assert.deepEqual(toolRes, { content: [{ type: 'text', text: 'npm package: aaa not found' }], }); const userRes = await sseClient.callTool({ name: 'testEchoUser', arguments: {}, }); assert.deepEqual(userRes, { content: [{ type: 'text', text: 'hello akita' }], }); const traceRes = await sseClient.callTool({ name: 'testTraceTest', arguments: {}, }); assert.deepEqual(traceRes, { content: [{ type: 'text', text: 'hello middleware' }], }); // notification const notificationResp = await startNotificationTool(sseClient, 'test-start-notification-stream'); await new Promise(resolve => setTimeout(resolve, 5000)); assert.deepEqual(notificationResp, [{ text: 'Started sending periodic notifications every 1000ms' }]); assert.deepEqual(sseNotifications, [ { level: 'info', data: 'Periodic notification #1' }, { level: 'info', data: 'Periodic notification #2' }, { level: 'info', data: 'Periodic notification #3' }, { level: 'info', data: 'Periodic notification #4' }, { level: 'info', data: 'Periodic notification #5' }, ]); // resources const resources = await sseClient.listResources(); assert.deepEqual(resources, { resources: [ { uri: 'mcp://npm/testEgg?version=4.10.0', name: 'testEgg' }, { uri: 'mcp://npm/testMcp?version=0.10.0', name: 'testMcp' }, ], }); const resourceRes = await sseClient.readResource({ uri: 'mcp://npm/testEgg?version=4.10.0', }); assert.deepEqual(resourceRes, { contents: [{ uri: 'mcp://npm/testEgg?version=4.10.0', text: 'MOCK TEXT' }], }); // prompts const prompts = await sseClient.listPrompts(); assert.deepEqual(prompts, { prompts: [ { name: 'testFoo', arguments: [{ name: 'name', required: true }] }, ], }); const promptRes = await sseClient.getPrompt({ name: 'testFoo', arguments: { name: 'bbb', }, }); assert.deepEqual(promptRes, { messages: [ { role: 'user', content: { type: 'text', text: 'Generate a concise but descriptive commit message for these changes:\n\nbbb', }, }, ], }); await sseTransport.close(); const middlewareStartTracelog = await fs.readFile(path.join(__dirname, '../fixtures/apps/mcp-app/logs/tracelog/mcpMiddlewareStart.log'), 'utf-8'); const middlewareEndTracelog = await fs.readFile(path.join(__dirname, '../fixtures/apps/mcp-app/logs/tracelog/mcpMiddlewareEnd.log'), 'utf-8'); assert.ok(middlewareStartTracelog.includes('mcp middleware start')); assert.ok(middlewareEndTracelog.includes('mcp middleware end')); }); it('multiple sse client should work', async () => { const sseClient1 = new Client({ name: 'sse-demo-client-1', version: '1.0.0', }); const baseUrl = await app.httpRequest() .get('/mcp/test/sse').url; const sseTransport1 = new SSEClientTransport( new URL(baseUrl), { authProvider: { get redirectUrl() { return 'http://localhost/callback'; }, get clientMetadata() { return { redirect_uris: [ 'http://localhost/callback' ] }; }, clientInformation: () => ({ client_id: 'test-client-id', client_secret: 'test-client-secret' }), tokens: () => { return { access_token: Buffer.from('akita').toString('base64'), token_type: 'Bearer', }; }, // eslint-disable-next-line @typescript-eslint/no-empty-function saveTokens: () => {}, // eslint-disable-next-line @typescript-eslint/no-empty-function redirectToAuthorization: () => {}, // eslint-disable-next-line @typescript-eslint/no-empty-function saveCodeVerifier: () => {}, codeVerifier: () => '', }, }, ); const sseClient2 = new Client({ name: 'sse-demo-client-2', version: '1.0.0', }); const sseTransport2 = new SSEClientTransport( new URL(baseUrl), { authProvider: { get redirectUrl() { return 'http://localhost/callback'; }, get clientMetadata() { return { redirect_uris: [ 'http://localhost/callback' ] }; }, clientInformation: () => ({ client_id: 'test-client-id', client_secret: 'test-client-secret' }), tokens: () => { return { access_token: Buffer.from('akita').toString('base64'), token_type: 'Bearer', }; }, // eslint-disable-next-line @typescript-eslint/no-empty-function saveTokens: () => {}, // eslint-disable-next-line @typescript-eslint/no-empty-function redirectToAuthorization: () => {}, // eslint-disable-next-line @typescript-eslint/no-empty-function saveCodeVerifier: () => {}, codeVerifier: () => '', }, }, ); await sseClient1.connect(sseTransport1); // tool const tools1 = await listTools(sseClient1); assert.deepEqual(tools1, [ { name: 'test-start-notification-stream', description: 'Starts sending periodic notifications for testing resumability', }, { description: undefined, name: 'testBar', }, { description: undefined, name: 'testEchoUser', }, { description: undefined, name: 'testTraceTest', }, ]); const toolRes1 = await sseClient1.callTool({ name: 'testBar', arguments: { name: 'aaa', }, }); assert.deepEqual(toolRes1, { content: [{ type: 'text', text: 'npm package: aaa not found' }], }); await sseClient2.connect(sseTransport2); const tools2 = await listTools(sseClient2); assert.deepEqual(tools2, [ { name: 'test-start-notification-stream', description: 'Starts sending periodic notifications for testing resumability', }, { description: undefined, name: 'testBar', }, { description: undefined, name: 'testEchoUser', }, { description: undefined, name: 'testTraceTest', }, ]); const toolRes2 = await sseClient2.callTool({ name: 'testBar', arguments: { name: 'aaa', }, }); assert.deepEqual(toolRes2, { content: [{ type: 'text', text: 'npm package: aaa not found' }], }); const userRes1 = await sseClient1.callTool({ name: 'testEchoUser', arguments: {}, }); assert.deepEqual(userRes1, { content: [{ type: 'text', text: 'hello akita' }], }); const userRes2 = await sseClient2.callTool({ name: 'testEchoUser', arguments: {}, }); assert.deepEqual(userRes2, { content: [{ type: 'text', text: 'hello akita' }], }); const traceRes1 = await sseClient1.callTool({ name: 'testTraceTest', arguments: {}, }); assert.deepEqual(traceRes1, { content: [{ type: 'text', text: 'hello middleware' }], }); const traceRes2 = await sseClient2.callTool({ name: 'testTraceTest', arguments: {}, }); assert.deepEqual(traceRes2, { content: [{ type: 'text', text: 'hello middleware' }], }); await sseTransport1.close(); await sseTransport2.close(); }); it('multiple streamable should work', async () => { const streamableClient = new Client({ name: 'streamable-demo-client', version: '1.0.0', }); const baseUrl = await app.httpRequest() .post('/mcp/test/stream').url; const streamableTransport = new StreamableHTTPClientTransport( new URL(baseUrl), { authProvider: { get redirectUrl() { return 'http://localhost/callback'; }, get clientMetadata() { return { redirect_uris: [ 'http://localhost/callback' ] }; }, clientInformation: () => ({ client_id: 'test-client-id', client_secret: 'test-client-secret' }), tokens: () => { return { access_token: Buffer.from('akita').toString('base64'), token_type: 'Bearer', }; }, // eslint-disable-next-line @typescript-eslint/no-empty-function saveTokens: () => {}, // eslint-disable-next-line @typescript-eslint/no-empty-function redirectToAuthorization: () => {}, // eslint-disable-next-line @typescript-eslint/no-empty-function saveCodeVerifier: () => {}, codeVerifier: () => '', }, requestInit: { headers: { 'custom-session-id': 'custom-session-id' } }, }, ); const streamableNotifications: { level: string, data: string }[] = []; streamableClient.setNotificationHandler(LoggingMessageNotificationSchema, notification => { streamableNotifications.push({ level: notification.params.level, data: notification.params.data as string }); }); await streamableClient.connect(streamableTransport); // tool const tools = await listTools(streamableClient); assert.deepEqual(streamableTransport.sessionId, 'custom-session-id'); assert.deepEqual(tools, [ { name: 'test-start-notification-stream', description: 'Starts sending periodic notifications for testing resumability', }, { description: undefined, name: 'testBar', }, { description: undefined, name: 'testEchoUser', }, { description: undefined, name: 'testTraceTest', }, ]); const toolRes = await streamableClient.callTool({ name: 'testBar', arguments: { name: 'aaa', }, }); assert.deepEqual(toolRes, { content: [{ type: 'text', text: 'npm package: aaa not found' }], }); const userRes = await streamableClient.callTool({ name: 'testEchoUser', arguments: {}, }); assert.deepEqual(userRes, { content: [{ type: 'text', text: 'hello akita' }], }); const traceRes = await streamableClient.callTool({ name: 'testTraceTest', arguments: {}, }); assert.deepEqual(traceRes, { content: [{ type: 'text', text: 'hello middleware' }], }); // notification const notificationResp = await startNotificationTool(streamableClient, 'test-start-notification-stream'); await new Promise(resolve => setTimeout(resolve, 5000)); assert.deepEqual(notificationResp, [{ text: 'Started sending periodic notifications every 1000ms' }]); assert.deepEqual(streamableNotifications, [ { level: 'info', data: 'Periodic notification #1' }, { level: 'info', data: 'Periodic notification #2' }, { level: 'info', data: 'Periodic notification #3' }, { level: 'info', data: 'Periodic notification #4' }, { level: 'info', data: 'Periodic notification #5' }, ]); // resources const resources = await streamableClient.listResources(); assert.deepEqual(resources, { resources: [ { uri: 'mcp://npm/testEgg?version=4.10.0', name: 'testEgg' }, { uri: 'mcp://npm/testMcp?version=0.10.0', name: 'testMcp' }, ], }); const resourceRes = await streamableClient.readResource({ uri: 'mcp://npm/testEgg?version=4.10.0', }); assert.deepEqual(resourceRes, { contents: [{ uri: 'mcp://npm/testEgg?version=4.10.0', text: 'MOCK TEXT' }], }); // prompts const prompts = await streamableClient.listPrompts(); assert.deepEqual(prompts, { prompts: [ { name: 'testFoo', arguments: [{ name: 'name', required: true }] }, ], }); const promptRes = await streamableClient.getPrompt({ name: 'testFoo', arguments: { name: 'bbb', }, }); assert.deepEqual(promptRes, { messages: [ { role: 'user', content: { type: 'text', text: 'Generate a concise but descriptive commit message for these changes:\n\nbbb', }, }, ], }); await streamableTransport.terminateSession(); await streamableClient.close(); const logContent = await fs.readFile(path.join(__dirname, '../fixtures/apps/mcp-app/logs/mcp-app/mcp-app-web.log')); assert.ok(logContent.includes('startNotificationStream finish')); const middlewareStartTracelog = await fs.readFile(path.join(__dirname, '../fixtures/apps/mcp-app/logs/tracelog/mcpMiddlewareStart.log'), 'utf-8'); const middlewareEndTracelog = await fs.readFile(path.join(__dirname, '../fixtures/apps/mcp-app/logs/tracelog/mcpMiddlewareEnd.log'), 'utf-8'); assert.ok(middlewareStartTracelog.includes(' POST /mcp/test/stream] mcp middleware start')); assert.ok(middlewareEndTracelog.includes(' POST /mcp/test/stream] mcp middleware end')); }); it('multiple streamable client should work', async () => { const streamableClient1 = new Client({ name: 'streamable-demo-client-1', version: '1.0.0', }); const baseUrl = await app.httpRequest() .post('/mcp/test/stream').url; const streamableTransport1 = new StreamableHTTPClientTransport( new URL(baseUrl), { authProvider: { get redirectUrl() { return 'http://localhost/callback'; }, get clientMetadata() { return { redirect_uris: [ 'http://localhost/callback' ] }; }, clientInformation: () => ({ client_id: 'test-client-id', client_secret: 'test-client-secret' }), tokens: () => { return { access_token: Buffer.from('akita').toString('base64'), token_type: 'Bearer', }; }, // eslint-disable-next-line @typescript-eslint/no-empty-function saveTokens: () => {}, // eslint-disable-next-line @typescript-eslint/no-empty-function redirectToAuthorization: () => {}, // eslint-disable-next-line @typescript-eslint/no-empty-function saveCodeVerifier: () => {}, codeVerifier: () => '', }, requestInit: { headers: { 'custom-session-id': 'custom-session-id' } }, }, ); await streamableClient1.connect(streamableTransport1); // tool const tools1 = await listTools(streamableClient1); assert.deepEqual(streamableTransport1.sessionId, 'custom-session-id'); assert.deepEqual(tools1, [ { name: 'test-start-notification-stream', description: 'Starts sending periodic notifications for testing resumability', }, { description: undefined, name: 'testBar', }, { description: undefined, name: 'testEchoUser', }, { description: undefined, name: 'testTraceTest', }, ]); const toolRes1 = await streamableClient1.callTool({ name: 'testBar', arguments: { name: 'aaa', }, }); assert.deepEqual(toolRes1, { content: [{ type: 'text', text: 'npm package: aaa not found' }], }); const streamableClient2 = new Client({ name: 'streamable-demo-client-2', version: '1.0.0', }); const streamableTransport2 = new StreamableHTTPClientTransport( new URL(baseUrl), { authProvider: { get redirectUrl() { return 'http://localhost/callback'; }, get clientMetadata() { return { redirect_uris: [ 'http://localhost/callback' ] }; }, clientInformation: () => ({ client_id: 'test-client-id', client_secret: 'test-client-secret' }), tokens: () => { return { access_token: Buffer.from('akita').toString('base64'), token_type: 'Bearer', }; }, // eslint-disable-next-line @typescript-eslint/no-empty-function saveTokens: () => {}, // eslint-disable-next-line @typescript-eslint/no-empty-function redirectToAuthorization: () => {}, // eslint-disable-next-line @typescript-eslint/no-empty-function saveCodeVerifier: () => {}, codeVerifier: () => '', }, requestInit: { headers: { 'custom-session-id': 'custom-session-id-2' } }, }, ); await streamableClient2.connect(streamableTransport2); const tools2 = await listTools(streamableClient2); assert.deepEqual(streamableTransport2.sessionId, 'custom-session-id-2'); assert.deepEqual(tools2, [ { name: 'test-start-notification-stream', description: 'Starts sending periodic notifications for testing resumability', }, { description: undefined, name: 'testBar', }, { description: undefined, name: 'testEchoUser', }, { description: undefined, name: 'testTraceTest', }, ]); const toolRes2 = await streamableClient2.callTool({ name: 'testBar', arguments: { name: 'aaa', }, }); assert.deepEqual(toolRes2, { content: [{ type: 'text', text: 'npm package: aaa not found' }], }); const userRes1 = await streamableClient1.callTool({ name: 'testEchoUser', arguments: {}, }); assert.deepEqual(userRes1, { content: [{ type: 'text', text: 'hello akita' }], }); const userRes2 = await streamableClient2.callTool({ name: 'testEchoUser', arguments: {}, }); assert.deepEqual(userRes2, { content: [{ type: 'text', text: 'hello akita' }], }); const traceRes1 = await streamableClient1.callTool({ name: 'testTraceTest', arguments: {}, }); assert.deepEqual(traceRes1, { content: [{ type: 'text', text: 'hello middleware' }], }); const traceRes2 = await streamableClient2.callTool({ name: 'testTraceTest', arguments: {}, }); assert.deepEqual(traceRes2, { content: [{ type: 'text', text: 'hello middleware' }], }); await streamableTransport1.terminateSession(); await streamableClient1.close(); await streamableTransport2.terminateSession(); await streamableClient2.close(); }); it('multiple stateless streamable should work', async () => { const streamableClient = new Client({ name: 'streamable-demo-client', version: '1.0.0', }); const baseUrl = await app.httpRequest() .post('/mcp/test/stateless/stream').url; const streamableTransport = new StreamableHTTPClientTransport( new URL(baseUrl), { authProvider: { get redirectUrl() { return 'http://localhost/callback'; }, get clientMetadata() { return { redirect_uris: [ 'http://localhost/callback' ] }; }, clientInformation: () => ({ client_id: 'test-client-id', client_secret: 'test-client-secret' }), tokens: () => { return { access_token: Buffer.from('akita').toString('base64'), token_type: 'Bearer', }; }, // eslint-disable-next-line @typescript-eslint/no-empty-function saveTokens: () => {}, // eslint-disable-next-line @typescript-eslint/no-empty-function redirectToAuthorization: () => {}, // eslint-disable-next-line @typescript-eslint/no-empty-function saveCodeVerifier: () => {}, codeVerifier: () => '', }, }, ); const streamableNotifications: { level: string, data: string }[] = []; streamableClient.setNotificationHandler(LoggingMessageNotificationSchema, notification => { streamableNotifications.push({ level: notification.params.level, data: notification.params.data as string }); }); await streamableClient.connect(streamableTransport); // tool const tools = await listTools(streamableClient); assert.deepEqual(tools, [ { name: 'test-start-notification-stream', description: 'Starts sending periodic notifications for testing resumability', }, { description: undefined, name: 'testBar', }, { description: undefined, name: 'testEchoUser', }, { description: undefined, name: 'testTraceTest', }, ]); const toolRes = await streamableClient.callTool({ name: 'testBar', arguments: { name: 'aaa', }, }); assert.deepEqual(toolRes, { content: [{ type: 'text', text: 'npm package: aaa not found' }], }); const userRes = await streamableClient.callTool({ name: 'testEchoUser', arguments: {}, }); assert.deepEqual(userRes, { content: [{ type: 'text', text: 'hello akita' }], }); const traceRes = await streamableClient.callTool({ name: 'testTraceTest', arguments: {}, }); assert.deepEqual(traceRes, { content: [{ type: 'text', text: 'hello middleware' }], }); // notification const notificationResp = await startNotificationTool(streamableClient, 'test-start-notification-stream'); await new Promise(resolve => setTimeout(resolve, 5000)); assert.deepEqual(notificationResp, [{ text: 'Started sending periodic notifications every 1000ms' }]); assert.deepEqual(streamableNotifications, [ { level: 'info', data: 'Periodic notification #1' }, { level: 'info', data: 'Periodic notification #2' }, { level: 'info', data: 'Periodic notification #3' }, { level: 'info', data: 'Periodic notification #4' }, { level: 'info', data: 'Periodic notification #5' }, ]); // resources const resources = await streamableClient.listResources(); assert.deepEqual(resources, { resources: [ { uri: 'mcp://npm/testEgg?version=4.10.0', name: 'testEgg' }, { uri: 'mcp://npm/testMcp?version=0.10.0', name: 'testMcp' }, ], }); const resourceRes = await streamableClient.readResource({ uri: 'mcp://npm/testEgg?version=4.10.0', }); assert.deepEqual(resourceRes, { contents: [{ uri: 'mcp://npm/testEgg?version=4.10.0', text: 'MOCK TEXT' }], }); // prompts const prompts = await streamableClient.listPrompts(); assert.deepEqual(prompts, { prompts: [ { name: 'testFoo', arguments: [{ name: 'name', required: true }] }, ], }); const promptRes = await streamableClient.getPrompt({ name: 'testFoo', arguments: { name: 'bbb', }, }); assert.deepEqual(promptRes, { messages: [ { role: 'user', content: { type: 'text', text: 'Generate a concise but descriptive commit message for these changes:\n\nbbb', }, }, ], }); await streamableTransport.terminateSession(); await streamableClient.close(); const middlewareStartTracelog = await fs.readFile(path.join(__dirname, '../fixtures/apps/mcp-app/logs/tracelog/mcpMiddlewareStart.log'), 'utf-8'); const middlewareEndTracelog = await fs.readFile(path.join(__dirname, '../fixtures/apps/mcp-app/logs/tracelog/mcpMiddlewareEnd.log'), 'utf-8'); assert.ok(middlewareStartTracelog.includes(' /mcp/test/stateless/stream] mcp middleware start')); assert.ok(middlewareEndTracelog.includes(' /mcp/test/stateless/stream] mcp middleware end')); }); it('sse session should be cleaned up after close', async () => { const sseClient = new Client({ name: 'sse-cleanup-client', version: '1.0.0', }); const baseUrl = await app.httpRequest() .get('/mcp/sse').url; const sseTransport = new SSEClientTransport( new URL(baseUrl), { authProvider: { get redirectUrl() { return 'http://localhost/callback'; }, get clientMetadata() { return { redirect_uris: [ 'http://localhost/callback' ] }; }, clientInformation: () => ({ client_id: 'test-client-id', client_secret: 'test-client-secret' }), tokens: () => { return { access_token: Buffer.from('akita').toString('base64'), token_type: 'Bearer', }; }, // eslint-disable-next-line @typescript-eslint/no-empty-function saveTokens: () => {}, // eslint-disable-next-line @typescript-eslint/no-empty-function redirectToAuthorization: () => {}, // eslint-disable-next-line @typescript-eslint/no-empty-function saveCodeVerifier: () => {}, codeVerifier: () => '', }, }, ); await sseClient.connect(sseTransport); // Call a tool to ensure session is active const toolRes = await sseClient.callTool({ name: 'bar', arguments: { name: 'aaa' }, }); assert.deepEqual(toolRes, { content: [{ type: 'text', text: 'npm package: aaa not found' }], }); // Verify session exists in MCPControllerRegister.instance before close const register = MCPControllerRegister.instance!; assert(register, 'MCPControllerRegister.instance should exist'); const sessionIds = Object.keys(register.transports); assert(sessionIds.length > 0, 'should have at least one active SSE transport'); // Record the session id for this connection const activeSessionId = sessionIds[sessionIds.length - 1]; assert(register.transports[activeSessionId], 'transport should exist for session'); assert(register.sseConnections.has(activeSessionId), 'sseConnections should have the session'); assert(register.mcpServerMap[activeSessionId], 'mcpServerMap should have the session'); // Close the SSE transport await sseTransport.close(); // Wait for cleanup to propagate await new Promise(resolve => setTimeout(resolve, 1000)); // Verify session is cleaned up assert(!register.transports[activeSessionId], 'transport should be cleaned up after close'); assert(!register.sseConnections.has(activeSessionId), 'sseConnections should be cleaned up after close'); assert(!register.mcpServerMap[activeSessionId], 'mcpServerMap should be cleaned up after close'); }); } }); ================================================ FILE: plugin/controller/test/mcp/mcpCluster.test.ts ================================================ import mm from 'egg-mock'; import path from 'path'; import fs from 'fs/promises'; import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js'; import { Client } from '@modelcontextprotocol/sdk/client/index.js'; import { CallToolRequest, CallToolResultSchema, ListToolsRequest, ListToolsResultSchema, LoggingMessageNotificationSchema, JSONRPCMessage } from '@modelcontextprotocol/sdk/types.js'; import assert from 'assert'; async function listTools(client: Client) { const toolsRequest: ListToolsRequest = { method: 'tools/list', params: {}, }; const toolsResult = await client.request(toolsRequest, ListToolsResultSchema); const tools: { name: string; description?: string; }[] = []; for (const tool of toolsResult.tools) { tools.push({ name: tool.name, description: tool.description, }); } return tools; } async function startNotificationTool(client: Client, name?: string) { // Call the notification tool using reasonable defaults const request: CallToolRequest = { method: 'tools/call', params: { name: name ?? 'start-notification-stream', arguments: { interval: 1000, // 1 second between notifications count: 5, // Send 5 notifications }, }, }; const result = await client.request(request, CallToolResultSchema); const notifications: { text: string }[] = []; result.content.forEach(item => { if (item.type === 'text') { notifications.push({ text: item.text, }); } else { notifications.push({ text: (item as any).data!.toString(), }); } }); return notifications; } describe('plugin/controller/test/mcp/mcpCluster.test.ts', () => { if (parseInt(process.version.slice(1, 3)) > 17) { // eslint-disable-next-line @typescript-eslint/no-var-requires const { StreamableHTTPClientTransport } = require('@modelcontextprotocol/sdk/client/streamableHttp.js'); let app; after(async () => { await app.close(); }); afterEach(() => { // mm.restore(); }); before(async () => { mm(process.env, 'EGG_TYPESCRIPT', true); mm(process, 'cwd', () => { return path.join(__dirname, '../..'); }); app = mm.cluster({ baseDir: path.join(__dirname, '../fixtures/apps/mcp-app'), framework: path.dirname(require.resolve('egg')), // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore workers: 3, sticky: false, opt: { env: { ...process.env, NODE_OPTIONS: '--require ts-node/register tsconfig-paths/register', }, }, }); await app.ready(); }); after(() => { return app.close(); }); it('sse should work', async () => { const sseClient = new Client({ name: 'sse-demo-client', version: '1.0.0', }); const baseUrl = await app.httpRequest() .get('/mcp/sse').url; const sseTransport = new SSEClientTransport( new URL(baseUrl), { authProvider: { get redirectUrl() { return 'http://localhost/callback'; }, get clientMetadata() { return { redirect_uris: [ 'http://localhost/callback' ] }; }, clientInformation: () => ({ client_id: 'test-client-id', client_secret: 'test-client-secret' }), tokens: () => { return { access_token: Buffer.from('akita').toString('base64'), token_type: 'Bearer', }; }, // eslint-disable-next-line @typescript-eslint/no-empty-function saveTokens: () => {}, // eslint-disable-next-line @typescript-eslint/no-empty-function redirectToAuthorization: () => {}, // eslint-disable-next-line @typescript-eslint/no-empty-function saveCodeVerifier: () => {}, codeVerifier: () => '', }, }, ); const pingMessages: JSONRPCMessage[] = []; const sseNotifications: { level: string, data: string }[] = []; sseClient.setNotificationHandler(LoggingMessageNotificationSchema, notification => { sseNotifications.push({ level: notification.params.level, data: notification.params.data as string }); }); sseTransport.onmessage = message => { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore if (message?.method === 'ping') { pingMessages.push(message); } }; await sseClient.connect(sseTransport); // tool const tools = await listTools(sseClient); assert.deepEqual(tools, [ { name: 'start-notification-stream', description: 'Starts sending periodic notifications for testing resumability', }, { description: undefined, name: 'bar', }, { description: undefined, name: 'mockError', }, { description: undefined, name: 'echoUser', }, { description: undefined, name: 'traceTest', }, ]); const toolRes = await sseClient.callTool({ name: 'bar', arguments: { name: '中文', }, }); assert.deepEqual(toolRes, { content: [{ type: 'text', text: 'npm package: 中文 not found' }], }); const userRes = await sseClient.callTool({ name: 'echoUser', arguments: {}, }); assert.deepEqual(userRes, { content: [{ type: 'text', text: 'hello akita' }], }); const traceRes = await sseClient.callTool({ name: 'traceTest', arguments: {}, }); assert.deepEqual(traceRes, { content: [{ type: 'text', text: 'hello middleware' }], }); // notification const notificationResp = await startNotificationTool(sseClient); await new Promise(resolve => setTimeout(resolve, 5000)); assert.deepEqual(notificationResp, [{ text: 'Started sending periodic notifications every 1000ms' }]); assert.deepEqual(sseNotifications, [ { level: 'info', data: 'Periodic notification #1' }, { level: 'info', data: 'Periodic notification #2' }, { level: 'info', data: 'Periodic notification #3' }, { level: 'info', data: 'Periodic notification #4' }, { level: 'info', data: 'Periodic notification #5' }, ]); // resources const resources = await sseClient.listResources(); assert.deepEqual(resources, { resources: [ { uri: 'mcp://npm/egg?version=4.10.0', name: 'egg' }, { uri: 'mcp://npm/mcp?version=0.10.0', name: 'mcp' }, ], }); const resourceRes = await sseClient.readResource({ uri: 'mcp://npm/egg?version=4.10.0', }); assert.deepEqual(resourceRes, { contents: [{ uri: 'mcp://npm/egg?version=4.10.0', text: 'MOCK TEXT' }], }); // prompts const prompts = await sseClient.listPrompts(); assert.deepEqual(prompts, { prompts: [ { name: 'foo', arguments: [{ name: 'name', required: true }] }, ], }); const promptRes = await sseClient.getPrompt({ name: 'foo', arguments: { name: 'bbb', }, }); assert.deepEqual(promptRes, { messages: [ { role: 'user', content: { type: 'text', text: 'Generate a concise but descriptive commit message for these changes:\n\nbbb', }, }, ], }); await sseTransport.close(); assert.ok(pingMessages.length > 0); }); it('multiple client should work', async () => { const sseClient1 = new Client({ name: 'sse-demo-client-1', version: '1.0.0', }); const baseUrl = await app.httpRequest() .get('/mcp/sse').url; const sseTransport1 = new SSEClientTransport( new URL(baseUrl), { authProvider: { get redirectUrl() { return 'http://localhost/callback'; }, get clientMetadata() { return { redirect_uris: [ 'http://localhost/callback' ] }; }, clientInformation: () => ({ client_id: 'test-client-id', client_secret: 'test-client-secret' }), tokens: () => { return { access_token: Buffer.from('akita').toString('base64'), token_type: 'Bearer', }; }, // eslint-disable-next-line @typescript-eslint/no-empty-function saveTokens: () => {}, // eslint-disable-next-line @typescript-eslint/no-empty-function redirectToAuthorization: () => {}, // eslint-disable-next-line @typescript-eslint/no-empty-function saveCodeVerifier: () => {}, codeVerifier: () => '', }, }, ); await sseClient1.connect(sseTransport1); // tool const tools1 = await listTools(sseClient1); assert.deepEqual(tools1, [ { name: 'start-notification-stream', description: 'Starts sending periodic notifications for testing resumability', }, { description: undefined, name: 'bar', }, { description: undefined, name: 'mockError', }, { description: undefined, name: 'echoUser', }, { description: undefined, name: 'traceTest', }, ]); const toolRes1 = await sseClient1.callTool({ name: 'bar', arguments: { name: 'aaa', }, }); assert.deepEqual(toolRes1, { content: [{ type: 'text', text: 'npm package: aaa not found' }], }); const sseClient2 = new Client({ name: 'sse-demo-client-2', version: '1.0.0', }); const sseTransport2 = new SSEClientTransport( new URL(baseUrl), { authProvider: { get redirectUrl() { return 'http://localhost/callback'; }, get clientMetadata() { return { redirect_uris: [ 'http://localhost/callback' ] }; }, clientInformation: () => ({ client_id: 'test-client-id', client_secret: 'test-client-secret' }), tokens: () => { return { access_token: Buffer.from('akita').toString('base64'), token_type: 'Bearer', }; }, // eslint-disable-next-line @typescript-eslint/no-empty-function saveTokens: () => {}, // eslint-disable-next-line @typescript-eslint/no-empty-function redirectToAuthorization: () => {}, // eslint-disable-next-line @typescript-eslint/no-empty-function saveCodeVerifier: () => {}, codeVerifier: () => '', }, }, ); await sseClient2.connect(sseTransport2); const tools2 = await listTools(sseClient2); assert.deepEqual(tools2, [ { name: 'start-notification-stream', description: 'Starts sending periodic notifications for testing resumability', }, { description: undefined, name: 'bar', }, { description: undefined, name: 'mockError', }, { description: undefined, name: 'echoUser', }, { description: undefined, name: 'traceTest', }, ]); const toolRes2 = await sseClient2.callTool({ name: 'bar', arguments: { name: 'aaa', }, }); assert.deepEqual(toolRes2, { content: [{ type: 'text', text: 'npm package: aaa not found' }], }); const userRes1 = await sseClient1.callTool({ name: 'echoUser', arguments: {}, }); assert.deepEqual(userRes1, { content: [{ type: 'text', text: 'hello akita' }], }); const userRes2 = await sseClient2.callTool({ name: 'echoUser', arguments: {}, }); assert.deepEqual(userRes2, { content: [{ type: 'text', text: 'hello akita' }], }); const traceRes1 = await sseClient1.callTool({ name: 'traceTest', arguments: {}, }); assert.deepEqual(traceRes1, { content: [{ type: 'text', text: 'hello middleware' }], }); const traceRes2 = await sseClient2.callTool({ name: 'traceTest', arguments: {}, }); assert.deepEqual(traceRes2, { content: [{ type: 'text', text: 'hello middleware' }], }); await sseTransport1.close(); await sseTransport2.close(); }); it('streamable should work', async () => { const streamableClient = new Client({ name: 'streamable-demo-client', version: '1.0.0', }); const baseUrl = await app.httpRequest() .post('/mcp/stream').url; const streamableTransport = new StreamableHTTPClientTransport( new URL(baseUrl), { authProvider: { get redirectUrl() { return 'http://localhost/callback'; }, get clientMetadata() { return { redirect_uris: [ 'http://localhost/callback' ] }; }, clientInformation: () => ({ client_id: 'test-client-id', client_secret: 'test-client-secret' }), tokens: () => { return { access_token: Buffer.from('akita').toString('base64'), token_type: 'Bearer', }; }, // eslint-disable-next-line @typescript-eslint/no-empty-function saveTokens: () => {}, // eslint-disable-next-line @typescript-eslint/no-empty-function redirectToAuthorization: () => {}, // eslint-disable-next-line @typescript-eslint/no-empty-function saveCodeVerifier: () => {}, codeVerifier: () => '', }, requestInit: { headers: { 'custom-session-id': 'custom-session-id' } }, }, ); const pingMessages: JSONRPCMessage[] = []; const streamableNotifications: { level: string, data: string }[] = []; streamableClient.setNotificationHandler(LoggingMessageNotificationSchema, notification => { streamableNotifications.push({ level: notification.params.level, data: notification.params.data as string }); }); streamableTransport.onmessage = (...args) => { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore if (args[0]?.method === 'ping') { pingMessages.push(args[0]); } }; await streamableClient.connect(streamableTransport); // tool const tools = await listTools(streamableClient); assert.deepEqual(streamableTransport.sessionId, 'custom-session-id'); assert.deepEqual(tools, [ { name: 'start-notification-stream', description: 'Starts sending periodic notifications for testing resumability', }, { description: undefined, name: 'bar', }, { description: undefined, name: 'mockError', }, { description: undefined, name: 'echoUser', }, { description: undefined, name: 'traceTest', }, ]); const toolRes = await streamableClient.callTool({ name: 'bar', arguments: { name: 'aaa', }, }); assert.deepEqual(toolRes, { content: [{ type: 'text', text: 'npm package: aaa not found' }], }); const userRes = await streamableClient.callTool({ name: 'echoUser', arguments: {}, }); assert.deepEqual(userRes, { content: [{ type: 'text', text: 'hello akita' }], }); const traceRes = await streamableClient.callTool({ name: 'traceTest', arguments: {}, }); assert.deepEqual(traceRes, { content: [{ type: 'text', text: 'hello middleware' }], }); // notification const notificationResp = await startNotificationTool(streamableClient); await new Promise(resolve => setTimeout(resolve, 5000)); assert.deepEqual(notificationResp, [{ text: 'Started sending periodic notifications every 1000ms' }]); assert.deepEqual(streamableNotifications, [ { level: 'info', data: 'Periodic notification #1' }, { level: 'info', data: 'Periodic notification #2' }, { level: 'info', data: 'Periodic notification #3' }, { level: 'info', data: 'Periodic notification #4' }, { level: 'info', data: 'Periodic notification #5' }, ]); // resources const resources = await streamableClient.listResources(); assert.deepEqual(resources, { resources: [ { uri: 'mcp://npm/egg?version=4.10.0', name: 'egg' }, { uri: 'mcp://npm/mcp?version=0.10.0', name: 'mcp' }, ], }); const resourceRes = await streamableClient.readResource({ uri: 'mcp://npm/egg?version=4.10.0', }); assert.deepEqual(resourceRes, { contents: [{ uri: 'mcp://npm/egg?version=4.10.0', text: 'MOCK TEXT' }], }); // prompts const prompts = await streamableClient.listPrompts(); assert.deepEqual(prompts, { prompts: [ { name: 'foo', arguments: [{ name: 'name', required: true }] }, ], }); const promptRes = await streamableClient.getPrompt({ name: 'foo', arguments: { name: 'bbb', }, }); assert.deepEqual(promptRes, { messages: [ { role: 'user', content: { type: 'text', text: 'Generate a concise but descriptive commit message for these changes:\n\nbbb', }, }, ], }); await streamableTransport.terminateSession(); await streamableClient.close(); const logContent = await fs.readFile(path.join(__dirname, '../fixtures/apps/mcp-app/logs/mcp-app/mcp-app-web.log')); assert.ok(logContent.includes('startNotificationStream finish')); assert.ok(pingMessages.length > 0); }); it('multiple streamable should work', async () => { const streamableClient1 = new Client({ name: 'streamable-demo-client-1', version: '1.0.0', }); const baseUrl = await app.httpRequest() .post('/mcp/stream').url; const streamableTransport1 = new StreamableHTTPClientTransport( new URL(baseUrl), { authProvider: { get redirectUrl() { return 'http://localhost/callback'; }, get clientMetadata() { return { redirect_uris: [ 'http://localhost/callback' ] }; }, clientInformation: () => ({ client_id: 'test-client-id', client_secret: 'test-client-secret' }), tokens: () => { return { access_token: Buffer.from('akita').toString('base64'), token_type: 'Bearer', }; }, // eslint-disable-next-line @typescript-eslint/no-empty-function saveTokens: () => {}, // eslint-disable-next-line @typescript-eslint/no-empty-function redirectToAuthorization: () => {}, // eslint-disable-next-line @typescript-eslint/no-empty-function saveCodeVerifier: () => {}, codeVerifier: () => '', }, requestInit: { headers: { 'custom-session-id': 'custom-session-id' } }, }, ); await streamableClient1.connect(streamableTransport1); // tool const tools1 = await listTools(streamableClient1); assert.deepEqual(streamableTransport1.sessionId, 'custom-session-id'); assert.deepEqual(tools1, [ { name: 'start-notification-stream', description: 'Starts sending periodic notifications for testing resumability', }, { description: undefined, name: 'bar', }, { description: undefined, name: 'mockError', }, { description: undefined, name: 'echoUser', }, { description: undefined, name: 'traceTest', }, ]); const toolRes1 = await streamableClient1.callTool({ name: 'bar', arguments: { name: 'aaa', }, }); assert.deepEqual(toolRes1, { content: [{ type: 'text', text: 'npm package: aaa not found' }], }); const streamableClient2 = new Client({ name: 'streamable-demo-client-2', version: '1.0.0', }); const streamableTransport2 = new StreamableHTTPClientTransport( new URL(baseUrl), { authProvider: { get redirectUrl() { return 'http://localhost/callback'; }, get clientMetadata() { return { redirect_uris: [ 'http://localhost/callback' ] }; }, clientInformation: () => ({ client_id: 'test-client-id', client_secret: 'test-client-secret' }), tokens: () => { return { access_token: Buffer.from('akita').toString('base64'), token_type: 'Bearer', }; }, // eslint-disable-next-line @typescript-eslint/no-empty-function saveTokens: () => {}, // eslint-disable-next-line @typescript-eslint/no-empty-function redirectToAuthorization: () => {}, // eslint-disable-next-line @typescript-eslint/no-empty-function saveCodeVerifier: () => {}, codeVerifier: () => '', }, requestInit: { headers: { 'custom-session-id': 'custom-session-id-2' } }, }, ); await streamableClient2.connect(streamableTransport2); const tools2 = await listTools(streamableClient2); assert.deepEqual(streamableTransport2.sessionId, 'custom-session-id-2'); assert.deepEqual(tools2, [ { name: 'start-notification-stream', description: 'Starts sending periodic notifications for testing resumability', }, { description: undefined, name: 'bar', }, { description: undefined, name: 'mockError', }, { description: undefined, name: 'echoUser', }, { description: undefined, name: 'traceTest', }, ]); const toolRes2 = await streamableClient2.callTool({ name: 'bar', arguments: { name: 'aaa', }, }); assert.deepEqual(toolRes2, { content: [{ type: 'text', text: 'npm package: aaa not found' }], }); const userRes1 = await streamableClient1.callTool({ name: 'echoUser', arguments: {}, }); assert.deepEqual(userRes1, { content: [{ type: 'text', text: 'hello akita' }], }); const userRes2 = await streamableClient2.callTool({ name: 'echoUser', arguments: {}, }); assert.deepEqual(userRes2, { content: [{ type: 'text', text: 'hello akita' }], }); const traceRes1 = await streamableClient1.callTool({ name: 'traceTest', arguments: {}, }); assert.deepEqual(traceRes1, { content: [{ type: 'text', text: 'hello middleware' }], }); const traceRes2 = await streamableClient2.callTool({ name: 'traceTest', arguments: {}, }); assert.deepEqual(traceRes2, { content: [{ type: 'text', text: 'hello middleware' }], }); await streamableTransport1.terminateSession(); await streamableClient1.close(); await streamableTransport2.terminateSession(); await streamableClient2.close(); }); it('stateless streamable should work', async () => { const streamableClient = new Client({ name: 'streamable-demo-client', version: '1.0.0', }); const baseUrl = await app.httpRequest() .post('/mcp/stateless/stream').url; const streamableTransport = new StreamableHTTPClientTransport( new URL(baseUrl), { authProvider: { get redirectUrl() { return 'http://localhost/callback'; }, get clientMetadata() { return { redirect_uris: [ 'http://localhost/callback' ] }; }, clientInformation: () => ({ client_id: 'test-client-id', client_secret: 'test-client-secret' }), tokens: () => { return { access_token: Buffer.from('akita').toString('base64'), token_type: 'Bearer', }; }, // eslint-disable-next-line @typescript-eslint/no-empty-function saveTokens: () => {}, // eslint-disable-next-line @typescript-eslint/no-empty-function redirectToAuthorization: () => {}, // eslint-disable-next-line @typescript-eslint/no-empty-function saveCodeVerifier: () => {}, codeVerifier: () => '', }, }, ); const streamableNotifications: { level: string, data: string }[] = []; streamableClient.setNotificationHandler(LoggingMessageNotificationSchema, notification => { streamableNotifications.push({ level: notification.params.level, data: notification.params.data as string }); }); await streamableClient.connect(streamableTransport); // tool const tools = await listTools(streamableClient); assert.deepEqual(tools, [ { name: 'start-notification-stream', description: 'Starts sending periodic notifications for testing resumability', }, { description: undefined, name: 'bar', }, { description: undefined, name: 'mockError', }, { description: undefined, name: 'echoUser', }, { description: undefined, name: 'traceTest', }, ]); const toolRes = await streamableClient.callTool({ name: 'bar', arguments: { name: 'aaa', }, }); assert.deepEqual(toolRes, { content: [{ type: 'text', text: 'npm package: aaa not found' }], }); const userRes = await streamableClient.callTool({ name: 'echoUser', arguments: {}, }); assert.deepEqual(userRes, { content: [{ type: 'text', text: 'hello akita' }], }); const traceRes = await streamableClient.callTool({ name: 'traceTest', arguments: {}, }); assert.deepEqual(traceRes, { content: [{ type: 'text', text: 'hello middleware' }], }); // notification const notificationResp = await startNotificationTool(streamableClient); await new Promise(resolve => setTimeout(resolve, 5000)); assert.deepEqual(notificationResp, [{ text: 'Started sending periodic notifications every 1000ms' }]); assert.deepEqual(streamableNotifications, [ { level: 'info', data: 'Periodic notification #1' }, { level: 'info', data: 'Periodic notification #2' }, { level: 'info', data: 'Periodic notification #3' }, { level: 'info', data: 'Periodic notification #4' }, { level: 'info', data: 'Periodic notification #5' }, ]); // resources const resources = await streamableClient.listResources(); assert.deepEqual(resources, { resources: [ { uri: 'mcp://npm/egg?version=4.10.0', name: 'egg' }, { uri: 'mcp://npm/mcp?version=0.10.0', name: 'mcp' }, ], }); const resourceRes = await streamableClient.readResource({ uri: 'mcp://npm/egg?version=4.10.0', }); assert.deepEqual(resourceRes, { contents: [{ uri: 'mcp://npm/egg?version=4.10.0', text: 'MOCK TEXT' }], }); // prompts const prompts = await streamableClient.listPrompts(); assert.deepEqual(prompts, { prompts: [ { name: 'foo', arguments: [{ name: 'name', required: true }] }, ], }); const promptRes = await streamableClient.getPrompt({ name: 'foo', arguments: { name: 'bbb', }, }); assert.deepEqual(promptRes, { messages: [ { role: 'user', content: { type: 'text', text: 'Generate a concise but descriptive commit message for these changes:\n\nbbb', }, }, ], }); await streamableTransport.terminateSession(); await streamableClient.close(); }); it('multiple sse should work', async () => { const sseClient = new Client({ name: 'sse-demo-client', version: '1.0.0', }); const baseUrl = await app.httpRequest() .get('/mcp/test/sse').url; const sseTransport = new SSEClientTransport( new URL(baseUrl), { authProvider: { get redirectUrl() { return 'http://localhost/callback'; }, get clientMetadata() { return { redirect_uris: [ 'http://localhost/callback' ] }; }, clientInformation: () => ({ client_id: 'test-client-id', client_secret: 'test-client-secret' }), tokens: () => { return { access_token: Buffer.from('akita').toString('base64'), token_type: 'Bearer', }; }, // eslint-disable-next-line @typescript-eslint/no-empty-function saveTokens: () => {}, // eslint-disable-next-line @typescript-eslint/no-empty-function redirectToAuthorization: () => {}, // eslint-disable-next-line @typescript-eslint/no-empty-function saveCodeVerifier: () => {}, codeVerifier: () => '', }, }, ); const sseNotifications: { level: string, data: string }[] = []; sseClient.setNotificationHandler(LoggingMessageNotificationSchema, notification => { sseNotifications.push({ level: notification.params.level, data: notification.params.data as string }); }); await sseClient.connect(sseTransport); // tool const tools = await listTools(sseClient); assert.deepEqual(tools, [ { name: 'test-start-notification-stream', description: 'Starts sending periodic notifications for testing resumability', }, { description: undefined, name: 'testBar', }, { description: undefined, name: 'testEchoUser', }, { description: undefined, name: 'testTraceTest', }, ]); const toolRes = await sseClient.callTool({ name: 'testBar', arguments: { name: 'aaa', }, }); assert.deepEqual(toolRes, { content: [{ type: 'text', text: 'npm package: aaa not found' }], }); const userRes = await sseClient.callTool({ name: 'testEchoUser', arguments: {}, }); assert.deepEqual(userRes, { content: [{ type: 'text', text: 'hello akita' }], }); const traceRes = await sseClient.callTool({ name: 'testTraceTest', arguments: {}, }); assert.deepEqual(traceRes, { content: [{ type: 'text', text: 'hello middleware' }], }); // notification const notificationResp = await startNotificationTool(sseClient, 'test-start-notification-stream'); await new Promise(resolve => setTimeout(resolve, 5000)); assert.deepEqual(notificationResp, [{ text: 'Started sending periodic notifications every 1000ms' }]); assert.deepEqual(sseNotifications, [ { level: 'info', data: 'Periodic notification #1' }, { level: 'info', data: 'Periodic notification #2' }, { level: 'info', data: 'Periodic notification #3' }, { level: 'info', data: 'Periodic notification #4' }, { level: 'info', data: 'Periodic notification #5' }, ]); // resources const resources = await sseClient.listResources(); assert.deepEqual(resources, { resources: [ { uri: 'mcp://npm/testEgg?version=4.10.0', name: 'testEgg' }, { uri: 'mcp://npm/testMcp?version=0.10.0', name: 'testMcp' }, ], }); const resourceRes = await sseClient.readResource({ uri: 'mcp://npm/testEgg?version=4.10.0', }); assert.deepEqual(resourceRes, { contents: [{ uri: 'mcp://npm/testEgg?version=4.10.0', text: 'MOCK TEXT' }], }); // prompts const prompts = await sseClient.listPrompts(); assert.deepEqual(prompts, { prompts: [ { name: 'testFoo', arguments: [{ name: 'name', required: true }] }, ], }); const promptRes = await sseClient.getPrompt({ name: 'testFoo', arguments: { name: 'bbb', }, }); assert.deepEqual(promptRes, { messages: [ { role: 'user', content: { type: 'text', text: 'Generate a concise but descriptive commit message for these changes:\n\nbbb', }, }, ], }); await sseTransport.close(); const middlewareStartTracelog = await fs.readFile(path.join(__dirname, '../fixtures/apps/mcp-app/logs/tracelog/mcpMiddlewareStart.log'), 'utf-8'); const middlewareEndTracelog = await fs.readFile(path.join(__dirname, '../fixtures/apps/mcp-app/logs/tracelog/mcpMiddlewareEnd.log'), 'utf-8'); assert.ok(middlewareStartTracelog.includes('mcp middleware start')); assert.ok(middlewareEndTracelog.includes('mcp middleware end')); }); it('multiple streamable should work', async () => { const streamableClient = new Client({ name: 'streamable-demo-client', version: '1.0.0', }); const baseUrl = await app.httpRequest() .post('/mcp/test/stream').url; const streamableTransport = new StreamableHTTPClientTransport( new URL(baseUrl), { authProvider: { get redirectUrl() { return 'http://localhost/callback'; }, get clientMetadata() { return { redirect_uris: [ 'http://localhost/callback' ] }; }, clientInformation: () => ({ client_id: 'test-client-id', client_secret: 'test-client-secret' }), tokens: () => { return { access_token: Buffer.from('akita').toString('base64'), token_type: 'Bearer', }; }, // eslint-disable-next-line @typescript-eslint/no-empty-function saveTokens: () => {}, // eslint-disable-next-line @typescript-eslint/no-empty-function redirectToAuthorization: () => {}, // eslint-disable-next-line @typescript-eslint/no-empty-function saveCodeVerifier: () => {}, codeVerifier: () => '', }, requestInit: { headers: { 'custom-session-id': 'custom-session-id' } }, }, ); const streamableNotifications: { level: string, data: string }[] = []; streamableClient.setNotificationHandler(LoggingMessageNotificationSchema, notification => { streamableNotifications.push({ level: notification.params.level, data: notification.params.data as string }); }); await streamableClient.connect(streamableTransport); // tool const tools = await listTools(streamableClient); assert.deepEqual(streamableTransport.sessionId, 'custom-session-id'); assert.deepEqual(tools, [ { name: 'test-start-notification-stream', description: 'Starts sending periodic notifications for testing resumability', }, { description: undefined, name: 'testBar', }, { description: undefined, name: 'testEchoUser', }, { description: undefined, name: 'testTraceTest', }, ]); const toolRes = await streamableClient.callTool({ name: 'testBar', arguments: { name: 'aaa', }, }); assert.deepEqual(toolRes, { content: [{ type: 'text', text: 'npm package: aaa not found' }], }); const userRes = await streamableClient.callTool({ name: 'testEchoUser', arguments: {}, }); assert.deepEqual(userRes, { content: [{ type: 'text', text: 'hello akita' }], }); const traceRes = await streamableClient.callTool({ name: 'testTraceTest', arguments: {}, }); assert.deepEqual(traceRes, { content: [{ type: 'text', text: 'hello middleware' }], }); // notification const notificationResp = await startNotificationTool(streamableClient, 'test-start-notification-stream'); await new Promise(resolve => setTimeout(resolve, 5000)); assert.deepEqual(notificationResp, [{ text: 'Started sending periodic notifications every 1000ms' }]); assert.deepEqual(streamableNotifications, [ { level: 'info', data: 'Periodic notification #1' }, { level: 'info', data: 'Periodic notification #2' }, { level: 'info', data: 'Periodic notification #3' }, { level: 'info', data: 'Periodic notification #4' }, { level: 'info', data: 'Periodic notification #5' }, ]); // resources const resources = await streamableClient.listResources(); assert.deepEqual(resources, { resources: [ { uri: 'mcp://npm/testEgg?version=4.10.0', name: 'testEgg' }, { uri: 'mcp://npm/testMcp?version=0.10.0', name: 'testMcp' }, ], }); const resourceRes = await streamableClient.readResource({ uri: 'mcp://npm/testEgg?version=4.10.0', }); assert.deepEqual(resourceRes, { contents: [{ uri: 'mcp://npm/testEgg?version=4.10.0', text: 'MOCK TEXT' }], }); // prompts const prompts = await streamableClient.listPrompts(); assert.deepEqual(prompts, { prompts: [ { name: 'testFoo', arguments: [{ name: 'name', required: true }] }, ], }); const promptRes = await streamableClient.getPrompt({ name: 'testFoo', arguments: { name: 'bbb', }, }); assert.deepEqual(promptRes, { messages: [ { role: 'user', content: { type: 'text', text: 'Generate a concise but descriptive commit message for these changes:\n\nbbb', }, }, ], }); await streamableTransport.terminateSession(); await streamableClient.close(); const logContent = await fs.readFile(path.join(__dirname, '../fixtures/apps/mcp-app/logs/mcp-app/mcp-app-web.log')); assert.ok(logContent.includes('startNotificationStream finish')); const middlewareStartTracelog = await fs.readFile(path.join(__dirname, '../fixtures/apps/mcp-app/logs/tracelog/mcpMiddlewareStart.log'), 'utf-8'); const middlewareEndTracelog = await fs.readFile(path.join(__dirname, '../fixtures/apps/mcp-app/logs/tracelog/mcpMiddlewareEnd.log'), 'utf-8'); assert.ok(middlewareStartTracelog.includes(' POST /mcp/test/stream] mcp middleware start')); assert.ok(middlewareEndTracelog.includes(' POST /mcp/test/stream] mcp middleware end')); }); it('multiple stateless streamable should work', async () => { const streamableClient = new Client({ name: 'streamable-demo-client', version: '1.0.0', }); const baseUrl = await app.httpRequest() .post('/mcp/test/stateless/stream').url; const streamableTransport = new StreamableHTTPClientTransport( new URL(baseUrl), { authProvider: { get redirectUrl() { return 'http://localhost/callback'; }, get clientMetadata() { return { redirect_uris: [ 'http://localhost/callback' ] }; }, clientInformation: () => ({ client_id: 'test-client-id', client_secret: 'test-client-secret' }), tokens: () => { return { access_token: Buffer.from('akita').toString('base64'), token_type: 'Bearer', }; }, // eslint-disable-next-line @typescript-eslint/no-empty-function saveTokens: () => {}, // eslint-disable-next-line @typescript-eslint/no-empty-function redirectToAuthorization: () => {}, // eslint-disable-next-line @typescript-eslint/no-empty-function saveCodeVerifier: () => {}, codeVerifier: () => '', }, }, ); const streamableNotifications: { level: string, data: string }[] = []; streamableClient.setNotificationHandler(LoggingMessageNotificationSchema, notification => { streamableNotifications.push({ level: notification.params.level, data: notification.params.data as string }); }); await streamableClient.connect(streamableTransport); // tool const tools = await listTools(streamableClient); assert.deepEqual(tools, [ { name: 'test-start-notification-stream', description: 'Starts sending periodic notifications for testing resumability', }, { description: undefined, name: 'testBar', }, { description: undefined, name: 'testEchoUser', }, { description: undefined, name: 'testTraceTest', }, ]); const toolRes = await streamableClient.callTool({ name: 'testBar', arguments: { name: 'aaa', }, }); assert.deepEqual(toolRes, { content: [{ type: 'text', text: 'npm package: aaa not found' }], }); const userRes = await streamableClient.callTool({ name: 'testEchoUser', arguments: {}, }); assert.deepEqual(userRes, { content: [{ type: 'text', text: 'hello akita' }], }); const traceRes = await streamableClient.callTool({ name: 'testTraceTest', arguments: {}, }); assert.deepEqual(traceRes, { content: [{ type: 'text', text: 'hello middleware' }], }); // notification const notificationResp = await startNotificationTool(streamableClient, 'test-start-notification-stream'); await new Promise(resolve => setTimeout(resolve, 5000)); assert.deepEqual(notificationResp, [{ text: 'Started sending periodic notifications every 1000ms' }]); assert.deepEqual(streamableNotifications, [ { level: 'info', data: 'Periodic notification #1' }, { level: 'info', data: 'Periodic notification #2' }, { level: 'info', data: 'Periodic notification #3' }, { level: 'info', data: 'Periodic notification #4' }, { level: 'info', data: 'Periodic notification #5' }, ]); // resources const resources = await streamableClient.listResources(); assert.deepEqual(resources, { resources: [ { uri: 'mcp://npm/testEgg?version=4.10.0', name: 'testEgg' }, { uri: 'mcp://npm/testMcp?version=0.10.0', name: 'testMcp' }, ], }); const resourceRes = await streamableClient.readResource({ uri: 'mcp://npm/testEgg?version=4.10.0', }); assert.deepEqual(resourceRes, { contents: [{ uri: 'mcp://npm/testEgg?version=4.10.0', text: 'MOCK TEXT' }], }); // prompts const prompts = await streamableClient.listPrompts(); assert.deepEqual(prompts, { prompts: [ { name: 'testFoo', arguments: [{ name: 'name', required: true }] }, ], }); const promptRes = await streamableClient.getPrompt({ name: 'testFoo', arguments: { name: 'bbb', }, }); assert.deepEqual(promptRes, { messages: [ { role: 'user', content: { type: 'text', text: 'Generate a concise but descriptive commit message for these changes:\n\nbbb', }, }, ], }); await streamableTransport.terminateSession(); await streamableClient.close(); const middlewareStartTracelog = await fs.readFile(path.join(__dirname, '../fixtures/apps/mcp-app/logs/tracelog/mcpMiddlewareStart.log'), 'utf-8'); const middlewareEndTracelog = await fs.readFile(path.join(__dirname, '../fixtures/apps/mcp-app/logs/tracelog/mcpMiddlewareEnd.log'), 'utf-8'); assert.ok(middlewareStartTracelog.includes(' /mcp/test/stateless/stream] mcp middleware start')); assert.ok(middlewareEndTracelog.includes(' /mcp/test/stateless/stream] mcp middleware end')); }); } }); ================================================ FILE: plugin/controller/tsconfig.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "baseUrl": "./" }, "exclude": [ "node_modules", "test" ] } ================================================ FILE: plugin/controller/tsconfig.pub.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "baseUrl": "./" }, "exclude": [ "node_modules", "test" ] } ================================================ FILE: plugin/controller/typings/index.d.ts ================================================ import 'egg'; import '@eggjs/tegg-plugin'; import '@eggjs/mcp-proxy'; import { RootProtoManager } from '../lib/RootProtoManager'; import { ControllerRegisterFactory } from '../lib/ControllerRegisterFactory'; import { ControllerMetaBuilderFactory } from '@eggjs/tegg'; declare module 'egg' { export interface TEggControllerApp extends MCPProxyApp { rootProtoManager: RootProtoManager; controllerRegisterFactory: ControllerRegisterFactory; controllerMetaBuilderFactory: typeof ControllerMetaBuilderFactory; } export interface Application extends TEggControllerApp { } } ================================================ FILE: plugin/dal/CHANGELOG.md ================================================ # Change Log All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. ## [3.78.15](https://github.com/eggjs/tegg/compare/v3.78.14...v3.78.15) (2026-04-23) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.78.14](https://github.com/eggjs/tegg/compare/v3.78.13...v3.78.14) (2026-04-21) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.78.13](https://github.com/eggjs/tegg/compare/v3.78.12...v3.78.13) (2026-04-21) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.78.12](https://github.com/eggjs/tegg/compare/v3.78.11...v3.78.12) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.78.11](https://github.com/eggjs/tegg/compare/v3.78.10...v3.78.11) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.78.10](https://github.com/eggjs/tegg/compare/v3.78.9...v3.78.10) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.78.9](https://github.com/eggjs/tegg/compare/v3.78.8...v3.78.9) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.78.8](https://github.com/eggjs/tegg/compare/v3.78.7...v3.78.8) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.78.7](https://github.com/eggjs/tegg/compare/v3.78.6...v3.78.7) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.78.6](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.6) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.78.5](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.5) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.78.4](https://github.com/eggjs/tegg/compare/v3.78.3...v3.78.4) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.78.3](https://github.com/eggjs/tegg/compare/v3.78.2...v3.78.3) (2026-04-15) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.78.2](https://github.com/eggjs/tegg/compare/v3.78.1...v3.78.2) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.78.1](https://github.com/eggjs/tegg/compare/v3.78.0...v3.78.1) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-dal-plugin # [3.78.0](https://github.com/eggjs/tegg/compare/v3.77.2...v3.78.0) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.77.2](https://github.com/eggjs/tegg/compare/v3.77.1...v3.77.2) (2026-04-07) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.77.1](https://github.com/eggjs/tegg/compare/v3.77.0...v3.77.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-dal-plugin # [3.77.0](https://github.com/eggjs/tegg/compare/v3.76.1...v3.77.0) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.76.1](https://github.com/eggjs/tegg/compare/v3.76.0...v3.76.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-dal-plugin # [3.76.0](https://github.com/eggjs/tegg/compare/v3.75.1...v3.76.0) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.75.1](https://github.com/eggjs/tegg/compare/v3.75.0...v3.75.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-dal-plugin # [3.75.0](https://github.com/eggjs/tegg/compare/v3.74.0...v3.75.0) (2026-03-30) **Note:** Version bump only for package @eggjs/tegg-dal-plugin # [3.74.0](https://github.com/eggjs/tegg/compare/v3.73.0...v3.74.0) (2026-03-30) **Note:** Version bump only for package @eggjs/tegg-dal-plugin # [3.73.0](https://github.com/eggjs/tegg/compare/v3.72.0...v3.73.0) (2026-03-25) **Note:** Version bump only for package @eggjs/tegg-dal-plugin # [3.72.0](https://github.com/eggjs/tegg/compare/v3.71.2...v3.72.0) (2026-02-04) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.71.2](https://github.com/eggjs/tegg/compare/v3.71.1...v3.71.2) (2026-01-30) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.71.1](https://github.com/eggjs/tegg/compare/v3.71.0...v3.71.1) (2026-01-28) **Note:** Version bump only for package @eggjs/tegg-dal-plugin # [3.71.0](https://github.com/eggjs/tegg/compare/v3.70.1...v3.71.0) (2026-01-26) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.70.1](https://github.com/eggjs/tegg/compare/v3.70.0...v3.70.1) (2026-01-14) **Note:** Version bump only for package @eggjs/tegg-dal-plugin # [3.70.0](https://github.com/eggjs/tegg/compare/v3.69.0...v3.70.0) (2026-01-07) **Note:** Version bump only for package @eggjs/tegg-dal-plugin # [3.69.0](https://github.com/eggjs/tegg/compare/v3.68.0...v3.69.0) (2026-01-06) **Note:** Version bump only for package @eggjs/tegg-dal-plugin # [3.68.0](https://github.com/eggjs/tegg/compare/v3.67.2...v3.68.0) (2025-12-29) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.67.2](https://github.com/eggjs/tegg/compare/v3.67.1...v3.67.2) (2025-12-24) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.67.1](https://github.com/eggjs/tegg/compare/v3.67.0...v3.67.1) (2025-12-23) **Note:** Version bump only for package @eggjs/tegg-dal-plugin # [3.67.0](https://github.com/eggjs/tegg/compare/v3.66.0...v3.67.0) (2025-12-18) **Note:** Version bump only for package @eggjs/tegg-dal-plugin # [3.66.0](https://github.com/eggjs/tegg/compare/v3.65.3...v3.66.0) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.65.3](https://github.com/eggjs/tegg/compare/v3.65.2...v3.65.3) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.65.2](https://github.com/eggjs/tegg/compare/v3.65.1...v3.65.2) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.65.1](https://github.com/eggjs/tegg/compare/v3.65.0...v3.65.1) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-dal-plugin # [3.65.0](https://github.com/eggjs/tegg/compare/v3.64.5...v3.65.0) (2025-12-10) ### Features * add parameterized query ([#366](https://github.com/eggjs/tegg/issues/366)) ([6d7d8d8](https://github.com/eggjs/tegg/commit/6d7d8d8383f4eea574d13e87ee03c57a33a319e7)) ## [3.64.5](https://github.com/eggjs/tegg/compare/v3.64.4...v3.64.5) (2025-12-08) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.64.4](https://github.com/eggjs/tegg/compare/v3.64.3...v3.64.4) (2025-12-06) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.64.3](https://github.com/eggjs/tegg/compare/v3.64.2...v3.64.3) (2025-12-06) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.64.2](https://github.com/eggjs/tegg/compare/v3.64.1...v3.64.2) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.64.1](https://github.com/eggjs/tegg/compare/v3.64.0...v3.64.1) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-dal-plugin # [3.64.0](https://github.com/eggjs/tegg/compare/v3.63.2...v3.64.0) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.63.2](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.2) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.63.1](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.1) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg-dal-plugin # [3.63.0](https://github.com/eggjs/tegg/compare/v3.62.3...v3.63.0) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.62.3](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.3) (2025-11-05) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.62.2](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.2) (2025-11-05) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.62.1](https://github.com/eggjs/tegg/compare/v3.62.0...v3.62.1) (2025-11-03) **Note:** Version bump only for package @eggjs/tegg-dal-plugin # [3.62.0](https://github.com/eggjs/tegg/compare/v3.61.0...v3.62.0) (2025-09-24) **Note:** Version bump only for package @eggjs/tegg-dal-plugin # [3.61.0](https://github.com/eggjs/tegg/compare/v3.60.3...v3.61.0) (2025-08-15) ### Features * allow inject MysqlDataSourceManager ([#342](https://github.com/eggjs/tegg/issues/342)) ([d13b2d7](https://github.com/eggjs/tegg/commit/d13b2d7cd11dd36960647cb40bfc4bf92ce704fd)) ## [3.60.3](https://github.com/eggjs/tegg/compare/v3.60.2...v3.60.3) (2025-08-06) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.60.2](https://github.com/eggjs/tegg/compare/v3.60.1...v3.60.2) (2025-08-06) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.60.1](https://github.com/eggjs/tegg/compare/v3.60.0...v3.60.1) (2025-07-28) **Note:** Version bump only for package @eggjs/tegg-dal-plugin # [3.60.0](https://github.com/eggjs/tegg/compare/v3.59.1...v3.60.0) (2025-07-28) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.59.1](https://github.com/eggjs/tegg/compare/v3.59.0...v3.59.1) (2025-07-16) **Note:** Version bump only for package @eggjs/tegg-dal-plugin # [3.59.0](https://github.com/eggjs/tegg/compare/v3.58.0...v3.59.0) (2025-07-07) **Note:** Version bump only for package @eggjs/tegg-dal-plugin # [3.58.0](https://github.com/eggjs/tegg/compare/v3.57.14...v3.58.0) (2025-07-01) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.57.14](https://github.com/eggjs/tegg/compare/v3.57.13...v3.57.14) (2025-06-18) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.57.13](https://github.com/eggjs/tegg/compare/v3.57.12...v3.57.13) (2025-06-18) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.57.12](https://github.com/eggjs/tegg/compare/v3.57.11...v3.57.12) (2025-06-16) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.57.11](https://github.com/eggjs/tegg/compare/v3.57.10...v3.57.11) (2025-06-16) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.57.10](https://github.com/eggjs/tegg/compare/v3.57.9...v3.57.10) (2025-06-12) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.57.9](https://github.com/eggjs/tegg/compare/v3.57.8...v3.57.9) (2025-05-29) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.57.8](https://github.com/eggjs/tegg/compare/v3.57.7...v3.57.8) (2025-05-29) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.57.7](https://github.com/eggjs/tegg/compare/v3.57.6...v3.57.7) (2025-05-28) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.57.6](https://github.com/eggjs/tegg/compare/v3.57.5...v3.57.6) (2025-05-27) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.57.5](https://github.com/eggjs/tegg/compare/v3.57.4...v3.57.5) (2025-05-15) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.57.4](https://github.com/eggjs/tegg/compare/v3.57.3...v3.57.4) (2025-05-14) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.57.3](https://github.com/eggjs/tegg/compare/v3.57.2...v3.57.3) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.57.2](https://github.com/eggjs/tegg/compare/v3.57.1...v3.57.2) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.57.1](https://github.com/eggjs/tegg/compare/v3.57.0...v3.57.1) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-dal-plugin # [3.57.0](https://github.com/eggjs/tegg/compare/v3.56.3...v3.57.0) (2025-05-09) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.56.3](https://github.com/eggjs/tegg/compare/v3.56.2...v3.56.3) (2025-05-07) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.56.2](https://github.com/eggjs/tegg/compare/v3.56.1...v3.56.2) (2025-05-01) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.56.1](https://github.com/eggjs/tegg/compare/v3.56.0...v3.56.1) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-dal-plugin # [3.56.0](https://github.com/eggjs/tegg/compare/v3.55.0...v3.56.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-dal-plugin # [3.55.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.55.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-dal-plugin # [3.54.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.54.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-dal-plugin # [3.53.0](https://github.com/eggjs/tegg/compare/v3.52.1...v3.53.0) (2025-04-21) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.52.1](https://github.com/eggjs/tegg/compare/v3.52.0...v3.52.1) (2025-03-14) **Note:** Version bump only for package @eggjs/tegg-dal-plugin # [3.52.0](https://github.com/eggjs/tegg/compare/v3.51.2...v3.52.0) (2024-12-30) ### Features * dal retry when init failed ([#260](https://github.com/eggjs/tegg/issues/260)) ([74e7c06](https://github.com/eggjs/tegg/commit/74e7c067c3ff7ae0ed705abaaa8a91f804e487e3)) ## [3.51.2](https://github.com/eggjs/tegg/compare/v3.51.1...v3.51.2) (2024-12-09) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.51.1](https://github.com/eggjs/tegg/compare/v3.51.0...v3.51.1) (2024-11-06) **Note:** Version bump only for package @eggjs/tegg-dal-plugin # [3.51.0](https://github.com/eggjs/tegg/compare/v3.50.1...v3.51.0) (2024-10-30) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.50.1](https://github.com/eggjs/tegg/compare/v3.50.0...v3.50.1) (2024-10-23) **Note:** Version bump only for package @eggjs/tegg-dal-plugin # [3.50.0](https://github.com/eggjs/tegg/compare/v3.49.0...v3.50.0) (2024-10-22) **Note:** Version bump only for package @eggjs/tegg-dal-plugin # [3.49.0](https://github.com/eggjs/tegg/compare/v3.48.1...v3.49.0) (2024-10-21) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.48.1](https://github.com/eggjs/tegg/compare/v3.48.0...v3.48.1) (2024-10-14) ### Bug Fixes * fix merge qualifier ([#250](https://github.com/eggjs/tegg/issues/250)) ([d5a8a93](https://github.com/eggjs/tegg/commit/d5a8a93abad570f69881f9fa42f39d7b5cd436be)) # [3.48.0](https://github.com/eggjs/tegg/compare/v3.47.2...v3.48.0) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.47.2](https://github.com/eggjs/tegg/compare/v3.47.1...v3.47.2) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.47.1](https://github.com/eggjs/tegg/compare/v3.47.0...v3.47.1) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-dal-plugin # [3.47.0](https://github.com/eggjs/tegg/compare/v3.46.4...v3.47.0) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.46.4](https://github.com/eggjs/tegg/compare/v3.46.3...v3.46.4) (2024-10-09) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.46.3](https://github.com/eggjs/tegg/compare/v3.46.2...v3.46.3) (2024-10-08) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.46.2](https://github.com/eggjs/tegg/compare/v3.46.1...v3.46.2) (2024-10-07) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.46.1](https://github.com/eggjs/tegg/compare/v3.46.0...v3.46.1) (2024-09-30) **Note:** Version bump only for package @eggjs/tegg-dal-plugin # [3.46.0](https://github.com/eggjs/tegg/compare/v3.45.0...v3.46.0) (2024-09-29) ### Features * impl MultiInstance inject MultiInstance ([#240](https://github.com/eggjs/tegg/issues/240)) ([08e3b0c](https://github.com/eggjs/tegg/commit/08e3b0cc02f3d2dbba767298a6aec6c00147f9ed)) # [3.45.0](https://github.com/eggjs/tegg/compare/v3.44.1...v3.45.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.44.1](https://github.com/eggjs/tegg/compare/v3.44.0...v3.44.1) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-dal-plugin # [3.44.0](https://github.com/eggjs/tegg/compare/v3.43.2...v3.44.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.43.2](https://github.com/eggjs/tegg/compare/v3.43.1...v3.43.2) (2024-09-14) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.43.1](https://github.com/eggjs/tegg/compare/v3.43.0...v3.43.1) (2024-09-14) **Note:** Version bump only for package @eggjs/tegg-dal-plugin # [3.43.0](https://github.com/eggjs/tegg/compare/v3.42.0...v3.43.0) (2024-09-13) **Note:** Version bump only for package @eggjs/tegg-dal-plugin # [3.42.0](https://github.com/eggjs/tegg/compare/v3.41.0...v3.42.0) (2024-09-10) **Note:** Version bump only for package @eggjs/tegg-dal-plugin # [3.41.0](https://github.com/eggjs/tegg/compare/v3.40.1...v3.41.0) (2024-08-26) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.40.1](https://github.com/eggjs/tegg/compare/v3.40.0...v3.40.1) (2024-08-23) **Note:** Version bump only for package @eggjs/tegg-dal-plugin # [3.40.0](https://github.com/eggjs/tegg/compare/v3.39.5...v3.40.0) (2024-08-22) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.39.5](https://github.com/eggjs/tegg/compare/v3.39.4...v3.39.5) (2024-08-09) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.39.4](https://github.com/eggjs/tegg/compare/v3.39.3...v3.39.4) (2024-07-09) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.39.3](https://github.com/eggjs/tegg/compare/v3.39.2...v3.39.3) (2024-04-28) ### Bug Fixes * mount clazzExtension/clazzExtension/tableSql to BaseDao ([#220](https://github.com/eggjs/tegg/issues/220)) ([ac322cf](https://github.com/eggjs/tegg/commit/ac322cfc4100841a1483b04b99e04d553af323eb)) ## [3.39.2](https://github.com/eggjs/tegg/compare/v3.39.1...v3.39.2) (2024-04-28) ### Bug Fixes * use loader to load TableClazzList ([#219](https://github.com/eggjs/tegg/issues/219)) ([15ef977](https://github.com/eggjs/tegg/commit/15ef977806dcb15831d6e906b92134257dd03654)) ## [3.39.1](https://github.com/eggjs/tegg/compare/v3.39.0...v3.39.1) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-dal-plugin # [3.39.0](https://github.com/eggjs/tegg/compare/v3.38.0...v3.39.0) (2024-04-19) **Note:** Version bump only for package @eggjs/tegg-dal-plugin # [3.38.0](https://github.com/eggjs/tegg/compare/v3.37.3...v3.38.0) (2024-04-18) ### Features * impl dal transaction ([#214](https://github.com/eggjs/tegg/issues/214)) ([b8b67dd](https://github.com/eggjs/tegg/commit/b8b67dd7e0fb282d78de7e68e68834ff79d30732)) ## [3.37.3](https://github.com/eggjs/tegg/compare/v3.37.2...v3.37.3) (2024-04-17) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.37.2](https://github.com/eggjs/tegg/compare/v3.37.1...v3.37.2) (2024-04-17) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.37.1](https://github.com/eggjs/tegg/compare/v3.37.0...v3.37.1) (2024-04-16) ### Bug Fixes * fix dal runtime dep ([#210](https://github.com/eggjs/tegg/issues/210)) ([5ad7f45](https://github.com/eggjs/tegg/commit/5ad7f4537114217924ae8dc7445e8fc77eee0b5a)) # [3.37.0](https://github.com/eggjs/tegg/compare/v3.36.3...v3.37.0) (2024-04-16) ### Bug Fixes * @eggjs/dal-runtime deps ([#209](https://github.com/eggjs/tegg/issues/209)) ([ffc8fdf](https://github.com/eggjs/tegg/commit/ffc8fdf342e7ea73400d6e31f82981155c2b0693)) ## [3.36.3](https://github.com/eggjs/tegg/compare/v3.36.2...v3.36.3) (2024-04-10) **Note:** Version bump only for package @eggjs/tegg-dal-plugin ## [3.36.2](https://github.com/eggjs/tegg/compare/v3.36.1...v3.36.2) (2024-04-08) ### Bug Fixes * fix dao extension in prod ([#206](https://github.com/eggjs/tegg/issues/206)) ([0498e9d](https://github.com/eggjs/tegg/commit/0498e9d11bd9e4d186160e8b6af07e627dde6a20)) ## [3.36.1](https://github.com/eggjs/tegg/compare/v3.36.0...v3.36.1) (2024-04-07) **Note:** Version bump only for package @eggjs/tegg-dal-plugin # [3.36.0](https://github.com/eggjs/tegg/compare/v3.35.1...v3.36.0) (2024-04-02) ### Features * impl ajv + typebox Validator ([#201](https://github.com/eggjs/tegg/issues/201)) ([9fd585d](https://github.com/eggjs/tegg/commit/9fd585de9b613466c96b73494a08a494db34ea57)) * impl dal forkDb ([#202](https://github.com/eggjs/tegg/issues/202)) ([a411f04](https://github.com/eggjs/tegg/commit/a411f04e074425419b5b348a362f120bf8189541)) * impl Date/timestamp on update ([#203](https://github.com/eggjs/tegg/issues/203)) ([e5c7b8d](https://github.com/eggjs/tegg/commit/e5c7b8d529f2854b77de2e99369c781a4ea9e070)) ## [3.35.1](https://github.com/eggjs/tegg/compare/v3.35.0...v3.35.1) (2024-03-26) **Note:** Version bump only for package @eggjs/tegg-dal-plugin # [3.35.0](https://github.com/eggjs/tegg/compare/v3.34.0...v3.35.0) (2024-03-26) **Note:** Version bump only for package @eggjs/tegg-dal-plugin # [3.34.0](https://github.com/eggjs/tegg/compare/v3.33.1...v3.34.0) (2024-03-22) ### Features * impl dal for standalone tegg ([#197](https://github.com/eggjs/tegg/issues/197)) ([56b259d](https://github.com/eggjs/tegg/commit/56b259d7215a9d9542b36e421996623819369846)) ## [3.33.1](https://github.com/eggjs/tegg/compare/v3.33.0...v3.33.1) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-dal-plugin # [3.33.0](https://github.com/eggjs/tegg/compare/v3.32.0...v3.33.0) (2024-03-22) ### Bug Fixes * set column canNull default to false ([#195](https://github.com/eggjs/tegg/issues/195)) ([24628ec](https://github.com/eggjs/tegg/commit/24628ec5a3cd167dc44a50017450d0dedec2c9ce)) ### Features * impl dal ([#192](https://github.com/eggjs/tegg/issues/192)) ([1c7d145](https://github.com/eggjs/tegg/commit/1c7d1454bc8c600cd58c3ec7b9cda4e8a98c7287)) ================================================ FILE: plugin/dal/README.md ================================================ # @eggjs/tegg-dal-plugin @eggjs/tegg-dal-plugin 支持使用注解的方式来开发 egg 中的 dal。 ## egg 模式 ### Install ```shell # tegg 注解 npm i --save @eggjs/tegg # tegg 插件 npm i --save @eggjs/tegg-plugin # tegg dal 插件 npm i --save @eggjs/tegg-dal-plugin ``` ### Prepare ```json // tsconfig.json { "extends": "@eggjs/tsconfig" } ``` ### Config ```js // config/plugin.js exports.tegg = { package: '@eggjs/tegg-plugin', enable: true, }; exports.teggDal = { package: '@eggjs/tegg-dal-plugin', enable: true, }; ``` ## standalone 模式 ### Install ```shell # tegg 注解 npm i --save @eggjs/tegg # tegg dal 插件 npm i --save @eggjs/tegg-dal-plugin ``` ### Prepare ```json // tsconfig.json { "extends": "@eggjs/tsconfig" } ``` ## Usage ### module.yml 通过 module.yml 来配置 module 中的 mysql 数据源。 ```yaml dataSource: # 数据源名称,可以在 @Table 注解中指定 # 如果 module 中只有一个 dataSource,@Table 会默认使用这个数据源 foo: connectionLimit: 100 database: 'test' host: '127.0.0.1' user: root port: 3306 ``` #### executeType 配置 可以通过在 `dataSource` 下配置 `executeType` 来指定 SQL 执行模式: ```yaml dataSource: foo: connectionLimit: 100 database: 'test' host: '127.0.0.1' user: root port: 3306 executeType: execute # 可选值: execute | query,默认为 query ``` **执行模式说明:** - `execute`: 使用 SQL 参数化查询,采用服务端序列化参数模式,通过预编译语句执行,某些情况下能提升查询性能 - `query`: 使用文本 SQL 模式,在本地将参数序列化到 SQL 语句中(nodejs 生态中 mysql1 只有这种模式) **注意事项:** - 由于 MySQL execute 模式反序列化 float 采用二进制模式,可能会导致精度丢失,建议使用 decimal 类型 - 当使用 `executeType: execute` 时,请确保数据库中的浮点数字段使用 `DECIMAL` 类型而不是 `FLOAT` 或 `DOUBLE` 类型,以避免精度问题 ### Table `TableModel` 定义一个表结构,包括表配置、列、索引。 ```ts import { Table, Index, Column, ColumnType, IndexType } from '@eggjs/tegg/dal'; // 定义了一个表 @Table({ comment: 'foo table', }) // 定义了一个唯一索引,列是 name @Index({ keys: [ 'name' ], type: IndexType.UNIQUE, }) export class Foo { // 定义了主键,类型是 int @Column({ type: ColumnType.INT, }, { primaryKey: true, }) id: number; // 定义了 name 列,类型是 varchar @Column({ type: ColumnType.VARCHAR, length: 100, }) name: string; } ``` 详细参数定义如下,具体参数值可以参考 https://dev.mysql.com/doc/refman/8.0/en/create-table.html 建表参数,使用方式为 `@Table(parmas?: TableParams)` ```ts export interface TableParams { // 数据库表名 name?: string; // 数据源名称,如果 module 只有一个 dataSource 则默认使用这个 dataSourceName?: string; comment?: string; autoExtendSize?: number; autoIncrement?: number; avgRowLength?: number; characterSet?: string; collate?: string; compression?: CompressionType; encryption?: boolean; engine?: string; engineAttribute?: string; insertMethod?: InsertMethod; keyBlockSize?: number; maxRows?: number; minRows?: number; rowFormat?: RowFormat; secondaryEngineAttribute?: string; } ``` 建索引参数,使用方式为 `@Index(parmas?: IndexParams)` ```ts export interface IndexParams { // 索引的列 keys: string[]; // 索引名称,如果未指定会用 列名拼接 // 如 [column1, column2 ] // 普通索引为 idx_column1_column2 // 唯一索引为 uk_column1_column2 name?: string; type?: IndexType, storeType?: IndexStoreType; comment?: string; engineAttribute?: string; secondaryEngineAttribute?: string; parser?: string; } ``` 建列参数,使用方式为 `@Column(type: ColumnTypeParams, parmas?: ColumnParams)` ```ts export interface ColumnParams { // 列名,默认转换规则 userName 至 user_name name?: string; // 默认值 default?: string; // 是否可控,默认为 false canNull?: boolean; comment?: string; visible?: boolean; autoIncrement?: boolean; uniqueKey?: boolean; primaryKey?: boolean; collate?: string; columnFormat?: ColumnFormat; engineAttribute?: string; secondaryEngineAttribute?: string; } ``` 支持的类型 ```ts export enum ColumnType { // Numeric BIT = 'BIT', TINYINT = 'TINYINT', BOOL = 'BOOL', SMALLINT = 'SMALLINT', MEDIUMINT = 'MEDIUMINT', INT = 'INT', BIGINT = 'BIGINT', DECIMAL = 'DECIMAL', FLOAT = 'FLOAT', DOUBLE = 'DOUBLE', // Date DATE = 'DATE', DATETIME = 'DATETIME', TIMESTAMP = 'TIMESTAMP', TIME = 'TIME', YEAR = 'YEAR', // String CHAR = 'CHAR', VARCHAR = 'VARCHAR', BINARY = 'BINARY', VARBINARY = 'VARBINARY', TINYBLOB = 'TINYBLOB', TINYTEXT = 'TINYTEXT', BLOB = 'BLOB', TEXT = 'TEXT', MEDIUMBLOB = 'MEDIUMBLOB', MEDIUMTEXT = 'MEDIUMTEXT', LONGBLOB = 'LONGBLOB', LONGTEXT = 'LONGTEXT', ENUM = 'ENUM', SET = 'SET', // JSON JSON = 'JSON', // Spatial GEOMETRY = 'GEOMETRY', POINT = 'POINT', LINESTRING = 'LINESTRING', POLYGON = 'POLYGON', MULTIPOINT = 'MULTIPOINT', MULTILINESTRING = 'MULTILINESTRING', MULTIPOLYGON = 'MULTIPOLYGON', GEOMETRYCOLLECTION = 'GEOMETRYCOLLECTION', } ``` 支持的类型参数,详细可参考 https://dev.mysql.com/doc/refman/8.0/en/data-types.html 如果 mysql 类型和 ts 类型对应关系不确定可直接使用 `ColumnTsType` 类型,如 ```ts import { Table, Index, Column, ColumnType, IndexType, ColumnTsType } from '@eggjs/tegg/dal'; // 定义了一个表 @Table({ comment: 'foo table', }) // 定义了一个唯一索引,列是 name @Index({ keys: [ 'name' ], type: IndexType.UNIQUE, }) export class Foo { // 定义了主键,类型是 int @Column({ type: ColumnType.INT, }, { primaryKey: true, }) id: ColumnTsType['INT']; // 定义了 name 列,类型是 varchar @Column({ type: ColumnType.VARCHAR, length: 100, }) name: ColumnTsType['VARCHAR']; } ``` ```ts // Bit 类型,对应 js 中的 Buffer export interface BitParams { type: ColumnType.BIT, // Bit 长度 length?: number; } // Bool 类型,注意在 js 中需要使用 0 或者 1 export interface BoolParams { type: ColumnType.BOOL, } // TinyInt 类型,对应 js 中的 number export interface TinyIntParams { type: ColumnType.TINYINT; length?: number; unsigned?: boolean; zeroFill?: boolean; } // SmallInt 类型,对应 js 中的 number export interface SmallIntParams { type: ColumnType.SMALLINT; length?: number; unsigned?: boolean; zeroFill?: boolean; } // MediumInt 类型,对应 js 中的 number export interface MediumIntParams { type: ColumnType.MEDIUMINT; length?: number; unsigned?: boolean; zeroFill?: boolean; } // MediumInt 类型,对应 js 中的 number export interface IntParams { type: ColumnType.INT; length?: number; unsigned?: boolean; zeroFill?: boolean; } // BigInt 类型,对应 js 中的 string export interface BigIntParams { type: ColumnType.BIGINT; length?: number; unsigned?: boolean; zeroFill?: boolean; } // Decimal 类型,对应 js 中的 string export interface DecimalParams { type: ColumnType.DECIMAL; length?: number; fractionalLength?: number; unsigned?: boolean; zeroFill?: boolean; } // Float 类型,对应 js 中的 number export interface FloatParams { type: ColumnType.FLOAT; length?: number; fractionalLength?: number; unsigned?: boolean; zeroFill?: boolean; } // Double 类型,对应 js 中的 number export interface DoubleParams { type: ColumnType.DOUBLE; length?: number; fractionalLength?: number; unsigned?: boolean; zeroFill?: boolean; } // Date 类型,对应 js 中的 Date export interface DateParams { type: ColumnType.DATE; } // DateTime 类型,对应 js 中的 Date export interface DateTimeParams { type: ColumnType.DATETIME; precision?: number; // 自动添加 ON UPDATE CURRENT_TIMESTAMP // 如果有精度则为 ON UPDATE CURRENT_TIMESTAMP(precision) autoUpdate?: boolean; } // Timestamp 类型,对应 js 中的 Date export interface TimestampParams { type: ColumnType.TIMESTAMP; precision?: number; // 自动添加 ON UPDATE CURRENT_TIMESTAMP // 如果有精度则为 ON UPDATE CURRENT_TIMESTAMP(precision) autoUpdate?: boolean; } // Times 类型,对应 js 中的 string export interface TimeParams { type: ColumnType.TIME; precision?: number; } // Year 类型,对应 js 中的 number export interface YearParams { type: ColumnType.YEAR; } // Char 类型,对应 js 中的 string export interface CharParams { type: ColumnType.CHAR; length?: number; characterSet?: string; collate?: string; } // VarChar 类型,对应 js 中的 string export interface VarCharParams { type: ColumnType.VARCHAR; length: number; characterSet?: string; collate?: string; } // Binary 类型,对应 js 中的 Buffer export interface BinaryParams { type: ColumnType.BINARY; length?: number; } // VarBinary 类型,对应 js 中的 Buffer export interface VarBinaryParams { type: ColumnType.VARBINARY; length: number; } // TinyBlob 类型,对应 js 中的 Buffer export interface TinyBlobParams { type: ColumnType.TINYBLOB; } // TinyText 类型,对应 js 中的 string export interface TinyTextParams { type: ColumnType.TINYTEXT; characterSet?: string; collate?: string; } // Blob 类型,对应 js 中的 Buffer export interface BlobParams { type: ColumnType.BLOB; length?: number; } // Text 类型,对应 js 中的 string export interface TextParams { type: ColumnType.TEXT; length?: number; characterSet?: string; collate?: string; } // MediumBlob 类型,对应 js 中的 Buffer export interface MediumBlobParams { type: ColumnType.MEDIUMBLOB; } // LongBlob 类型,对应 js 中的 Buffer export interface LongBlobParams { type: ColumnType.LONGBLOB; } // MediumText 类型,对应 js 中的 string export interface MediumTextParams { type: ColumnType.MEDIUMTEXT; characterSet?: string; collate?: string; } // LongText 类型,对应 js 中的 string export interface LongTextParams { type: ColumnType.LONGTEXT; characterSet?: string; collate?: string; } // Enum 类型,对应 js 中的 string export interface EnumParams { type: ColumnType.ENUM; enums: string[]; characterSet?: string; collate?: string; } // Set 类型,对应 js 中的 string export interface SetParams { type: ColumnType.SET; enums: string[]; characterSet?: string; collate?: string; } // Json 类型,对应 js 中的 Object export interface JsonParams { type: ColumnType.JSON; } // Gemotry 类型,对应 Point, Line, Polygon export interface GeometryParams { type: ColumnType.GEOMETRY; SRID?: number; } export interface PointParams { type: ColumnType.POINT; SRID?: number; } export interface LinestringParams { type: ColumnType.LINESTRING; SRID?: number; } export interface PolygonParams { type: ColumnType.POLYGON; SRID?: number; } export interface MultiPointParams { type: ColumnType.MULTIPOINT; SRID?: number; } export interface MultiLinestringParams { type: ColumnType.MULTILINESTRING; SRID?: number; } export interface MultiPolygonParams { type: ColumnType.MULTIPOLYGON; SRID?: number; } // GeometryCollection 对应 Array export interface GeometryCollectionParams { type: ColumnType.GEOMETRYCOLLECTION; SRID?: number; } ``` ### 目录结构 运行 `egg-bin dal gen` 即可生成 `dal` 相关目录,包括 dao、extension、structure ```plain dal ├── dao │ ├── FooDAO.ts │ └── base │ └── BaseFooDAO.ts ├── extension │ └── FooExtension.ts └── structure ├── Foo.json └── Foo.sql ``` - dao: 表访问类,生成的 BaseDAO 请勿修改,其中包含了根据表结构生成的基础访问方法,如 insert/update/delete 以及根据索引信息生成的 find 方法 - extension: 扩展文件,如果需要自定义 sql,需要在 extension 文件中定义 - structure: 建表语句以及表结构 ### DAO 注入 DAO 即可实现对表的访问 ```ts import { SingletonProto, Inject } from '@eggjs/tegg'; @SingletonProto() export class FooRepository { @Inject() private readonly fooDAO: FooDAO; async create(foo: Foo) { await this.fooDAO.insert(foo); } } ``` #### 自定义 SQL 1. 在 extension 中定义自定义 SQL ```ts // dal/extension/FooExtension.ts import { SqlMap, SqlType } from '@eggjs/tegg/dal'; export default { findByName: { type: SqlType.SELECT, sql: 'SELECT {{ allColumns }} FROM egg_foo WHERE name = {{ name | param }}', }, } as Record; ``` 2. 在 dao 中定义自定义方法 ```ts import { SingletonProto, AccessLevel } from '@eggjs/tegg'; import { BaseFooDAO } from './base/BaseFooDAO'; import { Foo } from '../../Foo'; @SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) export default class FooDAO extends BaseFooDAO { async findByName(name: string): Promise { return this.dataSource.execute('findByName', { name, }); } } ``` 支持的自定义 filter ``` - param: 参数化查询过滤器,用于防止 SQL 注入 - toPoint - toLine - toPolygon - toGeometry - toMultiPoint - toMultiLine - toMultiPolygon - toGeometryCollection ``` **param 过滤器** `param` 过滤器用于将值作为参数化查询参数,而不是直接拼接到 SQL 字符串中。可以有效利用到 sql parameters 的能力,小幅提升 db 性能与观测能力。 使用示例: ```ts export default { findByNameAndAge: { type: SqlType.SELECT, sql: ` SELECT {{ allColumns }} FROM egg_foo WHERE name = {{ name | param }} AND age > {{ age | param }} `, }, } as Record; ``` 生成的 SQL:`SELECT ... FROM egg_foo WHERE name = ? AND age > ?` 参数数组:`['John', 18]` 支持自定义 block 来简化 sql, 如内置的 allColumns ```ts export default { findByName: { type: SqlType.BLOCK, sql: 'id, name', }, } as Record; ``` ### DataSource DataSource 仅能在 DAO 中使用,可以将 MySQL 返回的数据反序列化为类。支持的方法有 ```ts export interface DataSource { // 将返回的行都转换为 T execute(sqlName: string, data?: any): Promise>; // 将返回的行都转换为 T, 仅返回第一条 executeScalar(sqlName: string, data?: any): Promise; // 直接返回 mysql 数据 executeRaw(sqlName: string, data?: any): Promise>; // 直接返回 mysql 数据, 仅返回第一条 executeRawScalar(sqlName: string, data?: any): Promise; // 返回分页数据 paginate(sqlName: string, data: any, currentPage: number, perPageCount: number): Promise; // 返回行数 count(sqlName: string, data?: any): Promise; } ``` ### 时区问题 注意连接配置中的时区必须和数据库的时区完全一致,否则可能出现时间错误的问题。 ```yaml dataSource: foo: connectionLimit: 100 database: 'test' host: '127.0.0.1' user: root port: 3306 timezone: '+08:00' ``` 可以通过以下 SQL 来查看数据库时区 ```sql SELECT @@GLOBAL.time_zone; ``` ## Unittest 可以在 `module.yml` 中开启 forkDb 配置,即可实现 unittest 环境自动创建数据库 ```yaml # module.yml dataSource: foo: # 开启 ci 环境自动创建数据库 forkDb: true ``` ================================================ FILE: plugin/dal/app/extend/application.ts ================================================ import { MysqlDataSourceManager } from '../../lib/MysqlDataSourceManager'; export default { get mysqlDataSourceManager() { return MysqlDataSourceManager.instance; }, }; ================================================ FILE: plugin/dal/app.ts ================================================ /// import { Application } from 'egg'; import { DalTableEggPrototypeHook } from './lib/DalTableEggPrototypeHook'; import { MysqlDataSourceManager } from './lib/MysqlDataSourceManager'; import { SqlMapManager } from './lib/SqlMapManager'; import { TableModelManager } from './lib/TableModelManager'; import { DalModuleLoadUnitHook } from './lib/DalModuleLoadUnitHook'; import { TransactionPrototypeHook } from './lib/TransactionPrototypeHook'; export default class ControllerAppBootHook { private readonly app: Application; private dalTableEggPrototypeHook: DalTableEggPrototypeHook; private dalModuleLoadUnitHook: DalModuleLoadUnitHook; private transactionPrototypeHook: TransactionPrototypeHook; constructor(app: Application) { this.app = app; } configWillLoad() { this.dalModuleLoadUnitHook = new DalModuleLoadUnitHook(this.app.config.env, this.app.moduleConfigs); this.dalTableEggPrototypeHook = new DalTableEggPrototypeHook(this.app.logger); this.transactionPrototypeHook = new TransactionPrototypeHook(this.app.moduleConfigs, this.app.logger); this.app.eggPrototypeLifecycleUtil.registerLifecycle(this.dalTableEggPrototypeHook); this.app.eggPrototypeLifecycleUtil.registerLifecycle(this.transactionPrototypeHook); this.app.loadUnitLifecycleUtil.registerLifecycle(this.dalModuleLoadUnitHook); } async beforeClose() { if (this.dalTableEggPrototypeHook) { this.app.eggPrototypeLifecycleUtil.deleteLifecycle(this.dalTableEggPrototypeHook); } if (this.dalModuleLoadUnitHook) { this.app.loadUnitLifecycleUtil.deleteLifecycle(this.dalModuleLoadUnitHook); } if (this.transactionPrototypeHook) { this.app.eggPrototypeLifecycleUtil.deleteLifecycle(this.transactionPrototypeHook); } MysqlDataSourceManager.instance.clear(); SqlMapManager.instance.clear(); TableModelManager.instance.clear(); } } ================================================ FILE: plugin/dal/index.ts ================================================ export { MysqlDataSourceManager } from './lib/MysqlDataSourceManager'; ================================================ FILE: plugin/dal/lib/DalModuleLoadUnitHook.ts ================================================ import { MysqlDataSourceManager } from './MysqlDataSourceManager'; import { LifecycleHook, Logger, ModuleConfigHolder } from '@eggjs/tegg'; import { DatabaseForker, DataSourceOptions } from '@eggjs/dal-runtime'; import { LoadUnit, LoadUnitLifecycleContext } from '@eggjs/tegg/helper'; export class DalModuleLoadUnitHook implements LifecycleHook { private readonly moduleConfigs: Record; private readonly env: string; private readonly logger?: Logger; constructor(env: string, moduleConfigs: Record, logger?: Logger) { this.env = env; this.moduleConfigs = moduleConfigs; this.logger = logger; } async preCreate(_: LoadUnitLifecycleContext, loadUnit: LoadUnit): Promise { const moduleConfigHolder = this.moduleConfigs[loadUnit.name]; if (!moduleConfigHolder) return; const dataSourceConfig: Record | undefined = (moduleConfigHolder.config as any).dataSource; if (!dataSourceConfig) return; await Promise.all(Object.entries(dataSourceConfig).map(async ([ name, config ]) => { const dataSourceOptions = { ...config, name, logger: this.logger, }; const forker = new DatabaseForker(this.env, dataSourceOptions); if (forker.shouldFork()) { await forker.forkDb(loadUnit.unitPath); } try { await MysqlDataSourceManager.instance.createDataSource(loadUnit.name, name, dataSourceOptions); } catch (e) { e.message = `create module ${loadUnit.name} datasource ${name} failed: ` + e.message; throw e; } })); } } ================================================ FILE: plugin/dal/lib/DalTableEggPrototypeHook.ts ================================================ import { Logger, LifecycleHook } from '@eggjs/tegg'; import { EggPrototype, EggPrototypeLifecycleContext } from '@eggjs/tegg/helper'; import { DaoInfoUtil, TableModel } from '@eggjs/dal-decorator'; import { SqlMapLoader } from '@eggjs/dal-runtime'; import { TableModelManager } from './TableModelManager'; import { SqlMapManager } from './SqlMapManager'; export class DalTableEggPrototypeHook implements LifecycleHook { private readonly logger: Logger; constructor(logger: Logger) { this.logger = logger; } async preCreate(ctx: EggPrototypeLifecycleContext): Promise { if (!DaoInfoUtil.getIsDao(ctx.clazz)) return; const tableClazz = ctx.clazz.clazzModel; const tableModel: TableModel = TableModel.build(tableClazz); TableModelManager.instance.set(ctx.loadUnit.name, tableModel); const loader = new SqlMapLoader(tableModel, ctx.clazz, this.logger); const sqlMap = loader.load(); SqlMapManager.instance.set(ctx.loadUnit.name, sqlMap); } } ================================================ FILE: plugin/dal/lib/DataSource.ts ================================================ import assert from 'node:assert'; import { AccessLevel, Inject, LoadUnitNameQualifierAttribute, MultiInstanceInfo, MultiInstanceProto, MultiInstancePrototypeGetObjectsContext, ObjectInfo, ObjectInitType, } from '@eggjs/tegg'; import { EggLoadUnitType, LoaderFactory, ModuleConfigUtil, } from '@eggjs/tegg/helper'; import { DataSourceInjectName, DataSourceQualifierAttribute, TableInfoUtil, TableModel, } from '@eggjs/tegg/dal'; import { DataSource } from '@eggjs/dal-runtime'; import { TableModelManager } from './TableModelManager'; import { MysqlDataSourceManager } from './MysqlDataSourceManager'; import { SqlMapManager } from './SqlMapManager'; import { TransactionalAOP } from './TransactionalAOP'; @MultiInstanceProto({ accessLevel: AccessLevel.PUBLIC, initType: ObjectInitType.SINGLETON, getObjects(ctx: MultiInstancePrototypeGetObjectsContext) { const config = ModuleConfigUtil.loadModuleConfigSync(ctx.unitPath) as any | undefined; const dataSources = Object.keys(config?.dataSource || {}); const result: ObjectInfo[] = []; const loader = LoaderFactory.createLoader(ctx.unitPath, EggLoadUnitType.MODULE); const clazzList = loader.load(); const tableClazzList = clazzList.filter(t => { return TableInfoUtil.getIsTable(t); }); const dataSourceLength = dataSources.length; for (const dataSource of dataSources) { const moduleClazzList = tableClazzList.filter(clazz => { const tableParams = TableInfoUtil.getTableParams(clazz); const dataSourceName = tableParams?.dataSourceName ?? 'default'; return dataSourceLength === 1 || dataSourceName === dataSource; }); for (const clazz of moduleClazzList) { result.push({ name: DataSourceInjectName, qualifiers: [{ attribute: DataSourceQualifierAttribute, value: `${ctx.moduleName}.${dataSource}.${clazz.name}`, }], }); } } return result; }, }) export class DataSourceDelegate extends DataSource { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore private transactionalAOP: TransactionalAOP; objInfo: ObjectInfo; constructor( @Inject({ name: 'transactionalAOP' }) transactionalAOP: TransactionalAOP, @MultiInstanceInfo([ DataSourceQualifierAttribute, LoadUnitNameQualifierAttribute ]) objInfo: ObjectInfo) { const dataSourceQualifierValue = objInfo.qualifiers.find(t => t.attribute === DataSourceQualifierAttribute)?.value; assert(dataSourceQualifierValue); // eslint-disable-next-line @typescript-eslint/no-unused-vars const [ moduleName, dataSource, clazzName ] = (dataSourceQualifierValue as string).split('.'); const tableModel = TableModelManager.instance.get(moduleName, clazzName); assert(tableModel, `not found table ${dataSourceQualifierValue}`); const mysqlDataSource = MysqlDataSourceManager.instance.get(moduleName, dataSource); assert(mysqlDataSource, `not found dataSource ${dataSource} in module ${moduleName}`); const sqlMap = SqlMapManager.instance.get(moduleName, clazzName); assert(sqlMap, `not found SqlMap ${clazzName} in module ${moduleName}`); super(tableModel as TableModel, mysqlDataSource, sqlMap); this.transactionalAOP = transactionalAOP; this.objInfo = objInfo; } } ================================================ FILE: plugin/dal/lib/MysqlDataSourceManager.ts ================================================ import { DataSourceOptions, MysqlDataSource } from '@eggjs/dal-runtime'; import crypto from 'node:crypto'; export class MysqlDataSourceManager { static instance = new MysqlDataSourceManager(); private readonly dataSourceIndices: Map>; private readonly dataSources: Map; constructor() { this.dataSourceIndices = new Map(); this.dataSources = new Map(); } get(moduleName: string, dataSourceName: string): MysqlDataSource | undefined { const dataSourceIndex = this.dataSourceIndices.get(moduleName) ?.get(dataSourceName); if (dataSourceIndex) { return this.dataSources.get(dataSourceIndex); } } async createDataSource(moduleName: string, dataSourceName: string, config: DataSourceOptions) { const { logger, ...dsConfig } = config || {}; const dataSourceConfig = { ...dsConfig, name: dataSourceName, }; const index = MysqlDataSourceManager.createDataSourceKey(dataSourceConfig); let dataSource = this.dataSources.get(index); if (!dataSource) { dataSource = new MysqlDataSource({ ...dataSourceConfig, logger }); this.dataSources.set(index, dataSource); } let moduledataSourceIndices = this.dataSourceIndices.get(moduleName); if (!moduledataSourceIndices) { moduledataSourceIndices = new Map(); this.dataSourceIndices.set(moduleName, moduledataSourceIndices); } moduledataSourceIndices.set(dataSourceName, index); await dataSource.ready(); } clear() { this.dataSourceIndices.clear(); } static createDataSourceKey(dataSourceOptions: DataSourceOptions): string { const hash = crypto.createHash('md5'); const keys = Object.keys(dataSourceOptions) .sort(); for (const key of keys) { const value = dataSourceOptions[key]; if (value) { hash.update(key); hash.update(String(value)); } } return hash.digest('hex'); } } ================================================ FILE: plugin/dal/lib/SqlMapManager.ts ================================================ import { TableSqlMap } from '@eggjs/dal-runtime'; export class SqlMapManager { static instance = new SqlMapManager(); private sqlMaps: Map>; constructor() { this.sqlMaps = new Map(); } get(moduleName: string, clazzName: string): TableSqlMap | undefined { return this.sqlMaps.get(moduleName)?.get(clazzName); } set(moduleName: string, sqlMap: TableSqlMap) { let tables = this.sqlMaps.get(moduleName); if (!tables) { tables = new Map(); this.sqlMaps.set(moduleName, tables); } tables.set(sqlMap.name, sqlMap); } clear() { this.sqlMaps.clear(); } } ================================================ FILE: plugin/dal/lib/TableModelManager.ts ================================================ import { TableModel } from '@eggjs/dal-decorator'; export class TableModelManager { static instance = new TableModelManager(); private tableModels: Map>; constructor() { this.tableModels = new Map(); } get(moduleName: string, clazzName: string): TableModel | undefined { return this.tableModels.get(moduleName)?.get(clazzName); } set(moduleName: string, tableModel: TableModel) { let tables = this.tableModels.get(moduleName); if (!tables) { tables = new Map(); this.tableModels.set(moduleName, tables); } tables.set(tableModel.clazz.name, tableModel); } clear() { this.tableModels.clear(); } } ================================================ FILE: plugin/dal/lib/TransactionPrototypeHook.ts ================================================ import assert from 'assert'; import { LifecycleHook, ModuleConfigHolder, Logger } from '@eggjs/tegg'; import { EggPrototype, EggPrototypeLifecycleContext } from '@eggjs/tegg-metadata'; import { PropagationType, TransactionMetaBuilder } from '@eggjs/tegg/transaction'; import { Pointcut } from '@eggjs/tegg/aop'; import { TransactionalAOP, TransactionalParams } from './TransactionalAOP'; import { MysqlDataSourceManager } from './MysqlDataSourceManager'; export class TransactionPrototypeHook implements LifecycleHook { private readonly moduleConfigs: Record; private readonly logger: Logger; constructor(moduleConfigs: Record, logger: Logger) { this.moduleConfigs = moduleConfigs; this.logger = logger; } public async preCreate(ctx: EggPrototypeLifecycleContext): Promise { const builder = new TransactionMetaBuilder(ctx.clazz); const transactionMetadataList = builder.build(); if (transactionMetadataList.length < 1) { return; } const moduleName = ctx.loadUnit.name; for (const transactionMetadata of transactionMetadataList) { const clazzName = `${moduleName}.${ctx.clazz.name}.${String(transactionMetadata.method)}`; const datasourceConfigs = (this.moduleConfigs[moduleName]?.config as any)?.dataSource || {}; let datasourceName: string; if (transactionMetadata.datasourceName) { assert(datasourceConfigs[transactionMetadata.datasourceName], `method ${clazzName} specified datasource ${transactionMetadata.datasourceName} not exists`); datasourceName = transactionMetadata.datasourceName; this.logger.info(`use datasource [${transactionMetadata.datasourceName}] for class ${clazzName}`); } else { const dataSources = Object.keys(datasourceConfigs); if (dataSources.length === 1) { datasourceName = dataSources[0]; } else { throw new Error(`method ${clazzName} not specified datasource, module ${moduleName} has multi datasource, should specify datasource name`); } this.logger.info(`use default datasource ${dataSources[0]} for class ${clazzName}`); } const adviceParams: TransactionalParams = { propagation: transactionMetadata.propagation, dataSourceGetter: () => { const mysqlDataSource = MysqlDataSourceManager.instance.get(moduleName, datasourceName); if (!mysqlDataSource) { throw new Error(`method ${clazzName} not found datasource ${datasourceName}`); } return mysqlDataSource; }, }; assert(adviceParams.propagation === PropagationType.REQUIRED, 'Transactional propagation only support required for now'); Pointcut(TransactionalAOP, { adviceParams })((ctx.clazz as any).prototype, transactionMetadata.method); } } } ================================================ FILE: plugin/dal/lib/TransactionalAOP.ts ================================================ import { Advice, AdviceContext, IAdvice } from '@eggjs/tegg/aop'; import { AccessLevel, EggProtoImplClass, ObjectInitType } from '@eggjs/tegg'; import { PropagationType } from '@eggjs/tegg/transaction'; import { MysqlDataSource } from '@eggjs/dal-runtime'; export interface TransactionalParams { propagation: PropagationType; dataSourceGetter: () => MysqlDataSource; } @Advice({ accessLevel: AccessLevel.PUBLIC, initType: ObjectInitType.SINGLETON, }) export class TransactionalAOP implements IAdvice { public async around(ctx: AdviceContext, next: () => Promise): Promise { const { propagation, dataSourceGetter } = ctx.adviceParams!; const dataSource = dataSourceGetter(); if (propagation === PropagationType.ALWAYS_NEW) { return await dataSource.beginTransactionScope(next); } return await dataSource.beginTransactionScope(next); } } ================================================ FILE: plugin/dal/package.json ================================================ { "name": "@eggjs/tegg-dal-plugin", "eggPlugin": { "name": "teggDal", "strict": false, "dependencies": [ "tegg" ] }, "eggModule": { "name": "teggDal" }, "version": "3.78.15", "description": "dal plugin for egg", "main": "index.js", "keywords": [ "egg", "plugin", "typescript", "module", "tegg", "dal" ], "files": [ "app.js", "app.d.ts", "index.js", "index.d.ts", "lib/**/*.js", "lib/**/*.d.ts", "app/**/*.js", "app/**/*.d.ts", "typings/*.d.ts" ], "scripts": { "test": "cross-env NODE_ENV=test NODE_OPTIONS='--no-deprecation' mocha", "clean": "tsc -b --clean", "tsc": "ut run clean && tsc -p ./tsconfig.json", "tsc:pub": "ut run clean && tsc -p ./tsconfig.pub.json", "prepublishOnly": "ut tsc:pub" }, "homepage": "https://github.com/eggjs/tegg", "bugs": { "url": "https://github.com/eggjs/tegg/issues" }, "repository": { "type": "git", "url": "git@github.com:eggjs/tegg.git", "directory": "plugin/dal" }, "engines": { "node": ">=14.0.0" }, "dependencies": { "@eggjs/dal-runtime": "^3.78.15" }, "devDependencies": { "@eggjs/tegg-aop-plugin": "^3.78.15", "@eggjs/tegg-config": "^3.78.15", "@eggjs/tegg-plugin": "^3.78.15", "@types/mocha": "^10.0.1", "@types/node": "^20.2.4", "cross-env": "^7.0.3", "egg": "^3.9.1", "egg-mock": "^5.5.0", "egg-tracer": "^2.0.0", "globby": "^11.1.0", "mocha": "^10.2.0", "ts-node": "^10.9.1", "typescript": "^5.0.4" }, "publishConfig": { "access": "public" }, "gitHead": "240a3e9f40fda65ebb7589727d197db5ce17916c" } ================================================ FILE: plugin/dal/test/dal.test.ts ================================================ import assert from 'assert'; import path from 'path'; import mm, { MockApplication } from 'egg-mock'; import FooDAO from './fixtures/apps/dal-app/modules/dal/dal/dao/FooDAO'; import { Foo } from './fixtures/apps/dal-app/modules/dal/Foo'; describe('plugin/dal/test/dal.test.ts', () => { let app: MockApplication; afterEach(async () => { mm.restore(); }); before(async () => { mm(process.env, 'EGG_TYPESCRIPT', true); mm(process, 'cwd', () => { return path.join(__dirname, '../'); }); app = mm.app({ baseDir: path.join(__dirname, './fixtures/apps/dal-app'), framework: require.resolve('egg'), }); await app.ready(); }); after(() => { return app.close(); }); it('should work', async () => { await app.mockModuleContextScope(async ctx => { const fooDAO = await ctx.getEggObject(FooDAO); const foo = new Foo(); foo.name = 'name'; foo.col1 = 'col1'; foo.bitColumn = Buffer.from([ 0, 0 ]); foo.boolColumn = 0; foo.tinyIntColumn = 0; foo.smallIntColumn = 1; foo.mediumIntColumn = 3; foo.intColumn = 3; foo.bigIntColumn = '00099'; foo.decimalColumn = '00002.33333'; foo.floatColumn = 2.3; foo.doubleColumn = 2.3; foo.dateColumn = new Date('2020-03-15T16:00:00.000Z'); foo.dateTimeColumn = new Date('2024-03-16T01:26:58.677Z'); foo.timestampColumn = new Date('2024-03-16T01:26:58.677Z'); foo.timeColumn = '838:59:50.123'; foo.yearColumn = 2024; foo.varCharColumn = 'var_char'; foo.binaryColumn = Buffer.from('b'); foo.varBinaryColumn = Buffer.from('var_binary'); foo.tinyBlobColumn = Buffer.from('tiny_blob'); foo.tinyTextColumn = 'text'; foo.blobColumn = Buffer.from('blob'); foo.textColumn = 'text'; foo.mediumBlobColumn = Buffer.from('medium_blob'); foo.longBlobColumn = Buffer.from('long_blob'); foo.mediumTextColumn = 'medium_text'; foo.longTextColumn = 'long_text'; foo.enumColumn = 'A'; foo.setColumn = 'B'; foo.geometryColumn = { x: 10, y: 10 }; foo.pointColumn = { x: 10, y: 10 }; foo.lineStringColumn = [ { x: 15, y: 15 }, { x: 20, y: 20 }, ]; foo.polygonColumn = [ [ { x: 0, y: 0 }, { x: 10, y: 0 }, { x: 10, y: 10 }, { x: 0, y: 10 }, { x: 0, y: 0 }, ], [ { x: 5, y: 5 }, { x: 7, y: 5 }, { x: 7, y: 7 }, { x: 5, y: 7 }, { x: 5, y: 5 }, ], ]; foo.multipointColumn = [ { x: 0, y: 0 }, { x: 20, y: 20 }, { x: 60, y: 60 }, ]; foo.multiLineStringColumn = [ [ { x: 10, y: 10 }, { x: 20, y: 20 }, ], [ { x: 15, y: 15 }, { x: 30, y: 15 }, ], ]; foo.multiPolygonColumn = [ [ [ { x: 0, y: 0 }, { x: 10, y: 0 }, { x: 10, y: 10 }, { x: 0, y: 10 }, { x: 0, y: 0 }, ], ], [ [ { x: 5, y: 5 }, { x: 7, y: 5 }, { x: 7, y: 7 }, { x: 5, y: 7 }, { x: 5, y: 5 }, ], ], ]; foo.geometryCollectionColumn = [ { x: 10, y: 10 }, { x: 30, y: 30 }, [ { x: 15, y: 15 }, { x: 20, y: 20 }, ], ]; foo.jsonColumn = { hello: 'json', }; const insertResult = await fooDAO.insert(foo); foo.id = insertResult.insertId; const findFoo = await fooDAO.findByPrimary(foo.id); assert(findFoo); const deleteResult = await fooDAO.delete(foo.id); assert.equal(deleteResult.affectedRows, 1); }); }); }); ================================================ FILE: plugin/dal/test/fixtures/apps/dal-app/config/config.default.js ================================================ 'use strict'; module.exports = function() { const config = { keys: 'test key', security: { csrf: { ignoreJSON: false, }, }, }; return config; }; ================================================ FILE: plugin/dal/test/fixtures/apps/dal-app/config/module.json ================================================ [ { "path": "../modules/dal" }, { "package": "../../../../" } ] ================================================ FILE: plugin/dal/test/fixtures/apps/dal-app/config/plugin.js ================================================ 'use strict'; exports.tracer = { package: 'egg-tracer', enable: true, }; exports.tegg = { package: '@eggjs/tegg-plugin', enable: true, }; exports.teggConfig = { package: '@eggjs/tegg-config', enable: true, }; exports.aopModule = { enable: true, package: '@eggjs/tegg-aop-plugin', }; ================================================ FILE: plugin/dal/test/fixtures/apps/dal-app/modules/dal/Foo.ts ================================================ import { Column, ColumnType, Geometry, GeometryCollection, Index, IndexType, IndexStoreType, Line, MultiLine, MultiPoint, MultiPolygon, Point, Polygon, Table, } from '@eggjs/dal-decorator'; @Table({ name: 'egg_foo', comment: 'foo table', characterSet: 'utf8mb4', collate: 'utf8mb4_unicode_ci', }) @Index({ keys: [ 'name', 'col1' ], type: IndexType.UNIQUE, storeType: IndexStoreType.BTREE, comment: 'index comment\n', }) @Index({ keys: [ 'col1' ], type: IndexType.FULLTEXT, comment: 'index comment\n', }) export class Foo { @Column({ type: ColumnType.INT, }, { primaryKey: true, autoIncrement: true, comment: 'the primary key', }) id: number; @Column({ type: ColumnType.VARCHAR, length: 100, }, { uniqueKey: true, }) name: string; @Column({ type: ColumnType.VARCHAR, length: 100, }, { name: 'col1', }) col1: string; @Column({ type: ColumnType.BIT, length: 10, }) bitColumn: Buffer; @Column({ type: ColumnType.BOOL, }) boolColumn: 0 | 1; @Column({ type: ColumnType.TINYINT, length: 5, unsigned: true, zeroFill: true, }) tinyIntColumn: number; @Column({ type: ColumnType.SMALLINT, length: 5, unsigned: true, zeroFill: true, }) smallIntColumn: number; @Column({ type: ColumnType.MEDIUMINT, length: 5, unsigned: true, zeroFill: true, }) mediumIntColumn: number; @Column({ type: ColumnType.INT, length: 5, unsigned: true, zeroFill: true, }) intColumn: number; @Column({ type: ColumnType.BIGINT, length: 5, unsigned: true, zeroFill: true, }) bigIntColumn: string; @Column({ type: ColumnType.DECIMAL, length: 10, fractionalLength: 5, unsigned: true, zeroFill: true, }) decimalColumn: string; @Column({ type: ColumnType.FLOAT, length: 10, fractionalLength: 5, unsigned: true, zeroFill: true, }) floatColumn: number; @Column({ type: ColumnType.DOUBLE, length: 10, fractionalLength: 5, unsigned: true, zeroFill: true, }) doubleColumn: number; @Column({ type: ColumnType.DATE, }) dateColumn: Date; @Column({ type: ColumnType.DATETIME, precision: 3, }) dateTimeColumn: Date; @Column({ type: ColumnType.TIMESTAMP, precision: 3, }) timestampColumn: Date; @Column({ type: ColumnType.TIME, precision: 3, }) timeColumn: string; @Column({ type: ColumnType.YEAR, }) yearColumn: number; @Column({ type: ColumnType.VARCHAR, length: 100, characterSet: 'utf8mb4', collate: 'utf8mb4_unicode_ci', }) varCharColumn: string; @Column({ type: ColumnType.BINARY, }) binaryColumn: Buffer; @Column({ type: ColumnType.VARBINARY, length: 100, }) varBinaryColumn: Buffer; @Column({ type: ColumnType.TINYBLOB, }) tinyBlobColumn: Buffer; @Column({ type: ColumnType.TINYTEXT, characterSet: 'utf8mb4', collate: 'utf8mb4_unicode_ci', }) tinyTextColumn: string; @Column({ type: ColumnType.BLOB, length: 100, }) blobColumn: Buffer; @Column({ type: ColumnType.TEXT, length: 100, characterSet: 'utf8mb4', collate: 'utf8mb4_unicode_ci', }) textColumn: string; @Column({ type: ColumnType.MEDIUMBLOB, }) mediumBlobColumn: Buffer; @Column({ type: ColumnType.LONGBLOB, }) longBlobColumn: Buffer; @Column({ type: ColumnType.MEDIUMTEXT, characterSet: 'utf8mb4', collate: 'utf8mb4_unicode_ci', }) mediumTextColumn: string; @Column({ type: ColumnType.LONGTEXT, characterSet: 'utf8mb4', collate: 'utf8mb4_unicode_ci', }) longTextColumn: string; @Column({ type: ColumnType.ENUM, enums: [ 'A', 'B' ], characterSet: 'utf8mb4', collate: 'utf8mb4_unicode_ci', }) enumColumn: string; @Column({ type: ColumnType.SET, enums: [ 'A', 'B' ], characterSet: 'utf8mb4', collate: 'utf8mb4_unicode_ci', }) setColumn: string; @Column({ type: ColumnType.GEOMETRY, }) geometryColumn: Geometry; @Column({ type: ColumnType.POINT, }) pointColumn: Point; @Column({ type: ColumnType.LINESTRING, }) lineStringColumn: Line; @Column({ type: ColumnType.POLYGON, }) polygonColumn: Polygon; @Column({ type: ColumnType.MULTIPOINT, }) multipointColumn: MultiPoint; @Column({ type: ColumnType.MULTILINESTRING, }) multiLineStringColumn: MultiLine; @Column({ type: ColumnType.MULTIPOLYGON, }) multiPolygonColumn: MultiPolygon; @Column({ type: ColumnType.GEOMETRYCOLLECTION, }) geometryCollectionColumn: GeometryCollection; @Column({ type: ColumnType.JSON, }) jsonColumn: object; static buildObj() { const foo = new Foo(); foo.name = 'name'; foo.col1 = 'col1'; foo.bitColumn = Buffer.from([ 0, 0 ]); foo.boolColumn = 0; foo.tinyIntColumn = 0; foo.smallIntColumn = 1; foo.mediumIntColumn = 3; foo.intColumn = 3; foo.bigIntColumn = '00099'; foo.decimalColumn = '00002.33333'; foo.floatColumn = 2.3; foo.doubleColumn = 2.3; foo.dateColumn = new Date('2020-03-15T16:00:00.000Z'); foo.dateTimeColumn = new Date('2024-03-16T01:26:58.677Z'); foo.timestampColumn = new Date('2024-03-16T01:26:58.677Z'); foo.timeColumn = '838:59:50.123'; foo.yearColumn = 2024; foo.varCharColumn = 'var_char'; foo.binaryColumn = Buffer.from('b'); foo.varBinaryColumn = Buffer.from('var_binary'); foo.tinyBlobColumn = Buffer.from('tiny_blob'); foo.tinyTextColumn = 'text'; foo.blobColumn = Buffer.from('blob'); foo.textColumn = 'text'; foo.mediumBlobColumn = Buffer.from('medium_blob'); foo.longBlobColumn = Buffer.from('long_blob'); foo.mediumTextColumn = 'medium_text'; foo.longTextColumn = 'long_text'; foo.enumColumn = 'A'; foo.setColumn = 'B'; foo.geometryColumn = { x: 10, y: 10 }; foo.pointColumn = { x: 10, y: 10 }; foo.lineStringColumn = [ { x: 15, y: 15 }, { x: 20, y: 20 }, ]; foo.polygonColumn = [ [ { x: 0, y: 0 }, { x: 10, y: 0 }, { x: 10, y: 10 }, { x: 0, y: 10 }, { x: 0, y: 0 }, ], [ { x: 5, y: 5 }, { x: 7, y: 5 }, { x: 7, y: 7 }, { x: 5, y: 7 }, { x: 5, y: 5 }, ], ]; foo.multipointColumn = [ { x: 0, y: 0 }, { x: 20, y: 20 }, { x: 60, y: 60 }, ]; foo.multiLineStringColumn = [ [ { x: 10, y: 10 }, { x: 20, y: 20 }, ], [ { x: 15, y: 15 }, { x: 30, y: 15 }, ], ]; foo.multiPolygonColumn = [ [ [ { x: 0, y: 0 }, { x: 10, y: 0 }, { x: 10, y: 10 }, { x: 0, y: 10 }, { x: 0, y: 0 }, ], ], [ [ { x: 5, y: 5 }, { x: 7, y: 5 }, { x: 7, y: 7 }, { x: 5, y: 7 }, { x: 5, y: 5 }, ], ], ]; foo.geometryCollectionColumn = [ { x: 10, y: 10 }, { x: 30, y: 30 }, [ { x: 15, y: 15 }, { x: 20, y: 20 }, ], ]; foo.jsonColumn = { hello: 'json', }; return foo; } } ================================================ FILE: plugin/dal/test/fixtures/apps/dal-app/modules/dal/FooService.ts ================================================ import { AccessLevel, Inject, SingletonProto } from '@eggjs/tegg'; import { Transactional } from '@eggjs/tegg/transaction'; import FooDAO from './dal/dao/FooDAO'; import { Foo } from './Foo'; import { MysqlDataSourceManager } from '../../../../../../lib/MysqlDataSourceManager'; @SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) export class FooService { @Inject() private readonly fooDAO: FooDAO; @Inject() private readonly mysqlDataSourceManager: MysqlDataSourceManager; @Transactional() async succeedTransaction() { const foo = Foo.buildObj(); foo.name = 'insert_succeed_transaction_1'; const foo2 = Foo.buildObj(); foo2.name = 'insert_succeed_transaction_2'; await this.fooDAO.insert(foo); await this.fooDAO.insert(foo2); } @Transactional() async failedTransaction() { const foo = Foo.buildObj(); foo.name = 'insert_failed_transaction_1'; const foo2 = Foo.buildObj(); foo2.name = 'insert_failed_transaction_2'; await this.fooDAO.insert(foo); await this.fooDAO.insert(foo2); throw new Error('mock error'); } foo() { return this.mysqlDataSourceManager; } } ================================================ FILE: plugin/dal/test/fixtures/apps/dal-app/modules/dal/dal/dao/FooDAO.ts ================================================ import { SingletonProto, AccessLevel } from '@eggjs/tegg'; import { BaseFooDAO } from './base/BaseFooDAO'; import { Foo } from '../../Foo'; /** * FooDAO 类 * @class FooDAO * @classdesc 在此扩展关于 Foo 数据的一切操作 * @augments BaseFooDAO */ @SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) export default class FooDAO extends BaseFooDAO { async findByName(name: string): Promise { return this.dataSource.execute('findByName', { name, }); } } ================================================ FILE: plugin/dal/test/fixtures/apps/dal-app/modules/dal/dal/dao/base/BaseFooDAO.ts ================================================ import fs from 'node:fs'; import path from 'node:path'; import type { InsertResult, UpdateResult, DeleteResult } from '@eggjs/dal-decorator'; import { Inject } from '@eggjs/tegg'; import { Dao } from '@eggjs/tegg/dal'; import { DataSource, DataSourceInjectName, DataSourceQualifier, ColumnTsType } from '@eggjs/dal-decorator'; import { Foo } from '../../../Foo'; import FooExtension from '../../extension/FooExtension'; import Structure from '../../structure/Foo.json'; const SQL = Symbol('Dao#sql'); type Optional = Omit < T, K > & Partial ; /** * 自动生成的 FooDAO 基类 * @class BaseFooDAO * @classdesc 该文件由 @eggjs/tegg 自动生成,请**不要**修改它! */ /* istanbul ignore next */ @Dao() export class BaseFooDAO { static clazzModel = Foo; static clazzExtension = FooExtension; static tableStature = Structure; static get tableSql() { if (!this[SQL]) { this[SQL] = fs.readFileSync(path.join(__dirname, '../../structure/Foo.sql'), 'utf8'); } return this[SQL]; } @Inject({ name: DataSourceInjectName, }) @DataSourceQualifier('dal.foo.Foo') protected readonly dataSource: DataSource; public async insert(raw: Optional): Promise { const data: Record = {}; let tmp; tmp = raw.id; if (tmp !== undefined) { data.$id = tmp; } tmp = raw.name; if (tmp !== undefined) { data.$name = tmp; } tmp = raw.col1; if (tmp !== undefined) { data.$col1 = tmp; } tmp = raw.bitColumn; if (tmp !== undefined) { data.$bitColumn = tmp; } tmp = raw.boolColumn; if (tmp !== undefined) { data.$boolColumn = tmp; } tmp = raw.tinyIntColumn; if (tmp !== undefined) { data.$tinyIntColumn = tmp; } tmp = raw.smallIntColumn; if (tmp !== undefined) { data.$smallIntColumn = tmp; } tmp = raw.mediumIntColumn; if (tmp !== undefined) { data.$mediumIntColumn = tmp; } tmp = raw.intColumn; if (tmp !== undefined) { data.$intColumn = tmp; } tmp = raw.bigIntColumn; if (tmp !== undefined) { data.$bigIntColumn = tmp; } tmp = raw.decimalColumn; if (tmp !== undefined) { data.$decimalColumn = tmp; } tmp = raw.floatColumn; if (tmp !== undefined) { data.$floatColumn = tmp; } tmp = raw.doubleColumn; if (tmp !== undefined) { data.$doubleColumn = tmp; } tmp = raw.dateColumn; if (tmp !== undefined) { data.$dateColumn = tmp; } tmp = raw.dateTimeColumn; if (tmp !== undefined) { data.$dateTimeColumn = tmp; } tmp = raw.timestampColumn; if (tmp !== undefined) { data.$timestampColumn = tmp; } tmp = raw.timeColumn; if (tmp !== undefined) { data.$timeColumn = tmp; } tmp = raw.yearColumn; if (tmp !== undefined) { data.$yearColumn = tmp; } tmp = raw.varCharColumn; if (tmp !== undefined) { data.$varCharColumn = tmp; } tmp = raw.binaryColumn; if (tmp !== undefined) { data.$binaryColumn = tmp; } tmp = raw.varBinaryColumn; if (tmp !== undefined) { data.$varBinaryColumn = tmp; } tmp = raw.tinyBlobColumn; if (tmp !== undefined) { data.$tinyBlobColumn = tmp; } tmp = raw.tinyTextColumn; if (tmp !== undefined) { data.$tinyTextColumn = tmp; } tmp = raw.blobColumn; if (tmp !== undefined) { data.$blobColumn = tmp; } tmp = raw.textColumn; if (tmp !== undefined) { data.$textColumn = tmp; } tmp = raw.mediumBlobColumn; if (tmp !== undefined) { data.$mediumBlobColumn = tmp; } tmp = raw.longBlobColumn; if (tmp !== undefined) { data.$longBlobColumn = tmp; } tmp = raw.mediumTextColumn; if (tmp !== undefined) { data.$mediumTextColumn = tmp; } tmp = raw.longTextColumn; if (tmp !== undefined) { data.$longTextColumn = tmp; } tmp = raw.enumColumn; if (tmp !== undefined) { data.$enumColumn = tmp; } tmp = raw.setColumn; if (tmp !== undefined) { data.$setColumn = tmp; } tmp = raw.geometryColumn; if (tmp !== undefined) { data.$geometryColumn = tmp; } tmp = raw.pointColumn; if (tmp !== undefined) { data.$pointColumn = tmp; } tmp = raw.lineStringColumn; if (tmp !== undefined) { data.$lineStringColumn = tmp; } tmp = raw.polygonColumn; if (tmp !== undefined) { data.$polygonColumn = tmp; } tmp = raw.multipointColumn; if (tmp !== undefined) { data.$multipointColumn = tmp; } tmp = raw.multiLineStringColumn; if (tmp !== undefined) { data.$multiLineStringColumn = tmp; } tmp = raw.multiPolygonColumn; if (tmp !== undefined) { data.$multiPolygonColumn = tmp; } tmp = raw.geometryCollectionColumn; if (tmp !== undefined) { data.$geometryCollectionColumn = tmp; } tmp = raw.jsonColumn; if (tmp !== undefined) { data.$jsonColumn = tmp; } return this.dataSource.executeRawScalar('insert', data); } public async update(id: ColumnTsType['INT'], data: Partial): Promise { const newData: Record = { primary: { id, }, }; let tmp; tmp = data.id; if (tmp !== undefined) { newData.$id = tmp; } tmp = data.name; if (tmp !== undefined) { newData.$name = tmp; } tmp = data.col1; if (tmp !== undefined) { newData.$col1 = tmp; } tmp = data.bitColumn; if (tmp !== undefined) { newData.$bitColumn = tmp; } tmp = data.boolColumn; if (tmp !== undefined) { newData.$boolColumn = tmp; } tmp = data.tinyIntColumn; if (tmp !== undefined) { newData.$tinyIntColumn = tmp; } tmp = data.smallIntColumn; if (tmp !== undefined) { newData.$smallIntColumn = tmp; } tmp = data.mediumIntColumn; if (tmp !== undefined) { newData.$mediumIntColumn = tmp; } tmp = data.intColumn; if (tmp !== undefined) { newData.$intColumn = tmp; } tmp = data.bigIntColumn; if (tmp !== undefined) { newData.$bigIntColumn = tmp; } tmp = data.decimalColumn; if (tmp !== undefined) { newData.$decimalColumn = tmp; } tmp = data.floatColumn; if (tmp !== undefined) { newData.$floatColumn = tmp; } tmp = data.doubleColumn; if (tmp !== undefined) { newData.$doubleColumn = tmp; } tmp = data.dateColumn; if (tmp !== undefined) { newData.$dateColumn = tmp; } tmp = data.dateTimeColumn; if (tmp !== undefined) { newData.$dateTimeColumn = tmp; } tmp = data.timestampColumn; if (tmp !== undefined) { newData.$timestampColumn = tmp; } tmp = data.timeColumn; if (tmp !== undefined) { newData.$timeColumn = tmp; } tmp = data.yearColumn; if (tmp !== undefined) { newData.$yearColumn = tmp; } tmp = data.varCharColumn; if (tmp !== undefined) { newData.$varCharColumn = tmp; } tmp = data.binaryColumn; if (tmp !== undefined) { newData.$binaryColumn = tmp; } tmp = data.varBinaryColumn; if (tmp !== undefined) { newData.$varBinaryColumn = tmp; } tmp = data.tinyBlobColumn; if (tmp !== undefined) { newData.$tinyBlobColumn = tmp; } tmp = data.tinyTextColumn; if (tmp !== undefined) { newData.$tinyTextColumn = tmp; } tmp = data.blobColumn; if (tmp !== undefined) { newData.$blobColumn = tmp; } tmp = data.textColumn; if (tmp !== undefined) { newData.$textColumn = tmp; } tmp = data.mediumBlobColumn; if (tmp !== undefined) { newData.$mediumBlobColumn = tmp; } tmp = data.longBlobColumn; if (tmp !== undefined) { newData.$longBlobColumn = tmp; } tmp = data.mediumTextColumn; if (tmp !== undefined) { newData.$mediumTextColumn = tmp; } tmp = data.longTextColumn; if (tmp !== undefined) { newData.$longTextColumn = tmp; } tmp = data.enumColumn; if (tmp !== undefined) { newData.$enumColumn = tmp; } tmp = data.setColumn; if (tmp !== undefined) { newData.$setColumn = tmp; } tmp = data.geometryColumn; if (tmp !== undefined) { newData.$geometryColumn = tmp; } tmp = data.pointColumn; if (tmp !== undefined) { newData.$pointColumn = tmp; } tmp = data.lineStringColumn; if (tmp !== undefined) { newData.$lineStringColumn = tmp; } tmp = data.polygonColumn; if (tmp !== undefined) { newData.$polygonColumn = tmp; } tmp = data.multipointColumn; if (tmp !== undefined) { newData.$multipointColumn = tmp; } tmp = data.multiLineStringColumn; if (tmp !== undefined) { newData.$multiLineStringColumn = tmp; } tmp = data.multiPolygonColumn; if (tmp !== undefined) { newData.$multiPolygonColumn = tmp; } tmp = data.geometryCollectionColumn; if (tmp !== undefined) { newData.$geometryCollectionColumn = tmp; } tmp = data.jsonColumn; if (tmp !== undefined) { newData.$jsonColumn = tmp; } return this.dataSource.executeRawScalar('update', newData); } public async delete(id: ColumnTsType['INT']): Promise { return this.dataSource.executeRawScalar('delete', { id, }); } public async del(id: ColumnTsType['INT']): Promise { return this.dataSource.executeRawScalar('delete', { id, }); } public async findByCol1($col1: ColumnTsType['VARCHAR']): Promise { return this.dataSource.execute('findByCol1', { $col1, }); } public async findOneByCol1($col1: ColumnTsType['VARCHAR']): Promise { return this.dataSource.executeScalar('findOneByCol1', { $col1, }); } public async findByUkNameCol1($name: ColumnTsType['VARCHAR'], $col1: ColumnTsType['VARCHAR']): Promise { return this.dataSource.execute('findByUkNameCol1', { $name, $col1, }); } public async findOneByUkNameCol1($name: ColumnTsType['VARCHAR'], $col1: ColumnTsType['VARCHAR']): Promise { return this.dataSource.executeScalar('findOneByUkNameCol1', { $name, $col1, }); } public async findById($id: ColumnTsType['INT']): Promise { return this.dataSource.executeScalar('findById', { $id, }); } public async findByPrimary($id: ColumnTsType['INT']): Promise { return this.dataSource.executeScalar('findById', { $id, }); } } ================================================ FILE: plugin/dal/test/fixtures/apps/dal-app/modules/dal/dal/extension/FooExtension.ts ================================================ import { SqlMap } from '@eggjs/tegg/dal'; /** * Define Custom SQLs * * import { SqlMap, SqlType } from '@eggjs/tegg/dal'; * * export default { * findByName: { * type: SqlType.SELECT, * sql: 'SELECT {{ allColumns }} from foo where name = {{ name }}' * }, * } */ export default { findByName: { type: 'SELECT', sql: 'SELECT {{ allColumns }} FROM egg_foo WHERE name = {{ name }}', }, } as Record; ================================================ FILE: plugin/dal/test/fixtures/apps/dal-app/modules/dal/dal/structure/Foo.json ================================================ { "name": "egg_foo", "dataSourceName": "default", "columns": [ { "columnName": "id", "propertyName": "id", "type": { "type": "INT" }, "canNull": false, "comment": "the primary key", "autoIncrement": true, "primaryKey": true }, { "columnName": "name", "propertyName": "name", "type": { "type": "VARCHAR", "length": 100 }, "canNull": true, "uniqueKey": true }, { "columnName": "col1", "propertyName": "col1", "type": { "type": "VARCHAR", "length": 100 }, "canNull": true }, { "columnName": "bit_column", "propertyName": "bitColumn", "type": { "type": "BIT", "length": 10 }, "canNull": true }, { "columnName": "bool_column", "propertyName": "boolColumn", "type": { "type": "BOOL" }, "canNull": true }, { "columnName": "tiny_int_column", "propertyName": "tinyIntColumn", "type": { "type": "TINYINT", "length": 5, "unsigned": true, "zeroFill": true }, "canNull": true }, { "columnName": "small_int_column", "propertyName": "smallIntColumn", "type": { "type": "SMALLINT", "length": 5, "unsigned": true, "zeroFill": true }, "canNull": true }, { "columnName": "medium_int_column", "propertyName": "mediumIntColumn", "type": { "type": "MEDIUMINT", "length": 5, "unsigned": true, "zeroFill": true }, "canNull": true }, { "columnName": "int_column", "propertyName": "intColumn", "type": { "type": "INT", "length": 5, "unsigned": true, "zeroFill": true }, "canNull": true }, { "columnName": "big_int_column", "propertyName": "bigIntColumn", "type": { "type": "BIGINT", "length": 5, "unsigned": true, "zeroFill": true }, "canNull": true }, { "columnName": "decimal_column", "propertyName": "decimalColumn", "type": { "type": "DECIMAL", "length": 10, "fractionalLength": 5, "unsigned": true, "zeroFill": true }, "canNull": true }, { "columnName": "float_column", "propertyName": "floatColumn", "type": { "type": "FLOAT", "length": 10, "fractionalLength": 5, "unsigned": true, "zeroFill": true }, "canNull": true }, { "columnName": "double_column", "propertyName": "doubleColumn", "type": { "type": "DOUBLE", "length": 10, "fractionalLength": 5, "unsigned": true, "zeroFill": true }, "canNull": true }, { "columnName": "date_column", "propertyName": "dateColumn", "type": { "type": "DATE" }, "canNull": true }, { "columnName": "date_time_column", "propertyName": "dateTimeColumn", "type": { "type": "DATETIME", "precision": 3 }, "canNull": true }, { "columnName": "timestamp_column", "propertyName": "timestampColumn", "type": { "type": "TIMESTAMP", "precision": 3 }, "canNull": true }, { "columnName": "time_column", "propertyName": "timeColumn", "type": { "type": "TIME", "precision": 3 }, "canNull": true }, { "columnName": "year_column", "propertyName": "yearColumn", "type": { "type": "YEAR" }, "canNull": true }, { "columnName": "var_char_column", "propertyName": "varCharColumn", "type": { "type": "VARCHAR", "length": 100, "characterSet": "utf8mb4", "collate": "utf8mb4_unicode_ci" }, "canNull": true }, { "columnName": "binary_column", "propertyName": "binaryColumn", "type": { "type": "BINARY" }, "canNull": true }, { "columnName": "var_binary_column", "propertyName": "varBinaryColumn", "type": { "type": "VARBINARY", "length": 100 }, "canNull": true }, { "columnName": "tiny_blob_column", "propertyName": "tinyBlobColumn", "type": { "type": "TINYBLOB" }, "canNull": true }, { "columnName": "tiny_text_column", "propertyName": "tinyTextColumn", "type": { "type": "TINYTEXT", "characterSet": "utf8mb4", "collate": "utf8mb4_unicode_ci" }, "canNull": true }, { "columnName": "blob_column", "propertyName": "blobColumn", "type": { "type": "BLOB", "length": 100 }, "canNull": true }, { "columnName": "text_column", "propertyName": "textColumn", "type": { "type": "TEXT", "length": 100, "characterSet": "utf8mb4", "collate": "utf8mb4_unicode_ci" }, "canNull": true }, { "columnName": "medium_blob_column", "propertyName": "mediumBlobColumn", "type": { "type": "MEDIUMBLOB" }, "canNull": true }, { "columnName": "long_blob_column", "propertyName": "longBlobColumn", "type": { "type": "LONGBLOB" }, "canNull": true }, { "columnName": "medium_text_column", "propertyName": "mediumTextColumn", "type": { "type": "MEDIUMTEXT", "characterSet": "utf8mb4", "collate": "utf8mb4_unicode_ci" }, "canNull": true }, { "columnName": "long_text_column", "propertyName": "longTextColumn", "type": { "type": "LONGTEXT", "characterSet": "utf8mb4", "collate": "utf8mb4_unicode_ci" }, "canNull": true }, { "columnName": "enum_column", "propertyName": "enumColumn", "type": { "type": "ENUM", "enums": [ "A", "B" ], "characterSet": "utf8mb4", "collate": "utf8mb4_unicode_ci" }, "canNull": true }, { "columnName": "set_column", "propertyName": "setColumn", "type": { "type": "SET", "enums": [ "A", "B" ], "characterSet": "utf8mb4", "collate": "utf8mb4_unicode_ci" }, "canNull": true }, { "columnName": "geometry_column", "propertyName": "geometryColumn", "type": { "type": "GEOMETRY" }, "canNull": true }, { "columnName": "point_column", "propertyName": "pointColumn", "type": { "type": "POINT" }, "canNull": true }, { "columnName": "line_string_column", "propertyName": "lineStringColumn", "type": { "type": "LINESTRING" }, "canNull": true }, { "columnName": "polygon_column", "propertyName": "polygonColumn", "type": { "type": "POLYGON" }, "canNull": true }, { "columnName": "multipoint_column", "propertyName": "multipointColumn", "type": { "type": "MULTIPOINT" }, "canNull": true }, { "columnName": "multi_line_string_column", "propertyName": "multiLineStringColumn", "type": { "type": "MULTILINESTRING" }, "canNull": true }, { "columnName": "multi_polygon_column", "propertyName": "multiPolygonColumn", "type": { "type": "MULTIPOLYGON" }, "canNull": true }, { "columnName": "geometry_collection_column", "propertyName": "geometryCollectionColumn", "type": { "type": "GEOMETRYCOLLECTION" }, "canNull": true }, { "columnName": "json_column", "propertyName": "jsonColumn", "type": { "type": "JSON" }, "canNull": true } ], "indices": [ { "name": "idx_col1", "keys": [ { "propertyName": "col1", "columnName": "col1" } ], "type": "FULLTEXT", "comment": "index comment\n" }, { "name": "uk_name_col1", "keys": [ { "propertyName": "name", "columnName": "name" }, { "propertyName": "col1", "columnName": "col1" } ], "type": "UNIQUE", "storeType": "BTREE", "comment": "index comment\n" } ], "comment": "foo table", "characterSet": "utf8mb4", "collate": "utf8mb4_unicode_ci" } ================================================ FILE: plugin/dal/test/fixtures/apps/dal-app/modules/dal/dal/structure/Foo.sql ================================================ CREATE TABLE IF NOT EXISTS egg_foo ( id INT NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT 'the primary key', name VARCHAR(100) NULL UNIQUE KEY, col1 VARCHAR(100) NULL, bit_column BIT(10) NULL, bool_column BOOL NULL, tiny_int_column TINYINT(5) UNSIGNED ZEROFILL NULL, small_int_column SMALLINT(5) UNSIGNED ZEROFILL NULL, medium_int_column MEDIUMINT(5) UNSIGNED ZEROFILL NULL, int_column INT(5) UNSIGNED ZEROFILL NULL, big_int_column BIGINT(5) UNSIGNED ZEROFILL NULL, decimal_column DECIMAL(10,5) UNSIGNED ZEROFILL NULL, float_column FLOAT(10,5) UNSIGNED ZEROFILL NULL, double_column DOUBLE(10,5) UNSIGNED ZEROFILL NULL, date_column DATE NULL, date_time_column DATETIME(3) NULL, timestamp_column TIMESTAMP(3) NULL, time_column TIME(3) NULL, year_column YEAR NULL, var_char_column VARCHAR(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL, binary_column BINARY NULL, var_binary_column VARBINARY(100) NULL, tiny_blob_column TINYBLOB NULL, tiny_text_column TINYTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL, blob_column BLOB(100) NULL, text_column TEXT(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL, medium_blob_column MEDIUMBLOB NULL, long_blob_column LONGBLOB NULL, medium_text_column MEDIUMTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL, long_text_column LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL, enum_column ENUM('A','B') CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL, set_column SET('A','B') CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL, geometry_column GEOMETRY NULL, point_column POINT NULL, line_string_column LINESTRING NULL, polygon_column POLYGON NULL, multipoint_column MULTIPOINT NULL, multi_line_string_column MULTILINESTRING NULL, multi_polygon_column MULTIPOLYGON NULL, geometry_collection_column GEOMETRYCOLLECTION NULL, json_column JSON NULL, FULLTEXT KEY idx_col1 (col1) COMMENT 'index comment\n', UNIQUE KEY uk_name_col1 (name,col1) USING BTREE COMMENT 'index comment\n' ) DEFAULT CHARACTER SET utf8mb4, DEFAULT COLLATE utf8mb4_unicode_ci, COMMENT='foo table'; ================================================ FILE: plugin/dal/test/fixtures/apps/dal-app/modules/dal/module.yml ================================================ dataSource: foo: connectionLimit: 100 database: 'test_dal_plugin' host: '127.0.0.1' user: root port: 3306 timezone: '+08:00' forkDb: true ================================================ FILE: plugin/dal/test/fixtures/apps/dal-app/modules/dal/package.json ================================================ { "name": "dal", "eggModule": { "name": "dal" } } ================================================ FILE: plugin/dal/test/fixtures/apps/dal-app/package.json ================================================ { "name": "dal-app" } ================================================ FILE: plugin/dal/test/transaction.test.ts ================================================ import assert from 'assert'; import path from 'path'; import mm, { MockApplication } from 'egg-mock'; import FooDAO from './fixtures/apps/dal-app/modules/dal/dal/dao/FooDAO'; import { FooService } from './fixtures/apps/dal-app/modules/dal/FooService'; import { MysqlDataSourceManager } from '../lib/MysqlDataSourceManager'; describe('plugin/dal/test/transaction.test.ts', () => { let app: MockApplication; afterEach(async () => { mm.restore(); }); before(async () => { mm(process.env, 'EGG_TYPESCRIPT', true); mm(process, 'cwd', () => { return path.join(__dirname, '../'); }); app = mm.app({ baseDir: path.join(__dirname, './fixtures/apps/dal-app'), framework: require.resolve('egg'), }); await app.ready(); }); afterEach(async () => { const dataSource = MysqlDataSourceManager.instance.get('dal', 'foo')!; await dataSource.query('delete from egg_foo;'); }); after(() => { return app.close(); }); describe('succeed transaction', () => { it('should commit', async () => { await app.mockModuleContextScope(async () => { const fooService = await app.getEggObject(FooService); const fooDao = await app.getEggObject(FooDAO); await fooService.succeedTransaction(); const foo1 = await fooDao.findByName('insert_succeed_transaction_1'); const foo2 = await fooDao.findByName('insert_succeed_transaction_2'); assert(foo1.length); assert(foo2.length); }); }); }); describe('failed transaction', () => { it('should rollback', async () => { await app.mockModuleContextScope(async () => { const fooService = await app.getEggObject(FooService); const fooDao = await app.getEggObject(FooDAO); await assert.rejects(async () => { await fooService.failedTransaction(); }); const foo1 = await fooDao.findByName('insert_failed_transaction_1'); const foo2 = await fooDao.findByName('insert_failed_transaction_2'); assert(!foo1.length); assert(!foo2.length); }); }); }); describe('transaction should be isolated', () => { it('should rollback', async () => { await app.mockModuleContextScope(async () => { const fooService = await app.getEggObject(FooService); const fooDao = await app.getEggObject(FooDAO); const [ failedRes, succeedRes ] = await Promise.allSettled([ fooService.failedTransaction(), fooService.succeedTransaction(), ]); assert.equal(failedRes.status, 'rejected'); assert.equal(succeedRes.status, 'fulfilled'); const foo1 = await fooDao.findByName('insert_failed_transaction_1'); const foo2 = await fooDao.findByName('insert_failed_transaction_2'); assert(!foo1.length); assert(!foo2.length); const foo3 = await fooDao.findByName('insert_succeed_transaction_1'); const foo4 = await fooDao.findByName('insert_succeed_transaction_2'); assert(foo3.length); assert(foo4.length); }); }); }); }); ================================================ FILE: plugin/dal/tsconfig.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "baseUrl": "./" }, "exclude": [ "node_modules" ] } ================================================ FILE: plugin/dal/tsconfig.pub.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "baseUrl": "./" }, "exclude": [ "node_modules", "test" ] } ================================================ FILE: plugin/dal/typings/index.d.ts ================================================ import 'egg'; import '@eggjs/tegg-plugin'; import '@eggjs/tegg-config'; ================================================ FILE: plugin/dns-cache/CHANGELOG.md ================================================ # Change Log All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. ## [3.78.15](https://github.com/eggjs/tegg/compare/v3.78.14...v3.78.15) (2026-04-23) **Note:** Version bump only for package @eggjs/tegg-dns-cache ## [3.78.14](https://github.com/eggjs/tegg/compare/v3.78.13...v3.78.14) (2026-04-21) **Note:** Version bump only for package @eggjs/tegg-dns-cache ## [3.78.13](https://github.com/eggjs/tegg/compare/v3.78.12...v3.78.13) (2026-04-21) **Note:** Version bump only for package @eggjs/tegg-dns-cache ## [3.78.12](https://github.com/eggjs/tegg/compare/v3.78.11...v3.78.12) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-dns-cache ## [3.78.11](https://github.com/eggjs/tegg/compare/v3.78.10...v3.78.11) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-dns-cache ## [3.78.10](https://github.com/eggjs/tegg/compare/v3.78.9...v3.78.10) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-dns-cache ## [3.78.9](https://github.com/eggjs/tegg/compare/v3.78.8...v3.78.9) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-dns-cache ## [3.78.8](https://github.com/eggjs/tegg/compare/v3.78.7...v3.78.8) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-dns-cache ## [3.78.7](https://github.com/eggjs/tegg/compare/v3.78.6...v3.78.7) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-dns-cache ## [3.78.6](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.6) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-dns-cache ## [3.78.5](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.5) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-dns-cache ## [3.78.4](https://github.com/eggjs/tegg/compare/v3.78.3...v3.78.4) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-dns-cache ## [3.78.3](https://github.com/eggjs/tegg/compare/v3.78.2...v3.78.3) (2026-04-15) **Note:** Version bump only for package @eggjs/tegg-dns-cache ## [3.78.2](https://github.com/eggjs/tegg/compare/v3.78.1...v3.78.2) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-dns-cache ## [3.78.1](https://github.com/eggjs/tegg/compare/v3.78.0...v3.78.1) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-dns-cache # [3.78.0](https://github.com/eggjs/tegg/compare/v3.77.2...v3.78.0) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-dns-cache ## [3.77.2](https://github.com/eggjs/tegg/compare/v3.77.1...v3.77.2) (2026-04-07) **Note:** Version bump only for package @eggjs/tegg-dns-cache ## [3.77.1](https://github.com/eggjs/tegg/compare/v3.77.0...v3.77.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-dns-cache # [3.77.0](https://github.com/eggjs/tegg/compare/v3.76.1...v3.77.0) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-dns-cache ## [3.76.1](https://github.com/eggjs/tegg/compare/v3.76.0...v3.76.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-dns-cache # [3.76.0](https://github.com/eggjs/tegg/compare/v3.75.1...v3.76.0) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-dns-cache ## [3.75.1](https://github.com/eggjs/tegg/compare/v3.75.0...v3.75.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-dns-cache # [3.75.0](https://github.com/eggjs/tegg/compare/v3.74.0...v3.75.0) (2026-03-30) **Note:** Version bump only for package @eggjs/tegg-dns-cache # [3.74.0](https://github.com/eggjs/tegg/compare/v3.73.0...v3.74.0) (2026-03-30) **Note:** Version bump only for package @eggjs/tegg-dns-cache # [3.73.0](https://github.com/eggjs/tegg/compare/v3.72.0...v3.73.0) (2026-03-25) **Note:** Version bump only for package @eggjs/tegg-dns-cache # [3.72.0](https://github.com/eggjs/tegg/compare/v3.71.2...v3.72.0) (2026-02-04) **Note:** Version bump only for package @eggjs/tegg-dns-cache ## [3.71.2](https://github.com/eggjs/tegg/compare/v3.71.1...v3.71.2) (2026-01-30) **Note:** Version bump only for package @eggjs/tegg-dns-cache ## [3.71.1](https://github.com/eggjs/tegg/compare/v3.71.0...v3.71.1) (2026-01-28) **Note:** Version bump only for package @eggjs/tegg-dns-cache # [3.71.0](https://github.com/eggjs/tegg/compare/v3.70.1...v3.71.0) (2026-01-26) **Note:** Version bump only for package @eggjs/tegg-dns-cache ## [3.70.1](https://github.com/eggjs/tegg/compare/v3.70.0...v3.70.1) (2026-01-14) **Note:** Version bump only for package @eggjs/tegg-dns-cache # [3.70.0](https://github.com/eggjs/tegg/compare/v3.69.0...v3.70.0) (2026-01-07) **Note:** Version bump only for package @eggjs/tegg-dns-cache # [3.69.0](https://github.com/eggjs/tegg/compare/v3.68.0...v3.69.0) (2026-01-06) ### Features * dns cache logger ([#388](https://github.com/eggjs/tegg/issues/388)) ([e9c1180](https://github.com/eggjs/tegg/commit/e9c1180424df18c48c73615133bbf6ed2f930e7a)) # [3.68.0](https://github.com/eggjs/tegg/compare/v3.67.2...v3.68.0) (2025-12-29) ### Features * impl dns cache plugin ([#385](https://github.com/eggjs/tegg/issues/385)) ([d319448](https://github.com/eggjs/tegg/commit/d31944827c02cb967f4335b0ac66eeea4e10251c)) ## 3.67.0 (2025-12-19) ### Features * Initial release of @eggjs/tegg-dns-cache ================================================ FILE: plugin/dns-cache/README.md ================================================ # @eggjs/tegg-dns-cache DNS cache plugin for tegg framework. This plugin provides DNS caching capabilities to improve performance and reduce DNS lookup time. ## Features - 🚀 DNS lookup caching with LRU algorithm - ⚖️ Round-robin address rotation for load balancing - 🔄 Support both `dns.lookup` and `dns.resolve` modes - ⏱️ Configurable cache TTL - 🔌 Automatic integration with egg httpclient - 🎯 Custom DNS nameservers support ## Installation ```bash npm install @eggjs/tegg-dns-cache --save ``` ## Usage ### Enable Plugin ```js // config/plugin.js exports.dnsCache = { enable: true, package: '@eggjs/tegg-dns-cache', }; ``` ### Configuration ```js // config/config.default.js exports.dnsCache = { // DNS resolution mode: 'lookup' or 'resolve' (default: 'resolve') // - lookup: Use dns.lookup, respects /etc/hosts, but no TTL support // - resolve: Use dns.resolve, queries DNS directly, TTL supported mode: 'resolve', // Custom DNS nameservers (only for 'resolve' mode) dnsServers: ['8.8.8.8', '1.1.1.1'], // Maximum cache entries (default: 1000) maxCacheLength: 1000, // Cache lookup interval in ms (only for 'lookup' mode, default: 10000) lookupInterval: 10000, // Enable address rotation for multiple IPs (default: true) addressRotation: true, }; ``` ## DNS Resolution Modes ### Resolve Mode (Recommended) Uses `dns.resolve4()` to query DNS servers directly. Supports TTL from DNS records. **Advantages:** - Respects DNS TTL from authoritative servers - More control over DNS resolution - Can specify custom nameservers **Note:** Does not respect `/etc/hosts` file. ```js exports.dnsCache = { mode: 'resolve', dnsServers: ['8.8.8.8', '1.1.1.1'], // Optional custom DNS servers }; ``` ### Lookup Mode Uses `dns.lookup()` which respects system DNS configuration and `/etc/hosts`. **Advantages:** - Respects `/etc/hosts` entries - Uses system DNS configuration **Disadvantages:** - Fixed cache interval (no real TTL) - Less control over resolution ```js exports.dnsCache = { mode: 'lookup', lookupInterval: 10000, // Cache refresh interval in ms }; ``` ## API ### Access DNS Resolver ```js // In controller or service const resolver = this.app.dnsResolver; // Get cached DNS record const record = resolver.getCacheRecord('example.com'); console.log(record); // { ip: '93.184.216.34', family: 4, ttl: 60000, ... } // Clear DNS cache resolver.resetCache(); // Get the underlying LRU cache const cache = resolver.getDnsCache(); ``` ## Address Rotation When a hostname resolves to multiple IP addresses, the plugin can automatically rotate through them for load balancing: ```js exports.dnsCache = { addressRotation: true, // Enable round-robin rotation }; ``` Each request to the same hostname will use the next IP address in rotation. ## Integration with HttpClient The plugin automatically integrates with egg's built-in httpclient. All HTTP requests will benefit from DNS caching: ```js // This request will use cached DNS await this.app.httpclient.request('https://example.com/api'); ``` ## Performance Benefits - **Reduced DNS lookup time**: Cached DNS results eliminate network round trips - **Lower DNS server load**: Fewer queries to DNS servers - **Improved request throughput**: Faster connection establishment - **Load distribution**: Address rotation spreads load across multiple IPs ## License MIT ================================================ FILE: plugin/dns-cache/app.ts ================================================ import { Application } from 'egg'; import { DnsResolver } from './lib/DnsResolver'; export default class DnsCacheAppHook { private readonly app: Application; private dnsResolver: DnsResolver; constructor(app: Application) { this.app = app; } configWillLoad() { if (!this.app.config.dnsCache) { this.app.logger.warn( '[tegg-dns-cache-plugin] DNS cache is disabled, please setup dnsCache config.', ); } } async configDidLoad() { const config = this.app.config.dnsCache || {}; // Create DNS resolver instance const useDNSResolver = config.mode !== 'lookup'; const dnsCacheLogger = this.app.coreLogger; this.dnsResolver = new DnsResolver( { useResolver: useDNSResolver, servers: config.dnsServers, max: config.maxCacheLength || 1000, dnsCacheLookupInterval: config.lookupInterval || 10000, addressRotation: config.addressRotation !== false, }, { logger: dnsCacheLogger }, ); const lookupFunction = this.dnsResolver.getLookupFunction(); this.app.config.httpclient = this.app.config.httpclient || {}; this.app.config.httpclient.lookup = lookupFunction; // Add dnsResolver to app this.app.dnsResolver = this.dnsResolver; } async didLoad() { await this.app.moduleHandler.ready(); } beforeClose() { // Cleanup DNS cache resources if (this.app.dnsResolver) { this.app.dnsResolver.resetCache(); } } } ================================================ FILE: plugin/dns-cache/config/config.default.ts ================================================ export default () => { const config = { /** * DNS Cache Configuration * * The DNS cache plugin provides DNS caching and resolution capabilities to improve performance * by caching DNS lookups and supports both dns.lookup and dns.resolve modes with address rotation. * * @property {'lookup' | 'resolve'} mode - Use dns.lookup or dns.resolve, default is 'resolve'. * - lookup: Use dns.lookup mode (old behavior), respects system DNS configuration and /etc/hosts, but does not support ttl. * - resolve: Use dns.resolve mode (new feature), queries DNS servers directly, ttl supported. * Note: When using resolve mode, /etc/hosts is not respected. You may need to implement custom logic * if you want to include /etc/hosts resolution. * @property {Number} maxCacheLength - Maximum number of DNS cache entries, default is 1000. * Uses LRU (Least Recently Used) algorithm to evict old entries when cache is full. * @property {Number} lookupInterval - Only works when mode is 'lookup'. DNS cache lookup interval in milliseconds, default is 10000 (10 seconds). * @property {Boolean} addressRotation - Enable round-robin address rotation when multiple IP addresses * are returned for a hostname, default is true. Helps distribute load across multiple servers. * @property {Array} dnsServers - Custom DNS nameservers for dns.resolve mode, e.g. ['8.8.8.8', '1.1.1.1']. * Only effective when mode is 'resolve'. If not set, uses system default DNS servers. */ dnsCache: { mode: 'resolve' as 'lookup' | 'resolve', maxCacheLength: 1000, lookupInterval: 10000, addressRotation: true, dnsServers: undefined as string[] | undefined, }, }; return config; }; ================================================ FILE: plugin/dns-cache/lib/DnsResolver.ts ================================================ import * as util from 'node:util'; import * as dns from 'node:dns'; import { LRU } from 'ylru'; import { EggLogger } from 'egg'; import { LookupAddress, LookupOneOptions } from 'node:dns'; const IP_REGEX = /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/; export interface DnsResolverOptions { useResolver?: boolean; max?: number; dnsCacheLookupInterval?: number; servers?: string[]; addressRotation?: boolean; } export interface DnsCacheRecord { ip: string; family: number; ttl: number; timestamp: number; index: number; } interface CacheEntry { records: DnsCacheRecord[]; currentIndex: number; } export class DnsResolver { private _maxCacheSize: number; private _dnsCache: LRU; private useResolver: boolean; private dnsCacheLookupInterval: number; private enableAddressRotation: boolean; private resolver?: dns.Resolver; private _resolve4?: typeof dns.resolve4.__promisify__; private _lookup?: typeof dns.lookup.__promisify__; private logger: EggLogger; /** * Create a DNS cache resolver instance * @param options - Configuration options * @param options.useResolver - enable dns.resolver, otherwise use dns.lookup by default * @param options.max - Maximum cache size, default is 1000 * @param options.dnsCacheLookupInterval - DNS cache lookup interval in milliseconds, effective when useResolver == false, default is 10000 * @param options.servers - Custom DNS nameservers, effective when useResolver == true, e.g. ['8.8.8.8', '1.1.1.1'] * @param options.addressRotation - Enable address rotation for both lookup and resolve modes, default is true */ constructor(options: DnsResolverOptions = {}, args: { logger: EggLogger }) { this._maxCacheSize = options.max || 1000; this._dnsCache = new LRU(this._maxCacheSize); this.logger = args.logger; // Set useResolver before using it this.useResolver = options.useResolver === true; this.dnsCacheLookupInterval = options.dnsCacheLookupInterval || 10000; // Address rotation is enabled by default this.enableAddressRotation = options.addressRotation !== false; if (this.useResolver) { this._initializeResolver(options.servers); } else { // Use dns.lookup mode (old behavior) this._lookup = util.promisify(dns.lookup); } this.resetCache = this.resetCache.bind(this); this._debugLog( `[dns-cache] DNS Resolver initialized in ${ this.useResolver ? 'resolve' : 'lookup' } mode, maxCacheSize: ${this._maxCacheSize}, addressRotation: ${ this.enableAddressRotation }`, ); } get maxCacheSize() { return this._maxCacheSize; } private _debugLog(msg: any, ...args: any[]) { this.logger.debug.apply(this.logger, [ msg, ...args ]); } resetCacheSize(size: number) { this._maxCacheSize = size; this.resetCache(true); } /** * Initialize DNS resolver with custom nameservers if provided * @param defaultServers - Custom DNS nameservers * @private */ private _initializeResolver(defaultServers?: string[]) { this.resolver = new dns.Resolver({ timeout: 3000, tries: 2, }); const hasDefaultServers = defaultServers && Array.isArray(defaultServers) && defaultServers.length > 0; if (hasDefaultServers) { this.resolver.setServers(defaultServers); this._debugLog( `[dns-cache] Custom DNS servers configured: ${defaultServers.join( ', ', )}`, ); } this._resolve4 = util.promisify(this.resolver.resolve4.bind(this.resolver)); } /** * Get the lookup function compatible with dns.lookup signature */ getLookupFunction(): typeof dns.lookup { return (( hostname: string, options: number | LookupOneOptions, callback: ( err: NodeJS.ErrnoException | null, address: string | LookupAddress[], family?: number ) => void, ) => { // signature handling: lookup(hostname, cb) or lookup(hostname, options, cb) if (typeof options === 'function') { callback = options; options = {}; } if (typeof options === 'number') { options = { family: options }; } if (typeof callback !== 'function') { throw new TypeError('callback must be a function'); } options = options || {}; if (!options.family) { options.family = 4; } // keep original dns.lookup behavior for literal IPs without hitting the network if (IP_REGEX.test(hostname)) { // theoretically this code will never be reached because urllib will // directly return for literal IPs before calling lookup function const family = typeof options.family === 'number' ? options.family : 4; this.logger.debug( `[dns-cache] literal IP ${hostname} lookup, bypassing cache`, ); if (options?.all) { return callback(null, [{ address: hostname, family }]); } return callback(null, hostname, family); } const record = this._dnsCache.get(hostname) as CacheEntry | undefined; const now = Date.now(); if (record) { // Check TTL - use the first record's TTL and timestamp const firstRecord = record.records[0]; const ttl = firstRecord.ttl || 0; const timestamp = firstRecord.timestamp || now; if (now - timestamp >= ttl) { // refresh in background, keep serving cached value this._debugLog( `[dns-cache] Cache TTL expired for ${hostname}, refreshing in background. Age: ${ now - timestamp }ms, TTL: ${ttl}ms`, ); this._updateDNS(hostname).catch(() => { // do nothing, error already logged in _updateDNS }); } else { this._debugLog( `[dns-cache] Cache hit for ${hostname}, remaining TTL: ${ ttl - (now - timestamp) }ms, records: ${record.records.length}`, ); } return this._callbackWithRecord(record, options, callback); } // No cached record, resolve and respond when ready this._debugLog(`[dns-cache] Cache miss for ${hostname}, resolving...`); this._updateDNS(hostname) .then(record => { this._callbackWithRecord(record, options, callback); }) .catch(err => { callback(err, ''); }); }) as typeof dns.lookup; } /** * This method may throw error if there is dns query in progress! * @throws Error * @param {string[]} servers eg. ['8.8.8.8'] */ setServers(servers: string[]) { if (this.resolver) { this.resolver.setServers(servers); } else { this.logger.warn( '[dns-cache] Cannot set DNS servers when useResolver is false', ); } } getDnsCache(): LRU { return this._dnsCache; } /** * Callback with record, handling rotation * @param record - DNS record with rotation state * @param options - Lookup options * @param callback - Callback function * @private */ private _callbackWithRecord( record: CacheEntry, options: LookupOneOptions, callback: Function, ) { // All records use the unified structure with rotation if (record.records && Array.isArray(record.records)) { const records = record.records; const currentRecord = records[record.currentIndex % records.length]; if (records.length > 1) { this._debugLog( `[dns-cache] Address rotation: using ${currentRecord.ip} (index ${ record.currentIndex % records.length }/${records.length})`, ); } // Rotate to next address for next call (if enabled) if (this.enableAddressRotation) { record.currentIndex = (record.currentIndex + 1) % records.length; } if (options.all) { return callback(null, [ { address: currentRecord.ip, family: currentRecord.family || 4 }, ]); } return callback(null, currentRecord.ip, currentRecord.family || 4); } // Should not reach here, all records should use records structure throw new Error('[dns_cache_error]: Invalid cache record structure'); } async lookup(hostname: string): Promise { // handle localhost (some name servers may not resolve it) if (hostname === 'localhost') { this.logger.debug('[dns-cache] localhost lookup, bypassing cache'); return [{ address: '127.0.0.1', family: 4 }]; } if (!this._lookup) { throw new Error('DNS Resolver not initialized for lookup mode'); } // Use { all: true } to get all addresses for rotation support const addresses = await this._lookup(hostname, { family: 4, all: true, }); return addresses; } async resolve4(hostname: string): Promise { // handle localhost (some name servers may not resolve it) if (hostname === 'localhost') { this.logger.debug('[dns-cache] localhost resolve, bypassing cache'); return [ { address: '127.0.0.1', ttl: Math.floor(Number.MAX_SAFE_INTEGER / 1000), }, ]; // provide a default TTL } if (!this._resolve4) { throw new Error('DNS Resolver not initialized for resolve mode'); } const addresses = await this._resolve4(hostname, { ttl: true, }); return addresses; } /** * Update DNS cache with fresh resolution * Supports both dns.lookup and dns.resolve modes * @param hostname - The hostname to resolve * @private */ private async _updateDNS(hostname: string): Promise { // Use dns.lookup if (!this.useResolver) { try { const addresses = await this.lookup(hostname); const addressArray = Array.isArray(addresses) ? addresses : [ addresses ]; if (addressArray.length === 0) { throw new Error(`empty address for ${hostname}`); } const records: DnsCacheRecord[] = addressArray.map((addr, index) => ({ ip: addr.address, family: addr.family || 4, ttl: this.dnsCacheLookupInterval, timestamp: Date.now(), index, })); const cacheEntry: CacheEntry = { records, currentIndex: 0, }; this._dnsCache.set(hostname, cacheEntry); this.logger.info( `[dns-cache] dns.lookup succeeded for ${hostname}, resolved ${ records.length } address(es): ${records.map(r => r.ip).join(', ')}, TTL: ${ this.dnsCacheLookupInterval }ms`, ); return cacheEntry; } catch (err) { this._errorDNS(err, 'lookup', hostname); throw err; } } // Use dns.resolve try { const addresses = await this.resolve4(hostname); const addressArray = Array.isArray(addresses) ? addresses : [ addresses ]; // Store all addresses with rotation index const records: DnsCacheRecord[] = addressArray.map((addr, index) => { const address = typeof addr === 'string' ? addr : addr.address; const ttlSeconds = addr && Number.isInteger(addr.ttl) && addr.ttl >= 0 ? addr.ttl : 0; if (ttlSeconds === 0) { this.logger.warn( `[dns-cache] Warning: TTL is 0 for ${hostname} address ${address}`, ); } return { ip: address, family: 4, ttl: ttlSeconds * 1000, timestamp: Date.now(), index, }; }); if (records.length === 0 || !records[0].ip) { throw new Error(`empty address for ${hostname}`); } // Store all records with rotation state const cacheEntry: CacheEntry = { records, currentIndex: 0, }; this._dnsCache.set(hostname, cacheEntry); this.logger.info( `[dns-cache] dns.resolve4 succeeded for ${hostname}, resolved ${ records.length } address(es): ${records .map(r => `${r.ip} (TTL: ${r.ttl}ms)`) .join(', ')}`, ); return cacheEntry; } catch (err) { this._errorDNS(err, 'resolve', hostname); throw err; } } /** * Debug DNS errors * @param err - Error object * @param mode - 'lookup' or 'resolve' * @private */ private _errorDNS(err: any, mode: 'lookup' | 'resolve', hostname: string) { this.logger.error( `[dns-cache] error occurred when resolving ${hostname} with dns.${mode}: ${ err && err.message ? err.message : err }`, ); } /** * Clear the DNS cache * @param recreate - Whether to recreate the cache instance, default is false. * If true, creates a new LRU instance even if cache already exists. */ resetCache(recreate = false) { this._debugLog(`[dns-cache] Resetting DNS cache (recreate: ${recreate})`); if (this._dnsCache) this._dnsCache.reset(); if (recreate) { this._dnsCache = new LRU(this._maxCacheSize); } } /** * Get a specific hostname's single record from cache * @param hostname - Hostname to query * @return {DnsCacheRecord | null} cache record */ getCacheRecord(hostname: string): DnsCacheRecord | null { const entry = this._dnsCache.get(hostname) as CacheEntry | undefined; if (entry && entry.records && Array.isArray(entry.records)) { return entry.records[entry.currentIndex % entry.records.length]; } return null; } } ================================================ FILE: plugin/dns-cache/lib/index.ts ================================================ export * from './DnsResolver'; ================================================ FILE: plugin/dns-cache/package.json ================================================ { "name": "@eggjs/tegg-dns-cache", "eggPlugin": { "name": "dnsCache", "dependencies": [ "tegg" ] }, "version": "3.78.15", "types": "typings/index.d.ts", "description": "tegg dns cache plugin", "keywords": [ "egg", "typescript", "dns", "cache", "tegg" ], "files": [ "app.js", "app.d.ts", "agent.js", "agent.d.ts", "index.js", "index.d.ts", "types.js", "types.d.ts", "config/**/*.js", "config/**/*.d.ts", "lib/**/*.js", "lib/**/*.d.ts", "app/**/*.js", "app/**/*.d.ts", "typings/*.d.ts" ], "eggModule": { "name": "teggDnsCache" }, "scripts": { "test": "cross-env NODE_ENV=test NODE_OPTIONS='--no-deprecation' mocha", "clean": "tsc -b --clean", "tsc": "ut run clean && tsc -p ./tsconfig.json", "tsc:pub": "ut run clean && tsc -p ./tsconfig.pub.json", "prepublishOnly": "ut tsc:pub" }, "homepage": "https://github.com/eggjs/tegg", "bugs": { "url": "https://github.com/eggjs/tegg/issues" }, "repository": { "type": "git", "url": "git@github.com:eggjs/tegg.git", "directory": "plugin/dns-cache" }, "egg": { "typescript": true }, "engines": { "node": ">=18.0.0" }, "peerDependencies": { "egg": ">=3.32.0" }, "dependencies": { "@eggjs/tegg": "^3.78.15", "ylru": "^2.0.0" }, "devDependencies": { "@eggjs/tegg-config": "^3.78.15", "@eggjs/tegg-plugin": "^3.78.15", "@types/mocha": "^10.0.10", "@types/node": "^20.2.4", "cross-env": "^7.0.3", "egg": "^3.32.0", "egg-mock": "^5.5.0", "mocha": "^10.2.0", "ts-node": "^10.9.1", "typescript": "^5.0.4" }, "publishConfig": { "access": "public" } } ================================================ FILE: plugin/dns-cache/test/dns_cache_lookup.test.ts ================================================ import mm, { type MockApplication } from 'egg-mock'; import assert from 'assert'; import { promises as dns } from 'dns'; import * as utils from './utils'; import { parse as urlParse } from 'url'; import path from 'path'; const mockLookupPromise = (app: any) => { mm(app.dnsResolver, 'lookup', async () => { return [{ address: '127.0.0.1', family: 4 }]; }); }; describe('test/dns_cache_lookup.test.ts', () => { let app: MockApplication; let url: string; let host: string; let originalDNSServers: string[]; before(async () => { mm(process.env, 'EGG_TYPESCRIPT', true); mm(process, 'cwd', () => { return path.join(__dirname, '..'); }); app = mm.app({ baseDir: path.join(__dirname, './fixtures/apps/dns_cache_lookup'), framework: require.resolve('egg'), }); await app.ready(); url = await utils.startLocalServer(); url = url.replace('127.0.0.1', 'localhost'); host = new URL(url).host!; originalDNSServers = dns.getServers(); if (!process.env.CI) { try { dns.setServers([ '223.5.5.5', '223.6.6.6' ]); } catch (error) { app.logger.error('set dns servers error:', error); } } }); after(() => { try { dns.setServers(originalDNSServers); } catch (error) { app.logger.error('set dns servers error:', error); } app.close(); }); afterEach(() => { app.dnsResolver.resetCache(true); mm.restore(); }); it('should ctx.curl work and set host', async () => { await app .httpRequest() .get('/?url=' + encodeURIComponent(url + '/get_headers')) .expect(200) .expect(/"host":"localhost:\d+"/); await app .httpRequest() .get( '/?url=' + encodeURIComponent(url + '/get_headers') + '&host=localhost.foo.com', ) .expect(200) .expect(/"host":"localhost\.foo\.com"/); await app .httpRequest() .get( '/?url=' + encodeURIComponent(url + '/get_headers') + '&Host=localhost2.foo.com', ) .expect(200) .expect(/"host":"localhost2\.foo\.com"/); }); it('should throw error when the first dns lookup fail', async () => { await app .httpRequest() .get( '/?url=' + encodeURIComponent('http://notexists-1111111local-domain.com'), ) .expect(500) .expect(/getaddrinfo ENOTFOUND notexists-1111111local-domain\.com/); }); it('should use local cache dns result when dns lookup error', async () => { mockLookupPromise(app); await app .httpRequest() .get('/?url=' + encodeURIComponent(url + '/get_headers')) .expect(200) .expect(/"host":"localhost:\d+"/); // mock local cache expires and mock dns lookup throw error const record = app.dnsResolver.getCacheRecord('localhost'); if (record) record.timestamp = 0; mm.error(app.dnsResolver, 'lookup', 'mock dns lookup error'); await app .httpRequest() .get('/?url=' + encodeURIComponent(url + '/get_headers')) .expect(200) .expect(/"host":"localhost:\d+"/); }); it('should app.curl work', async () => { const result = await app.curl(url + '/get_headers', { dataType: 'json' }); assert(result.status === 200); assert(result.data.host === host); const result2 = await app.httpclient.curl(url + '/get_headers', { dataType: 'json', }); assert(result2.status === 200); assert(result2.data.host === host); }); it('should app.curl work on lookup error', async () => { const result = await app.curl(url + '/get_headers', { dataType: 'json' }); assert(result.status === 200); assert(result.data.host === host); // mock local cache expires and mock dns lookup throw error const record = app.dnsResolver.getCacheRecord('localhost'); if (record) record.timestamp = 0; mm.error(app.dnsResolver, 'lookup', 'mock dns lookup error'); const result2 = await app.httpclient.curl(url + '/get_headers', { dataType: 'json', }); assert(result2.status === 200); assert(result2.data.host === host); }); it('should app.curl(obj)', async () => { const obj = urlParse(url + '/get_headers'); const result = await app.curl(obj as any, { dataType: 'json' }); assert(result.status === 200); assert(result.data.host === host); const obj2 = urlParse(url + '/get_headers'); // mock obj2.host obj2.host = ''; const result2 = await app.curl(obj2 as any, { dataType: 'json' }); assert(result2.status === 200); assert(result2.data.host === host); }); it('should dnsCacheMaxLength work', async () => { const originalMaxLength = app.dnsResolver.maxCacheSize; app.dnsResolver.resetCacheSize(1); app.dnsResolver.resetCache(true); // mockLookupPromise(app); let obj = urlParse(url + '/get_headers'); let result = await app.curl(obj as any, { dataType: 'json', timeout: 500, }); assert(result.status === 200); assert(result.data.host === host); assert(app.dnsResolver.getCacheRecord('localhost')); obj = urlParse(url.replace('localhost', 'another.com') + '/get_headers'); result = await app.curl(obj as any, { dataType: 'json', timeout: 500 }); assert(result.status === 200); assert(result.data.host === obj.host); assert(!app.dnsResolver.getCacheRecord('localhost')); assert(app.dnsResolver.getCacheRecord('another.com')); app.dnsResolver.resetCacheSize(originalMaxLength); app.dnsResolver.resetCache(); }); it('should cache and update', async () => { mockLookupPromise(app); let obj = urlParse(url + '/get_headers'); let result = await app.curl(obj as any, { dataType: 'json' }); assert(result.status === 200); assert(result.data.host === host); let record = app.dnsResolver.getCacheRecord('localhost'); const timestamp = record?.timestamp; assert(record); obj = urlParse(url + '/get_headers'); result = await app.curl(obj as any, { dataType: 'json' }); assert(result.status === 200); assert(result.data.host === host); record = app.dnsResolver.getCacheRecord('localhost'); assert(record && timestamp === record.timestamp); await utils.sleep(3500); // should be longer than dnsCacheLookupInterval to trigger update obj = urlParse(url + '/get_headers'); result = await app.curl(obj as any, { dataType: 'json' }); assert(result.status === 200); assert(result.data.host === host); record = app.dnsResolver.getCacheRecord('localhost'); assert(record && timestamp !== record.timestamp); }); it('should not cache ip', async () => { const obj = urlParse( url.replace('localhost', '127.0.0.1') + '/get_headers', ); const result = await app.curl(obj as any, { dataType: 'json', }); assert(result.status === 200); assert(result.data.host === obj.host); assert(!app.dnsResolver.getCacheRecord('127.0.0.1')); }); describe('Fetch Integration', () => { it('should app.fetch work and set host', async () => { if (!app.fetch) { return; } let record = app.dnsResolver.getCacheRecord('localhost'); assert(!record); const res = await app.fetch(url + '/get_headers'); const data = (await res.json()) as any; assert(data?.host?.startsWith('localhost')); record = app.dnsResolver.getCacheRecord('localhost'); assert(record); }); it('should app.fetch work on lookup error', async () => { if (!app.fetch) { return; } // Manually wait for previous keepalive to expire await utils.sleep(3000); await app.fetch(url + '/get_headers', { keepalive: false }); const record = app.dnsResolver.getCacheRecord('localhost'); assert(record); mm.error(app.dnsResolver, 'lookup', 'mock dns lookup error'); await utils.sleep(3000); const res = await app.fetch(url + '/get_headers', { keepalive: false }); const data = (await res.json()) as any; assert(data?.host?.startsWith('localhost')); }); it('should app.fetch work on lookup error with no cache', async () => { if (!app.fetch) { return; } await utils.sleep(3000); app.dnsResolver.resetCache(true); mm.error(app.dnsResolver, 'lookup', 'mock dns lookup error'); let err: Error | null = null; try { await app.fetch(url + '/get_headers', { keepalive: false }); } catch (e) { err = e as Error; } assert(err); assert(err!.message); }); it('should throw error when the first fetch dns lookup fail', async () => { if (!app.fetch) { return; } try { await utils.sleep(3000); await app.fetch('http://notexists-1111111local-domain.com'); assert.fail('Should have thrown an error'); } catch (err) { // Fetch API wraps DNS errors in a TypeError with the actual error in err.cause assert(err instanceof TypeError); assert.strictEqual(err.message, 'fetch failed'); assert((err as any).cause); assert.strictEqual((err as any).cause.code, 'ENOTFOUND'); assert( (err as any).cause.message.includes( 'getaddrinfo ENOTFOUND notexists-1111111local-domain.com', ), ); } }); }); describe('DNS Address Rotation', () => { before(async () => { mm.restore(); }); it('should rotate addresses when multiple IPs are returned', async () => { // Mock lookup to return multiple addresses mm(app.dnsResolver, 'lookup', async () => { return [ { address: '127.0.0.1', family: 4 }, { address: '127.0.0.2', family: 4 }, { address: '127.0.0.3', family: 4 }, ]; }); // First request to populate cache await app.curl(url + '/get_headers', { dataType: 'json' }); // Get cache entry to verify multiple records const entry: any = app.dnsResolver.getDnsCache().get('localhost'); assert(entry); assert(entry.records); assert.strictEqual(entry.records.length, 3); assert.strictEqual(entry.records[0].ip, '127.0.0.1'); assert.strictEqual(entry.records[1].ip, '127.0.0.2'); assert.strictEqual(entry.records[2].ip, '127.0.0.3'); // Verify rotation is enabled assert.strictEqual((app.dnsResolver as any).enableAddressRotation, true); const initialIndex = entry.currentIndex; // Make another request, should trigger rotation try { await app.curl(url + '/get_headers', { dataType: 'json', }); } catch (err) { app.logger?.error(err); } // Index should have rotated const newIndex = entry.currentIndex; assert.strictEqual(newIndex, (initialIndex + 1) % 3); }); it('should respect dnsAddressRotation=false config', async () => { // Temporarily disable rotation const originalRotation = (app.dnsResolver as any).enableAddressRotation; (app.dnsResolver as any).enableAddressRotation = false; app.dnsResolver.resetCache(); // Mock lookup to return multiple addresses mm(app.dnsResolver, 'lookup', async () => { return [ { address: '127.0.0.1', family: 4 }, { address: '127.0.0.2', family: 4 }, ]; }); // Populate cache await app.curl(url + '/get_headers', { dataType: 'json' }); const entry: any = app.dnsResolver.getDnsCache().get('localhost'); const initialIndex = entry.currentIndex; // Make multiple requests for (let i = 0; i < 3; i++) { await app.curl(url + '/get_headers', { dataType: 'json' }); } // Index should not change when rotation is disabled assert.strictEqual(entry.currentIndex, initialIndex); // Restore original setting (app.dnsResolver as any).enableAddressRotation = originalRotation; }); it('should rotate through all addresses cyclically', async () => { // Mock lookup to return 3 addresses (all pointing to 127.0.0.1 for testing) mm(app.dnsResolver, 'lookup', async () => { return [ { address: '127.0.0.1', family: 4 }, { address: '127.0.0.1', family: 4 }, { address: '127.0.0.1', family: 4 }, ]; }); // Reset cache to ensure clean state app.dnsResolver.resetCache(true); // First request to populate cache await app.curl(url + '/get_headers', { dataType: 'json', timeout: 500, }); const entry: any = app.dnsResolver.getDnsCache().get('localhost'); assert(entry); assert.strictEqual(entry.records.length, 3); // Record initial index after first request const indices = [ entry.currentIndex ]; // Make 5 more requests (total 6) to complete 2 full cycles for (let i = 0; i < 5; i++) { await app.curl(url + '/get_headers', { dataType: 'json', timeout: 500, }); indices.push(entry.currentIndex); } // Verify cyclic pattern // Each curl triggers one DNS lookup, advancing index by 1 assert.strictEqual(indices[0], 1); // after 1st request: 0->1 assert.strictEqual(indices[1], 2); // after 2nd request: 1->2 assert.strictEqual(indices[2], 0); // after 3rd request: 2->0 (wraps) assert.strictEqual(indices[3], 1); // after 4th request: 0->1 assert.strictEqual(indices[4], 2); // after 5th request: 1->2 assert.strictEqual(indices[5], 0); // after 6th request: 2->0 (wraps) }); it('should rotate with fetch', async () => { if (!app.fetch) { return; } mm(app.dnsResolver, 'lookup', async () => { return [ { address: '127.0.0.1', family: 4 }, { address: '127.0.0.1', family: 4 }, ]; }); // First request to populate cache await app.fetch(url + '/get_headers'); const entry: any = app.dnsResolver.getDnsCache().get('localhost'); assert(entry); const initialIndex = entry.currentIndex; // Make another request, should trigger rotation await app.fetch(url + '/get_headers'); // Index should have rotated const newIndex = entry.currentIndex; assert.strictEqual(newIndex, (initialIndex + 1) % 1); }); }); }); ================================================ FILE: plugin/dns-cache/test/dns_cache_lookup_http_next.test.ts ================================================ import mm, { type MockApplication } from 'egg-mock'; import assert from 'assert'; import { promises as dns } from 'dns'; import * as utils from './utils'; import { parse as urlParse } from 'url'; import path from 'path'; const mockLookupPromise = (app: any) => { mm(app.dnsResolver, 'lookup', async () => { return [{ address: '127.0.0.1', family: 4 }]; }); }; describe('test/dns_cache_lookup_http_next.test.ts', () => { let app: MockApplication; let url: string; let host: string; let originalDNSServers: string[]; before(async () => { mm(process.env, 'EGG_TYPESCRIPT', true); mm(process, 'cwd', () => { return path.join(__dirname, '..'); }); app = mm.app({ baseDir: path.join( __dirname, './fixtures/apps/dns_cache_lookup_http_next', ), framework: require.resolve('egg'), }); await app.ready(); url = await utils.startLocalServer(); url = url.replace('127.0.0.1', 'localhost'); host = new URL(url).host!; originalDNSServers = dns.getServers(); if (!process.env.CI) { try { dns.setServers([ '223.5.5.5', '223.6.6.6' ]); } catch (error) { app.logger.error('set dns servers error:', error); } } }); after(() => { try { dns.setServers(originalDNSServers); } catch (error) { app.logger.error('set dns servers error:', error); } app.close(); }); afterEach(() => { app.dnsResolver.resetCache(true); mm.restore(); }); it('should ctx.curl work and set host', async () => { await app .httpRequest() .get('/?url=' + encodeURIComponent(url + '/get_headers')) .expect(200) .expect(/"host":"localhost:\d+"/); await app .httpRequest() .get( '/?url=' + encodeURIComponent(url + '/get_headers') + '&host=localhost.foo.com', ) .expect(200) .expect(/"host":"localhost\.foo\.com"/); await app .httpRequest() .get( '/?url=' + encodeURIComponent(url + '/get_headers') + '&Host=localhost2.foo.com', ) .expect(200) .expect(/"host":"localhost2\.foo\.com"/); }); it('should throw error when the first dns lookup fail', async () => { await app .httpRequest() .get( '/?url=' + encodeURIComponent('http://notexists-1111111local-domain.com'), ) .expect(500) .expect(/getaddrinfo ENOTFOUND notexists-1111111local-domain\.com/); }); it('should use local cache dns result when dns lookup error', async () => { await app .httpRequest() .get('/?url=' + encodeURIComponent(url + '/get_headers')) .expect(200) .expect(/"host":"localhost:\d+"/); // mock local cache expires and mock dns lookup throw error const record = app.dnsResolver.getCacheRecord('localhost'); if (record) record.timestamp = 0; mm.error(app.dnsResolver, 'lookup', 'mock dns lookup error'); await app .httpRequest() .get('/?url=' + encodeURIComponent(url + '/get_headers')) .expect(200) .expect(/"host":"localhost:\d+"/); }); it('should app.curl work', async () => { const result = await app.curl(url + '/get_headers', { dataType: 'json' }); assert(result.status === 200); assert(result.data.host === host); const result2 = await app.httpclient.curl(url + '/get_headers', { dataType: 'json', }); assert(result2.status === 200); assert(result2.data.host === host); }); it('should app.curl work on lookup error', async () => { const result = await app.curl(url + '/get_headers', { dataType: 'json' }); assert(result.status === 200); assert(result.data.host === host); // mock local cache expires and mock dns lookup throw error const record = app.dnsResolver.getCacheRecord('localhost'); if (record) record.timestamp = 0; mm.error(app.dnsResolver, 'lookup', 'mock dns lookup error'); const result2 = await app.httpclient.curl(url + '/get_headers', { dataType: 'json', }); assert(result2.status === 200); assert(result2.data.host === host); }); it('should app.curl(obj)', async () => { const obj = new URL(url + '/get_headers'); const result = await app.curl(obj, { dataType: 'json' }); assert(result.status === 200); assert(result.data.host === host); const obj2 = new URL(url + '/get_headers'); // mock obj2.host obj2.host = ''; const result2 = await app.curl(obj2, { dataType: 'json' }); assert(result2.status === 200); assert(result2.data.host === host); }); it('should dnsCacheMaxLength work', async () => { mockLookupPromise(app); const originalMaxLength = app.dnsResolver.maxCacheSize; app.dnsResolver.resetCacheSize(1); app.dnsResolver.resetCache(true); let obj = new URL(url + '/get_headers'); let result = await app.curl(obj, { dataType: 'json' }); assert(result.status === 200); assert(result.data.host === host); assert(app.dnsResolver.getCacheRecord('localhost')); obj = new URL(url.replace('localhost', 'another.com') + '/get_headers'); result = await app.curl(obj, { dataType: 'json' }); assert(result.status === 200); assert(result.data.host === obj.host); assert(!app.dnsResolver.getCacheRecord('localhost')); assert(app.dnsResolver.getCacheRecord('another.com')); app.dnsResolver.resetCacheSize(originalMaxLength); app.dnsResolver.resetCache(); }); it('should cache and update', async () => { let obj = new URL(url + '/get_headers'); let result = await app.curl(obj, { dataType: 'json' }); assert(result.status === 200); assert(result.data.host === host); let record = app.dnsResolver.getCacheRecord('localhost'); const timestamp = record?.timestamp; assert(record); obj = new URL(url + '/get_headers'); result = await app.curl(obj, { dataType: 'json' }); assert(result.status === 200); assert(result.data.host === host); record = app.dnsResolver.getCacheRecord('localhost'); assert(record && timestamp === record.timestamp); await utils.sleep(3500); // should be longer than dnsCacheLookupInterval to trigger update obj = new URL(url + '/get_headers'); result = await app.curl(obj, { dataType: 'json' }); assert(result.status === 200); assert(result.data.host === host); record = app.dnsResolver.getCacheRecord('localhost'); assert(record && timestamp !== record.timestamp); }); it('should not cache ip', async () => { const obj = urlParse( url.replace('localhost', '127.0.0.1') + '/get_headers', ); const result = await app.curl(obj as any, { dataType: 'json', }); assert(result.status === 200); assert(result.data.host === obj.host); assert(!app.dnsResolver.getCacheRecord('127.0.0.1')); }); describe('DNS Address Rotation', () => { before(async () => { mm.restore(); }); it('should rotate addresses when multiple IPs are returned', async () => { // Mock lookup to return multiple addresses mm(app.dnsResolver, 'lookup', async () => { return [ { address: '127.0.0.1', family: 4 }, { address: '127.0.0.2', family: 4 }, { address: '127.0.0.3', family: 4 }, ]; }); // First request to populate cache await app.curl(url + '/get_headers', { dataType: 'json' }); // Get cache entry to verify multiple records const entry: any = app.dnsResolver.getDnsCache().get('localhost'); assert(entry); assert(entry.records); assert.strictEqual(entry.records.length, 3); assert.strictEqual(entry.records[0].ip, '127.0.0.1'); assert.strictEqual(entry.records[1].ip, '127.0.0.2'); assert.strictEqual(entry.records[2].ip, '127.0.0.3'); // Verify rotation is enabled assert.strictEqual((app.dnsResolver as any).enableAddressRotation, true); const initialIndex = entry.currentIndex; // Make another request, should trigger rotation try { await app.curl(url + '/get_headers', { dataType: 'json', }); } catch (err) { app.logger?.error(err); } // Index should have rotated const newIndex = entry.currentIndex; assert.strictEqual(newIndex, (initialIndex + 1) % 3); }); it('should respect dnsAddressRotation=false config', async () => { // Temporarily disable rotation const originalRotation = (app.dnsResolver as any).enableAddressRotation; (app.dnsResolver as any).enableAddressRotation = false; app.dnsResolver.resetCache(); // Mock lookup to return multiple addresses mm(app.dnsResolver, 'lookup', async () => { return [ { address: '127.0.0.1', family: 4 }, { address: '127.0.0.2', family: 4 }, ]; }); // Populate cache await app.curl(url + '/get_headers', { dataType: 'json' }); const entry: any = app.dnsResolver.getDnsCache().get('localhost'); const initialIndex = entry.currentIndex; // Make multiple requests for (let i = 0; i < 3; i++) { await app.curl(url + '/get_headers', { dataType: 'json' }); } // Index should not change when rotation is disabled assert.strictEqual(entry.currentIndex, initialIndex); // Restore original setting (app.dnsResolver as any).enableAddressRotation = originalRotation; }); it('should rotate through all addresses cyclically', async () => { // Mock lookup to return 3 addresses (all pointing to 127.0.0.1 for testing) mm(app.dnsResolver, 'lookup', async () => { return [ { address: '127.0.0.1', family: 4 }, { address: '127.0.0.1', family: 4 }, { address: '127.0.0.1', family: 4 }, ]; }); // Reset cache to ensure clean state app.dnsResolver.resetCache(true); // First request to populate cache await app.curl(url + '/get_headers', { dataType: 'json' }); const entry: any = app.dnsResolver.getDnsCache().get('localhost'); assert(entry); assert.strictEqual(entry.records.length, 3); // Record initial index after first request const indices = [ entry.currentIndex ]; // Make 5 more requests (total 6) to complete 2 full cycles for (let i = 0; i < 5; i++) { await app.curl(url + '/get_headers', { dataType: 'json', timeout: 500, }); indices.push(entry.currentIndex); } // Verify cyclic pattern // Each curl triggers one DNS lookup, advancing index by 1 assert.strictEqual(indices[0], 1); // after 1st request: 0->1 assert.strictEqual(indices[1], 2); // after 2nd request: 1->2 assert.strictEqual(indices[2], 0); // after 3rd request: 2->0 (wraps) assert.strictEqual(indices[3], 1); // after 4th request: 0->1 assert.strictEqual(indices[4], 2); // after 5th request: 1->2 assert.strictEqual(indices[5], 0); // after 6th request: 2->0 (wraps) }); }); }); ================================================ FILE: plugin/dns-cache/test/dns_cache_resolve.test.ts ================================================ import mm, { type MockApplication } from 'egg-mock'; import assert from 'assert'; import path from 'path'; import * as utils from './utils'; describe('test/dns_cache_resolve.test.ts', () => { let app: MockApplication; let url = ''; before(async () => { mm(process.env, 'EGG_TYPESCRIPT', true); mm(process, 'cwd', () => { return path.join(__dirname, '..'); }); app = mm.app({ baseDir: path.join(__dirname, './fixtures/apps/dns_cache_resolve'), framework: require.resolve('egg'), }); await app.ready(); url = await utils.startLocalServer(); try { app.dnsResolver.setServers([ '223.5.5.5', '223.6.6.6' ]); } catch (error) { app.logger.error( `[dns-cache] Failed to set DNS servers: ${error.message}`, ); } }); after(() => app.close()); afterEach(mm.restore); it('should dns resolver exist', () => { assert(app.dnsResolver); assert(app.dnsResolver.getDnsCache()); }); it('should ctx.curl dns work', async () => { // Test with 127.0.0.1 (no DNS lookup needed for IP addresses) const result1 = await app.curl(url + '/get_headers', { method: 'GET' }); assert(result1.status === 200); assert(result1.data); // Test with real domain name (requires DNS resolution) url = url.replace('127.0.0.1', 'localhost'); const result2 = await app.curl(url + '/get_headers', { method: 'GET' }); assert(result2.status === 200); const result3 = await app .httpRequest() .get('/?url=' + encodeURIComponent('https://www.aliyun.com/')); const status = result3.status; assert(status === 200 || status === 301 || status === 302); }); it('should throw error when the first dns lookup fail', async () => { await app .httpRequest() .get( '/?url=' + encodeURIComponent('http://notexists-1111111local-domain.com'), ) .expect(500) .expect(/queryA ENOTFOUND notexists-1111111local-domain\.com/); }); }); ================================================ FILE: plugin/dns-cache/test/fixtures/apps/dns_cache_lookup/app/controller/home.js ================================================ 'use strict'; module.exports = async function () { let args; if (this.query.host) { args = args || {}; args.headers = { host: this.query.host }; } if (this.query.Host) { args = args || {}; args.headers = { Host: this.query.Host }; } const result = await this.curl(this.query.url, args); this.status = result.status; this.set(result.headers); this.body = result.data; }; ================================================ FILE: plugin/dns-cache/test/fixtures/apps/dns_cache_lookup/app/router.js ================================================ 'use strict'; module.exports = function (app) { app.get('/', 'home'); }; ================================================ FILE: plugin/dns-cache/test/fixtures/apps/dns_cache_lookup/config/config.js ================================================ 'use strict'; module.exports = function (appInfo) { const config = { keys: 'test key', logger: { level: 'DEBUG', consoleLevel: 'NONE', }, httpclient: { httpAgent: { keepAlive: false, timeout: 30000, }, }, dnsCache: { mode: 'lookup', lookupInterval: 3000, addressRotation: true, resolveLocalhost: false, }, } return config; }; ================================================ FILE: plugin/dns-cache/test/fixtures/apps/dns_cache_lookup/config/plugin.js ================================================ 'use strict'; // eslint-disable-next-line @typescript-eslint/no-var-requires const path = require('node:path'); exports.tegg = { package: '@eggjs/tegg-plugin', enable: true, }; exports.teggConfig = { package: '@eggjs/tegg-config', enable: true, }; exports.dnsCache = { enable: true, path: path.join(__dirname, '../../../../../'), }; exports.teggController = { package: '@eggjs/tegg-controller-plugin', enable: true, }; exports.watcher = false; ================================================ FILE: plugin/dns-cache/test/fixtures/apps/dns_cache_lookup/package.json ================================================ { "name": "dns_cache" } ================================================ FILE: plugin/dns-cache/test/fixtures/apps/dns_cache_lookup_http_next/app/controller/home.js ================================================ 'use strict'; module.exports = async function () { let args; if (this.query.host) { args = args || {}; args.headers = { host: this.query.host }; } if (this.query.Host) { args = args || {}; args.headers = { Host: this.query.Host }; } const result = await this.curl(this.query.url, args); this.status = result.status; this.set(result.headers); this.body = result.data; }; ================================================ FILE: plugin/dns-cache/test/fixtures/apps/dns_cache_lookup_http_next/app/router.js ================================================ 'use strict'; module.exports = function (app) { app.get('/', 'home'); }; ================================================ FILE: plugin/dns-cache/test/fixtures/apps/dns_cache_lookup_http_next/config/config.js ================================================ 'use strict'; module.exports = function () { const config = { keys: 'test key', logger: { level: 'DEBUG', consoleLevel: 'DEBUG', }, httpclient: { useHttpClientNext: true, request: { reset: true, timeout: 3000, }, httpAgent: { keepAlive: false, timeout: 30000, }, }, dnsCache: { mode: 'lookup', lookupInterval: 3000, addressRotation: true, resolveLocalhost: false, } } return config; }; ================================================ FILE: plugin/dns-cache/test/fixtures/apps/dns_cache_lookup_http_next/config/plugin.js ================================================ 'use strict'; // eslint-disable-next-line @typescript-eslint/no-var-requires const path = require('node:path'); exports.tegg = { package: '@eggjs/tegg-plugin', enable: true, }; exports.teggConfig = { package: '@eggjs/tegg-config', enable: true, }; exports.dnsCache = { path: path.join(__dirname, '../../../../../'), enable: true, }; exports.teggController = { package: '@eggjs/tegg-controller-plugin', enable: true, }; exports.watcher = false; ================================================ FILE: plugin/dns-cache/test/fixtures/apps/dns_cache_lookup_http_next/package.json ================================================ { "name": "dns_cache" } ================================================ FILE: plugin/dns-cache/test/fixtures/apps/dns_cache_resolve/app/controller/home.js ================================================ 'use strict'; module.exports = async function () { let args; if (this.query.host) { args = args || {}; args.headers = { host: this.query.host }; } if (this.query.Host) { args = args || {}; args.headers = { Host: this.query.Host }; } const result = await this.curl(this.query.url, args); this.status = result.status; // Filter out conflicting headers const filteredHeaders = { ...result.headers }; delete filteredHeaders['content-length']; delete filteredHeaders['transfer-encoding']; delete filteredHeaders['connection']; this.set(filteredHeaders); this.body = result.data; }; ================================================ FILE: plugin/dns-cache/test/fixtures/apps/dns_cache_resolve/app/router.js ================================================ 'use strict'; module.exports = function (app) { app.get('/', 'home'); }; ================================================ FILE: plugin/dns-cache/test/fixtures/apps/dns_cache_resolve/config/config.js ================================================ 'use strict'; module.exports = function () { const config = { keys: 'test key', logger: { level: 'DEBUG', consoleLevel: 'DEBUG', }, httpclient: { httpAgent: { keepAlive: false, timeout: 30000, }, }, dnsCache: { mode: 'resolve', addressRotation: true, resolveLocalhost: false, } } return config; }; ================================================ FILE: plugin/dns-cache/test/fixtures/apps/dns_cache_resolve/config/plugin.js ================================================ 'use strict'; // eslint-disable-next-line @typescript-eslint/no-var-requires const path = require('node:path'); exports.tegg = { package: '@eggjs/tegg-plugin', enable: true, }; exports.teggConfig = { package: '@eggjs/tegg-config', enable: true, }; exports.dnsCache = { enable: true, path: path.join(__dirname, '../../../../../'), }; exports.teggController = { package: '@eggjs/tegg-controller-plugin', enable: true, }; exports.watcher = false; ================================================ FILE: plugin/dns-cache/test/fixtures/apps/dns_cache_resolve/package.json ================================================ { "name": "dns_cache_resolve" } ================================================ FILE: plugin/dns-cache/test/utils.ts ================================================ import * as http from 'http'; import { IncomingMessage, ServerResponse } from 'http'; export const sleep = (ms: number): Promise => new Promise(resolve => setTimeout(resolve, ms)); export const startLocalServer = (ip = '127.0.0.1'): Promise => { let localServer: http.Server | null = null; process.once('exit', () => { if (localServer && localServer.listening) { localServer.close(); } }); return new Promise((resolve, reject) => { if (localServer) { const address = localServer.address(); const port = typeof address === 'string' ? address : address?.port; return resolve(`http://${ip}:` + port); } let retry = false; const requestHandler = async ( req: IncomingMessage, res: ServerResponse, ): Promise => { const method = req.method || 'GET'; const url = req.url || '/'; const path = url.split('?')[0]; // /get_headers: 返回请求头 if (path === '/get_headers') { res.statusCode = 200; res.setHeader('Content-Type', 'application/json; charset=utf-8'); res.end(JSON.stringify(req.headers)); return; } // /timeout: 延时 10s 再返回 if (path === '/timeout') { await sleep(10000); res.statusCode = 200; res.setHeader('Content-Type', 'text/plain; charset=utf-8'); res.end(`${method} ${path}`); return; } // /error: 500 错误 if (path === '/error') { res.statusCode = 500; res.setHeader('Content-Type', 'text/plain; charset=utf-8'); res.end('this is an error'); return; } // /retry: 第一次 500,第二次 200 并带上 x-retry 头 if (path === '/retry') { if (!retry) { retry = true; res.statusCode = 500; res.end(); } else { retry = false; res.statusCode = 200; res.setHeader('x-retry', '1'); res.setHeader('Content-Type', 'text/plain; charset=utf-8'); res.end('retry suc'); } return; } // 默认返回 method + path res.statusCode = 200; res.setHeader('Content-Type', 'text/plain; charset=utf-8'); res.end(`${method} ${path}`); }; localServer = http.createServer((req, res) => { // 支持 async 处理函数 Promise.resolve(requestHandler(req, res)).catch((err: any) => { res.statusCode = 500; res.setHeader('Content-Type', 'text/plain; charset=utf-8'); res.end(err && err.message ? err.message : 'internal error'); }); }); localServer.listen(0, (err?: Error) => { if (err) return reject(err); const address = localServer!.address(); const port = typeof address === 'string' ? address : address?.port; return resolve(`http://${ip}:` + port); }); }); }; ================================================ FILE: plugin/dns-cache/tsconfig.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "baseUrl": "./" }, "exclude": ["node_modules", "test"], "ts-node": { "transpileOnly": true } } ================================================ FILE: plugin/dns-cache/tsconfig.pub.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "baseUrl": "./" }, "exclude": ["node_modules", "test"] } ================================================ FILE: plugin/dns-cache/typings/index.d.ts ================================================ import 'egg'; import '@eggjs/tegg-plugin'; import * as dns from 'node:dns'; import { DnsResolver, DnsCacheRecord } from '../lib'; export { DnsResolver, DnsCacheRecord }; declare module 'egg' { interface TeggDnsCacheApplication { /** * DNS resolver instance, provides DNS caching capabilities */ dnsResolver: DnsResolver; } interface Application extends TeggDnsCacheApplication {} interface EggAppConfig { /** * DNS Cache Configuration */ dnsCache: { /** Use dns.lookup or dns.resolve, default is 'resolve' */ mode?: 'lookup' | 'resolve'; /** Custom DNS nameservers for dns.resolve mode */ dnsServers?: string[]; /** Maximum number of DNS cache entries, default is 1000 */ maxCacheLength?: number; /** DNS cache lookup interval in milliseconds, default is 10000 */ lookupInterval?: number; /** Enable round-robin address rotation, default is true */ addressRotation?: boolean; }; } } ================================================ FILE: plugin/eventbus/CHANGELOG.md ================================================ # Change Log All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. ## [3.78.15](https://github.com/eggjs/tegg/compare/v3.78.14...v3.78.15) (2026-04-23) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.78.14](https://github.com/eggjs/tegg/compare/v3.78.13...v3.78.14) (2026-04-21) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.78.13](https://github.com/eggjs/tegg/compare/v3.78.12...v3.78.13) (2026-04-21) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.78.12](https://github.com/eggjs/tegg/compare/v3.78.11...v3.78.12) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.78.11](https://github.com/eggjs/tegg/compare/v3.78.10...v3.78.11) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.78.10](https://github.com/eggjs/tegg/compare/v3.78.9...v3.78.10) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.78.9](https://github.com/eggjs/tegg/compare/v3.78.8...v3.78.9) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.78.8](https://github.com/eggjs/tegg/compare/v3.78.7...v3.78.8) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.78.7](https://github.com/eggjs/tegg/compare/v3.78.6...v3.78.7) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.78.6](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.6) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.78.5](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.5) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.78.4](https://github.com/eggjs/tegg/compare/v3.78.3...v3.78.4) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.78.3](https://github.com/eggjs/tegg/compare/v3.78.2...v3.78.3) (2026-04-15) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.78.2](https://github.com/eggjs/tegg/compare/v3.78.1...v3.78.2) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.78.1](https://github.com/eggjs/tegg/compare/v3.78.0...v3.78.1) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.78.0](https://github.com/eggjs/tegg/compare/v3.77.2...v3.78.0) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.77.2](https://github.com/eggjs/tegg/compare/v3.77.1...v3.77.2) (2026-04-07) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.77.1](https://github.com/eggjs/tegg/compare/v3.77.0...v3.77.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.77.0](https://github.com/eggjs/tegg/compare/v3.76.1...v3.77.0) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.76.1](https://github.com/eggjs/tegg/compare/v3.76.0...v3.76.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.76.0](https://github.com/eggjs/tegg/compare/v3.75.1...v3.76.0) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.75.1](https://github.com/eggjs/tegg/compare/v3.75.0...v3.75.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.75.0](https://github.com/eggjs/tegg/compare/v3.74.0...v3.75.0) (2026-03-30) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.74.0](https://github.com/eggjs/tegg/compare/v3.73.0...v3.74.0) (2026-03-30) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.73.0](https://github.com/eggjs/tegg/compare/v3.72.0...v3.73.0) (2026-03-25) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.72.0](https://github.com/eggjs/tegg/compare/v3.71.2...v3.72.0) (2026-02-04) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.71.2](https://github.com/eggjs/tegg/compare/v3.71.1...v3.71.2) (2026-01-30) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.71.1](https://github.com/eggjs/tegg/compare/v3.71.0...v3.71.1) (2026-01-28) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.71.0](https://github.com/eggjs/tegg/compare/v3.70.1...v3.71.0) (2026-01-26) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.70.1](https://github.com/eggjs/tegg/compare/v3.70.0...v3.70.1) (2026-01-14) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.70.0](https://github.com/eggjs/tegg/compare/v3.69.0...v3.70.0) (2026-01-07) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.69.0](https://github.com/eggjs/tegg/compare/v3.68.0...v3.69.0) (2026-01-06) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.68.0](https://github.com/eggjs/tegg/compare/v3.67.2...v3.68.0) (2025-12-29) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.67.2](https://github.com/eggjs/tegg/compare/v3.67.1...v3.67.2) (2025-12-24) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.67.1](https://github.com/eggjs/tegg/compare/v3.67.0...v3.67.1) (2025-12-23) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.67.0](https://github.com/eggjs/tegg/compare/v3.66.0...v3.67.0) (2025-12-18) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.66.0](https://github.com/eggjs/tegg/compare/v3.65.3...v3.66.0) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.65.3](https://github.com/eggjs/tegg/compare/v3.65.2...v3.65.3) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.65.2](https://github.com/eggjs/tegg/compare/v3.65.1...v3.65.2) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.65.1](https://github.com/eggjs/tegg/compare/v3.65.0...v3.65.1) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.65.0](https://github.com/eggjs/tegg/compare/v3.64.5...v3.65.0) (2025-12-10) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.64.5](https://github.com/eggjs/tegg/compare/v3.64.4...v3.64.5) (2025-12-08) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.64.4](https://github.com/eggjs/tegg/compare/v3.64.3...v3.64.4) (2025-12-06) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.64.3](https://github.com/eggjs/tegg/compare/v3.64.2...v3.64.3) (2025-12-06) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.64.2](https://github.com/eggjs/tegg/compare/v3.64.1...v3.64.2) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.64.1](https://github.com/eggjs/tegg/compare/v3.64.0...v3.64.1) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.64.0](https://github.com/eggjs/tegg/compare/v3.63.2...v3.64.0) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.63.2](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.2) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.63.1](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.1) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.63.0](https://github.com/eggjs/tegg/compare/v3.62.3...v3.63.0) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.62.3](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.3) (2025-11-05) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.62.2](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.2) (2025-11-05) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.62.1](https://github.com/eggjs/tegg/compare/v3.62.0...v3.62.1) (2025-11-03) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.62.0](https://github.com/eggjs/tegg/compare/v3.61.0...v3.62.0) (2025-09-24) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.61.0](https://github.com/eggjs/tegg/compare/v3.60.3...v3.61.0) (2025-08-15) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.60.3](https://github.com/eggjs/tegg/compare/v3.60.2...v3.60.3) (2025-08-06) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.60.2](https://github.com/eggjs/tegg/compare/v3.60.1...v3.60.2) (2025-08-06) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.60.1](https://github.com/eggjs/tegg/compare/v3.60.0...v3.60.1) (2025-07-28) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.60.0](https://github.com/eggjs/tegg/compare/v3.59.1...v3.60.0) (2025-07-28) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.59.1](https://github.com/eggjs/tegg/compare/v3.59.0...v3.59.1) (2025-07-16) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.59.0](https://github.com/eggjs/tegg/compare/v3.58.0...v3.59.0) (2025-07-07) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.58.0](https://github.com/eggjs/tegg/compare/v3.57.14...v3.58.0) (2025-07-01) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.57.14](https://github.com/eggjs/tegg/compare/v3.57.13...v3.57.14) (2025-06-18) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.57.13](https://github.com/eggjs/tegg/compare/v3.57.12...v3.57.13) (2025-06-18) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.57.12](https://github.com/eggjs/tegg/compare/v3.57.11...v3.57.12) (2025-06-16) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.57.11](https://github.com/eggjs/tegg/compare/v3.57.10...v3.57.11) (2025-06-16) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.57.10](https://github.com/eggjs/tegg/compare/v3.57.9...v3.57.10) (2025-06-12) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.57.9](https://github.com/eggjs/tegg/compare/v3.57.8...v3.57.9) (2025-05-29) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.57.8](https://github.com/eggjs/tegg/compare/v3.57.7...v3.57.8) (2025-05-29) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.57.7](https://github.com/eggjs/tegg/compare/v3.57.6...v3.57.7) (2025-05-28) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.57.6](https://github.com/eggjs/tegg/compare/v3.57.5...v3.57.6) (2025-05-27) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.57.5](https://github.com/eggjs/tegg/compare/v3.57.4...v3.57.5) (2025-05-15) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.57.4](https://github.com/eggjs/tegg/compare/v3.57.3...v3.57.4) (2025-05-14) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.57.3](https://github.com/eggjs/tegg/compare/v3.57.2...v3.57.3) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.57.2](https://github.com/eggjs/tegg/compare/v3.57.1...v3.57.2) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.57.1](https://github.com/eggjs/tegg/compare/v3.57.0...v3.57.1) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.57.0](https://github.com/eggjs/tegg/compare/v3.56.3...v3.57.0) (2025-05-09) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.56.3](https://github.com/eggjs/tegg/compare/v3.56.2...v3.56.3) (2025-05-07) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.56.2](https://github.com/eggjs/tegg/compare/v3.56.1...v3.56.2) (2025-05-01) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.56.1](https://github.com/eggjs/tegg/compare/v3.56.0...v3.56.1) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.56.0](https://github.com/eggjs/tegg/compare/v3.55.0...v3.56.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.55.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.55.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.54.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.54.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.53.0](https://github.com/eggjs/tegg/compare/v3.52.1...v3.53.0) (2025-04-21) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.52.1](https://github.com/eggjs/tegg/compare/v3.52.0...v3.52.1) (2025-03-14) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.52.0](https://github.com/eggjs/tegg/compare/v3.51.2...v3.52.0) (2024-12-30) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.51.2](https://github.com/eggjs/tegg/compare/v3.51.1...v3.51.2) (2024-12-09) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.51.1](https://github.com/eggjs/tegg/compare/v3.51.0...v3.51.1) (2024-11-06) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.51.0](https://github.com/eggjs/tegg/compare/v3.50.1...v3.51.0) (2024-10-30) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.50.1](https://github.com/eggjs/tegg/compare/v3.50.0...v3.50.1) (2024-10-23) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.50.0](https://github.com/eggjs/tegg/compare/v3.49.0...v3.50.0) (2024-10-22) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.49.0](https://github.com/eggjs/tegg/compare/v3.48.1...v3.49.0) (2024-10-21) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.48.1](https://github.com/eggjs/tegg/compare/v3.48.0...v3.48.1) (2024-10-14) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.48.0](https://github.com/eggjs/tegg/compare/v3.47.2...v3.48.0) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.47.2](https://github.com/eggjs/tegg/compare/v3.47.1...v3.47.2) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.47.1](https://github.com/eggjs/tegg/compare/v3.47.0...v3.47.1) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.47.0](https://github.com/eggjs/tegg/compare/v3.46.4...v3.47.0) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.46.4](https://github.com/eggjs/tegg/compare/v3.46.3...v3.46.4) (2024-10-09) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.46.3](https://github.com/eggjs/tegg/compare/v3.46.2...v3.46.3) (2024-10-08) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.46.2](https://github.com/eggjs/tegg/compare/v3.46.1...v3.46.2) (2024-10-07) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.46.1](https://github.com/eggjs/tegg/compare/v3.46.0...v3.46.1) (2024-09-30) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.46.0](https://github.com/eggjs/tegg/compare/v3.45.0...v3.46.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.45.0](https://github.com/eggjs/tegg/compare/v3.44.1...v3.45.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.44.1](https://github.com/eggjs/tegg/compare/v3.44.0...v3.44.1) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.44.0](https://github.com/eggjs/tegg/compare/v3.43.2...v3.44.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.43.2](https://github.com/eggjs/tegg/compare/v3.43.1...v3.43.2) (2024-09-14) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.43.1](https://github.com/eggjs/tegg/compare/v3.43.0...v3.43.1) (2024-09-14) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.43.0](https://github.com/eggjs/tegg/compare/v3.42.0...v3.43.0) (2024-09-13) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.42.0](https://github.com/eggjs/tegg/compare/v3.41.0...v3.42.0) (2024-09-10) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.41.0](https://github.com/eggjs/tegg/compare/v3.40.1...v3.41.0) (2024-08-26) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.40.1](https://github.com/eggjs/tegg/compare/v3.40.0...v3.40.1) (2024-08-23) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.40.0](https://github.com/eggjs/tegg/compare/v3.39.5...v3.40.0) (2024-08-22) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.39.5](https://github.com/eggjs/tegg/compare/v3.39.4...v3.39.5) (2024-08-09) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.39.4](https://github.com/eggjs/tegg/compare/v3.39.3...v3.39.4) (2024-07-09) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.39.3](https://github.com/eggjs/tegg/compare/v3.39.2...v3.39.3) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.39.2](https://github.com/eggjs/tegg/compare/v3.39.1...v3.39.2) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.39.1](https://github.com/eggjs/tegg/compare/v3.39.0...v3.39.1) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.39.0](https://github.com/eggjs/tegg/compare/v3.38.0...v3.39.0) (2024-04-19) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.38.0](https://github.com/eggjs/tegg/compare/v3.37.3...v3.38.0) (2024-04-18) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.37.3](https://github.com/eggjs/tegg/compare/v3.37.2...v3.37.3) (2024-04-17) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.37.2](https://github.com/eggjs/tegg/compare/v3.37.1...v3.37.2) (2024-04-17) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.37.1](https://github.com/eggjs/tegg/compare/v3.37.0...v3.37.1) (2024-04-16) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.37.0](https://github.com/eggjs/tegg/compare/v3.36.3...v3.37.0) (2024-04-16) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.36.3](https://github.com/eggjs/tegg/compare/v3.36.2...v3.36.3) (2024-04-10) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.36.2](https://github.com/eggjs/tegg/compare/v3.36.1...v3.36.2) (2024-04-08) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.36.1](https://github.com/eggjs/tegg/compare/v3.36.0...v3.36.1) (2024-04-07) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.36.0](https://github.com/eggjs/tegg/compare/v3.35.1...v3.36.0) (2024-04-02) ### Features * impl ajv + typebox Validator ([#201](https://github.com/eggjs/tegg/issues/201)) ([9fd585d](https://github.com/eggjs/tegg/commit/9fd585de9b613466c96b73494a08a494db34ea57)) ## [3.35.1](https://github.com/eggjs/tegg/compare/v3.35.0...v3.35.1) (2024-03-26) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.35.0](https://github.com/eggjs/tegg/compare/v3.34.0...v3.35.0) (2024-03-26) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.34.0](https://github.com/eggjs/tegg/compare/v3.33.1...v3.34.0) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.33.1](https://github.com/eggjs/tegg/compare/v3.33.0...v3.33.1) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.33.0](https://github.com/eggjs/tegg/compare/v3.32.0...v3.33.0) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.32.0](https://github.com/eggjs/tegg/compare/v3.31.0...v3.32.0) (2024-02-19) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.31.0](https://github.com/eggjs/tegg/compare/v3.30.1...v3.31.0) (2024-01-31) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.30.1](https://github.com/eggjs/tegg/compare/v3.30.0...v3.30.1) (2024-01-25) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.30.0](https://github.com/eggjs/tegg/compare/v3.29.0...v3.30.0) (2024-01-17) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.29.0](https://github.com/eggjs/tegg/compare/v3.28.2...v3.29.0) (2023-12-26) ### Features * allow a handler to subscribe to multiple events ([#179](https://github.com/eggjs/tegg/issues/179)) ([1d460a5](https://github.com/eggjs/tegg/commit/1d460a5a6bdcf9a3d61b13d3527633c8b990a38c)) ## [3.28.2](https://github.com/eggjs/tegg/compare/v3.28.1...v3.28.2) (2023-12-12) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.28.1](https://github.com/eggjs/tegg/compare/v3.28.0...v3.28.1) (2023-12-11) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.28.0](https://github.com/eggjs/tegg/compare/v3.27.0...v3.28.0) (2023-12-10) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.27.0](https://github.com/eggjs/tegg/compare/v3.26.0...v3.27.0) (2023-11-23) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.26.0](https://github.com/eggjs/tegg/compare/v3.25.2...v3.26.0) (2023-11-17) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.25.2](https://github.com/eggjs/tegg/compare/v3.25.1...v3.25.2) (2023-11-06) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.25.1](https://github.com/eggjs/tegg/compare/v3.25.0...v3.25.1) (2023-11-03) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.25.0](https://github.com/eggjs/tegg/compare/v3.24.0...v3.25.0) (2023-11-03) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.24.0](https://github.com/eggjs/tegg/compare/v3.23.0...v3.24.0) (2023-10-26) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.23.0](https://github.com/eggjs/tegg/compare/v3.22.0...v3.23.0) (2023-09-20) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.20.0](https://github.com/eggjs/tegg/compare/v3.19.0...v3.20.0) (2023-09-07) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.19.0](https://github.com/eggjs/tegg/compare/v3.18.1...v3.19.0) (2023-08-31) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.18.1](https://github.com/eggjs/tegg/compare/v3.18.0...v3.18.1) (2023-08-29) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.18.0](https://github.com/eggjs/tegg/compare/v3.17.0...v3.18.0) (2023-08-29) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.17.0](https://github.com/eggjs/tegg/compare/v3.16.0...v3.17.0) (2023-08-24) ### Features * impl MultiInstanceProto ([#145](https://github.com/eggjs/tegg/issues/145)) ([12fd5cf](https://github.com/eggjs/tegg/commit/12fd5cff4004578bcc737dcdf4f7e9d1159f5633)) # [3.16.0](https://github.com/eggjs/tegg/compare/v3.15.0...v3.16.0) (2023-08-24) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.14.3](https://github.com/eggjs/tegg/compare/v3.14.2...v3.14.3) (2023-08-14) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.14.2](https://github.com/eggjs/tegg/compare/v3.14.1...v3.14.2) (2023-08-14) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.14.1](https://github.com/eggjs/tegg/compare/v3.14.0...v3.14.1) (2023-08-11) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.14.0](https://github.com/eggjs/tegg/compare/v3.13.0...v3.14.0) (2023-07-31) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.13.0](https://github.com/eggjs/tegg/compare/v3.12.0...v3.13.0) (2023-07-25) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.12.0](https://github.com/eggjs/tegg/compare/v3.11.1...v3.12.0) (2023-07-13) ### Bug Fixes * after call mockModuleContext, hasMockModuleContext should be true ([#134](https://github.com/eggjs/tegg/issues/134)) ([88b3caa](https://github.com/eggjs/tegg/commit/88b3caadd24f08221b8098c42733e26376338cae)) ## [3.11.1](https://github.com/eggjs/tegg/compare/v3.11.0...v3.11.1) (2023-06-29) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.11.0](https://github.com/eggjs/tegg/compare/v3.10.0...v3.11.0) (2023-06-29) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.10.0](https://github.com/eggjs/tegg/compare/v3.9.0...v3.10.0) (2023-06-28) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.9.0](https://github.com/eggjs/tegg/compare/v3.8.0...v3.9.0) (2023-06-20) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.8.0](https://github.com/eggjs/tegg/compare/v3.7.0...v3.8.0) (2023-05-30) ### Features * impl EggObjectLifecycle hook in decorator ([#119](https://github.com/eggjs/tegg/issues/119)) ([cced8a2](https://github.com/eggjs/tegg/commit/cced8a2e009c33d5040fa21d00409fddef471b0e)) # [3.7.0](https://github.com/eggjs/tegg/compare/v3.6.3...v3.7.0) (2023-04-03) ### Bug Fixes * don't check eventbus plugin name ([#113](https://github.com/eggjs/tegg/issues/113)) ([2a94a57](https://github.com/eggjs/tegg/commit/2a94a57c58e4fd971400966c15597aace4bb1ecc)) ## [3.6.3](https://github.com/eggjs/tegg/compare/v3.6.2...v3.6.3) (2023-03-02) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.6.2](https://github.com/eggjs/tegg/compare/v3.6.1...v3.6.2) (2023-02-16) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.6.1](https://github.com/eggjs/tegg/compare/v3.6.0...v3.6.1) (2023-02-14) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.6.0](https://github.com/eggjs/tegg/compare/v3.5.2...v3.6.0) (2023-02-13) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.5.2](https://github.com/eggjs/tegg/compare/v3.5.1...v3.5.2) (2023-02-10) ### Bug Fixes * eventbus cork should support reentry ([#98](https://github.com/eggjs/tegg/issues/98)) ([077044c](https://github.com/eggjs/tegg/commit/077044c040f8423572605eb2980e3cc6da8c038e)) ## [3.5.1](https://github.com/eggjs/tegg/compare/v3.5.0...v3.5.1) (2023-02-10) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.5.0](https://github.com/eggjs/tegg/compare/v3.4.1...v3.5.0) (2023-02-10) ### Features * use SingletonProto for egg ctx object ([#92](https://github.com/eggjs/tegg/issues/92)) ([3385d57](https://github.com/eggjs/tegg/commit/3385d571b076d3148978f252188f29d9cf2c6781)) ## [3.4.1](https://github.com/eggjs/tegg/compare/v3.4.0...v3.4.1) (2023-02-02) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.4.0](https://github.com/eggjs/tegg/compare/v3.3.4...v3.4.0) (2023-02-01) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.3.4](https://github.com/eggjs/tegg/compare/v3.3.3...v3.3.4) (2023-01-29) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.3.3](https://github.com/eggjs/tegg/compare/v3.3.2...v3.3.3) (2023-01-29) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.3.2](https://github.com/eggjs/tegg/compare/v3.3.1...v3.3.2) (2023-01-29) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.3.1](https://github.com/eggjs/tegg/compare/v3.3.0...v3.3.1) (2023-01-28) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.3.0](https://github.com/eggjs/tegg/compare/v3.2.4...v3.3.0) (2023-01-28) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.2.3](https://github.com/eggjs/tegg/compare/v3.2.2...v3.2.3) (2023-01-16) ### Bug Fixes * cork/uncork should can be called multi times in same ctx ([#78](https://github.com/eggjs/tegg/issues/78)) ([269cda6](https://github.com/eggjs/tegg/commit/269cda6327122111c230e6f69abb525ce4ab5be1)) ## [3.2.2](https://github.com/eggjs/tegg/compare/v3.2.1...v3.2.2) (2023-01-06) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [3.2.1](https://github.com/eggjs/tegg/compare/v3.2.0...v3.2.1) (2022-12-28) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.2.0](https://github.com/eggjs/tegg/compare/v3.1.0...v3.2.0) (2022-12-28) ### Features * impl mockModuleContextScope ([#73](https://github.com/eggjs/tegg/issues/73)) ([041881c](https://github.com/eggjs/tegg/commit/041881ca317ad81366172a35ac56b7b2dc0a0488)) # [3.1.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.1.0) (2022-12-27) ### Bug Fixes * inject context proto to singleton proto ([#72](https://github.com/eggjs/tegg/issues/72)) ([fcc0b2b](https://github.com/eggjs/tegg/commit/fcc0b2b48fc9bce580c1f2bcfcc38039ae909951)) ### Features * **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) * implement cork/uncork for eventbus ([#60](https://github.com/eggjs/tegg/issues/60)) ([38114bd](https://github.com/eggjs/tegg/commit/38114bd7ea3b46cc4a79556a005ef18b2ae11ec2)) # [3.0.0](https://github.com/eggjs/tegg/compare/v3.0.0-alpha.0...v3.0.0) (2022-12-26) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [3.0.0-alpha.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.0.0-alpha.0) (2022-12-22) ### Features * **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) * implement cork/uncork for eventbus ([#60](https://github.com/eggjs/tegg/issues/60)) ([38114bd](https://github.com/eggjs/tegg/commit/38114bd7ea3b46cc4a79556a005ef18b2ae11ec2)) ## [1.3.9](https://github.com/eggjs/tegg/compare/@eggjs/tegg-eventbus-plugin@1.3.8...@eggjs/tegg-eventbus-plugin@1.3.9) (2022-09-05) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [1.3.8](https://github.com/eggjs/tegg/compare/@eggjs/tegg-eventbus-plugin@1.3.7...@eggjs/tegg-eventbus-plugin@1.3.8) (2022-09-04) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [1.3.7](https://github.com/eggjs/tegg/compare/@eggjs/tegg-eventbus-plugin@1.3.6...@eggjs/tegg-eventbus-plugin@1.3.7) (2022-08-24) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [1.3.6](https://github.com/eggjs/tegg/compare/@eggjs/tegg-eventbus-plugin@1.3.5...@eggjs/tegg-eventbus-plugin@1.3.6) (2022-08-16) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [1.3.4](https://github.com/eggjs/tegg/compare/@eggjs/tegg-eventbus-plugin@1.3.3...@eggjs/tegg-eventbus-plugin@1.3.4) (2022-07-28) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [1.3.3](https://github.com/eggjs/tegg/compare/@eggjs/tegg-eventbus-plugin@1.3.2...@eggjs/tegg-eventbus-plugin@1.3.3) (2022-07-20) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ## [1.3.2](https://github.com/eggjs/tegg/compare/@eggjs/tegg-eventbus-plugin@1.3.1...@eggjs/tegg-eventbus-plugin@1.3.2) (2022-07-20) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [1.3.0](https://github.com/eggjs/tegg/compare/v1.2.0...v1.3.0) (2022-07-01) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin # [0.2.0](https://github.com/eggjs/tegg/compare/v0.1.19...v0.2.0) (2022-01-20) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin ================================================ FILE: plugin/eventbus/README.md ================================================ # @eggjs/tegg-eventbus-plugin ## Usage ```js // plugin.js export.eventbusModule = { enable: true, package: '@eggjs/tegg-eventbus-plugin', }; ``` ## Unittest ```ts // test/fixtures/apps/event-app/app/event-module/HelloService @ContextProto({ accessLevel: AccessLevel.PUBLIC, }) export class HelloService { @Inject() private readonly eventBus: EventBus; hello() { this.eventBus.emit('hello', '01'); } } // test/fixtures/apps/event-app/app/event-module/HelloLogger @Event('helloEgg') export class HelloLogger { handle(msg: string) { console.log('hello, ', msg); } } // test/event.test.ts import assert from 'assert'; import path from 'path'; import mm from 'egg-mock'; import { HelloService } from './fixtures/apps/event-app/app/event-module/HelloService'; import { HelloLogger } from './fixtures/apps/event-app/app/event-module/HelloLogger'; describe('test/eventbus.test.ts', () => { let app; let ctx; afterEach(async () => { await app.destroyModuleContext(ctx); mm.restore(); }); before(async () => { app = mm.app(); await app.ready(); }); after(() => { return app.close(); }); it('msg should work', async () => { ctx = await app.mockModuleContext(); const helloService = await ctx.getEggObject(HelloService); let msg: string | undefined; // helloLogger is in child context, should mock the prototype mm(HelloLogger.prototype, 'handle', m => { msg = m; }); const eventWaiter = await app.getEventWaiter(); const helloEvent = eventWaiter.await('hello'); helloService.hello(); await helloEvent; assert(msg === '01'); }); }); ``` ================================================ FILE: plugin/eventbus/app/extend/application.unittest.ts ================================================ import { Application } from 'egg'; import { PrototypeUtil, EventBus, EventWaiter } from '@eggjs/tegg'; import { SingletonEventBus } from '@eggjs/tegg-eventbus-runtime'; import { EggPrototype } from '@eggjs/tegg-metadata'; export default { async getEventbus(this: Application): Promise { const proto = PrototypeUtil.getClazzProto(SingletonEventBus) as EggPrototype; const eggObject = await this.eggContainerFactory.getOrCreateEggObject(proto, proto.name); return eggObject.obj as EventBus; }, async getEventWaiter(this: Application): Promise { const proto = PrototypeUtil.getClazzProto(SingletonEventBus) as EggPrototype; const eggObject = await this.eggContainerFactory.getOrCreateEggObject(proto, proto.name); return eggObject.obj as EventWaiter; }, }; ================================================ FILE: plugin/eventbus/app/extend/context.ts ================================================ import { Context } from 'egg'; import { EggContextEventBus } from '../../lib/EggContextEventBus'; const EVENT_BUS = Symbol.for('context#eventBus'); export default { get eventBus() { if (!this[EVENT_BUS]) { this[EVENT_BUS] = new EggContextEventBus(this as unknown as Context); } return this[EVENT_BUS]; }, }; ================================================ FILE: plugin/eventbus/app.ts ================================================ import { Application } from 'egg'; import { EventHandlerProtoManager } from './lib/EventHandlerProtoManager'; import { EventbusLoadUnitHook } from './lib/EventbusLoadUnitHook'; import { EventbusProtoHook } from './lib/EventbusProtoHook'; export default class EventbusAppHook { private readonly app: Application; private readonly eventHandlerProtoManager: EventHandlerProtoManager; private readonly eventbusLoadUnitHook: EventbusLoadUnitHook; private readonly eventbusProtoHook: EventbusProtoHook; constructor(app) { this.app = app; this.eventHandlerProtoManager = new EventHandlerProtoManager(app); this.eventbusLoadUnitHook = new EventbusLoadUnitHook(); this.eventbusProtoHook = new EventbusProtoHook(this.eventHandlerProtoManager); } configDidLoad() { this.app.eggPrototypeLifecycleUtil.registerLifecycle(this.eventbusProtoHook); this.app.loadUnitLifecycleUtil.registerLifecycle(this.eventbusLoadUnitHook); } async didLoad() { await this.app.moduleHandler.ready(); await this.eventHandlerProtoManager.register(); } beforeClose() { this.app.eggPrototypeLifecycleUtil.deleteLifecycle(this.eventbusProtoHook); this.app.loadUnitLifecycleUtil.deleteLifecycle(this.eventbusLoadUnitHook); } } ================================================ FILE: plugin/eventbus/lib/EggContextEventBus.ts ================================================ import assert from 'assert'; import { Context } from 'egg'; import { Events, PrototypeUtil, CORK_ID, ContextEventBus } from '@eggjs/tegg'; import { SingletonEventBus } from '@eggjs/tegg-eventbus-runtime'; import { EggPrototype } from '@eggjs/tegg-metadata'; import { ContextHandler, EggContext } from '@eggjs/tegg-runtime'; import type { Arguments } from '@eggjs/tegg'; export class EggContextEventBus implements ContextEventBus { private readonly eventBus: SingletonEventBus; private readonly context: EggContext; private corkId?: string; constructor(ctx: Context) { const proto = PrototypeUtil.getClazzProto(SingletonEventBus) as EggPrototype; const eggObject = ctx.app.eggContainerFactory.getEggObject(proto, proto.name); this.context = ContextHandler.getContext()!; this.eventBus = eggObject.obj as SingletonEventBus; } cork() { if (!this.corkId) { this.corkId = this.eventBus.generateCorkId(); this.context.set(CORK_ID, this.corkId); } this.eventBus.cork(this.corkId); } uncork() { assert(this.corkId, 'eventbus uncork without cork'); if (this.eventBus.uncork(this.corkId)) { this.context.set(CORK_ID, null); this.corkId = undefined; } } emit(event: E, ...args: Arguments): boolean { return this.eventBus.emitWithContext(this.context, event, args); } } ================================================ FILE: plugin/eventbus/lib/EggEventContext.ts ================================================ import { Context, Application } from 'egg'; import { AbstractEggContext, EggContext } from '@eggjs/tegg-runtime'; import { IdenticalUtil } from '@eggjs/tegg'; import { EGG_CONTEXT, TEGG_CONTEXT } from '@eggjs/egg-module-common'; import { ContextCreator } from '@eggjs/tegg-eventbus-runtime'; // AbstractEggContext use lots of static method // In chair application mode plugin is in .sff // Make different @eggjs/tegg-runtime exits export function eggEventContextFactory(AbstractEggContextClazz: typeof AbstractEggContext, identicalUtil: typeof IdenticalUtil) { class EggEventContext extends AbstractEggContextClazz { readonly id: string; constructor(context: Context) { super(); this.set(EGG_CONTEXT, context); (context as any)[TEGG_CONTEXT] = this; // In chair application mode, // Plugin event may install in app dir, // Plugin tegg may install in layer dir, // Will has multi IdenticalUtil instance. this.id = identicalUtil.createContextId(context.tracer?.traceId); } static createContextFactory(app: Application): ContextCreator { return (): EggContext => { const eggCtx = app.createAnonymousContext(); return new EggEventContext(eggCtx); }; } } return EggEventContext.createContextFactory; } ================================================ FILE: plugin/eventbus/lib/EventHandlerProtoManager.ts ================================================ import { Application } from 'egg'; import { EggPrototype } from '@eggjs/tegg-metadata'; import { EventContextFactory, EventHandlerFactory } from '@eggjs/tegg-eventbus-runtime'; import { EVENT_NAME, EventName } from '@eggjs/tegg'; import { eggEventContextFactory } from './EggEventContext'; export class EventHandlerProtoManager { private readonly protos: Set = new Set(); private readonly app: Application; constructor(app: Application) { this.app = app; } addProto(proto: EggPrototype) { this.protos.add(proto); } async register() { const eventHandlerFactory = await this.app.getEggObject(EventHandlerFactory); for (const proto of this.protos) { const eventList = proto.getMetaData(EVENT_NAME) as EventName[] ?? []; eventList.forEach(event => eventHandlerFactory.registerHandler(event, proto)); } const eventFactory = await this.app.getEggObject(EventContextFactory); const createContextFactory = eggEventContextFactory(this.app.abstractEggContext, this.app.identicalUtil); eventFactory.registerContextCreator(createContextFactory(this.app)); } getProtos() { return Array.from(this.protos); } } ================================================ FILE: plugin/eventbus/lib/EventbusLoadUnitHook.ts ================================================ import { EggQualifierAttribute, EggType, LifecycleHook, QualifierUtil } from '@eggjs/tegg'; import { EggLoadUnitType, EggPrototypeCreatorFactory, EggPrototypeFactory, LoadUnit, LoadUnitLifecycleContext, } from '@eggjs/tegg-metadata'; import { EventContextFactory, EventHandlerFactory, SingletonEventBus } from '@eggjs/tegg-eventbus-runtime'; const REGISTER_CLAZZ = [ EventHandlerFactory, EventContextFactory, SingletonEventBus, ]; // EggQualifier only for egg plugin QualifierUtil.addProperQualifier(SingletonEventBus, 'logger', EggQualifierAttribute, EggType.APP); export class EventbusLoadUnitHook implements LifecycleHook { async postCreate(_ctx: LoadUnitLifecycleContext, loadUnit: LoadUnit): Promise { if (loadUnit.type === EggLoadUnitType.APP) { for (const clazz of REGISTER_CLAZZ) { const protos = await EggPrototypeCreatorFactory.createProto(clazz, loadUnit); for (const proto of protos) { EggPrototypeFactory.instance.registerPrototype(proto, loadUnit); } } } } } ================================================ FILE: plugin/eventbus/lib/EventbusProtoHook.ts ================================================ import { LifecycleHook, EVENT_NAME } from '@eggjs/tegg'; import { EggPrototype, EggPrototypeLifecycleContext } from '@eggjs/tegg-metadata'; import { EventHandlerProtoManager } from './EventHandlerProtoManager'; export class EventbusProtoHook implements LifecycleHook { private eventHandlerProtoManager: EventHandlerProtoManager; constructor(eventHandlerProtoManager: EventHandlerProtoManager) { this.eventHandlerProtoManager = eventHandlerProtoManager; } async postCreate(_ctx: EggPrototypeLifecycleContext, obj: EggPrototype): Promise { const event = obj.getMetaData(EVENT_NAME); if (!event) { return; } this.eventHandlerProtoManager.addProto(obj); } } ================================================ FILE: plugin/eventbus/package.json ================================================ { "name": "@eggjs/tegg-eventbus-plugin", "version": "3.78.15", "eggPlugin": { "name": "eventbusModule", "strict": false, "dependencies": [ "tegg" ] }, "description": "tegg event plugin", "keywords": [ "egg", "typescript", "decorator", "eventbus", "tegg" ], "files": [ "app.js", "app.d.ts", "lib/**/*.js", "lib/**/*.d.ts", "app/**/*.js", "app/**/*.d.ts", "typings/*.d.ts", "config/*.js", "config/*.d.ts" ], "types": "typings/index.d.ts", "scripts": { "test": "cross-env NODE_ENV=test NODE_OPTIONS='--no-deprecation' mocha", "clean": "tsc -b --clean", "tsc": "ut run clean && tsc -p ./tsconfig.json", "tsc:pub": "ut run clean && tsc -p ./tsconfig.pub.json", "prepublishOnly": "ut tsc:pub" }, "homepage": "https://github.com/eggjs/tegg", "bugs": { "url": "https://github.com/eggjs/tegg/issues" }, "repository": { "type": "git", "url": "git@github.com:eggjs/tegg.git", "directory": "plugin/eventbus" }, "engines": { "node": ">=14.0.0" }, "dependencies": { "@eggjs/egg-module-common": "^3.78.15", "@eggjs/tegg": "^3.78.15", "@eggjs/tegg-eventbus-runtime": "^3.78.15", "@eggjs/tegg-metadata": "^3.78.15", "@eggjs/tegg-runtime": "^3.78.15" }, "devDependencies": { "@eggjs/tegg-common-util": "^3.78.15", "@eggjs/tegg-config": "^3.78.15", "@eggjs/tegg-plugin": "^3.78.15", "@types/mocha": "^10.0.1", "@types/node": "^20.2.4", "await-event": "^2.1.0", "cross-env": "^7.0.3", "egg": "^3.9.1", "egg-mock": "^5.5.0", "egg-tracer": "^2.0.0", "mocha": "^10.2.0", "ts-node": "^10.9.1", "typescript": "^5.0.4" }, "publishConfig": { "access": "public" }, "gitHead": "240a3e9f40fda65ebb7589727d197db5ce17916c" } ================================================ FILE: plugin/eventbus/test/eventbus.test.ts ================================================ import assert from 'assert'; import path from 'path'; import mm, { MockApplication } from 'egg-mock'; import { TimerUtil } from '@eggjs/tegg-common-util'; import { HelloService } from './fixtures/apps/event-app/app/event-module/HelloService'; import { HelloLogger } from './fixtures/apps/event-app/app/event-module/HelloLogger'; import { MultiEventHandler } from './fixtures/apps/event-app/app/event-module/MultiEventHandler'; describe('plugin/eventbus/test/eventbus.test.ts', () => { let app: MockApplication; afterEach(async () => { mm.restore(); }); before(async () => { mm(process.env, 'EGG_TYPESCRIPT', true); mm(process, 'cwd', () => { return path.join(__dirname, '../'); }); app = mm.app({ baseDir: path.join(__dirname, './fixtures/apps/event-app'), framework: require.resolve('egg'), }); await app.ready(); }); after(() => { return app.close(); }); it('msg should work', async () => { await app.mockModuleContextScope(async ctx => { const helloService = await ctx.getEggObject(HelloService); let msg: string | undefined; // helloLogger is in child context mm(HelloLogger.prototype, 'handle', m => { msg = m; }); const eventWaiter = await app.getEventWaiter(); const helloEvent = eventWaiter.await('helloEgg'); helloService.hello(); await helloEvent; assert(msg === '01'); }); }); it('cork/uncork should work', async () => { await app.mockModuleContextScope(async ctx => { const helloService = await ctx.getEggObject(HelloService); let helloTime = 0; // helloLogger is in child context mm(HelloLogger.prototype, 'handle', () => { helloTime = Date.now(); }); helloService.cork(); const triggerTime = Date.now(); helloService.hello(); await TimerUtil.sleep(100); helloService.uncork(); const eventWaiter = await app.getEventWaiter(); const helloEvent = eventWaiter.await('helloEgg'); await helloEvent; assert(helloTime >= triggerTime + 100); }); }); it('can call cork/uncork multi times', async () => { await app.mockModuleContextScope(async ctx => { const helloService = await ctx.getEggObject(HelloService); const eventWaiter = await app.getEventWaiter(); let helloCalled = 0; // helloLogger is in child context mm(HelloLogger.prototype, 'handle', () => { helloCalled++; }); helloService.cork(); helloService.hello(); helloService.uncork(); await eventWaiter.await('helloEgg'); helloService.cork(); helloService.hello(); helloService.uncork(); await eventWaiter.await('helloEgg'); assert(helloCalled === 2); }); }); it('reentry cork/uncork should work', async () => { await app.mockModuleContextScope(async ctx => { const helloService = await ctx.getEggObject(HelloService); const eventWaiter = await app.getEventWaiter(); let helloCalled = 0; // helloLogger is in child context mm(HelloLogger.prototype, 'handle', () => { helloCalled++; }); helloService.cork(); helloService.cork(); helloService.hello(); helloService.uncork(); helloService.uncork(); await eventWaiter.await('helloEgg'); assert(helloCalled === 1); }); }); it('concurrent cork/uncork should work', async () => { let helloCalled = 0; // helloLogger is in child context mm(HelloLogger.prototype, 'handle', () => { helloCalled++; }); await Promise.all([ await app.mockModuleContextScope(async ctx => { const helloService = await ctx.getEggObject(HelloService); const eventWaiter = await app.getEventWaiter(); helloService.cork(); helloService.hello(); await TimerUtil.sleep(100); helloService.uncork(); await eventWaiter.await('helloEgg'); }), await app.mockModuleContextScope(async ctx => { const helloService = await ctx.getEggObject(HelloService); const eventWaiter = await app.getEventWaiter(); helloService.cork(); helloService.hello(); await TimerUtil.sleep(100); helloService.uncork(); await eventWaiter.await('helloEgg'); }), ]); assert(helloCalled === 2); }); it('multi event handler should work', async function() { await app.mockModuleContextScope(async ctx => { const helloService = await ctx.getEggObject(HelloService); let eventName = ''; let msg = ''; mm(MultiEventHandler.prototype, 'handle', (ctx, m) => { eventName = ctx.eventName; msg = m; }); const eventWaiter = await app.getEventWaiter(); const helloEvent = eventWaiter.await('helloEgg'); helloService.hello(); await helloEvent; assert.equal(eventName, 'helloEgg'); assert.equal(msg, '01'); const hiEvent = eventWaiter.await('hiEgg'); helloService.hi(); await hiEvent; assert.equal(eventName, 'hiEgg'); assert.equal(msg, 'Ydream'); }); }); }); ================================================ FILE: plugin/eventbus/test/fixtures/apps/event-app/app/event-module/HelloLogger.ts ================================================ import { Event } from '@eggjs/tegg'; @Event('helloEgg') export class HelloLogger { handle(msg: string) { console.log('hello, ', msg); } } ================================================ FILE: plugin/eventbus/test/fixtures/apps/event-app/app/event-module/HelloService.ts ================================================ import { AccessLevel, ContextProto, Inject, ContextEventBus } from '@eggjs/tegg'; declare module '@eggjs/tegg' { interface Events { helloEgg: (msg: string) => void; hiEgg: (msg: string) => void; trace: () => void, } } @ContextProto({ accessLevel: AccessLevel.PUBLIC, }) export class HelloService { @Inject() private readonly eventBus: ContextEventBus; cork() { this.eventBus.cork(); } uncork() { this.eventBus.uncork(); } hello() { this.eventBus.emit('helloEgg', '01'); } hi() { this.eventBus.emit('hiEgg', 'Ydream'); } traceTest() { this.eventBus.emit('trace'); } } ================================================ FILE: plugin/eventbus/test/fixtures/apps/event-app/app/event-module/MultiEventHandler.ts ================================================ import { Event, EventContext, IEventContext } from '@eggjs/tegg'; @Event('helloEgg') @Event('hiEgg') export class MultiEventHandler { handle(@EventContext()ctx: IEventContext, msg: string) { console.log('How are you', msg, ctx); } } ================================================ FILE: plugin/eventbus/test/fixtures/apps/event-app/app/event-module/package.json ================================================ { "name": "multi-module-common", "eggModule": { "name": "multi-module-common" } } ================================================ FILE: plugin/eventbus/test/fixtures/apps/event-app/config/config.default.js ================================================ 'use strict'; module.exports = function() { const config = { keys: 'test key', security: { csrf: { ignoreJSON: false, } }, }; return config; }; ================================================ FILE: plugin/eventbus/test/fixtures/apps/event-app/config/plugin.js ================================================ 'use strict'; exports.tracer = { package: 'egg-tracer', enable: true, }; exports.tegg = { package: '@eggjs/tegg-plugin', enable: true, }; exports.teggConfig = { package: '@eggjs/tegg-config', enable: true, }; ================================================ FILE: plugin/eventbus/test/fixtures/apps/event-app/package.json ================================================ { "name": "event-app" } ================================================ FILE: plugin/eventbus/tsconfig.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "baseUrl": "./" }, "exclude": [ "node_modules", "test" ] } ================================================ FILE: plugin/eventbus/tsconfig.pub.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "baseUrl": "./" }, "exclude": [ "node_modules", "test" ] } ================================================ FILE: plugin/eventbus/typings/index.d.ts ================================================ import 'egg'; import '@eggjs/tegg-plugin'; import '@eggjs/tegg-config' import { EventBus, EventWaiter } from '@eggjs/tegg'; declare module 'egg' { interface EventbusApplication { getEventbus(): Promise; getEventWaiter(): Promise; } interface Application extends EventbusApplication { } } ================================================ FILE: plugin/langchain/CHANGELOG.md ================================================ # Change Log All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. ## [3.78.15](https://github.com/eggjs/tegg/compare/v3.78.14...v3.78.15) (2026-04-23) **Note:** Version bump only for package @eggjs/tegg-langchain ## [3.78.14](https://github.com/eggjs/tegg/compare/v3.78.13...v3.78.14) (2026-04-21) **Note:** Version bump only for package @eggjs/tegg-langchain ## [3.78.13](https://github.com/eggjs/tegg/compare/v3.78.12...v3.78.13) (2026-04-21) **Note:** Version bump only for package @eggjs/tegg-langchain ## [3.78.12](https://github.com/eggjs/tegg/compare/v3.78.11...v3.78.12) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-langchain ## [3.78.11](https://github.com/eggjs/tegg/compare/v3.78.10...v3.78.11) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-langchain ## [3.78.10](https://github.com/eggjs/tegg/compare/v3.78.9...v3.78.10) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-langchain ## [3.78.9](https://github.com/eggjs/tegg/compare/v3.78.8...v3.78.9) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-langchain ## [3.78.8](https://github.com/eggjs/tegg/compare/v3.78.7...v3.78.8) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-langchain ## [3.78.7](https://github.com/eggjs/tegg/compare/v3.78.6...v3.78.7) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-langchain ## [3.78.6](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.6) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-langchain ## [3.78.5](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.5) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-langchain ## [3.78.4](https://github.com/eggjs/tegg/compare/v3.78.3...v3.78.4) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-langchain ## [3.78.3](https://github.com/eggjs/tegg/compare/v3.78.2...v3.78.3) (2026-04-15) **Note:** Version bump only for package @eggjs/tegg-langchain ## [3.78.2](https://github.com/eggjs/tegg/compare/v3.78.1...v3.78.2) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-langchain ## [3.78.1](https://github.com/eggjs/tegg/compare/v3.78.0...v3.78.1) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-langchain # [3.78.0](https://github.com/eggjs/tegg/compare/v3.77.2...v3.78.0) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-langchain ## [3.77.2](https://github.com/eggjs/tegg/compare/v3.77.1...v3.77.2) (2026-04-07) **Note:** Version bump only for package @eggjs/tegg-langchain ## [3.77.1](https://github.com/eggjs/tegg/compare/v3.77.0...v3.77.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-langchain # [3.77.0](https://github.com/eggjs/tegg/compare/v3.76.1...v3.77.0) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-langchain ## [3.76.1](https://github.com/eggjs/tegg/compare/v3.76.0...v3.76.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-langchain # [3.76.0](https://github.com/eggjs/tegg/compare/v3.75.1...v3.76.0) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-langchain ## [3.75.1](https://github.com/eggjs/tegg/compare/v3.75.0...v3.75.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-langchain # [3.75.0](https://github.com/eggjs/tegg/compare/v3.74.0...v3.75.0) (2026-03-30) **Note:** Version bump only for package @eggjs/tegg-langchain # [3.74.0](https://github.com/eggjs/tegg/compare/v3.73.0...v3.74.0) (2026-03-30) **Note:** Version bump only for package @eggjs/tegg-langchain # [3.73.0](https://github.com/eggjs/tegg/compare/v3.72.0...v3.73.0) (2026-03-25) **Note:** Version bump only for package @eggjs/tegg-langchain # [3.72.0](https://github.com/eggjs/tegg/compare/v3.71.2...v3.72.0) (2026-02-04) ### Features * add structured tool ([#387](https://github.com/eggjs/tegg/issues/387)) ([56c23ad](https://github.com/eggjs/tegg/commit/56c23adb0af25ce0fd3624491eaf7af3fb1570cf)) ## [3.71.2](https://github.com/eggjs/tegg/compare/v3.71.1...v3.71.2) (2026-01-30) ### Bug Fixes * duplicate wrap tracer ([#398](https://github.com/eggjs/tegg/issues/398)) ([c20f55c](https://github.com/eggjs/tegg/commit/c20f55c8ad26ee2236911b3153466cfc17c95c19)) ## [3.71.1](https://github.com/eggjs/tegg/compare/v3.71.0...v3.71.1) (2026-01-28) **Note:** Version bump only for package @eggjs/tegg-langchain # [3.71.0](https://github.com/eggjs/tegg/compare/v3.70.1...v3.71.0) (2026-01-26) ### Features * add trace to log ([#395](https://github.com/eggjs/tegg/issues/395)) ([bcfb895](https://github.com/eggjs/tegg/commit/bcfb89554f1ad0d83acba6e1fc424edbe93ad774)) ## [3.70.1](https://github.com/eggjs/tegg/compare/v3.70.0...v3.70.1) (2026-01-14) ### Bug Fixes * add stream log and fix add node options ([#394](https://github.com/eggjs/tegg/issues/394)) ([9fce038](https://github.com/eggjs/tegg/commit/9fce038b876100f22344ced707a8c8039594aa3b)) # [3.70.0](https://github.com/eggjs/tegg/compare/v3.69.0...v3.70.0) (2026-01-07) **Note:** Version bump only for package @eggjs/tegg-langchain # [3.69.0](https://github.com/eggjs/tegg/compare/v3.68.0...v3.69.0) (2026-01-06) **Note:** Version bump only for package @eggjs/tegg-langchain # [3.68.0](https://github.com/eggjs/tegg/compare/v3.67.2...v3.68.0) (2025-12-29) **Note:** Version bump only for package @eggjs/tegg-langchain ## [3.67.2](https://github.com/eggjs/tegg/compare/v3.67.1...v3.67.2) (2025-12-24) ### Bug Fixes * langchain version ([#384](https://github.com/eggjs/tegg/issues/384)) ([2bdb3b4](https://github.com/eggjs/tegg/commit/2bdb3b49a1891bdbd9bb24c30ca52295ef4833d4)) ## [3.67.1](https://github.com/eggjs/tegg/compare/v3.67.0...v3.67.1) (2025-12-23) **Note:** Version bump only for package @eggjs/tegg-langchain # [3.67.0](https://github.com/eggjs/tegg/compare/v3.66.0...v3.67.0) (2025-12-18) **Note:** Version bump only for package @eggjs/tegg-langchain # [3.66.0](https://github.com/eggjs/tegg/compare/v3.65.3...v3.66.0) (2025-12-16) ### Features * use langchain for deepagents ([#376](https://github.com/eggjs/tegg/issues/376)) ([0af84c7](https://github.com/eggjs/tegg/commit/0af84c7b143cba9234dceb6675ea14004b8b3c9c)) ## [3.65.3](https://github.com/eggjs/tegg/compare/v3.65.2...v3.65.3) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-langchain ## [3.65.2](https://github.com/eggjs/tegg/compare/v3.65.1...v3.65.2) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-langchain ## [3.65.1](https://github.com/eggjs/tegg/compare/v3.65.0...v3.65.1) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-langchain # [3.65.0](https://github.com/eggjs/tegg/compare/v3.64.5...v3.65.0) (2025-12-10) **Note:** Version bump only for package @eggjs/tegg-langchain ## [3.64.5](https://github.com/eggjs/tegg/compare/v3.64.4...v3.64.5) (2025-12-08) **Note:** Version bump only for package @eggjs/tegg-langchain ## [3.64.4](https://github.com/eggjs/tegg/compare/v3.64.3...v3.64.4) (2025-12-06) **Note:** Version bump only for package @eggjs/tegg-langchain ## [3.64.3](https://github.com/eggjs/tegg/compare/v3.64.2...v3.64.3) (2025-12-06) ### Bug Fixes * update trace logger check ([#372](https://github.com/eggjs/tegg/issues/372)) ([b974762](https://github.com/eggjs/tegg/commit/b974762dfccf1bb7b188c233be33014b6336bfee)) ## [3.64.2](https://github.com/eggjs/tegg/compare/v3.64.1...v3.64.2) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-langchain ## [3.64.1](https://github.com/eggjs/tegg/compare/v3.64.0...v3.64.1) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-langchain # [3.64.0](https://github.com/eggjs/tegg/compare/v3.63.2...v3.64.0) (2025-11-27) ### Bug Fixes * mcp zod type and langchain test version ([#369](https://github.com/eggjs/tegg/issues/369)) ([8178168](https://github.com/eggjs/tegg/commit/81781685c392346d21c56b649bfe8bb7a99bc9fb)) ### Features * add langchain decorator ([#356](https://github.com/eggjs/tegg/issues/356)) ([b176c73](https://github.com/eggjs/tegg/commit/b176c7325009c372ce9d17f348b4fc1f1b6d7fb1)) # [3.62.0](https://github.com/eggjs/tegg/compare/v3.61.0...v3.62.0) (2025-09-24) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.61.0](https://github.com/eggjs/tegg/compare/v3.60.3...v3.61.0) (2025-08-15) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.60.3](https://github.com/eggjs/tegg/compare/v3.60.2...v3.60.3) (2025-08-06) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.60.2](https://github.com/eggjs/tegg/compare/v3.60.1...v3.60.2) (2025-08-06) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.60.1](https://github.com/eggjs/tegg/compare/v3.60.0...v3.60.1) (2025-07-28) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.60.0](https://github.com/eggjs/tegg/compare/v3.59.1...v3.60.0) (2025-07-28) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.59.1](https://github.com/eggjs/tegg/compare/v3.59.0...v3.59.1) (2025-07-16) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.59.0](https://github.com/eggjs/tegg/compare/v3.58.0...v3.59.0) (2025-07-07) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.58.0](https://github.com/eggjs/tegg/compare/v3.57.14...v3.58.0) (2025-07-01) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.57.14](https://github.com/eggjs/tegg/compare/v3.57.13...v3.57.14) (2025-06-18) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.57.13](https://github.com/eggjs/tegg/compare/v3.57.12...v3.57.13) (2025-06-18) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.57.12](https://github.com/eggjs/tegg/compare/v3.57.11...v3.57.12) (2025-06-16) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.57.11](https://github.com/eggjs/tegg/compare/v3.57.10...v3.57.11) (2025-06-16) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.57.10](https://github.com/eggjs/tegg/compare/v3.57.9...v3.57.10) (2025-06-12) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.57.9](https://github.com/eggjs/tegg/compare/v3.57.8...v3.57.9) (2025-05-29) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.57.8](https://github.com/eggjs/tegg/compare/v3.57.7...v3.57.8) (2025-05-29) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.57.7](https://github.com/eggjs/tegg/compare/v3.57.6...v3.57.7) (2025-05-28) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.57.6](https://github.com/eggjs/tegg/compare/v3.57.5...v3.57.6) (2025-05-27) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.57.5](https://github.com/eggjs/tegg/compare/v3.57.4...v3.57.5) (2025-05-15) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.57.4](https://github.com/eggjs/tegg/compare/v3.57.3...v3.57.4) (2025-05-14) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.57.3](https://github.com/eggjs/tegg/compare/v3.57.2...v3.57.3) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.57.2](https://github.com/eggjs/tegg/compare/v3.57.1...v3.57.2) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.57.1](https://github.com/eggjs/tegg/compare/v3.57.0...v3.57.1) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.57.0](https://github.com/eggjs/tegg/compare/v3.56.3...v3.57.0) (2025-05-09) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.56.3](https://github.com/eggjs/tegg/compare/v3.56.2...v3.56.3) (2025-05-07) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.56.2](https://github.com/eggjs/tegg/compare/v3.56.1...v3.56.2) (2025-05-01) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.56.1](https://github.com/eggjs/tegg/compare/v3.56.0...v3.56.1) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.56.0](https://github.com/eggjs/tegg/compare/v3.55.0...v3.56.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.55.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.55.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.54.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.54.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.53.0](https://github.com/eggjs/tegg/compare/v3.52.1...v3.53.0) (2025-04-21) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.52.1](https://github.com/eggjs/tegg/compare/v3.52.0...v3.52.1) (2025-03-14) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.52.0](https://github.com/eggjs/tegg/compare/v3.51.2...v3.52.0) (2024-12-30) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.51.2](https://github.com/eggjs/tegg/compare/v3.51.1...v3.51.2) (2024-12-09) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.51.1](https://github.com/eggjs/tegg/compare/v3.51.0...v3.51.1) (2024-11-06) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.51.0](https://github.com/eggjs/tegg/compare/v3.50.1...v3.51.0) (2024-10-30) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.50.1](https://github.com/eggjs/tegg/compare/v3.50.0...v3.50.1) (2024-10-23) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.50.0](https://github.com/eggjs/tegg/compare/v3.49.0...v3.50.0) (2024-10-22) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.49.0](https://github.com/eggjs/tegg/compare/v3.48.1...v3.49.0) (2024-10-21) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.48.1](https://github.com/eggjs/tegg/compare/v3.48.0...v3.48.1) (2024-10-14) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.48.0](https://github.com/eggjs/tegg/compare/v3.47.2...v3.48.0) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.47.2](https://github.com/eggjs/tegg/compare/v3.47.1...v3.47.2) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.47.1](https://github.com/eggjs/tegg/compare/v3.47.0...v3.47.1) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.47.0](https://github.com/eggjs/tegg/compare/v3.46.4...v3.47.0) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.46.4](https://github.com/eggjs/tegg/compare/v3.46.3...v3.46.4) (2024-10-09) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.46.3](https://github.com/eggjs/tegg/compare/v3.46.2...v3.46.3) (2024-10-08) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.46.2](https://github.com/eggjs/tegg/compare/v3.46.1...v3.46.2) (2024-10-07) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.46.1](https://github.com/eggjs/tegg/compare/v3.46.0...v3.46.1) (2024-09-30) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.46.0](https://github.com/eggjs/tegg/compare/v3.45.0...v3.46.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.45.0](https://github.com/eggjs/tegg/compare/v3.44.1...v3.45.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.44.1](https://github.com/eggjs/tegg/compare/v3.44.0...v3.44.1) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.44.0](https://github.com/eggjs/tegg/compare/v3.43.2...v3.44.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.43.2](https://github.com/eggjs/tegg/compare/v3.43.1...v3.43.2) (2024-09-14) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.43.1](https://github.com/eggjs/tegg/compare/v3.43.0...v3.43.1) (2024-09-14) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.43.0](https://github.com/eggjs/tegg/compare/v3.42.0...v3.43.0) (2024-09-13) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.42.0](https://github.com/eggjs/tegg/compare/v3.41.0...v3.42.0) (2024-09-10) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.41.0](https://github.com/eggjs/tegg/compare/v3.40.1...v3.41.0) (2024-08-26) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.40.1](https://github.com/eggjs/tegg/compare/v3.40.0...v3.40.1) (2024-08-23) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.40.0](https://github.com/eggjs/tegg/compare/v3.39.5...v3.40.0) (2024-08-22) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.39.5](https://github.com/eggjs/tegg/compare/v3.39.4...v3.39.5) (2024-08-09) ### Bug Fixes * generate index name with column name ([#230](https://github.com/eggjs/tegg/issues/230)) ([82ec72d](https://github.com/eggjs/tegg/commit/82ec72d4fb8628c847b32d0ddf23a95119ca6ccf)) ## [3.39.4](https://github.com/eggjs/tegg/compare/v3.39.3...v3.39.4) (2024-07-09) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.39.3](https://github.com/eggjs/tegg/compare/v3.39.2...v3.39.3) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.39.2](https://github.com/eggjs/tegg/compare/v3.39.1...v3.39.2) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.39.1](https://github.com/eggjs/tegg/compare/v3.39.0...v3.39.1) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.39.0](https://github.com/eggjs/tegg/compare/v3.38.0...v3.39.0) (2024-04-19) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.38.0](https://github.com/eggjs/tegg/compare/v3.37.3...v3.38.0) (2024-04-18) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.37.3](https://github.com/eggjs/tegg/compare/v3.37.2...v3.37.3) (2024-04-17) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.37.2](https://github.com/eggjs/tegg/compare/v3.37.1...v3.37.2) (2024-04-17) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.37.1](https://github.com/eggjs/tegg/compare/v3.37.0...v3.37.1) (2024-04-16) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.37.0](https://github.com/eggjs/tegg/compare/v3.36.3...v3.37.0) (2024-04-16) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.36.3](https://github.com/eggjs/tegg/compare/v3.36.2...v3.36.3) (2024-04-10) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.36.2](https://github.com/eggjs/tegg/compare/v3.36.1...v3.36.2) (2024-04-08) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.36.1](https://github.com/eggjs/tegg/compare/v3.36.0...v3.36.1) (2024-04-07) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.36.0](https://github.com/eggjs/tegg/compare/v3.35.1...v3.36.0) (2024-04-02) ### Features * impl ajv + typebox Validator ([#201](https://github.com/eggjs/tegg/issues/201)) ([9fd585d](https://github.com/eggjs/tegg/commit/9fd585de9b613466c96b73494a08a494db34ea57)) ## [3.35.1](https://github.com/eggjs/tegg/compare/v3.35.0...v3.35.1) (2024-03-26) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.35.0](https://github.com/eggjs/tegg/compare/v3.34.0...v3.35.0) (2024-03-26) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.34.0](https://github.com/eggjs/tegg/compare/v3.33.1...v3.34.0) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.33.1](https://github.com/eggjs/tegg/compare/v3.33.0...v3.33.1) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.33.0](https://github.com/eggjs/tegg/compare/v3.32.0...v3.33.0) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.32.0](https://github.com/eggjs/tegg/compare/v3.31.0...v3.32.0) (2024-02-19) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.31.0](https://github.com/eggjs/tegg/compare/v3.30.1...v3.31.0) (2024-01-31) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.30.1](https://github.com/eggjs/tegg/compare/v3.30.0...v3.30.1) (2024-01-25) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.30.0](https://github.com/eggjs/tegg/compare/v3.29.0...v3.30.0) (2024-01-17) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.29.0](https://github.com/eggjs/tegg/compare/v3.28.2...v3.29.0) (2023-12-26) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.28.2](https://github.com/eggjs/tegg/compare/v3.28.1...v3.28.2) (2023-12-12) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.28.1](https://github.com/eggjs/tegg/compare/v3.28.0...v3.28.1) (2023-12-11) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.28.0](https://github.com/eggjs/tegg/compare/v3.27.0...v3.28.0) (2023-12-10) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.27.0](https://github.com/eggjs/tegg/compare/v3.26.0...v3.27.0) (2023-11-23) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.26.0](https://github.com/eggjs/tegg/compare/v3.25.2...v3.26.0) (2023-11-17) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.25.2](https://github.com/eggjs/tegg/compare/v3.25.1...v3.25.2) (2023-11-06) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.25.1](https://github.com/eggjs/tegg/compare/v3.25.0...v3.25.1) (2023-11-03) ### Bug Fixes * fix standalone import ConfigSource ([#163](https://github.com/eggjs/tegg/issues/163)) ([6922071](https://github.com/eggjs/tegg/commit/6922071219413a8a11387be3d05f0e3970ce4f48)) # [3.25.0](https://github.com/eggjs/tegg/compare/v3.24.0...v3.25.0) (2023-11-03) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.24.0](https://github.com/eggjs/tegg/compare/v3.23.0...v3.24.0) (2023-10-26) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.23.0](https://github.com/eggjs/tegg/compare/v3.22.0...v3.23.0) (2023-09-20) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.20.0](https://github.com/eggjs/tegg/compare/v3.19.0...v3.20.0) (2023-09-07) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.19.0](https://github.com/eggjs/tegg/compare/v3.18.1...v3.19.0) (2023-08-31) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.18.1](https://github.com/eggjs/tegg/compare/v3.18.0...v3.18.1) (2023-08-29) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.18.0](https://github.com/eggjs/tegg/compare/v3.17.0...v3.18.0) (2023-08-29) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.17.0](https://github.com/eggjs/tegg/compare/v3.16.0...v3.17.0) (2023-08-24) ### Features * impl MultiInstanceProto ([#145](https://github.com/eggjs/tegg/issues/145)) ([12fd5cf](https://github.com/eggjs/tegg/commit/12fd5cff4004578bcc737dcdf4f7e9d1159f5633)) # [3.16.0](https://github.com/eggjs/tegg/compare/v3.15.0...v3.16.0) (2023-08-24) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.14.3](https://github.com/eggjs/tegg/compare/v3.14.2...v3.14.3) (2023-08-14) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.14.2](https://github.com/eggjs/tegg/compare/v3.14.1...v3.14.2) (2023-08-14) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.14.1](https://github.com/eggjs/tegg/compare/v3.14.0...v3.14.1) (2023-08-11) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.14.0](https://github.com/eggjs/tegg/compare/v3.13.0...v3.14.0) (2023-07-31) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.13.0](https://github.com/eggjs/tegg/compare/v3.12.0...v3.13.0) (2023-07-25) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.12.0](https://github.com/eggjs/tegg/compare/v3.11.1...v3.12.0) (2023-07-13) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.11.1](https://github.com/eggjs/tegg/compare/v3.11.0...v3.11.1) (2023-06-29) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.11.0](https://github.com/eggjs/tegg/compare/v3.10.0...v3.11.0) (2023-06-29) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.10.0](https://github.com/eggjs/tegg/compare/v3.9.0...v3.10.0) (2023-06-28) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.9.0](https://github.com/eggjs/tegg/compare/v3.8.0...v3.9.0) (2023-06-20) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.8.0](https://github.com/eggjs/tegg/compare/v3.7.0...v3.8.0) (2023-05-30) ### Features * impl EggObjectLifecycle hook in decorator ([#119](https://github.com/eggjs/tegg/issues/119)) ([cced8a2](https://github.com/eggjs/tegg/commit/cced8a2e009c33d5040fa21d00409fddef471b0e)) # [3.7.0](https://github.com/eggjs/tegg/compare/v3.6.3...v3.7.0) (2023-04-03) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.6.3](https://github.com/eggjs/tegg/compare/v3.6.2...v3.6.3) (2023-03-02) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.6.2](https://github.com/eggjs/tegg/compare/v3.6.1...v3.6.2) (2023-02-16) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.6.1](https://github.com/eggjs/tegg/compare/v3.6.0...v3.6.1) (2023-02-14) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.6.0](https://github.com/eggjs/tegg/compare/v3.5.2...v3.6.0) (2023-02-13) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.5.2](https://github.com/eggjs/tegg/compare/v3.5.1...v3.5.2) (2023-02-10) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.5.1](https://github.com/eggjs/tegg/compare/v3.5.0...v3.5.1) (2023-02-10) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.5.0](https://github.com/eggjs/tegg/compare/v3.4.1...v3.5.0) (2023-02-10) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.4.1](https://github.com/eggjs/tegg/compare/v3.4.0...v3.4.1) (2023-02-02) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.4.0](https://github.com/eggjs/tegg/compare/v3.3.4...v3.4.0) (2023-02-01) ### Features * use singleton model insteadof context ([#89](https://github.com/eggjs/tegg/issues/89)) ([cfdfc05](https://github.com/eggjs/tegg/commit/cfdfc05f13048806274de1a35b1207c073a8519d)) ## [3.3.4](https://github.com/eggjs/tegg/compare/v3.3.3...v3.3.4) (2023-01-29) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.3.3](https://github.com/eggjs/tegg/compare/v3.3.2...v3.3.3) (2023-01-29) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.3.2](https://github.com/eggjs/tegg/compare/v3.3.1...v3.3.2) (2023-01-29) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.3.1](https://github.com/eggjs/tegg/compare/v3.3.0...v3.3.1) (2023-01-28) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.3.0](https://github.com/eggjs/tegg/compare/v3.2.4...v3.3.0) (2023-01-28) ### Features * export singleton orm client ([#82](https://github.com/eggjs/tegg/issues/82)) ([5320af7](https://github.com/eggjs/tegg/commit/5320af77d7e7c5c73b80560a576f2ce01fc21fff)) ## [3.2.3](https://github.com/eggjs/tegg/compare/v3.2.2...v3.2.3) (2023-01-16) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.2.2](https://github.com/eggjs/tegg/compare/v3.2.1...v3.2.2) (2023-01-06) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.2.1](https://github.com/eggjs/tegg/compare/v3.2.0...v3.2.1) (2022-12-28) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.2.0](https://github.com/eggjs/tegg/compare/v3.1.0...v3.2.0) (2022-12-28) ### Features * impl mockModuleContextScope ([#73](https://github.com/eggjs/tegg/issues/73)) ([041881c](https://github.com/eggjs/tegg/commit/041881ca317ad81366172a35ac56b7b2dc0a0488)) # [3.1.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.1.0) (2022-12-27) ### Features * **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) * impl Inject Model ([#43](https://github.com/eggjs/tegg/issues/43)) ([ced2ce2](https://github.com/eggjs/tegg/commit/ced2ce2134964dcb410410c0192a34f77507c42d)) * impl Schedule decorator ([#52](https://github.com/eggjs/tegg/issues/52)) ([7f95005](https://github.com/eggjs/tegg/commit/7f950050b548ca542addbd7b466675da4e81ce3f)) # [3.0.0](https://github.com/eggjs/tegg/compare/v3.0.0-alpha.0...v3.0.0) (2022-12-26) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.0.0-alpha.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.0.0-alpha.0) (2022-12-22) ### Features * **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) * impl Inject Model ([#43](https://github.com/eggjs/tegg/issues/43)) ([ced2ce2](https://github.com/eggjs/tegg/commit/ced2ce2134964dcb410410c0192a34f77507c42d)) * impl Schedule decorator ([#52](https://github.com/eggjs/tegg/issues/52)) ([7f95005](https://github.com/eggjs/tegg/commit/7f950050b548ca542addbd7b466675da4e81ce3f)) ## [2.2.1](https://github.com/eggjs/tegg/compare/@eggjs/tegg-orm-plugin@2.2.0...@eggjs/tegg-orm-plugin@2.2.1) (2022-09-05) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [2.2.0](https://github.com/eggjs/tegg/compare/@eggjs/tegg-orm-plugin@2.1.4...@eggjs/tegg-orm-plugin@2.2.0) (2022-09-04) ### Features * impl Schedule decorator ([#52](https://github.com/eggjs/tegg/issues/52)) ([7f95005](https://github.com/eggjs/tegg/commit/7f950050b548ca542addbd7b466675da4e81ce3f)) ## [2.1.4](https://github.com/eggjs/tegg/compare/@eggjs/tegg-orm-plugin@2.1.3...@eggjs/tegg-orm-plugin@2.1.4) (2022-08-16) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [2.1.2](https://github.com/eggjs/tegg/compare/@eggjs/tegg-orm-plugin@2.1.1...@eggjs/tegg-orm-plugin@2.1.2) (2022-07-28) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [2.1.1](https://github.com/eggjs/tegg/compare/@eggjs/tegg-orm-plugin@2.1.0...@eggjs/tegg-orm-plugin@2.1.1) (2022-07-20) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [2.1.0](https://github.com/eggjs/tegg/compare/@eggjs/tegg-orm-plugin@2.0.0...@eggjs/tegg-orm-plugin@2.1.0) (2022-07-20) ### Features * impl Inject Model ([#43](https://github.com/eggjs/tegg/issues/43)) ([ced2ce2](https://github.com/eggjs/tegg/commit/ced2ce2134964dcb410410c0192a34f77507c42d)) # [1.3.0](https://github.com/eggjs/tegg/compare/v1.2.0...v1.3.0) (2022-07-01) ### Features * support leoric hooks ([#41](https://github.com/eggjs/tegg/issues/41)) ([9bdbc2c](https://github.com/eggjs/tegg/commit/9bdbc2cbe96df9f66f96b4f8e208883e99957946)) # [0.2.0](https://github.com/eggjs/tegg/compare/v0.1.19...v0.2.0) (2022-01-20) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ================================================ FILE: plugin/langchain/README.md ================================================ # @eggjs/tegg-langchain 使用注解的方式来开发 egg 中的 orm ## Install ```shell npm i --save @eggjs/tegg-orm-plugin ``` ## Config ```js // config/plugin.js exports.teggOrm = { package: '@eggjs/tegg-orm-plugin', enable: true, }; ``` ================================================ FILE: plugin/langchain/app.ts ================================================ import type { Application, IBoot } from 'egg'; import { GlobalGraph } from '@eggjs/tegg-metadata'; import { GraphObjectHook } from './lib/graph/GraphObjectHook'; import { GraphLoadUnitHook } from './lib/graph/GraphLoadUnitHook'; import { CompiledStateGraphProto } from './lib/graph/CompiledStateGraphProto'; import { CompiledStateGraphObject } from './lib/graph/CompiledStateGraphObject'; import { BoundModelObjectHook } from './lib/boundModel/BoundModelObjectHook'; import { GraphPrototypeHook } from './lib/graph/GraphPrototypeHook'; import { GraphBuildHook } from './lib/graph/GraphBuildHook'; export default class ModuleLangChainHook implements IBoot { readonly #app: Application; readonly #graphObjectHook: GraphObjectHook; readonly #graphLoadUnitHook: GraphLoadUnitHook; readonly #boundModelObjectHook: BoundModelObjectHook; readonly #graphPrototypeHook: GraphPrototypeHook; constructor(app: Application) { this.#app = app; this.#graphObjectHook = new GraphObjectHook(); this.#graphLoadUnitHook = new GraphLoadUnitHook(this.#app.eggPrototypeFactory); this.#boundModelObjectHook = new BoundModelObjectHook(); this.#graphPrototypeHook = new GraphPrototypeHook(); this.#app.loadUnitLifecycleUtil.registerLifecycle(this.#graphLoadUnitHook); } configWillLoad() { this.#app.eggObjectLifecycleUtil.registerLifecycle(this.#graphObjectHook); this.#app.eggObjectLifecycleUtil.registerLifecycle(this.#boundModelObjectHook); this.#app.eggObjectFactory.registerEggObjectCreateMethod(CompiledStateGraphProto, CompiledStateGraphObject.createObject); this.#app.eggPrototypeLifecycleUtil.registerLifecycle(this.#graphPrototypeHook); } configDidLoad() { GlobalGraph.instance!.registerBuildHook(GraphBuildHook); } async beforeClose() { this.#app.eggObjectLifecycleUtil.deleteLifecycle(this.#graphObjectHook); this.#app.eggObjectLifecycleUtil.deleteLifecycle(this.#boundModelObjectHook); this.#app.loadUnitLifecycleUtil.deleteLifecycle(this.#graphLoadUnitHook); this.#app.eggPrototypeLifecycleUtil.deleteLifecycle(this.#graphPrototypeHook); } } ================================================ FILE: plugin/langchain/index.ts ================================================ export * from './lib/ChatOpenAI'; export * from './lib/boundModel/BoundModelObjectHook'; export * from './lib/config/QualifierUtil'; export * from './lib/graph/CompiledStateGraphObject'; export * from './lib/graph/CompiledStateGraphProto'; export * from './lib/graph/GraphBuildHook'; export * from './lib/graph/GraphLoadUnitHook'; export * from './lib/graph/GraphObjectHook'; export * from './lib/graph/GraphPrototypeHook'; export * from './lib/tracing/LangGraphTracer'; ================================================ FILE: plugin/langchain/lib/ChatModelHelper.ts ================================================ import { ChatModelInjectName, ChatModelQualifierAttribute, } from '@eggjs/tegg-langchain-decorator'; export class ChatModelHelper { static getChatModelQualifier(clientName: string) { return { [ChatModelInjectName]: [{ attribute: ChatModelQualifierAttribute, value: clientName, }], }; } } ================================================ FILE: plugin/langchain/lib/ChatOpenAI.ts ================================================ import { AccessLevel, Inject, MultiInstanceInfo, MultiInstanceProto, MultiInstancePrototypeGetObjectsContext, ObjectInfo, ObjectInitType, } from '@eggjs/tegg'; import { ModuleConfig, ModuleConfigUtil } from '@eggjs/tegg-common-util'; import { ChatOpenAI } from '@langchain/openai'; import { getChatModelConfig, getClientNames } from './util'; import { QualifierUtil } from './config/QualifierUtil'; import { fetch, FetchFactory, Agent } from 'urllib'; import { ChatModelInjectName, ChatModelQualifierAttribute } from '@eggjs/tegg-langchain-decorator'; import { ChatModelHelper } from './ChatModelHelper'; @MultiInstanceProto({ accessLevel: AccessLevel.PUBLIC, initType: ObjectInitType.SINGLETON, // 从 module.yml 中动态获取配置来决定需要初始化几个对象 getObjects(ctx: MultiInstancePrototypeGetObjectsContext) { const config: ModuleConfig = ModuleConfigUtil.loadModuleConfigSync(ctx.unitPath); const moduleName = ModuleConfigUtil.readModuleNameSync(ctx.unitPath); return getClientNames(config, 'ChatModel') .filter(name => { return (config as any).ChatModel.clients[name].type === 'openai'; }) .map(name => { return { name: ChatModelInjectName, qualifiers: ChatModelHelper.getChatModelQualifier(name)[ChatModelInjectName], properQualifiers: { ...QualifierUtil.getModuleConfigQualifier(moduleName), }, }; }); }, }) export class ChatOpenAIModel extends ChatOpenAI { constructor( @Inject() readonly moduleConfig: ModuleConfig, @MultiInstanceInfo([ ChatModelQualifierAttribute ]) objInfo: ObjectInfo, ) { const chatConfig = getChatModelConfig(moduleConfig, objInfo); chatConfig.configuration = chatConfig.configuration || {}; // 如果依赖中存在低版本 urllib 会引发问题,因此需要手动设置好 FetchFactory.setDispatcher(new Agent({ allowH2: true })); (chatConfig.configuration as any).fetch = fetch as any; super(chatConfig as any); } } ================================================ FILE: plugin/langchain/lib/boundModel/BoundModelObjectHook.ts ================================================ import * as z from 'zod/v4'; import { MCPInfoUtil, type LifecycleHook } from '@eggjs/tegg'; import { ChatModelInjectName, ChatModelQualifierAttribute, IGraphNode, IGraphTool, BOUND_MODEL_METADATA, GraphToolInfoUtil, IBoundModelMetadata, } from '@eggjs/tegg-langchain-decorator'; import { ConfigurableModel } from 'langchain/chat_models/universal'; import { MCPClientInjectName, MCPClientQualifierAttribute } from '@eggjs/mcp-client'; import { loadMcpTools } from '@langchain/mcp-adapters'; import { EggContainerFactory, EggObject, EggObjectLifeCycleContext } from '@eggjs/tegg-runtime'; import { EggPrototypeWithClazz } from '@eggjs/tegg/helper'; import { DynamicStructuredTool } from 'langchain'; class BoundModelHandler { boundModelMetadata: IBoundModelMetadata; eggObject: EggObject; constructor(nodeMetadata: IBoundModelMetadata, eggObject: EggObject) { this.boundModelMetadata = nodeMetadata; this.eggObject = eggObject; } async findGraphTools() { const tools = this.boundModelMetadata.tools ?? []; let dTools: Parameters['0'] = []; for (let i = 0; i < tools.length; i++) { const toolsObj = await EggContainerFactory.getOrCreateEggObjectFromClazz(tools[i]); const toolMetadata = GraphToolInfoUtil.getGraphToolMetadata((toolsObj.proto as unknown as EggPrototypeWithClazz).clazz!); const ToolDetail = MCPInfoUtil.getMCPToolArgsIndex((toolsObj.proto as unknown as EggPrototypeWithClazz).clazz!, 'execute'); if (toolMetadata && ToolDetail) { const tool = new DynamicStructuredTool({ description: toolMetadata.description, name: toolMetadata.toolName, func: (toolsObj.obj as unknown as IGraphTool).execute.bind(toolsObj.obj), schema: z.object(ToolDetail.argsSchema) as any, }); dTools = dTools.concat(tool); } } return dTools; } async findMcpServerTools() { const mcpServers = this.boundModelMetadata.mcpServers ?? []; let sTools: Parameters['0'] = []; for (let i = 0; i < mcpServers.length; i++) { const mcpClientObj = await EggContainerFactory.getOrCreateEggObjectFromName(MCPClientInjectName, [{ attribute: MCPClientQualifierAttribute, value: mcpServers[i], }]); const tool = await loadMcpTools(mcpServers[i], mcpClientObj.obj as any, { throwOnLoadError: true, prefixToolNameWithServerName: false, additionalToolNamePrefix: '', }); sTools = sTools.concat(tool); } return sTools; } async boundTools() { const nodeObj = this.eggObject.obj as IGraphNode; const dTools = await this.findGraphTools(); const sTools = await this.findMcpServerTools(); const chatModelObj = await EggContainerFactory.getOrCreateEggObjectFromName(ChatModelInjectName, [{ attribute: ChatModelQualifierAttribute, value: this.boundModelMetadata.modelName, }]); const chatModel = chatModelObj.obj as ConfigurableModel; const boundChatModel = chatModel.bindTools([ ...dTools, ...sTools ]); Object.setPrototypeOf(Object.getPrototypeOf(nodeObj), boundChatModel); } } export class BoundModelObjectHook implements LifecycleHook { async postCreate(_: EggObjectLifeCycleContext, eggObject: EggObject) { const BoundModelMetadata = eggObject.proto.getMetaData(BOUND_MODEL_METADATA); // 找到 graph node if (BoundModelMetadata) { const handler = new BoundModelHandler(BoundModelMetadata, eggObject); await handler.boundTools(); return; } } } ================================================ FILE: plugin/langchain/lib/config/QualifierUtil.ts ================================================ import { ConfigSourceQualifierAttribute } from '@eggjs/tegg'; // TODO refactor to ModuleConfig and mist impl export class QualifierUtil { static getModuleConfigQualifier(moduleName: string) { return { moduleConfig: [{ attribute: ConfigSourceQualifierAttribute, value: moduleName, }], }; } } ================================================ FILE: plugin/langchain/lib/graph/CompiledStateGraphObject.ts ================================================ import { ContextHandler, EggContainerFactory, EggContext, EggObject, EggObjectStatus, } from '@eggjs/tegg-runtime'; import { EggObjectName, EggPrototypeName, Id, IdenticalUtil } from '@eggjs/tegg'; import { CompiledStateGraphProto } from './CompiledStateGraphProto'; import { EggPrototype } from '@eggjs/tegg-metadata'; import { ChatCheckpointSaverInjectName, ChatCheckpointSaverQualifierAttribute, GRAPH_EDGE_METADATA, GRAPH_NODE_METADATA, GraphEdgeMetadata, GraphMetadata, GraphNodeMetadata, IGraph, IGraphEdge, IGraphNode, TeggToolNode } from '@eggjs/tegg-langchain-decorator'; import { LangGraphTracer } from '../tracing/LangGraphTracer'; import { BaseCheckpointSaver, CompiledStateGraph } from '@langchain/langgraph'; import { EGG_CONTEXT } from '@eggjs/egg-module-common'; export class CompiledStateGraphObject implements EggObject { private status: EggObjectStatus = EggObjectStatus.PENDING; id: Id; readonly name: EggPrototypeName; readonly proto: CompiledStateGraphProto; readonly ctx: EggContext; readonly daoName: string; private _obj: object; readonly graphMetadata: GraphMetadata; readonly graphName: string; constructor(name: EggObjectName, proto: CompiledStateGraphProto) { this.name = name; this.proto = proto; this.ctx = ContextHandler.getContext()!; this.id = IdenticalUtil.createObjectId(this.proto.id, this.ctx?.id); this.graphMetadata = proto.graphMetadata; this.graphName = proto.graphName; } async init() { this._obj = await this.build(); const graph = this._obj as CompiledStateGraph; const originalStream = graph.stream; const langGraphTraceObj = await EggContainerFactory.getOrCreateEggObjectFromName('langGraphTracer'); const tracer = langGraphTraceObj.obj as LangGraphTracer; tracer.setName(this.graphName); graph.stream = (input: any, config?: any) => this.wrapGraphMethod(originalStream.bind(graph), input, config); this.status = EggObjectStatus.READY; } async build() { const stateGraph = await EggContainerFactory.getOrCreateEggObjectFromName(this.graphName); await this.boundNodes(stateGraph); await this.boundEdges(stateGraph); const graphObj = stateGraph.obj as IGraph; const checkpoint = this.graphMetadata.checkpoint; let compileGraph; if (checkpoint) { let checkpointObj: EggObject; if (typeof checkpoint !== 'string') { checkpointObj = await EggContainerFactory.getOrCreateEggObjectFromClazz(checkpoint); } else { checkpointObj = await EggContainerFactory.getOrCreateEggObjectFromName(ChatCheckpointSaverInjectName, [{ attribute: ChatCheckpointSaverQualifierAttribute, value: checkpoint, }]); } compileGraph = graphObj.compile({ checkpointer: checkpointObj.obj as BaseCheckpointSaver, }); } else { compileGraph = graphObj.compile(); } return compileGraph; } async boundNodes(stateGraph: EggObject) { const graphObj = stateGraph.obj as IGraph; const nodes = this.graphMetadata.nodes ?? []; for (let i = 0; i < nodes.length; i++) { const node = await EggContainerFactory.getOrCreateEggObjectFromClazz(nodes[i]); const nodeObj = node.obj as unknown as IGraphNode; const nodeMetadata = node.proto.getMetaData(GRAPH_NODE_METADATA); if (nodeMetadata) { if (TeggToolNode.prototype.isPrototypeOf(nodeObj)) { graphObj.addNode(nodeMetadata.nodeName, (nodeObj as unknown as TeggToolNode).toolNode); } else { graphObj.addNode(nodeMetadata.nodeName, nodeObj.execute.bind(nodeObj), nodeObj.options); } } } } async boundEdges(stateGraph: EggObject) { const graphObj = stateGraph.obj as IGraph; const edges = this.graphMetadata.edges ?? []; for (let i = 0; i < edges.length; i++) { const edge = await EggContainerFactory.getOrCreateEggObjectFromClazz(edges[i]); const edgeObj = edge.obj as unknown as IGraphEdge; const edgeMetadata = edge.proto.getMetaData(GRAPH_EDGE_METADATA); if (edgeMetadata) { if (edgeObj.execute) { graphObj.addConditionalEdges(edgeMetadata.fromNodeName, edgeObj.execute.bind(edgeObj), edgeMetadata.toNodeNames); } else { graphObj.addEdge(edgeMetadata.fromNodeName, edgeMetadata.toNodeNames[0]); } } } } /** * 包装 graph 方法,添加 tracing */ async wrapGraphMethod( originalMethod: (input: any, config?: any) => Promise, input: any, config?: any, ) { // 确保 config 对象存在 const finalConfig = config || {}; // 准备 tracer const shouldTrace = finalConfig.tags?.includes('trace-log'); if (shouldTrace) { const langGraphTraceObj = await EggContainerFactory.getOrCreateEggObjectFromClazz(LangGraphTracer); const tracer = langGraphTraceObj.obj as LangGraphTracer; tracer.setName(this.graphName); finalConfig.callbacks = [ tracer, ...(finalConfig.callbacks || []) ]; } // 设置 runId if (!finalConfig.runId) { const trace = await this.getTracer(); finalConfig.runId = trace?.traceId; } return await originalMethod(input, finalConfig); } async getTracer() { const ctx = ContextHandler.getContext()!.get(EGG_CONTEXT); return ctx.tracer; } injectProperty() { throw new Error('never call GraphObject#injectProperty'); } get isReady() { return this.status === EggObjectStatus.READY; } get obj() { return this._obj; } static async createObject(name: EggObjectName, proto: EggPrototype): Promise { const compiledStateGraphObject = new CompiledStateGraphObject(name, proto as CompiledStateGraphProto); await compiledStateGraphObject.init(); return compiledStateGraphObject; } } ================================================ FILE: plugin/langchain/lib/graph/CompiledStateGraphProto.ts ================================================ import { EggPrototype, LoadUnit } from '@eggjs/tegg-metadata'; import { InjectObjectProto, } from '@eggjs/tegg-types'; import { AccessLevel, EggPrototypeName, ObjectInitType, QualifierInfo, Id, IdenticalUtil, QualifierAttribute, QualifierValue, } from '@eggjs/tegg'; import { GraphMetadata } from '@eggjs/tegg-langchain-decorator'; export class CompiledStateGraphProto implements EggPrototype { private readonly qualifiers: QualifierInfo[]; readonly accessLevel = AccessLevel.PUBLIC; id: Id; readonly initType = ObjectInitType.SINGLETON; readonly injectObjects: InjectObjectProto[] = []; loadUnitId: string; readonly name: EggPrototypeName; readonly graphMetadata: GraphMetadata; readonly graphName: string; constructor(loadUnit: LoadUnit, protoName: string, graphName: string, graphMetadata: GraphMetadata) { this.loadUnitId = loadUnit.id; this.qualifiers = []; this.name = protoName; this.graphMetadata = graphMetadata; this.graphName = graphName; this.id = IdenticalUtil.createProtoId(loadUnit.id, protoName); } constructEggObject(): object { return {}; } getMetaData(): T | undefined { // TODO set default metadata return; } verifyQualifier(qualifier: QualifierInfo): boolean { const selfQualifiers = this.qualifiers.find(t => t.attribute === qualifier.attribute); return selfQualifiers?.value === qualifier.value; } verifyQualifiers(qualifiers: QualifierInfo[]): boolean { for (const qualifier of qualifiers) { if (!this.verifyQualifier(qualifier)) { return false; } } return true; } getQualifier(attribute: QualifierAttribute): QualifierValue | undefined { const qualifier = this.qualifiers.find(t => t.attribute === attribute); return qualifier?.value; } } ================================================ FILE: plugin/langchain/lib/graph/GraphBuildHook.ts ================================================ import { AbstractStateGraph } from '@eggjs/tegg-langchain-decorator'; import { LangGraphTracer } from '../tracing/LangGraphTracer'; import { ClassProtoDescriptor, GlobalGraph } from '@eggjs/tegg-metadata'; export function GraphBuildHook(globalGraph: GlobalGraph) { let langchainGraphTracerProtoNode; for (const moduleNode of globalGraph.moduleGraph.nodes.values()) { for (const protoNode of moduleNode.val.protos) { if ((protoNode.val.proto as ClassProtoDescriptor)?.clazz && (LangGraphTracer.isPrototypeOf((protoNode.val.proto as ClassProtoDescriptor).clazz) || (protoNode.val.proto as ClassProtoDescriptor).clazz === LangGraphTracer)) { langchainGraphTracerProtoNode = protoNode; } } } for (const moduleNode of globalGraph.moduleGraph.nodes.values()) { for (const protoNode of moduleNode.val.protos) { if ( (protoNode.val.proto as ClassProtoDescriptor)?.clazz && AbstractStateGraph.isPrototypeOf((protoNode.val.proto as ClassProtoDescriptor).clazz) ) { globalGraph.addInject( moduleNode, protoNode, langchainGraphTracerProtoNode, langchainGraphTracerProtoNode.val.proto.name, ); } } } } ================================================ FILE: plugin/langchain/lib/graph/GraphLoadUnitHook.ts ================================================ import { AccessLevel, EggProtoImplClass, LifecycleHook, LifecyclePostInject, MCPInfoUtil, SingletonProto } from '@eggjs/tegg'; import { ClassProtoDescriptor, EggPrototypeCreatorFactory, EggPrototypeFactory, EggPrototypeWithClazz, LoadUnit, LoadUnitLifecycleContext, ProtoDescriptorHelper, } from '@eggjs/tegg-metadata'; import { CompiledStateGraphProto } from './CompiledStateGraphProto'; import { GraphInfoUtil, GraphToolInfoUtil, IGraphMetadata, IGraphTool, IGraphToolMetadata } from '@eggjs/tegg-langchain-decorator'; import assert from 'node:assert'; import { EggContainerFactory } from '@eggjs/tegg-runtime'; import { DynamicStructuredTool } from 'langchain'; import * as z from 'zod/v4'; export class GraphLoadUnitHook implements LifecycleHook { private readonly eggPrototypeFactory: EggPrototypeFactory; clazzMap: Map; graphCompiledNameMap: Map = new Map(); tools: Map; constructor(eggPrototypeFactory: EggPrototypeFactory) { this.eggPrototypeFactory = eggPrototypeFactory; this.clazzMap = GraphInfoUtil.getAllGraphMetadata(); this.tools = GraphToolInfoUtil.getAllGraphToolMetadata(); } async preCreate(ctx: LoadUnitLifecycleContext, loadUnit: LoadUnit): Promise { const clazzList = await ctx.loader.load(); for (const clazz of clazzList) { const meta = this.clazzMap.get(clazz as EggProtoImplClass); if (meta) { const protoName = clazz.name[0].toLowerCase() + clazz.name.substring(1); const graphMetadata = GraphInfoUtil.getGraphMetadata(clazz as EggProtoImplClass); assert(graphMetadata, `${clazz.name} graphMetadata should not be null`); const proto = new CompiledStateGraphProto(loadUnit, `compiled${protoName.replace(protoName[0], protoName[0].toUpperCase())}`, protoName, graphMetadata); this.eggPrototypeFactory.registerPrototype(proto, loadUnit); this.graphCompiledNameMap.set(protoName, proto); } const toolMeta = this.tools.get(clazz as EggProtoImplClass); if (toolMeta) { const StructuredTool = this.createStructuredTool(clazz, toolMeta); const protoDescriptor = ProtoDescriptorHelper.createByInstanceClazz(StructuredTool, { moduleName: loadUnit.name, unitPath: loadUnit.unitPath, }) as ClassProtoDescriptor; const proto = await EggPrototypeCreatorFactory.createProtoByDescriptor(protoDescriptor, loadUnit); this.eggPrototypeFactory.registerPrototype(proto, loadUnit); } } } createStructuredTool(clazz: EggProtoImplClass, toolMeta: IGraphToolMetadata) { class StructuredTool { @LifecyclePostInject() async init() { const toolsObj = await EggContainerFactory.getOrCreateEggObjectFromClazz(clazz); const toolMetadata = GraphToolInfoUtil.getGraphToolMetadata((toolsObj.proto as unknown as EggPrototypeWithClazz).clazz!); const ToolDetail = MCPInfoUtil.getMCPToolArgsIndex((toolsObj.proto as unknown as EggPrototypeWithClazz).clazz!, 'execute'); if (toolMetadata && ToolDetail) { const tool = new DynamicStructuredTool({ description: toolMetadata.description, name: toolMetadata.toolName, func: (toolsObj.obj as unknown as IGraphTool).execute.bind(toolsObj.obj), schema: z.object(ToolDetail.argsSchema) as any, }); Object.setPrototypeOf(this, tool); } else { throw new Error(`graph tool ${toolMeta.name ?? clazz.name} not found`); } } } SingletonProto({ name: `structured${toolMeta.name ?? clazz.name}`, accessLevel: AccessLevel.PUBLIC })(StructuredTool); return StructuredTool; } async postCreate(_ctx: LoadUnitLifecycleContext, obj: LoadUnit): Promise { for (const graphName of this.graphCompiledNameMap.keys()) { const graphProto = obj.getEggPrototype(graphName, [])[0]; if (graphProto) { const compiledGraphProto = this.graphCompiledNameMap.get(graphName) as CompiledStateGraphProto; if (!compiledGraphProto.injectObjects.find(injectObject => injectObject.objName === graphProto.name)) { compiledGraphProto.injectObjects.push({ refName: 'stateGraph', objName: graphProto.name, qualifiers: [], proto: graphProto, }); } } } } } ================================================ FILE: plugin/langchain/lib/graph/GraphObjectHook.ts ================================================ import * as z from 'zod/v4'; import { MCPInfoUtil, type LifecycleHook } from '@eggjs/tegg'; import { EggObjectLifeCycleContext, EggObject, EggContainerFactory, EggPrototypeWithClazz } from '@eggjs/tegg/helper'; import { ChatModelInjectName, ChatModelQualifierAttribute, IGraphNode, IGraphTool, GraphNodeMetadata, GRAPH_NODE_METADATA, TeggToolNode, GraphToolInfoUtil, } from '@eggjs/tegg-langchain-decorator'; import { MCPClientInjectName, MCPClientQualifierAttribute } from '@eggjs/mcp-client'; import { loadMcpTools } from '@langchain/mcp-adapters'; import { ConfigurableModel } from 'langchain/chat_models/universal'; import { DynamicStructuredTool } from 'langchain'; import { ToolNode } from '@langchain/langgraph/prebuilt'; class GraphNodeHandler { nodeMetadata: GraphNodeMetadata; eggObject: EggObject; constructor(nodeMetadata: GraphNodeMetadata, eggObject: EggObject) { this.nodeMetadata = nodeMetadata; this.eggObject = eggObject; } async findGraphTools() { const tools = this.nodeMetadata.tools ?? []; let dTools: Parameters['0'] = []; for (let i = 0; i < tools.length; i++) { const toolsObj = await EggContainerFactory.getOrCreateEggObjectFromClazz(tools[i]); const toolMetadata = GraphToolInfoUtil.getGraphToolMetadata((toolsObj.proto as unknown as EggPrototypeWithClazz).clazz!); const ToolDetail = MCPInfoUtil.getMCPToolArgsIndex((toolsObj.proto as unknown as EggPrototypeWithClazz).clazz!, 'execute'); if (toolMetadata && ToolDetail) { const tool = new DynamicStructuredTool({ description: toolMetadata.description, name: toolMetadata.toolName, func: (toolsObj.obj as unknown as IGraphTool).execute.bind(toolsObj.obj), schema: z.object(ToolDetail.argsSchema) as any, }); dTools = dTools.concat(tool); } } return dTools; } async findMcpServerTools() { const mcpServers = this.nodeMetadata.mcpServers ?? []; let sTools: Parameters['0'] = []; for (let i = 0; i < mcpServers.length; i++) { const mcpClientObj = await EggContainerFactory.getOrCreateEggObjectFromName(MCPClientInjectName, [{ attribute: MCPClientQualifierAttribute, value: mcpServers[i], }]); const tool = await loadMcpTools(mcpServers[i], mcpClientObj.obj as any, { throwOnLoadError: true, prefixToolNameWithServerName: false, additionalToolNamePrefix: '', }); sTools = sTools.concat(tool); } return sTools; } async boundTools() { const nodeObj = this.eggObject.obj as IGraphNode; const dTools = await this.findGraphTools(); const sTools = await this.findMcpServerTools(); if (TeggToolNode.prototype.isPrototypeOf(nodeObj)) { const toolNode = new ToolNode([ ...(dTools as DynamicStructuredTool[]), ...(sTools as DynamicStructuredTool[]) ]); Object.defineProperty(nodeObj, 'toolNode', { get: () => toolNode, }); return; } // 如果用户写了 build 方法,则让他自己绑定 if (nodeObj.build) { nodeObj.build!([ ...dTools, ...sTools ]); } else { // 否则自动绑定 const injectObjects = this.eggObject.proto.injectObjects; for (let i = 0; i < injectObjects.length; i++) { const injectObject = injectObjects[i]; const qualifiers = injectObject.qualifiers; for (let j = 0; j < qualifiers.length; j++) { const qualifier = qualifiers[j]; if (qualifier.attribute === ChatModelQualifierAttribute) { // 找到当前 eggObject 上的 chatModel const chatModelObj = await EggContainerFactory.getOrCreateEggObjectFromName(ChatModelInjectName, [{ attribute: ChatModelQualifierAttribute, value: qualifier.value, }]); const chatModel = chatModelObj.obj as ConfigurableModel; // 绑定 const boundChatModel = chatModel.bindTools([ ...dTools, ...sTools ]); // 劫持 Object.defineProperty(nodeObj, injectObject.objName, { get: () => boundChatModel, }); } } } } } } export class GraphObjectHook implements LifecycleHook { async postCreate(_: EggObjectLifeCycleContext, eggObject: EggObject) { const nodeMetadata = eggObject.proto.getMetaData(GRAPH_NODE_METADATA); // 找到 graph node if (nodeMetadata) { const handler = new GraphNodeHandler(nodeMetadata, eggObject); await handler.boundTools(); return; } } } ================================================ FILE: plugin/langchain/lib/graph/GraphPrototypeHook.ts ================================================ import { EggProtoImplClass, LifecycleHook } from '@eggjs/tegg'; import { EggPrototype, EggPrototypeLifecycleContext } from '@eggjs/tegg-metadata'; import { ChatCheckpointSaverInjectName, ChatCheckpointSaverQualifierAttribute, GraphEdgeInfoUtil, GraphInfoUtil, GraphNodeInfoUtil, GraphToolInfoUtil } from '@eggjs/tegg-langchain-decorator'; import { MCPClientInjectName, MCPClientQualifierAttribute } from '@eggjs/mcp-client'; import { BaseCheckpointSaver } from '@langchain/langgraph'; export class GraphPrototypeHook implements LifecycleHook { toolProtoMap: Map = new Map(); nodeProtoMap: Map = new Map(); nodeMcpServerProtoMap: Map = new Map(); mcpServerProto: EggPrototype; checkPointProto: EggPrototype; checkPointList: EggProtoImplClass[] = []; checkPointProtoMap: Map = new Map(); checkPointGraphMap: Map = new Map(); graphCheckpointProtoMap: Map = new Map(); graphNodeProtoMap: Map = new Map(); nodeProto: Map = new Map(); graphEdgeProtoMap: Map = new Map(); edgeProto: Map = new Map(); async postCreate(ctx: EggPrototypeLifecycleContext, proto: EggPrototype): Promise { await this.makeNodeDependencies(ctx, proto); await this.makeGraphDependencies(ctx, proto); } async makeNodeDependencies(ctx: EggPrototypeLifecycleContext, proto: EggPrototype): Promise { const nodeMetadata = GraphNodeInfoUtil.getGraphNodeMetadata(ctx.clazz); if (nodeMetadata) { const tools = nodeMetadata.tools ?? []; // 找到所有已经创建好的 tool proto for (let i = 0; i < tools.length; i++) { if (this.toolProtoMap.has(tools[i])) { const toolProto = this.toolProtoMap.get(tools[i])!; proto.injectObjects.push({ refName: `__GRAPH_TOOL_${String(toolProto.name)}__`, objName: toolProto.name, qualifiers: [], proto: toolProto, }); } } const mcpServers = nodeMetadata.mcpServers ?? []; if (this.mcpServerProto) { for (let i = 0; i < mcpServers.length; i++) { proto.injectObjects.push({ refName: `__GRAPH_MCPSERVER_${String(mcpServers[i])}__`, objName: MCPClientInjectName, qualifiers: [{ attribute: MCPClientQualifierAttribute, value: mcpServers[i], }], proto: this.mcpServerProto, }); } } // 存入 nodeProtoMap,等 tool 创建好了再注入 this.nodeProtoMap.set(proto, tools); // 存入 nodeMcpServerProtoMap,等 mcpServer 创建好了再注入 this.nodeMcpServerProtoMap.set(proto, mcpServers); } const toolMetadata = GraphToolInfoUtil.getGraphToolMetadata(ctx.clazz); if (toolMetadata) { // 注入到所有用到这个 tool 的 node 上 for (const [ nodeProto, toolClazzList ] of this.nodeProtoMap.entries()) { if (toolClazzList.includes(ctx.clazz)) { nodeProto.injectObjects.push({ refName: `__GRAPH_TOOL_${String(proto.name)}__`, objName: proto.name, qualifiers: [], proto, }); } } // 记录已经创建好的 tool proto,以便后续 node 注入 this.toolProtoMap.set(ctx.clazz, proto); } if (proto.name === MCPClientInjectName) { this.mcpServerProto = proto; // 注入到所有用到 mcpServer 的 node 上 for (const [ nodeProto, mcpServers ] of this.nodeMcpServerProtoMap.entries()) { for (let i = 0; i < mcpServers.length; i++) { nodeProto.injectObjects.push({ refName: `__GRAPH_MCPSERVER_${String(mcpServers[i])}__`, objName: MCPClientInjectName, qualifiers: [{ attribute: MCPClientQualifierAttribute, value: mcpServers[i], }], proto, }); } } } } async makeGraphDependencies(ctx: EggPrototypeLifecycleContext, proto: EggPrototype): Promise { const graphMetadata = GraphInfoUtil.getGraphMetadata(ctx.clazz); if (graphMetadata) { if (graphMetadata.checkpoint) { // module.yml ChatCheckpointSaver if (typeof graphMetadata.checkpoint === 'string') { // 如果 ChatCheckpointSaver 已经创建好了,则直接注入 if (this.checkPointProto) { proto.injectObjects.push({ refName: 'checkpoint', objName: ChatCheckpointSaverInjectName, qualifiers: [{ attribute: ChatCheckpointSaverQualifierAttribute, value: graphMetadata.checkpoint, }], proto: this.checkPointProto, }); } // 如果没有创建好,则记录下来,等 ChatCheckpointSaver 创建好了再注入 this.graphCheckpointProtoMap.set(proto, graphMetadata.checkpoint); } else { // 用户自己编写的 CheckpointSaver class if (this.checkPointProtoMap.has(graphMetadata.checkpoint)) { // 如果已经创建好了,则直接注入 const checkPointProto = this.checkPointProtoMap.get(graphMetadata.checkpoint)!; proto.injectObjects.push({ refName: 'checkpoint', objName: checkPointProto.name, qualifiers: [], proto: checkPointProto, }); } // 如果没有创建好,则记录下来,等 CheckpointSaver 创建好了再注入 this.checkPointList.push(graphMetadata.checkpoint); if (this.checkPointGraphMap.has(graphMetadata.checkpoint)) { this.checkPointGraphMap.get(graphMetadata.checkpoint)!.push(proto); } else { this.checkPointGraphMap.set(graphMetadata.checkpoint, [ proto ]); } } this.graphNodeProtoMap.set(proto, []); this.graphEdgeProtoMap.set(proto, []); // 将所有 node 注入到 graph 上 for (const nodeProto of graphMetadata.nodes ?? []) { // 如果有,则是扫描过了,直接注入 if (this.nodeProto.has(nodeProto)) { proto.injectObjects.push({ refName: `__GRAPH_NODE_${String(nodeProto)}__`, objName: nodeProto.name, qualifiers: [], proto: this.nodeProto.get(nodeProto)!, }); } else { // 如果没有,记录下来,等 node 创建好了再注入 this.graphNodeProtoMap.get(proto)!.push(nodeProto); } } // 将所有 edge 注入到 graph 上 for (const edgeProto of graphMetadata.edges ?? []) { // 如果有,则是扫描过了,直接注入 if (this.edgeProto.has(edgeProto)) { proto.injectObjects.push({ refName: `__GRAPH_EDGE_${String(edgeProto)}__`, objName: edgeProto.name, qualifiers: [], proto: this.edgeProto.get(edgeProto)!, }); } else { // 如果没有,记录下来,等 edge 创建好了再注入 this.graphEdgeProtoMap.get(proto)!.push(edgeProto); } } } } // 此时创建的是 ChatCheckpointSaver if (proto.name === ChatCheckpointSaverInjectName) { this.checkPointProto = proto; // 注入到所有用到 checkpoint 的 graph 上 for (const [ graphProto, checkpoint ] of this.graphCheckpointProtoMap.entries()) { if (checkpoint === graphMetadata?.checkpoint) { if (!graphProto.injectObjects.find(injectObject => injectObject.refName === 'checkpoint')) { continue; } graphProto.injectObjects.push({ refName: 'checkpoint', objName: ChatCheckpointSaverInjectName, qualifiers: [{ attribute: ChatCheckpointSaverQualifierAttribute, value: checkpoint, }], proto, }); } } } // 此时创建的是用户自定义的 BaseCheckpointSaver 的子类 if (BaseCheckpointSaver.isPrototypeOf(ctx.clazz)) { // 如果有 graph 依赖这个 CheckpointSaver,则注入 const graphProto = this.checkPointGraphMap.get(ctx.clazz) ?? []; for (let i = 0; i < graphProto.length; i++) { if (!graphProto[i].injectObjects.find(injectObject => injectObject.refName === 'checkpoint')) { continue; } graphProto[i].injectObjects.push({ refName: 'checkpoint', objName: proto.name, qualifiers: [], proto, }); } this.checkPointProtoMap.set(ctx.clazz, proto); } // 此时创建的是 graph node const nodeMeta = GraphNodeInfoUtil.getGraphNodeMetadata(ctx.clazz); if (nodeMeta) { this.nodeProto.set(ctx.clazz, proto); // 注入到所有依赖这个 graph node 的 graph for (const [ graphProto, nodeProtos ] of this.graphNodeProtoMap.entries()) { if (nodeProtos.includes(ctx.clazz)) { if (graphProto.injectObjects.find(injectObject => injectObject.refName === `__GRAPH_NODE_${String(ctx.clazz)}__`)) { continue; } graphProto.injectObjects.push({ refName: `__GRAPH_NODE_${String(ctx.clazz)}__`, objName: proto.name, qualifiers: [], proto, }); } } } // 此时创建的是 graph edge const edgeMeta = GraphEdgeInfoUtil.getGraphEdgeMetadata(ctx.clazz); if (edgeMeta) { this.edgeProto.set(ctx.clazz, proto); // 注入到所有依赖这个 graph edge 的 graph for (const [ graphProto, edgeProtos ] of this.graphEdgeProtoMap.entries()) { if (edgeProtos.includes(ctx.clazz)) { if (graphProto.injectObjects.find(injectObject => injectObject.refName === `__GRAPH_EDGE_${String(ctx.clazz)}__`)) { continue; } graphProto.injectObjects.push({ refName: `__GRAPH_EDGE_${String(ctx.clazz)}__`, objName: proto.name, qualifiers: [], proto, }); } } } } } ================================================ FILE: plugin/langchain/lib/tracing/LangGraphTracer.ts ================================================ import { SingletonProto, Inject, Logger, AccessLevel } from '@eggjs/tegg'; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore import { BaseTracer, Run } from '@langchain/core/tracers/base'; @SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) export class LangGraphTracer extends BaseTracer { @Inject() logger: Logger; name = 'LangGraphTracer'; setName(name: string): void { this.name = name; } protected persistRun(run: Run): Promise { this.logger.info(`[agent_run][${this.name}]:traceId=${run.trace_id},run=${JSON.stringify(run)}`); return Promise.resolve(undefined); } } ================================================ FILE: plugin/langchain/lib/util.ts ================================================ import { ObjectInfo, ModuleConfig } from '@eggjs/tegg'; import { ChatModelQualifierAttribute, } from '@eggjs/tegg-langchain-decorator'; import assert from 'node:assert'; export type ConfigTypeHelper = Required[T]['clients'][keyof Required[T]['clients']]; export function getClientNames(config: ModuleConfig | undefined, key: string): string[] { const clients = config?.[key]?.clients; if (!clients) return []; return Object.keys(clients); } export function getChatModelConfig(config: ModuleConfig, objectInfo: ObjectInfo): ConfigTypeHelper<'ChatModel'> { const chatModelName = objectInfo.qualifiers.find(t => t.attribute === ChatModelQualifierAttribute)?.value; assert(chatModelName, 'not found ChatModel name'); const chatModelConfig = config.ChatModel?.clients[chatModelName]; if (!config) { throw new Error(`not found ChatModel config for ${chatModelName}`); } return chatModelConfig!; } ================================================ FILE: plugin/langchain/package.json ================================================ { "name": "@eggjs/tegg-langchain", "eggPlugin": { "name": "teggLangChain", "dependencies": [ "tegg" ] }, "eggModule": { "name": "teggLangChain" }, "main": "index.js", "version": "3.78.15", "description": "langchain for egg", "keywords": [ "egg", "plugin", "typescript", "module", "tegg", "langchain" ], "files": [ "app.js", "app.d.ts", "index.js", "index.d.ts", "lib/**/*.js", "lib/**/*.d.ts", "app/**/*.js", "app/**/*.d.ts", "typings/*.d.ts" ], "types": "typings/index.d.ts", "scripts": { "test": "mocha", "clean": "tsc -b --clean", "tsc": "npm run clean && tsc -p ./tsconfig.json", "tsc:pub": "npm run clean && tsc -p ./tsconfig.pub.json", "prepublishOnly": "npm run tsc:pub" }, "homepage": "https://github.com/eggjs/tegg", "bugs": { "url": "https://github.com/eggjs/tegg/issues" }, "repository": { "type": "git", "url": "git@github.com:eggjs/tegg.git", "directory": "plugin/langchain" }, "egg": { "typescript": true }, "engines": { "node": ">=18.0.0" }, "dependencies": { "@eggjs/egg-module-common": "^3.78.15", "@eggjs/mcp-client": "^3.78.15", "@eggjs/tegg": "^3.78.15", "@eggjs/tegg-common-util": "^3.78.15", "@eggjs/tegg-langchain-decorator": "^3.78.15", "@eggjs/tegg-lifecycle": "^3.78.15", "@eggjs/tegg-loader": "^3.78.15", "@eggjs/tegg-mcp-client": "^3.78.15", "@eggjs/tegg-metadata": "^3.78.15", "@eggjs/tegg-runtime": "^3.78.15", "@langchain/community": "^1.0.0", "@langchain/core": "^1.1.1", "@langchain/langgraph": "^1.0.2", "@langchain/mcp-adapters": "^1.0.0", "@langchain/openai": "^1.0.0", "@types/koa-router": "^7.0.40", "koa-compose": "^3.2.1", "langchain": "^1.1.2", "sdk-base": "^4.2.0", "urllib": "^4.4.0", "zod": "^4.0.0" }, "devDependencies": { "@eggjs/module-test-util": "^3.78.15", "@eggjs/router": "^2.0.0", "@eggjs/tegg-config": "^3.78.15", "@eggjs/tegg-controller-plugin": "^3.78.15", "@eggjs/tegg-plugin": "^3.78.15", "@types/mocha": "^10.0.1", "@types/node": "^20.2.4", "cross-env": "^7.0.3", "egg": "^3.9.1", "egg-mock": "^5.5.0", "egg-tracer": "^2.0.0", "koa-router": "^8.0.8", "mocha": "^10.2.0", "mysql": "^2.18.1", "ts-node": "^10.9.1", "typescript": "^5.0.4" }, "publishConfig": { "access": "public" }, "gitHead": "240a3e9f40fda65ebb7589727d197db5ce17916c" } ================================================ FILE: plugin/langchain/test/fixtures/apps/langchain/app/modules/bar/controller/AppController.ts ================================================ import { HTTPController, HTTPMethod, HTTPMethodEnum, Inject, } from '@eggjs/tegg'; import { ChatModelQualifier, IGraphStructuredTool, TeggBoundModel, TeggCompiledStateGraph } from '@eggjs/tegg-langchain-decorator'; import { ChatOpenAIModel } from '../../../../../../../../lib/ChatOpenAI'; import { BoundChatModel } from '../service/BoundChatModel'; import { FooGraph, FooTool } from '../service/Graph'; import { AIMessage } from 'langchain'; @HTTPController({ path: '/llm', }) export class AppController { @Inject() @ChatModelQualifier('chat') chatModel: ChatOpenAIModel; @Inject() boundChatModel: TeggBoundModel; @Inject() compiledFooGraph: TeggCompiledStateGraph; @Inject() structuredFooTool: IGraphStructuredTool; @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/hello', }) async hello() { const res = await this.chatModel.invoke('hello'); return res; } @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/bound-chat', }) async boundChat() { const res = await this.boundChatModel.invoke('hello'); return res; } @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/graph' }) async get() { const res = await this.compiledFooGraph.invoke({ messages: [], aggregate: [], }, { configurable: { thread_id: '1', }, tags: [ 'trace-log' ], }); return { value: res.messages.filter(msg => AIMessage.prototype.isPrototypeOf(msg)).reduce((pre, cur) => { return cur.content + pre; }, ''), }; } @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/structured' }) async structured() { return { name: this.structuredFooTool.name, description: this.structuredFooTool.description, }; } } ================================================ FILE: plugin/langchain/test/fixtures/apps/langchain/app/modules/bar/module.yml ================================================ ChatModel: clients: chat: apiKey: mock_api_key model: Qwen2_5_7B_Instruct temperature: 0 timeout: 10 type: openai configuration: baseURL: https://antchat.alipay.com/v1 mcp: clients: bar: url: http://127.0.0.1:17283/mcp/sse clientName: barSse version: 1.0.0 transportType: SSE type: http ================================================ FILE: plugin/langchain/test/fixtures/apps/langchain/app/modules/bar/package.json ================================================ { "name": "llm-test-module", "eggModule": { "name": "llmTestModule" } } ================================================ FILE: plugin/langchain/test/fixtures/apps/langchain/app/modules/bar/service/BoundChatModel.ts ================================================ import { BoundModel } from '@eggjs/tegg-langchain-decorator'; import { FooTool } from './Graph'; @BoundModel({ modelName: 'chat', tools: [ FooTool ], mcpServers: [ 'bar' ], }) export class BoundChatModel {} ================================================ FILE: plugin/langchain/test/fixtures/apps/langchain/app/modules/bar/service/Graph.ts ================================================ import { AccessLevel, Inject, Logger, SingletonProto, ToolArgs, ToolArgsSchema } from '@eggjs/tegg'; import { Graph, GraphEdge, IGraphEdge, AbstractStateGraph, GraphNode, IGraphNode, GraphStateType, GraphTool, IGraphTool, TeggToolNode, GraphRuntime } from '@eggjs/tegg-langchain-decorator'; import { Annotation, MemorySaver } from '@langchain/langgraph'; // import { AIMessage, BaseMessage, ToolMessage } from '@langchain/core/messages'; import * as z from 'zod/v4'; import { AIMessage, BaseMessage, ToolMessage } from 'langchain'; export enum FooGraphNodeName { START = '__start__', END = '__end__', ACTION = 'action', AGENT = 'agent', TOOLS = 'tools', NODE_A = 'a', NODE_B = 'b', NODE_C = 'c', NODE_D = 'd', } @SingletonProto() export class FooSaver extends MemorySaver {} // state export const fooAnnotationStateDefinition = { messages: Annotation({ reducer: (x, y) => x.concat(y), }), aggregate: Annotation({ reducer: (x, y) => x.concat(y), }), }; export type fooAnnotationStateDefinitionType = typeof fooAnnotationStateDefinition; export const ToolType = { query: z.string().describe('npm package name'), }; @GraphTool({ toolName: 'search', description: 'Call the foo tool', }) export class FooTool implements IGraphTool { async execute(@ToolArgsSchema(ToolType) args: ToolArgs) { console.log('query: ', args.query); return `hello ${args.query}`; } } @GraphNode({ nodeName: FooGraphNodeName.ACTION, tools: [ FooTool ], mcpServers: [ 'bar' ], }) export class FooNode implements IGraphNode { @Inject() logger: Logger; async execute(state: GraphStateType, options: GraphRuntime) { this.logger.info('Executing FooNode thread_id is', options.configurable?.thread_id); console.log('response: ', state.messages); const messages = state.messages; const lastMessage = messages[messages.length - 1]; if (ToolMessage.prototype.isPrototypeOf(lastMessage)) { return { messages: [ new AIMessage(lastMessage!.text!), ], }; } return { messages: [ new AIMessage({ tool_calls: [ { name: 'search', args: { query: 'graph tool', }, id: 'fc-6b565ce5-e0cf-4af3-8ed0-0ca75c509d9e', type: 'tool_call', }, ], content: 'hello world', }), ], }; } } @GraphNode({ nodeName: FooGraphNodeName.TOOLS, tools: [ FooTool ], }) export class ToolNode extends TeggToolNode {} @GraphEdge({ fromNodeName: FooGraphNodeName.ACTION, toNodeNames: [ FooGraphNodeName.TOOLS, FooGraphNodeName.END ], }) export class FooContinueEdge implements IGraphEdge { async execute( state: GraphStateType, ): Promise { console.log('response: ', state.messages); const messages = state.messages; const lastMessage = messages[messages.length - 1] as AIMessage; if (lastMessage?.tool_calls?.length) { return FooGraphNodeName.TOOLS; } return FooGraphNodeName.END; } } @GraphEdge({ fromNodeName: FooGraphNodeName.TOOLS, toNodeNames: [ FooGraphNodeName.ACTION ], }) export class ToolsContinueEdge implements IGraphEdge {} @GraphEdge({ fromNodeName: FooGraphNodeName.START, toNodeNames: [ FooGraphNodeName.ACTION ], }) export class FooStartContinueEdge implements IGraphEdge {} @Graph({ accessLevel: AccessLevel.PUBLIC, nodes: [ FooNode, ToolNode ], edges: [ FooContinueEdge, FooStartContinueEdge, ToolsContinueEdge ], checkpoint: FooSaver, }) export class FooGraph extends AbstractStateGraph { constructor() { super(fooAnnotationStateDefinition); } } ================================================ FILE: plugin/langchain/test/fixtures/apps/langchain/config/config.default.js ================================================ 'use strict'; module.exports = function() { const config = { keys: 'test key', security: { csrf: { enable: false, }, }, bodyParser: { enable: false, }, }; return config; }; ================================================ FILE: plugin/langchain/test/fixtures/apps/langchain/config/module.json ================================================ [ { "path": "../app/modules/bar" }, { "package": "../../../../" }, { "package": "@eggjs/tegg-mcp-client" } ] ================================================ FILE: plugin/langchain/test/fixtures/apps/langchain/config/plugin.js ================================================ 'use strict'; // eslint-disable-next-line @typescript-eslint/no-var-requires const path = require('node:path'); exports.tegg = { package: '@eggjs/tegg-plugin', enable: true, }; exports.teggConfig = { package: '@eggjs/tegg-config', enable: true, }; exports.teggLangChain = { enable: true, path: path.join(__dirname, '../../../../../'), }; exports.teggController = { package: '@eggjs/tegg-controller-plugin', enable: true, }; exports.teggMcpClient = { enable: true, package: '@eggjs/tegg-mcp-client', }; exports.tracer = { package: 'egg-tracer', enable: true, }; exports.watcher = false; ================================================ FILE: plugin/langchain/test/fixtures/apps/langchain/package.json ================================================ { "name": "egg-app" } ================================================ FILE: plugin/langchain/test/fixtures/apps/langchain/tsconfig.json ================================================ { "compilerOptions": { "outDir": "dist", "module": "node18", "moduleResolution": "node16", "experimentalDecorators": true, "emitDecoratorMetadata": true, "baseUrl": "./" }, "exclude": [ "dist", "node_modules", "test" ] } ================================================ FILE: plugin/langchain/test/fixtures/sse-mcp-server/http.ts ================================================ import http from 'node:http'; import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js'; import * as z from 'zod/v4'; // Create an MCP server const server = new McpServer({ name: 'Demo', version: '1.0.0', }); // Add an addition tool server.registerTool('add', { inputSchema: { a: z.number(), b: z.number() }, }, async ({ a, b }) => ({ content: [{ type: 'text', text: String(a + b) }], }), ); // Add a dynamic greeting resource server.registerResource( 'greeting', 'greeting://{name}', {}, async uri => ({ contents: [{ uri: uri.href, text: `Hello, ${uri.hostname}!`, }], }), ); const transports = {}; export const headers = {}; export let httpServer; export async function startSSEServer(port = 17233) { const httpServer = http.createServer(async (req, res) => { const url = new URL(`http://127.0.0.1:${port}${req.url!}`); const headerKey = `${req.method}${url.pathname}`; const serverCode = req.headers['x-mcp-server-code'] as string; headers[serverCode] = headers[serverCode] || {}; headers[serverCode][headerKey] = headers[serverCode][headerKey] || []; headers[serverCode][headerKey].push(req.headers); if (req.method === 'GET') { const transport = new SSEServerTransport('/mcp', res); transports[transport.sessionId] = transport; // Connect the transport to the MCP server await server.connect(transport); } else if (req.method === 'POST') { const sessionId = url.searchParams.get('sessionId'); // const chunks: Buffer[] = []; // for await (const chunk of req) { // chunks.push(chunk); // } // const body = JSON.parse(Buffer.concat(chunks).toString('utf-8')); const transport = transports[sessionId!] as SSEServerTransport; await transport.handlePostMessage(req, res); res.statusCode = 201; res.end(); } }); return new Promise(resolve => { httpServer.listen(port, resolve); }); } export async function stopSSEServer() { server.close(); } ================================================ FILE: plugin/langchain/test/llm.test.ts ================================================ import mm from 'egg-mock'; import path from 'path'; import assert from 'assert'; import Tracer from 'egg-tracer/lib/tracer'; describe('plugin/langchain/test/llm.test.ts', () => { // https://github.com/langchain-ai/langchainjs/blob/main/libs/langchain/package.json#L9 if (parseInt(process.version.slice(1, 3)) > 19) { // eslint-disable-next-line @typescript-eslint/no-var-requires const { startSSEServer, stopSSEServer } = require('./fixtures/sse-mcp-server/http'); // eslint-disable-next-line @typescript-eslint/no-var-requires const { ChatOpenAIModel } = require('../lib/ChatOpenAI'); // eslint-disable-next-line @typescript-eslint/no-var-requires const { BaseChatOpenAI } = require('@langchain/openai'); let app; before(async () => { await startSSEServer(17283); }); after(async () => { await app.close(); await stopSSEServer(); }); afterEach(() => { mm.restore(); }); before(async () => { mm(process.env, 'EGG_TYPESCRIPT', true); mm(process, 'cwd', () => { return path.join(__dirname, '..'); }); app = mm.app({ baseDir: path.join(__dirname, 'fixtures/apps/langchain'), framework: path.dirname(require.resolve('egg')), }); await app.ready(); }); after(() => { return app.close(); }); it('should work', async () => { mm(ChatOpenAIModel.prototype, 'invoke', async () => { return { text: 'hello world', }; }); const res = await app.httpRequest() .get('/llm/hello') .expect(200); assert.deepStrictEqual(res.body, { text: 'hello world', }); }); it('should bound work', async () => { mm(BaseChatOpenAI.prototype, 'invoke', async () => { return { text: 'hello world 2', }; }); const res = await app.httpRequest() .get('/llm/bound-chat') .expect(200); assert.deepStrictEqual(res.body, { text: 'hello world 2', }); }); it('should graph work', async () => { app.mockLog(); mm(Tracer.prototype, 'traceId', 'test-trace-id'); await app.httpRequest() .get('/llm/graph') .expect(200, { value: 'hello graph toolhello world' }); app.expectLog(/agent_run/); app.expectLog(/Executing FooNode thread_id is 1/); app.expectLog(/traceId=test-trace-id/); }); it('should persistRun be triggered when graph.invoke is called', async () => { // eslint-disable-next-line @typescript-eslint/no-var-requires const { LangGraphTracer } = require('../lib/tracing/LangGraphTracer'); const persistRunCalls: any[] = []; const originalPersistRun = LangGraphTracer.prototype.persistRun; mm(LangGraphTracer.prototype, 'persistRun', function(this: any, run: any) { persistRunCalls.push(run); return originalPersistRun.call(this, run); }); app.mockLog(); mm(Tracer.prototype, 'traceId', 'test-persist-run-trace-id'); await app.httpRequest().get('/llm/graph'); assert(persistRunCalls.length > 0, 'persistRun should be called at least once'); const hasCorrectTraceId = persistRunCalls.some(run => run.trace_id === 'test-persist-run-trace-id'); assert(hasCorrectTraceId, 'persistRun should receive correct trace_id from invoke call'); app.expectLog(/agent_run/); app.expectLog(/traceId=test-persist-run-trace-id/); }); it('should structured work', async () => { const res = await app.httpRequest() .get('/llm/structured') .expect(200); assert.deepStrictEqual(res.body, { name: 'search', description: 'Call the foo tool', }); }); } }); ================================================ FILE: plugin/langchain/tsconfig.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "baseUrl": "./", "module": "nodenext", "moduleResolution": "nodenext" }, "exclude": [ "node_modules", "test" ] } ================================================ FILE: plugin/langchain/tsconfig.pub.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "baseUrl": "./", "module": "nodenext", "moduleResolution": "nodenext" }, "exclude": [ "node_modules", "test" ] } ================================================ FILE: plugin/langchain/typings/index.d.ts ================================================ import { EggContainerFactory } from '@eggjs/tegg-runtime'; import { Type, Static } from '@eggjs/tegg/ajv'; import 'egg'; import '@eggjs/tegg-plugin/typings'; import '@eggjs/tegg-controller-plugin/typings'; import '@eggjs/tegg-mcp-client/typings'; export const ChatModelConfigModuleConfigSchema = Type.Object({ clients: Type.Record(Type.String(), Type.Object({ temperature: Type.Optional(Type.Number({ description: 'Sampling temperature to use', })), maxTokens: Type.Optional(Type.Number({ description: 'Maximum number of tokens to generate in the completion. -1 returns as many tokens as possible given the prompt and the model\'s maximum context size.', })), topP: Type.Optional(Type.Number({ description: 'Total probability mass of tokens to consider at each step', })), frequencyPenalty: Type.Optional(Type.Number({ description: 'Penalizes repeated tokens according to frequency', })), presencePenalty: Type.Optional(Type.Number({ description: 'Penalizes repeated tokens', })), n: Type.Optional(Type.Number({ description: 'Number of completions to generate for each prompt', })), logitBias: Type.Optional(Type.Record(Type.String(), Type.Number(), { description: 'Dictionary used to adjust the probability of specific tokens being generated', })), user: Type.Optional(Type.String({ description: 'Unique string identifier representing your end-user, which can help OpenAI to monitor and detect abuse.', })), streaming: Type.Optional(Type.Boolean({ description: 'Whether to stream the results or not. Enabling disables tokenUsage reporting', })), streamUsage: Type.Optional(Type.Boolean({ description: 'Whether or not to include token usage data in streamed chunks. @default true', })), modelName: Type.Optional(Type.String({ description: 'Model name to use, Alias for `model`', })), model: Type.Optional(Type.String({ description: 'Model name to use.', })), modelKwargs: Type.Optional(Type.Record(Type.String(), Type.Any(), { description: 'Holds any additional parameters that are valid to pass to {@link https://platform.openai.com/docs/api-reference/completions/create | `openai.createCompletion`} that are not explicitly specified on this class.', })), /** * List of stop words to use when generating. Alias for `stopSequences` */ stop: Type.Optional(Type.Array(Type.String(), { description: 'List of stop words to use when generating. Alias for `stopSequences`', })), stopSequences: Type.Optional(Type.Array(Type.String(), { description: 'List of stop words to use when generating.', })), timeout: Type.Optional(Type.Number({ description: 'Timeout to use when making requests to OpenAI.', })), /** * API key to use when making requests to OpenAI. Defaults to the value of `OPENAI_API_KEY` environment variable. Alias for `apiKey` */ openAIApiKey: Type.Optional(Type.String({ description: 'API key to use when making requests to OpenAI. Defaults to the value of `OPENAI_API_KEY` environment variable. Alias for `apiKey`', })), apiKey: Type.Optional(Type.String({ description: 'API key to use when making requests to OpenAI. Defaults to the value of `OPENAI_API_KEY` environment variable.', })), maxConcurrency: Type.Optional(Type.Number({ description: 'The maximum number of concurrent calls that can be made. Defaults to `Infinity`, which means no limit.', })), maxRetries: Type.Optional(Type.Number({ description: 'The maximum number of retries that can be made for a single call, with an exponential backoff between each attempt. Defaults to 6.', })), verbose: Type.Optional(Type.Boolean({ description: 'debug option', })), tags: Type.Optional(Type.Array(Type.String())), metadata: Type.Optional(Type.Record(Type.String(), Type.Any())), configuration: Type.Optional(Type.Object({ apiKey: Type.Optional(Type.String({ description: 'The OpenAI API key to use.', })), baseURL: Type.Optional(Type.String({ description: 'Override the default base URL for the API, e.g., "https://api.example.com/v2/"', })), timeout: Type.Optional(Type.Number({ description: 'The maximum amount of time (in milliseconds) that the client should wait for a response from the server before timing out a single request. Note that request timeouts are retried by default, so in a worst-case scenario you may wait much longer than this timeout before the promise succeeds or fails.', })), maxRetries: Type.Optional(Type.Number({ description: 'The maximum number of times that the client will retry a request in case of a temporary failure, like a network error or a 5XX error from the server.', default: 2, })), defaultHeaders: Type.Optional(Type.Record(Type.String(), Type.Union([ Type.String(), Type.Array(Type.String()), ]), { description: 'Default headers to include with every request to the API. These can be removed in individual requests by explicitly setting the header to `undefined` or `null` in request options.', })), defaultQuery: Type.Optional(Type.Record(Type.String(), Type.String(), { description: 'Default query parameters to include with every request to the API. These can be removed in individual requests by explicitly setting the param to `undefined` in request options.', })), })), })), }, { title: 'ChatModel 设置', name: 'ChatModel', }); export type ChatModelConfigModuleConfigType = Static; declare module '@eggjs/tegg' { export type LangChainModuleConfig = { ChatModel?: ChatModelConfigModuleConfigType; }; export interface ModuleConfig extends LangChainModuleConfig { } } declare module 'egg' { export interface ModuleConfigApplication { moduleReferences: readonly ModuleReference[]; moduleConfigs: Record; eggContainerFactory: typeof EggContainerFactory; } export interface Application extends ModuleConfigApplication { } } ================================================ FILE: plugin/mcp-client/CHANGELOG.md ================================================ # Change Log All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. ## [3.78.15](https://github.com/eggjs/tegg/compare/v3.78.14...v3.78.15) (2026-04-23) **Note:** Version bump only for package @eggjs/tegg-mcp-client ## [3.78.14](https://github.com/eggjs/tegg/compare/v3.78.13...v3.78.14) (2026-04-21) **Note:** Version bump only for package @eggjs/tegg-mcp-client ## [3.78.13](https://github.com/eggjs/tegg/compare/v3.78.12...v3.78.13) (2026-04-21) **Note:** Version bump only for package @eggjs/tegg-mcp-client ## [3.78.12](https://github.com/eggjs/tegg/compare/v3.78.11...v3.78.12) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-mcp-client ## [3.78.11](https://github.com/eggjs/tegg/compare/v3.78.10...v3.78.11) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-mcp-client ## [3.78.10](https://github.com/eggjs/tegg/compare/v3.78.9...v3.78.10) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-mcp-client ## [3.78.9](https://github.com/eggjs/tegg/compare/v3.78.8...v3.78.9) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-mcp-client ## [3.78.8](https://github.com/eggjs/tegg/compare/v3.78.7...v3.78.8) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-mcp-client ## [3.78.7](https://github.com/eggjs/tegg/compare/v3.78.6...v3.78.7) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-mcp-client ## [3.78.6](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.6) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-mcp-client ## [3.78.5](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.5) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-mcp-client ## [3.78.4](https://github.com/eggjs/tegg/compare/v3.78.3...v3.78.4) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-mcp-client ## [3.78.3](https://github.com/eggjs/tegg/compare/v3.78.2...v3.78.3) (2026-04-15) **Note:** Version bump only for package @eggjs/tegg-mcp-client ## [3.78.2](https://github.com/eggjs/tegg/compare/v3.78.1...v3.78.2) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-mcp-client ## [3.78.1](https://github.com/eggjs/tegg/compare/v3.78.0...v3.78.1) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-mcp-client # [3.78.0](https://github.com/eggjs/tegg/compare/v3.77.2...v3.78.0) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-mcp-client ## [3.77.2](https://github.com/eggjs/tegg/compare/v3.77.1...v3.77.2) (2026-04-07) **Note:** Version bump only for package @eggjs/tegg-mcp-client ## [3.77.1](https://github.com/eggjs/tegg/compare/v3.77.0...v3.77.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-mcp-client # [3.77.0](https://github.com/eggjs/tegg/compare/v3.76.1...v3.77.0) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-mcp-client ## [3.76.1](https://github.com/eggjs/tegg/compare/v3.76.0...v3.76.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-mcp-client # [3.76.0](https://github.com/eggjs/tegg/compare/v3.75.1...v3.76.0) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-mcp-client ## [3.75.1](https://github.com/eggjs/tegg/compare/v3.75.0...v3.75.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-mcp-client # [3.75.0](https://github.com/eggjs/tegg/compare/v3.74.0...v3.75.0) (2026-03-30) **Note:** Version bump only for package @eggjs/tegg-mcp-client # [3.74.0](https://github.com/eggjs/tegg/compare/v3.73.0...v3.74.0) (2026-03-30) **Note:** Version bump only for package @eggjs/tegg-mcp-client # [3.73.0](https://github.com/eggjs/tegg/compare/v3.72.0...v3.73.0) (2026-03-25) ### Bug Fixes * compatibility with @modelcontextprotocol/sdk 1.26 ([#404](https://github.com/eggjs/tegg/issues/404)) ([dbda39a](https://github.com/eggjs/tegg/commit/dbda39ad2f4be5879f1e0540cf26a242cecd05e3)) # [3.72.0](https://github.com/eggjs/tegg/compare/v3.71.2...v3.72.0) (2026-02-04) ### Features * add structured tool ([#387](https://github.com/eggjs/tegg/issues/387)) ([56c23ad](https://github.com/eggjs/tegg/commit/56c23adb0af25ce0fd3624491eaf7af3fb1570cf)) ## [3.71.2](https://github.com/eggjs/tegg/compare/v3.71.1...v3.71.2) (2026-01-30) **Note:** Version bump only for package @eggjs/tegg-mcp-client ## [3.71.1](https://github.com/eggjs/tegg/compare/v3.71.0...v3.71.1) (2026-01-28) **Note:** Version bump only for package @eggjs/tegg-mcp-client # [3.71.0](https://github.com/eggjs/tegg/compare/v3.70.1...v3.71.0) (2026-01-26) **Note:** Version bump only for package @eggjs/tegg-mcp-client ## [3.70.1](https://github.com/eggjs/tegg/compare/v3.70.0...v3.70.1) (2026-01-14) **Note:** Version bump only for package @eggjs/tegg-mcp-client # [3.70.0](https://github.com/eggjs/tegg/compare/v3.69.0...v3.70.0) (2026-01-07) **Note:** Version bump only for package @eggjs/tegg-mcp-client # [3.69.0](https://github.com/eggjs/tegg/compare/v3.68.0...v3.69.0) (2026-01-06) **Note:** Version bump only for package @eggjs/tegg-mcp-client # [3.68.0](https://github.com/eggjs/tegg/compare/v3.67.2...v3.68.0) (2025-12-29) **Note:** Version bump only for package @eggjs/tegg-mcp-client ## [3.67.2](https://github.com/eggjs/tegg/compare/v3.67.1...v3.67.2) (2025-12-24) **Note:** Version bump only for package @eggjs/tegg-mcp-client ## [3.67.1](https://github.com/eggjs/tegg/compare/v3.67.0...v3.67.1) (2025-12-23) **Note:** Version bump only for package @eggjs/tegg-mcp-client # [3.67.0](https://github.com/eggjs/tegg/compare/v3.66.0...v3.67.0) (2025-12-18) **Note:** Version bump only for package @eggjs/tegg-mcp-client # [3.66.0](https://github.com/eggjs/tegg/compare/v3.65.3...v3.66.0) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-mcp-client ## [3.65.3](https://github.com/eggjs/tegg/compare/v3.65.2...v3.65.3) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-mcp-client ## [3.65.2](https://github.com/eggjs/tegg/compare/v3.65.1...v3.65.2) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-mcp-client ## [3.65.1](https://github.com/eggjs/tegg/compare/v3.65.0...v3.65.1) (2025-12-16) ### Bug Fixes * hono node v16 ([#374](https://github.com/eggjs/tegg/issues/374)) ([870b5e3](https://github.com/eggjs/tegg/commit/870b5e34f41399a44023756614b8bb5c59efc6ee)) # [3.65.0](https://github.com/eggjs/tegg/compare/v3.64.5...v3.65.0) (2025-12-10) **Note:** Version bump only for package @eggjs/tegg-mcp-client ## [3.64.5](https://github.com/eggjs/tegg/compare/v3.64.4...v3.64.5) (2025-12-08) **Note:** Version bump only for package @eggjs/tegg-mcp-client ## [3.64.4](https://github.com/eggjs/tegg/compare/v3.64.3...v3.64.4) (2025-12-06) **Note:** Version bump only for package @eggjs/tegg-mcp-client ## [3.64.3](https://github.com/eggjs/tegg/compare/v3.64.2...v3.64.3) (2025-12-06) ### Bug Fixes * update trace logger check ([#372](https://github.com/eggjs/tegg/issues/372)) ([b974762](https://github.com/eggjs/tegg/commit/b974762dfccf1bb7b188c233be33014b6336bfee)) ## [3.64.2](https://github.com/eggjs/tegg/compare/v3.64.1...v3.64.2) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-mcp-client ## [3.64.1](https://github.com/eggjs/tegg/compare/v3.64.0...v3.64.1) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-mcp-client # [3.64.0](https://github.com/eggjs/tegg/compare/v3.63.2...v3.64.0) (2025-11-27) ### Bug Fixes * mcp zod type and langchain test version ([#369](https://github.com/eggjs/tegg/issues/369)) ([8178168](https://github.com/eggjs/tegg/commit/81781685c392346d21c56b649bfe8bb7a99bc9fb)) ### Features * add langchain decorator ([#356](https://github.com/eggjs/tegg/issues/356)) ([b176c73](https://github.com/eggjs/tegg/commit/b176c7325009c372ce9d17f348b4fc1f1b6d7fb1)) # [3.62.0](https://github.com/eggjs/tegg/compare/v3.61.0...v3.62.0) (2025-09-24) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.61.0](https://github.com/eggjs/tegg/compare/v3.60.3...v3.61.0) (2025-08-15) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.60.3](https://github.com/eggjs/tegg/compare/v3.60.2...v3.60.3) (2025-08-06) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.60.2](https://github.com/eggjs/tegg/compare/v3.60.1...v3.60.2) (2025-08-06) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.60.1](https://github.com/eggjs/tegg/compare/v3.60.0...v3.60.1) (2025-07-28) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.60.0](https://github.com/eggjs/tegg/compare/v3.59.1...v3.60.0) (2025-07-28) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.59.1](https://github.com/eggjs/tegg/compare/v3.59.0...v3.59.1) (2025-07-16) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.59.0](https://github.com/eggjs/tegg/compare/v3.58.0...v3.59.0) (2025-07-07) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.58.0](https://github.com/eggjs/tegg/compare/v3.57.14...v3.58.0) (2025-07-01) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.57.14](https://github.com/eggjs/tegg/compare/v3.57.13...v3.57.14) (2025-06-18) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.57.13](https://github.com/eggjs/tegg/compare/v3.57.12...v3.57.13) (2025-06-18) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.57.12](https://github.com/eggjs/tegg/compare/v3.57.11...v3.57.12) (2025-06-16) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.57.11](https://github.com/eggjs/tegg/compare/v3.57.10...v3.57.11) (2025-06-16) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.57.10](https://github.com/eggjs/tegg/compare/v3.57.9...v3.57.10) (2025-06-12) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.57.9](https://github.com/eggjs/tegg/compare/v3.57.8...v3.57.9) (2025-05-29) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.57.8](https://github.com/eggjs/tegg/compare/v3.57.7...v3.57.8) (2025-05-29) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.57.7](https://github.com/eggjs/tegg/compare/v3.57.6...v3.57.7) (2025-05-28) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.57.6](https://github.com/eggjs/tegg/compare/v3.57.5...v3.57.6) (2025-05-27) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.57.5](https://github.com/eggjs/tegg/compare/v3.57.4...v3.57.5) (2025-05-15) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.57.4](https://github.com/eggjs/tegg/compare/v3.57.3...v3.57.4) (2025-05-14) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.57.3](https://github.com/eggjs/tegg/compare/v3.57.2...v3.57.3) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.57.2](https://github.com/eggjs/tegg/compare/v3.57.1...v3.57.2) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.57.1](https://github.com/eggjs/tegg/compare/v3.57.0...v3.57.1) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.57.0](https://github.com/eggjs/tegg/compare/v3.56.3...v3.57.0) (2025-05-09) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.56.3](https://github.com/eggjs/tegg/compare/v3.56.2...v3.56.3) (2025-05-07) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.56.2](https://github.com/eggjs/tegg/compare/v3.56.1...v3.56.2) (2025-05-01) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.56.1](https://github.com/eggjs/tegg/compare/v3.56.0...v3.56.1) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.56.0](https://github.com/eggjs/tegg/compare/v3.55.0...v3.56.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.55.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.55.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.54.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.54.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.53.0](https://github.com/eggjs/tegg/compare/v3.52.1...v3.53.0) (2025-04-21) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.52.1](https://github.com/eggjs/tegg/compare/v3.52.0...v3.52.1) (2025-03-14) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.52.0](https://github.com/eggjs/tegg/compare/v3.51.2...v3.52.0) (2024-12-30) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.51.2](https://github.com/eggjs/tegg/compare/v3.51.1...v3.51.2) (2024-12-09) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.51.1](https://github.com/eggjs/tegg/compare/v3.51.0...v3.51.1) (2024-11-06) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.51.0](https://github.com/eggjs/tegg/compare/v3.50.1...v3.51.0) (2024-10-30) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.50.1](https://github.com/eggjs/tegg/compare/v3.50.0...v3.50.1) (2024-10-23) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.50.0](https://github.com/eggjs/tegg/compare/v3.49.0...v3.50.0) (2024-10-22) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.49.0](https://github.com/eggjs/tegg/compare/v3.48.1...v3.49.0) (2024-10-21) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.48.1](https://github.com/eggjs/tegg/compare/v3.48.0...v3.48.1) (2024-10-14) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.48.0](https://github.com/eggjs/tegg/compare/v3.47.2...v3.48.0) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.47.2](https://github.com/eggjs/tegg/compare/v3.47.1...v3.47.2) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.47.1](https://github.com/eggjs/tegg/compare/v3.47.0...v3.47.1) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.47.0](https://github.com/eggjs/tegg/compare/v3.46.4...v3.47.0) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.46.4](https://github.com/eggjs/tegg/compare/v3.46.3...v3.46.4) (2024-10-09) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.46.3](https://github.com/eggjs/tegg/compare/v3.46.2...v3.46.3) (2024-10-08) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.46.2](https://github.com/eggjs/tegg/compare/v3.46.1...v3.46.2) (2024-10-07) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.46.1](https://github.com/eggjs/tegg/compare/v3.46.0...v3.46.1) (2024-09-30) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.46.0](https://github.com/eggjs/tegg/compare/v3.45.0...v3.46.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.45.0](https://github.com/eggjs/tegg/compare/v3.44.1...v3.45.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.44.1](https://github.com/eggjs/tegg/compare/v3.44.0...v3.44.1) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.44.0](https://github.com/eggjs/tegg/compare/v3.43.2...v3.44.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.43.2](https://github.com/eggjs/tegg/compare/v3.43.1...v3.43.2) (2024-09-14) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.43.1](https://github.com/eggjs/tegg/compare/v3.43.0...v3.43.1) (2024-09-14) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.43.0](https://github.com/eggjs/tegg/compare/v3.42.0...v3.43.0) (2024-09-13) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.42.0](https://github.com/eggjs/tegg/compare/v3.41.0...v3.42.0) (2024-09-10) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.41.0](https://github.com/eggjs/tegg/compare/v3.40.1...v3.41.0) (2024-08-26) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.40.1](https://github.com/eggjs/tegg/compare/v3.40.0...v3.40.1) (2024-08-23) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.40.0](https://github.com/eggjs/tegg/compare/v3.39.5...v3.40.0) (2024-08-22) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.39.5](https://github.com/eggjs/tegg/compare/v3.39.4...v3.39.5) (2024-08-09) ### Bug Fixes * generate index name with column name ([#230](https://github.com/eggjs/tegg/issues/230)) ([82ec72d](https://github.com/eggjs/tegg/commit/82ec72d4fb8628c847b32d0ddf23a95119ca6ccf)) ## [3.39.4](https://github.com/eggjs/tegg/compare/v3.39.3...v3.39.4) (2024-07-09) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.39.3](https://github.com/eggjs/tegg/compare/v3.39.2...v3.39.3) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.39.2](https://github.com/eggjs/tegg/compare/v3.39.1...v3.39.2) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.39.1](https://github.com/eggjs/tegg/compare/v3.39.0...v3.39.1) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.39.0](https://github.com/eggjs/tegg/compare/v3.38.0...v3.39.0) (2024-04-19) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.38.0](https://github.com/eggjs/tegg/compare/v3.37.3...v3.38.0) (2024-04-18) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.37.3](https://github.com/eggjs/tegg/compare/v3.37.2...v3.37.3) (2024-04-17) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.37.2](https://github.com/eggjs/tegg/compare/v3.37.1...v3.37.2) (2024-04-17) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.37.1](https://github.com/eggjs/tegg/compare/v3.37.0...v3.37.1) (2024-04-16) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.37.0](https://github.com/eggjs/tegg/compare/v3.36.3...v3.37.0) (2024-04-16) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.36.3](https://github.com/eggjs/tegg/compare/v3.36.2...v3.36.3) (2024-04-10) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.36.2](https://github.com/eggjs/tegg/compare/v3.36.1...v3.36.2) (2024-04-08) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.36.1](https://github.com/eggjs/tegg/compare/v3.36.0...v3.36.1) (2024-04-07) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.36.0](https://github.com/eggjs/tegg/compare/v3.35.1...v3.36.0) (2024-04-02) ### Features * impl ajv + typebox Validator ([#201](https://github.com/eggjs/tegg/issues/201)) ([9fd585d](https://github.com/eggjs/tegg/commit/9fd585de9b613466c96b73494a08a494db34ea57)) ## [3.35.1](https://github.com/eggjs/tegg/compare/v3.35.0...v3.35.1) (2024-03-26) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.35.0](https://github.com/eggjs/tegg/compare/v3.34.0...v3.35.0) (2024-03-26) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.34.0](https://github.com/eggjs/tegg/compare/v3.33.1...v3.34.0) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.33.1](https://github.com/eggjs/tegg/compare/v3.33.0...v3.33.1) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.33.0](https://github.com/eggjs/tegg/compare/v3.32.0...v3.33.0) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.32.0](https://github.com/eggjs/tegg/compare/v3.31.0...v3.32.0) (2024-02-19) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.31.0](https://github.com/eggjs/tegg/compare/v3.30.1...v3.31.0) (2024-01-31) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.30.1](https://github.com/eggjs/tegg/compare/v3.30.0...v3.30.1) (2024-01-25) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.30.0](https://github.com/eggjs/tegg/compare/v3.29.0...v3.30.0) (2024-01-17) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.29.0](https://github.com/eggjs/tegg/compare/v3.28.2...v3.29.0) (2023-12-26) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.28.2](https://github.com/eggjs/tegg/compare/v3.28.1...v3.28.2) (2023-12-12) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.28.1](https://github.com/eggjs/tegg/compare/v3.28.0...v3.28.1) (2023-12-11) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.28.0](https://github.com/eggjs/tegg/compare/v3.27.0...v3.28.0) (2023-12-10) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.27.0](https://github.com/eggjs/tegg/compare/v3.26.0...v3.27.0) (2023-11-23) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.26.0](https://github.com/eggjs/tegg/compare/v3.25.2...v3.26.0) (2023-11-17) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.25.2](https://github.com/eggjs/tegg/compare/v3.25.1...v3.25.2) (2023-11-06) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.25.1](https://github.com/eggjs/tegg/compare/v3.25.0...v3.25.1) (2023-11-03) ### Bug Fixes * fix standalone import ConfigSource ([#163](https://github.com/eggjs/tegg/issues/163)) ([6922071](https://github.com/eggjs/tegg/commit/6922071219413a8a11387be3d05f0e3970ce4f48)) # [3.25.0](https://github.com/eggjs/tegg/compare/v3.24.0...v3.25.0) (2023-11-03) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.24.0](https://github.com/eggjs/tegg/compare/v3.23.0...v3.24.0) (2023-10-26) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.23.0](https://github.com/eggjs/tegg/compare/v3.22.0...v3.23.0) (2023-09-20) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.20.0](https://github.com/eggjs/tegg/compare/v3.19.0...v3.20.0) (2023-09-07) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.19.0](https://github.com/eggjs/tegg/compare/v3.18.1...v3.19.0) (2023-08-31) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.18.1](https://github.com/eggjs/tegg/compare/v3.18.0...v3.18.1) (2023-08-29) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.18.0](https://github.com/eggjs/tegg/compare/v3.17.0...v3.18.0) (2023-08-29) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.17.0](https://github.com/eggjs/tegg/compare/v3.16.0...v3.17.0) (2023-08-24) ### Features * impl MultiInstanceProto ([#145](https://github.com/eggjs/tegg/issues/145)) ([12fd5cf](https://github.com/eggjs/tegg/commit/12fd5cff4004578bcc737dcdf4f7e9d1159f5633)) # [3.16.0](https://github.com/eggjs/tegg/compare/v3.15.0...v3.16.0) (2023-08-24) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.14.3](https://github.com/eggjs/tegg/compare/v3.14.2...v3.14.3) (2023-08-14) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.14.2](https://github.com/eggjs/tegg/compare/v3.14.1...v3.14.2) (2023-08-14) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.14.1](https://github.com/eggjs/tegg/compare/v3.14.0...v3.14.1) (2023-08-11) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.14.0](https://github.com/eggjs/tegg/compare/v3.13.0...v3.14.0) (2023-07-31) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.13.0](https://github.com/eggjs/tegg/compare/v3.12.0...v3.13.0) (2023-07-25) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.12.0](https://github.com/eggjs/tegg/compare/v3.11.1...v3.12.0) (2023-07-13) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.11.1](https://github.com/eggjs/tegg/compare/v3.11.0...v3.11.1) (2023-06-29) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.11.0](https://github.com/eggjs/tegg/compare/v3.10.0...v3.11.0) (2023-06-29) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.10.0](https://github.com/eggjs/tegg/compare/v3.9.0...v3.10.0) (2023-06-28) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.9.0](https://github.com/eggjs/tegg/compare/v3.8.0...v3.9.0) (2023-06-20) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.8.0](https://github.com/eggjs/tegg/compare/v3.7.0...v3.8.0) (2023-05-30) ### Features * impl EggObjectLifecycle hook in decorator ([#119](https://github.com/eggjs/tegg/issues/119)) ([cced8a2](https://github.com/eggjs/tegg/commit/cced8a2e009c33d5040fa21d00409fddef471b0e)) # [3.7.0](https://github.com/eggjs/tegg/compare/v3.6.3...v3.7.0) (2023-04-03) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.6.3](https://github.com/eggjs/tegg/compare/v3.6.2...v3.6.3) (2023-03-02) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.6.2](https://github.com/eggjs/tegg/compare/v3.6.1...v3.6.2) (2023-02-16) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.6.1](https://github.com/eggjs/tegg/compare/v3.6.0...v3.6.1) (2023-02-14) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.6.0](https://github.com/eggjs/tegg/compare/v3.5.2...v3.6.0) (2023-02-13) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.5.2](https://github.com/eggjs/tegg/compare/v3.5.1...v3.5.2) (2023-02-10) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.5.1](https://github.com/eggjs/tegg/compare/v3.5.0...v3.5.1) (2023-02-10) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.5.0](https://github.com/eggjs/tegg/compare/v3.4.1...v3.5.0) (2023-02-10) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.4.1](https://github.com/eggjs/tegg/compare/v3.4.0...v3.4.1) (2023-02-02) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.4.0](https://github.com/eggjs/tegg/compare/v3.3.4...v3.4.0) (2023-02-01) ### Features * use singleton model insteadof context ([#89](https://github.com/eggjs/tegg/issues/89)) ([cfdfc05](https://github.com/eggjs/tegg/commit/cfdfc05f13048806274de1a35b1207c073a8519d)) ## [3.3.4](https://github.com/eggjs/tegg/compare/v3.3.3...v3.3.4) (2023-01-29) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.3.3](https://github.com/eggjs/tegg/compare/v3.3.2...v3.3.3) (2023-01-29) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.3.2](https://github.com/eggjs/tegg/compare/v3.3.1...v3.3.2) (2023-01-29) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.3.1](https://github.com/eggjs/tegg/compare/v3.3.0...v3.3.1) (2023-01-28) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.3.0](https://github.com/eggjs/tegg/compare/v3.2.4...v3.3.0) (2023-01-28) ### Features * export singleton orm client ([#82](https://github.com/eggjs/tegg/issues/82)) ([5320af7](https://github.com/eggjs/tegg/commit/5320af77d7e7c5c73b80560a576f2ce01fc21fff)) ## [3.2.3](https://github.com/eggjs/tegg/compare/v3.2.2...v3.2.3) (2023-01-16) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.2.2](https://github.com/eggjs/tegg/compare/v3.2.1...v3.2.2) (2023-01-06) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.2.1](https://github.com/eggjs/tegg/compare/v3.2.0...v3.2.1) (2022-12-28) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.2.0](https://github.com/eggjs/tegg/compare/v3.1.0...v3.2.0) (2022-12-28) ### Features * impl mockModuleContextScope ([#73](https://github.com/eggjs/tegg/issues/73)) ([041881c](https://github.com/eggjs/tegg/commit/041881ca317ad81366172a35ac56b7b2dc0a0488)) # [3.1.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.1.0) (2022-12-27) ### Features * **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) * impl Inject Model ([#43](https://github.com/eggjs/tegg/issues/43)) ([ced2ce2](https://github.com/eggjs/tegg/commit/ced2ce2134964dcb410410c0192a34f77507c42d)) * impl Schedule decorator ([#52](https://github.com/eggjs/tegg/issues/52)) ([7f95005](https://github.com/eggjs/tegg/commit/7f950050b548ca542addbd7b466675da4e81ce3f)) # [3.0.0](https://github.com/eggjs/tegg/compare/v3.0.0-alpha.0...v3.0.0) (2022-12-26) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.0.0-alpha.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.0.0-alpha.0) (2022-12-22) ### Features * **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) * impl Inject Model ([#43](https://github.com/eggjs/tegg/issues/43)) ([ced2ce2](https://github.com/eggjs/tegg/commit/ced2ce2134964dcb410410c0192a34f77507c42d)) * impl Schedule decorator ([#52](https://github.com/eggjs/tegg/issues/52)) ([7f95005](https://github.com/eggjs/tegg/commit/7f950050b548ca542addbd7b466675da4e81ce3f)) ## [2.2.1](https://github.com/eggjs/tegg/compare/@eggjs/tegg-orm-plugin@2.2.0...@eggjs/tegg-orm-plugin@2.2.1) (2022-09-05) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [2.2.0](https://github.com/eggjs/tegg/compare/@eggjs/tegg-orm-plugin@2.1.4...@eggjs/tegg-orm-plugin@2.2.0) (2022-09-04) ### Features * impl Schedule decorator ([#52](https://github.com/eggjs/tegg/issues/52)) ([7f95005](https://github.com/eggjs/tegg/commit/7f950050b548ca542addbd7b466675da4e81ce3f)) ## [2.1.4](https://github.com/eggjs/tegg/compare/@eggjs/tegg-orm-plugin@2.1.3...@eggjs/tegg-orm-plugin@2.1.4) (2022-08-16) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [2.1.2](https://github.com/eggjs/tegg/compare/@eggjs/tegg-orm-plugin@2.1.1...@eggjs/tegg-orm-plugin@2.1.2) (2022-07-28) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [2.1.1](https://github.com/eggjs/tegg/compare/@eggjs/tegg-orm-plugin@2.1.0...@eggjs/tegg-orm-plugin@2.1.1) (2022-07-20) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [2.1.0](https://github.com/eggjs/tegg/compare/@eggjs/tegg-orm-plugin@2.0.0...@eggjs/tegg-orm-plugin@2.1.0) (2022-07-20) ### Features * impl Inject Model ([#43](https://github.com/eggjs/tegg/issues/43)) ([ced2ce2](https://github.com/eggjs/tegg/commit/ced2ce2134964dcb410410c0192a34f77507c42d)) # [1.3.0](https://github.com/eggjs/tegg/compare/v1.2.0...v1.3.0) (2022-07-01) ### Features * support leoric hooks ([#41](https://github.com/eggjs/tegg/issues/41)) ([9bdbc2c](https://github.com/eggjs/tegg/commit/9bdbc2cbe96df9f66f96b4f8e208883e99957946)) # [0.2.0](https://github.com/eggjs/tegg/compare/v0.1.19...v0.2.0) (2022-01-20) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ================================================ FILE: plugin/mcp-client/README.md ================================================ # @eggjs/tegg-mcp-client ## Install ```shell npm i --save @eggjs/tegg-mcp-client ``` ## Config ```js // config/plugin.js exports.mcpClient = { package: '@eggjs/tegg-mcp-client', enable: true, }; ``` ================================================ FILE: plugin/mcp-client/index.ts ================================================ export * from './lib/constants'; export * from './lib/QualifierUtil'; export * from './lib/HttpMCPClientFactory'; ================================================ FILE: plugin/mcp-client/lib/EggHttpMCPClient.ts ================================================ import { Logger, } from '@eggjs/tegg'; import type { fetch } from 'urllib'; import { HttpClientOptions, mergeHeaders, HttpMCPClient, } from '@eggjs/mcp-client'; import { ContextHandler } from '@eggjs/tegg-runtime'; import { McpMethod } from './constants'; const MCP_METHOD = Symbol('Context#mcpMethod'); const MCP_TOOL = Symbol('Context#mcpTool'); export interface EggHttpMCPClientOptions { clientName: string; clientVersion: string; logger: Logger; transportOptions?: HttpClientOptions['transportOptions']; requestOptions?: HttpClientOptions['requestOptions']; fetch: typeof fetch; transportType: HttpClientOptions['transportType']; url: string; } export class EggHttpMCPClient extends HttpMCPClient { protected readonly logger: Logger; constructor(options: EggHttpMCPClientOptions) { super({ name: options.clientName, version: options.clientVersion, }, { fetch: options.fetch, transportType: options.transportType, url: options.url, logger: options.logger, transportOptions: { ...(options.transportOptions ?? {}), get requestInit() { return { ...(options.transportOptions?.requestInit ?? {}), get headers() { return mergeHeaders( options.transportOptions?.requestInit?.headers, ); }, }; }, }, requestOptions: options.requestOptions, }); this.logger = options.logger; } async init() { await super.init(); } async listTools( params?: Parameters['0'], options?: Parameters['1'], ) { const context = ContextHandler.getContext(); if (context) { context.set(MCP_METHOD, McpMethod.LIST_TOOLS); } return super.listTools(params, options); } async listPrompts( params?: Parameters['0'], options?: Parameters['1'], ) { const context = ContextHandler.getContext(); if (context) { context.set(MCP_METHOD, McpMethod.LIST_PROMPTS); } return super.listPrompts(params, options); } async listResources( params?: Parameters['0'], options?: Parameters['1'], ) { const context = ContextHandler.getContext(); if (context) { context.set(MCP_METHOD, McpMethod.LIST_RESOURCES); } return super.listResources(params, options); } async connect( transport: Parameters['0'], options?: Parameters['1']) { const context = ContextHandler.getContext(); if (context) { context.set(MCP_METHOD, McpMethod.INITIALIZE); } return super.connect(transport, options); } async notification( notification: Parameters['0'], options?: Parameters['1'], ) { if (notification?.method === 'notifications/initialized') { const context = ContextHandler.getContext(); if (context) { context.set(MCP_METHOD, McpMethod.NOTIFICATION_INIT); } } return super.notification(notification, options); } async callTool( params: Parameters['0'], resultSchema?: Parameters['1'], options?: Parameters['2'], ) { const context = ContextHandler.getContext(); if (context) { context.set(MCP_TOOL, params?.name); context.set(MCP_METHOD, McpMethod.CALL_TOOLS); } return super.callTool(params, resultSchema, options); } } ================================================ FILE: plugin/mcp-client/lib/EggHttpStaticMCPClient.ts ================================================ import { AccessLevel, Inject, Logger, ModuleConfig, LifecycleInit, MultiInstanceProto, ObjectInitType, MultiInstancePrototypeGetObjectsContext, MultiInstanceInfo, ObjectInfo, QualifierInfo, } from '@eggjs/tegg'; import { fetch } from 'urllib'; import { ModuleConfigUtil } from '@eggjs/tegg/helper'; import { getMCPClientConfig, getMCPClientName, MCPClientInjectName, MCPClientQualifierAttribute, } from '@eggjs/mcp-client'; import { QualifierUtil } from './QualifierUtil'; import { EggHttpMCPClient } from './EggHttpMCPClient'; import assert from 'node:assert'; @MultiInstanceProto({ accessLevel: AccessLevel.PUBLIC, initType: ObjectInitType.SINGLETON, // 从 module.yml 中动态获取配置来决定需要初始化几个对象 getObjects(ctx: MultiInstancePrototypeGetObjectsContext) { const config = ModuleConfigUtil.loadModuleConfigSync(ctx.unitPath) as ModuleConfig | undefined; const moduleName = ModuleConfigUtil.readModuleNameSync(ctx.unitPath); const clients = config?.mcp?.clients; if (!clients) return []; return Object.keys(clients) .filter(clientName => { return clients[clientName].type === 'http'; }) .map((clientName: string) => { const properQualifiers: Record = { ...QualifierUtil.getModuleConfigQualifier(moduleName), }; return { name: MCPClientInjectName, qualifiers: [{ attribute: MCPClientQualifierAttribute, value: clientName, }], properQualifiers, }; }); }, }) export class EggHttpStaticMCPClient extends EggHttpMCPClient { constructor( @Inject() moduleConfig: ModuleConfig, @Inject() logger: Logger, @MultiInstanceInfo([ MCPClientQualifierAttribute ]) objInfo: ObjectInfo, ) { const configName = getMCPClientName(objInfo); const sseClientConfig = getMCPClientConfig(moduleConfig, objInfo); const clientName = sseClientConfig.clientName ?? configName; const mcpServerSubConfig = { ...sseClientConfig, }; assert(mcpServerSubConfig.url, `not found mcpServerSubConfig.url for ${clientName}`); super({ clientName, clientVersion: sseClientConfig.version ?? '1.0.0', transportType: mcpServerSubConfig.transportType as any, url: mcpServerSubConfig.url, logger, fetch, }); } @LifecycleInit() async _init() { await super.init(); } } ================================================ FILE: plugin/mcp-client/lib/HttpMCPClientFactory.ts ================================================ import { AccessLevel, Inject, Logger, SingletonProto, } from '@eggjs/tegg'; import { Implementation } from '@modelcontextprotocol/sdk/types.js'; import { HttpClientOptions, } from '@eggjs/mcp-client'; import { EggHttpMCPClient } from './EggHttpMCPClient'; import { fetch } from 'urllib'; @SingletonProto({ accessLevel: AccessLevel.PUBLIC, name: 'httpMcpClientFactory', }) export class HttpMCPClientFactory { @Inject() private readonly logger: Logger; async build(clientInfo: Implementation, options: Omit): Promise { const httpMCPClient = new EggHttpMCPClient({ clientName: clientInfo.name, clientVersion: clientInfo.version, fetch, ...options as HttpClientOptions, logger: this.logger, }); await httpMCPClient.init(); return httpMCPClient; } } ================================================ FILE: plugin/mcp-client/lib/QualifierUtil.ts ================================================ import { ConfigSourceQualifierAttribute } from '@eggjs/tegg'; // TODO refactor to ModuleConfig and mist impl export class QualifierUtil { static getModuleConfigQualifier(moduleName: string) { return { moduleConfig: [{ attribute: ConfigSourceQualifierAttribute, value: moduleName, }], }; } } ================================================ FILE: plugin/mcp-client/lib/constants.ts ================================================ export enum McpMethod { INITIALIZE='initialize', NOTIFICATION_INIT='notifications/initialized', LIST_TOOLS='tools/list', CALL_TOOLS='tools/call', LIST_PROMPTS='prompts/list', LIST_RESOURCES='resources/list', } ================================================ FILE: plugin/mcp-client/package.json ================================================ { "name": "@eggjs/tegg-mcp-client", "eggPlugin": { "name": "teggMcpClient", "dependencies": [ "tegg" ] }, "eggModule": { "name": "teggMcpClient" }, "main": "index.js", "version": "3.78.15", "description": "mcp client for egg", "keywords": [ "egg", "plugin", "typescript", "module", "tegg", "mcp" ], "files": [ "app.js", "app.d.ts", "index.js", "index.d.ts", "lib/**/*.js", "lib/**/*.d.ts", "app/**/*.js", "app/**/*.d.ts", "typings/*.d.ts" ], "types": "typings/index.d.ts", "scripts": { "test": "cross-env NODE_ENV=test NODE_OPTIONS='--no-deprecation' mocha", "clean": "tsc -b --clean", "tsc": "npm run clean && tsc -p ./tsconfig.json", "tsc:pub": "npm run clean && tsc -p ./tsconfig.pub.json", "prepublishOnly": "npm run tsc:pub" }, "homepage": "https://github.com/eggjs/tegg", "bugs": { "url": "https://github.com/eggjs/tegg/issues" }, "repository": { "type": "git", "url": "git@github.com:eggjs/tegg.git", "directory": "plugin/mcp-client" }, "egg": { "typescript": true }, "engines": { "node": ">=18.0.0" }, "dependencies": { "@eggjs/egg-module-common": "^3.78.15", "@eggjs/mcp-client": "^3.78.15", "@eggjs/tegg": "^3.78.15", "@eggjs/tegg-common-util": "^3.78.15", "@eggjs/tegg-lifecycle": "^3.78.15", "@eggjs/tegg-loader": "^3.78.15", "@eggjs/tegg-metadata": "^3.78.15", "@eggjs/tegg-runtime": "^3.78.15", "@types/koa-router": "^7.0.40", "koa-compose": "^3.2.1", "sdk-base": "^4.2.0", "urllib": "^4.4.0" }, "devDependencies": { "@eggjs/module-test-util": "^3.78.15", "@eggjs/router": "^2.0.0", "@eggjs/tegg-config": "^3.78.15", "@eggjs/tegg-controller-plugin": "^3.78.15", "@eggjs/tegg-plugin": "^3.78.15", "@types/mocha": "^10.0.1", "@types/node": "^20.2.4", "cross-env": "^7.0.3", "egg": "^3.9.1", "egg-mock": "^5.5.0", "egg-tracer": "^2.0.0", "koa-router": "^8.0.8", "mocha": "^10.2.0", "mysql": "^2.18.1", "ts-node": "^10.9.1", "typescript": "^5.0.4" }, "publishConfig": { "access": "public" }, "gitHead": "240a3e9f40fda65ebb7589727d197db5ce17916c" } ================================================ FILE: plugin/mcp-client/test/fixtures/apps/mcpclient/app/modules/bar/controller/AppController.ts ================================================ import { HTTPController, HTTPMethod, HTTPMethodEnum, Inject, } from '@eggjs/tegg'; import { MCPClientQualifier, MCPClientInjectName, HttpMCPClient, } from '@eggjs/mcp-client'; import { HttpMCPClientFactory } from '../../../../../../../../index'; @HTTPController({ path: '/mcpclient', }) export class AppController { @Inject() @MCPClientQualifier('bar') private readonly mcpClient: HttpMCPClient; @Inject({ name: MCPClientInjectName, }) @MCPClientQualifier('foo') private readonly StreamMcpClient: HttpMCPClient; @Inject() private readonly httpMCPClientFactory: HttpMCPClientFactory; @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/hello-sse', }) async hello() { const res = await this.mcpClient.listTools(); return res; } @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/hello-streamable', }) async streamable() { const res = await this.StreamMcpClient.listTools(); return res; } @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/hello-factory', }) async factory() { const client = await this.httpMCPClientFactory.build({ name: 'foo', version: '1.0.0', }, { transportType: 'STREAMABLE_HTTP', url: 'http://127.0.0.1:17263/', }); const res = await client.listTools(); return res; } @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/hello-langchain-tools', }) async langchainTools() { const tools = await this.mcpClient.getLangChainTool(); return { length: tools.length, tools: tools.map(tool => ({ name: tool.name, description: tool.description, schema: tool.schema, })), }; } } ================================================ FILE: plugin/mcp-client/test/fixtures/apps/mcpclient/app/modules/bar/module.yml ================================================ mcp: clients: bar: url: http://127.0.0.1:17253/mcp/sse clientName: barSse version: 1.0.0 transportType: SSE type: http foo: url: http://127.0.0.1:17263/ clientName: barStreamable version: 1.0.0 transportType: STREAMABLE_HTTP type: http ================================================ FILE: plugin/mcp-client/test/fixtures/apps/mcpclient/app/modules/bar/package.json ================================================ { "name": "mcp-client-module", "eggModule": { "name": "mcpClientModule" } } ================================================ FILE: plugin/mcp-client/test/fixtures/apps/mcpclient/config/config.default.js ================================================ 'use strict'; module.exports = function() { const config = { keys: 'test key', security: { csrf: { enable: false, }, }, bodyParser: { enable: false, }, }; return config; }; ================================================ FILE: plugin/mcp-client/test/fixtures/apps/mcpclient/config/module.json ================================================ [ { "path": "../app/modules/bar" }, { "package": "../../../../" } ] ================================================ FILE: plugin/mcp-client/test/fixtures/apps/mcpclient/config/plugin.js ================================================ 'use strict'; // eslint-disable-next-line @typescript-eslint/no-var-requires const path = require('node:path'); exports.tegg = { package: '@eggjs/tegg-plugin', enable: true, }; exports.teggConfig = { package: '@eggjs/tegg-config', enable: true, }; exports.teggMcpClient = { enable: true, path: path.join(__dirname, '../../../../../'), }; exports.teggController = { package: '@eggjs/tegg-controller-plugin', enable: true, }; exports.watcher = false; ================================================ FILE: plugin/mcp-client/test/fixtures/apps/mcpclient/package.json ================================================ { "name": "egg-app" } ================================================ FILE: plugin/mcp-client/test/fixtures/sse-mcp-server/http.ts ================================================ import http from 'node:http'; import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js'; import * as z from 'zod/v4'; // Create an MCP server const server = new McpServer({ name: 'Demo', version: '1.0.0', }); // Add an addition tool server.registerTool('add', { inputSchema: { a: z.number(), b: z.number() }, }, async ({ a, b }) => ({ content: [{ type: 'text', text: String(a + b) }], }), ); // Add a dynamic greeting resource server.registerResource( 'greeting', 'greeting://{name}', {}, async uri => ({ contents: [{ uri: uri.href, text: `Hello, ${uri.hostname}!`, }], }), ); const transports = {}; export const headers = {}; export let httpServer; export async function startSSEServer(port = 17233) { const httpServer = http.createServer(async (req, res) => { const url = new URL(`http://127.0.0.1:${port}${req.url!}`); const headerKey = `${req.method}${url.pathname}`; const serverCode = req.headers['x-mcp-server-code'] as string; headers[serverCode] = headers[serverCode] || {}; headers[serverCode][headerKey] = headers[serverCode][headerKey] || []; headers[serverCode][headerKey].push(req.headers); if (req.method === 'GET') { const transport = new SSEServerTransport('/mcp', res); transports[transport.sessionId] = transport; // Connect the transport to the MCP server await server.connect(transport); } else if (req.method === 'POST') { const sessionId = url.searchParams.get('sessionId'); // const chunks: Buffer[] = []; // for await (const chunk of req) { // chunks.push(chunk); // } // const body = JSON.parse(Buffer.concat(chunks).toString('utf-8')); const transport = transports[sessionId!] as SSEServerTransport; await transport.handlePostMessage(req, res); res.statusCode = 201; res.end(); } }); return new Promise(resolve => { httpServer.listen(port, resolve); }); } export async function stopSSEServer() { server.close(); } ================================================ FILE: plugin/mcp-client/test/fixtures/streamable-mcp-server/http.ts ================================================ import http from 'node:http'; import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import * as z from 'zod/v4'; // Create an MCP server const server = new McpServer({ name: 'Demo', version: '1.0.0', }); // Add an addition tool server.registerTool('add', { inputSchema: { a: z.number(), b: z.number() }, }, async ({ a, b }) => ({ content: [{ type: 'text', text: String(a + b) }], }), ); // Add a dynamic greeting resource server.registerResource( 'greeting', 'greeting://{name}', {}, async uri => ({ contents: [{ uri: uri.href, text: `Hello, ${uri.hostname}!`, }], }), ); export const headers = {}; export let httpServer; export async function startStreamableServer(port = 17243) { const httpServer = http.createServer(async (req, res) => { // eslint-disable-next-line @typescript-eslint/no-var-requires const { StreamableHTTPServerTransport } = require('@modelcontextprotocol/sdk/server/streamableHttp.js'); const url = new URL(`http://127.0.0.1:${port}${req.url!}`); const headerKey = `${req.method}${url.pathname}`; const serverCode = req.headers['x-mcp-server-code'] as string; headers[serverCode] = headers[serverCode] || {}; headers[serverCode][headerKey] = headers[serverCode][headerKey] || []; headers[serverCode][headerKey].push(req.headers); if (req.method === 'POST') { try { const transport: typeof StreamableHTTPServerTransport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined, }); await server.connect(transport); await transport.handleRequest(req, res); res.on('close', () => { console.log('Request closed'); transport.close(); server.close(); }); } catch (error) { console.error('Error handling MCP request:', error); if (!res.headersSent) { res.statusCode = 500; res.setHeader('Content-Type', 'application/json'); res.end(JSON.stringify({ jsonrpc: '2.0', error: { code: -32603, message: 'Internal server error', }, id: null, })); } } } else { res.statusCode = 405; res.setHeader('Content-Type', 'application/json'); res.end(JSON.stringify({ jsonrpc: '2.0', error: { code: -32601, message: 'Method not found', }, id: null, })); } }); return new Promise(resolve => { httpServer.listen(port, resolve); }); } export async function stopStreamableServer() { server.close(); } ================================================ FILE: plugin/mcp-client/test/mcpclient.test.ts ================================================ import mm from 'egg-mock'; import path from 'path'; import assert from 'assert'; describe('plugin/mcp-client/test/mcpclient.test.ts', () => { if (parseInt(process.version.slice(1, 3)) > 17) { // eslint-disable-next-line @typescript-eslint/no-var-requires const { startSSEServer, stopSSEServer } = require('./fixtures/sse-mcp-server/http'); // eslint-disable-next-line @typescript-eslint/no-var-requires const { startStreamableServer, stopStreamableServer } = require('./fixtures/streamable-mcp-server/http'); let app; before(async () => { await startStreamableServer(17263); await startSSEServer(17253); }); after(async () => { await app.close(); await stopSSEServer(); await stopStreamableServer(); }); afterEach(() => { mm.restore(); }); before(async () => { mm(process.env, 'EGG_TYPESCRIPT', true); mm(process, 'cwd', () => { return path.join(__dirname, '..'); }); app = mm.app({ baseDir: path.join(__dirname, 'fixtures/apps/mcpclient'), framework: path.dirname(require.resolve('egg')), }); await app.ready(); }); after(() => { return app.close(); }); it('should sse work', async () => { const res = await app.httpRequest() .get('/mcpclient/hello-sse') .expect(200); assert.deepStrictEqual(res.body, { tools: [ { execution: { taskSupport: 'forbidden', }, name: 'add', inputSchema: { $schema: 'http://json-schema.org/draft-07/schema#', type: 'object', properties: { a: { type: 'number', }, b: { type: 'number', }, }, required: [ 'a', 'b', ], }, }, ], }); }); it('should streamable work', async () => { const res = await app.httpRequest() .get('/mcpclient/hello-streamable') .expect(200); assert.deepStrictEqual(res.body, { tools: [ { name: 'add', execution: { taskSupport: 'forbidden', }, inputSchema: { $schema: 'http://json-schema.org/draft-07/schema#', type: 'object', properties: { a: { type: 'number', }, b: { type: 'number', }, }, required: [ 'a', 'b', ], }, }, ], }); }); it('should factory work', async () => { const res = await app.httpRequest() .get('/mcpclient/hello-factory') .expect(200); assert.deepStrictEqual(res.body, { tools: [ { name: 'add', execution: { taskSupport: 'forbidden', }, inputSchema: { $schema: 'http://json-schema.org/draft-07/schema#', type: 'object', properties: { a: { type: 'number', }, b: { type: 'number', }, }, required: [ 'a', 'b', ], }, }, ], }); }); it('should langchain tools work', async () => { const res = await app.httpRequest() .get('/mcpclient/hello-langchain-tools') .expect(200); assert.deepStrictEqual(res.body, { length: 1, tools: [ { name: 'add', description: '', schema: { type: 'object', properties: { a: { type: 'number', }, b: { type: 'number', }, }, required: [ 'a', 'b', ], }, }, ], }); }); } }); ================================================ FILE: plugin/mcp-client/tsconfig.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "baseUrl": "./" }, "exclude": [ "node_modules", "test" ] } ================================================ FILE: plugin/mcp-client/tsconfig.pub.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "baseUrl": "./" }, "exclude": [ "node_modules", "test" ] } ================================================ FILE: plugin/mcp-client/typings/index.d.ts ================================================ import '@eggjs/mcp-client/typings'; ================================================ FILE: plugin/mcp-proxy/CHANGELOG.md ================================================ # Change Log All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. ## [3.78.15](https://github.com/eggjs/tegg/compare/v3.78.14...v3.78.15) (2026-04-23) **Note:** Version bump only for package @eggjs/mcp-proxy ## [3.78.14](https://github.com/eggjs/tegg/compare/v3.78.13...v3.78.14) (2026-04-21) **Note:** Version bump only for package @eggjs/mcp-proxy ## [3.78.13](https://github.com/eggjs/tegg/compare/v3.78.12...v3.78.13) (2026-04-21) **Note:** Version bump only for package @eggjs/mcp-proxy ## [3.78.12](https://github.com/eggjs/tegg/compare/v3.78.11...v3.78.12) (2026-04-18) **Note:** Version bump only for package @eggjs/mcp-proxy ## [3.78.11](https://github.com/eggjs/tegg/compare/v3.78.10...v3.78.11) (2026-04-18) **Note:** Version bump only for package @eggjs/mcp-proxy ## [3.78.10](https://github.com/eggjs/tegg/compare/v3.78.9...v3.78.10) (2026-04-18) **Note:** Version bump only for package @eggjs/mcp-proxy ## [3.78.9](https://github.com/eggjs/tegg/compare/v3.78.8...v3.78.9) (2026-04-18) **Note:** Version bump only for package @eggjs/mcp-proxy ## [3.78.8](https://github.com/eggjs/tegg/compare/v3.78.7...v3.78.8) (2026-04-18) **Note:** Version bump only for package @eggjs/mcp-proxy ## [3.78.7](https://github.com/eggjs/tegg/compare/v3.78.6...v3.78.7) (2026-04-18) ### Bug Fixes * ts ([4e9baaa](https://github.com/eggjs/tegg/commit/4e9baaad4a78e98148979fc3336e24f872300c7d)) ## [3.78.6](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.6) (2026-04-18) **Note:** Version bump only for package @eggjs/mcp-proxy ## [3.78.5](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.5) (2026-04-18) **Note:** Version bump only for package @eggjs/mcp-proxy ## [3.78.4](https://github.com/eggjs/tegg/compare/v3.78.3...v3.78.4) (2026-04-18) **Note:** Version bump only for package @eggjs/mcp-proxy ## [3.78.3](https://github.com/eggjs/tegg/compare/v3.78.2...v3.78.3) (2026-04-15) ### Bug Fixes * mcp_client_dispatcher ([#435](https://github.com/eggjs/tegg/issues/435)) ([82cdf41](https://github.com/eggjs/tegg/commit/82cdf41230af10b15758c56ec59756d2c5885e5e)) ## [3.78.2](https://github.com/eggjs/tegg/compare/v3.78.1...v3.78.2) (2026-04-09) **Note:** Version bump only for package @eggjs/mcp-proxy ## [3.78.1](https://github.com/eggjs/tegg/compare/v3.78.0...v3.78.1) (2026-04-09) **Note:** Version bump only for package @eggjs/mcp-proxy # [3.78.0](https://github.com/eggjs/tegg/compare/v3.77.2...v3.78.0) (2026-04-09) **Note:** Version bump only for package @eggjs/mcp-proxy ## [3.77.2](https://github.com/eggjs/tegg/compare/v3.77.1...v3.77.2) (2026-04-07) ### Bug Fixes * streamable get timeout ([#431](https://github.com/eggjs/tegg/issues/431)) ([ebacefa](https://github.com/eggjs/tegg/commit/ebacefaa0512cfe9a0d5e7465a0386041cb9d07c)) ## [3.77.1](https://github.com/eggjs/tegg/compare/v3.77.0...v3.77.1) (2026-04-01) **Note:** Version bump only for package @eggjs/mcp-proxy # [3.77.0](https://github.com/eggjs/tegg/compare/v3.76.1...v3.77.0) (2026-04-01) **Note:** Version bump only for package @eggjs/mcp-proxy ## [3.76.1](https://github.com/eggjs/tegg/compare/v3.76.0...v3.76.1) (2026-04-01) **Note:** Version bump only for package @eggjs/mcp-proxy # [3.76.0](https://github.com/eggjs/tegg/compare/v3.75.1...v3.76.0) (2026-04-01) **Note:** Version bump only for package @eggjs/mcp-proxy ## [3.75.1](https://github.com/eggjs/tegg/compare/v3.75.0...v3.75.1) (2026-04-01) **Note:** Version bump only for package @eggjs/mcp-proxy # [3.75.0](https://github.com/eggjs/tegg/compare/v3.74.0...v3.75.0) (2026-03-30) **Note:** Version bump only for package @eggjs/mcp-proxy # [3.74.0](https://github.com/eggjs/tegg/compare/v3.73.0...v3.74.0) (2026-03-30) **Note:** Version bump only for package @eggjs/mcp-proxy # [3.73.0](https://github.com/eggjs/tegg/compare/v3.72.0...v3.73.0) (2026-03-25) **Note:** Version bump only for package @eggjs/mcp-proxy # [3.72.0](https://github.com/eggjs/tegg/compare/v3.71.2...v3.72.0) (2026-02-04) **Note:** Version bump only for package @eggjs/mcp-proxy ## [3.71.2](https://github.com/eggjs/tegg/compare/v3.71.1...v3.71.2) (2026-01-30) **Note:** Version bump only for package @eggjs/mcp-proxy ## [3.71.1](https://github.com/eggjs/tegg/compare/v3.71.0...v3.71.1) (2026-01-28) ### Bug Fixes * mcp proxy header ([#397](https://github.com/eggjs/tegg/issues/397)) ([d79cf73](https://github.com/eggjs/tegg/commit/d79cf735e535fe41756a4a349e1b04a556f81ce9)) # [3.71.0](https://github.com/eggjs/tegg/compare/v3.70.1...v3.71.0) (2026-01-26) **Note:** Version bump only for package @eggjs/mcp-proxy ## [3.70.1](https://github.com/eggjs/tegg/compare/v3.70.0...v3.70.1) (2026-01-14) **Note:** Version bump only for package @eggjs/mcp-proxy # [3.70.0](https://github.com/eggjs/tegg/compare/v3.69.0...v3.70.0) (2026-01-07) **Note:** Version bump only for package @eggjs/mcp-proxy # [3.69.0](https://github.com/eggjs/tegg/compare/v3.68.0...v3.69.0) (2026-01-06) **Note:** Version bump only for package @eggjs/mcp-proxy # [3.68.0](https://github.com/eggjs/tegg/compare/v3.67.2...v3.68.0) (2025-12-29) **Note:** Version bump only for package @eggjs/mcp-proxy ## [3.67.2](https://github.com/eggjs/tegg/compare/v3.67.1...v3.67.2) (2025-12-24) **Note:** Version bump only for package @eggjs/mcp-proxy ## [3.67.1](https://github.com/eggjs/tegg/compare/v3.67.0...v3.67.1) (2025-12-23) ### Bug Fixes * zod v4 ([#381](https://github.com/eggjs/tegg/issues/381)) ([43614c8](https://github.com/eggjs/tegg/commit/43614c8734084a98b1a25c6e907c9c12ff41cb8f)) # [3.67.0](https://github.com/eggjs/tegg/compare/v3.66.0...v3.67.0) (2025-12-18) **Note:** Version bump only for package @eggjs/mcp-proxy # [3.66.0](https://github.com/eggjs/tegg/compare/v3.65.3...v3.66.0) (2025-12-16) **Note:** Version bump only for package @eggjs/mcp-proxy ## [3.65.3](https://github.com/eggjs/tegg/compare/v3.65.2...v3.65.3) (2025-12-16) **Note:** Version bump only for package @eggjs/mcp-proxy ## [3.65.2](https://github.com/eggjs/tegg/compare/v3.65.1...v3.65.2) (2025-12-16) **Note:** Version bump only for package @eggjs/mcp-proxy ## [3.65.1](https://github.com/eggjs/tegg/compare/v3.65.0...v3.65.1) (2025-12-16) **Note:** Version bump only for package @eggjs/mcp-proxy # [3.65.0](https://github.com/eggjs/tegg/compare/v3.64.5...v3.65.0) (2025-12-10) **Note:** Version bump only for package @eggjs/mcp-proxy ## [3.64.5](https://github.com/eggjs/tegg/compare/v3.64.4...v3.64.5) (2025-12-08) **Note:** Version bump only for package @eggjs/mcp-proxy ## [3.64.4](https://github.com/eggjs/tegg/compare/v3.64.3...v3.64.4) (2025-12-06) **Note:** Version bump only for package @eggjs/mcp-proxy ## [3.64.3](https://github.com/eggjs/tegg/compare/v3.64.2...v3.64.3) (2025-12-06) **Note:** Version bump only for package @eggjs/mcp-proxy ## [3.64.2](https://github.com/eggjs/tegg/compare/v3.64.1...v3.64.2) (2025-11-27) **Note:** Version bump only for package @eggjs/mcp-proxy ## [3.64.1](https://github.com/eggjs/tegg/compare/v3.64.0...v3.64.1) (2025-11-27) **Note:** Version bump only for package @eggjs/mcp-proxy # [3.64.0](https://github.com/eggjs/tegg/compare/v3.63.2...v3.64.0) (2025-11-27) ### Bug Fixes * mcp zod type and langchain test version ([#369](https://github.com/eggjs/tegg/issues/369)) ([8178168](https://github.com/eggjs/tegg/commit/81781685c392346d21c56b649bfe8bb7a99bc9fb)) ### Features * add langchain decorator ([#356](https://github.com/eggjs/tegg/issues/356)) ([b176c73](https://github.com/eggjs/tegg/commit/b176c7325009c372ce9d17f348b4fc1f1b6d7fb1)) ## [3.63.2](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.2) (2025-11-18) **Note:** Version bump only for package @eggjs/mcp-proxy ## [3.63.1](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.1) (2025-11-18) **Note:** Version bump only for package @eggjs/mcp-proxy # [3.63.0](https://github.com/eggjs/tegg/compare/v3.62.3...v3.63.0) (2025-11-18) ### Features * add MiddlewareGraphHook to handle controller middleware depende… ([#361](https://github.com/eggjs/tegg/issues/361)) ([7ab3eae](https://github.com/eggjs/tegg/commit/7ab3eae1af20e14101e1df63628a426cb5f6d3db)) ## [3.62.3](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.3) (2025-11-05) ### Bug Fixes * mcp args chinese ([61dad90](https://github.com/eggjs/tegg/commit/61dad903b2ba6140dd52a5b5600a36caef26373f)) ## [3.62.2](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.2) (2025-11-05) ### Bug Fixes * mcp args chinese ([61dad90](https://github.com/eggjs/tegg/commit/61dad903b2ba6140dd52a5b5600a36caef26373f)) ## [3.62.1](https://github.com/eggjs/tegg/compare/v3.62.0...v3.62.1) (2025-11-03) ### Bug Fixes * multi mcp client ([#357](https://github.com/eggjs/tegg/issues/357)) ([f9e4728](https://github.com/eggjs/tegg/commit/f9e47289160dffc5b47e93b60128a25ffd94fb4e)) # [3.62.0](https://github.com/eggjs/tegg/compare/v3.61.0...v3.62.0) (2025-09-24) ### Features * add mcp middleware hook ([#344](https://github.com/eggjs/tegg/issues/344)) ([7215645](https://github.com/eggjs/tegg/commit/72156452a2d69ff8f31b4fe76324dd4164761698)) # [3.61.0](https://github.com/eggjs/tegg/compare/v3.60.3...v3.61.0) (2025-08-15) **Note:** Version bump only for package @eggjs/mcp-proxy ## [3.60.3](https://github.com/eggjs/tegg/compare/v3.60.2...v3.60.3) (2025-08-06) ### Bug Fixes * mcp middleware ([#340](https://github.com/eggjs/tegg/issues/340)) ([a47db22](https://github.com/eggjs/tegg/commit/a47db2295a899113aad46d7f4ca0857d91d44774)) ## [3.60.2](https://github.com/eggjs/tegg/compare/v3.60.1...v3.60.2) (2025-08-06) **Note:** Version bump only for package @eggjs/mcp-proxy ## [3.60.1](https://github.com/eggjs/tegg/compare/v3.60.0...v3.60.1) (2025-07-28) **Note:** Version bump only for package @eggjs/mcp-proxy # [3.60.0](https://github.com/eggjs/tegg/compare/v3.59.1...v3.60.0) (2025-07-28) **Note:** Version bump only for package @eggjs/mcp-proxy ## [3.59.1](https://github.com/eggjs/tegg/compare/v3.59.0...v3.59.1) (2025-07-16) **Note:** Version bump only for package @eggjs/mcp-proxy # [3.59.0](https://github.com/eggjs/tegg/compare/v3.58.0...v3.59.0) (2025-07-07) ### Features * add mcp global middleware ([#335](https://github.com/eggjs/tegg/issues/335)) ([7722102](https://github.com/eggjs/tegg/commit/772210298a937b7fbae9fd4fd1e1bc318b754cef)) # [3.58.0](https://github.com/eggjs/tegg/compare/v3.57.14...v3.58.0) (2025-07-01) **Note:** Version bump only for package @eggjs/mcp-proxy ## [3.57.14](https://github.com/eggjs/tegg/compare/v3.57.13...v3.57.14) (2025-06-18) **Note:** Version bump only for package @eggjs/mcp-proxy ## [3.57.13](https://github.com/eggjs/tegg/compare/v3.57.12...v3.57.13) (2025-06-18) **Note:** Version bump only for package @eggjs/mcp-proxy ## [3.57.12](https://github.com/eggjs/tegg/compare/v3.57.11...v3.57.12) (2025-06-16) **Note:** Version bump only for package @eggjs/mcp-proxy ## [3.57.11](https://github.com/eggjs/tegg/compare/v3.57.10...v3.57.11) (2025-06-16) **Note:** Version bump only for package @eggjs/mcp-proxy ## [3.57.10](https://github.com/eggjs/tegg/compare/v3.57.9...v3.57.10) (2025-06-12) **Note:** Version bump only for package @eggjs/mcp-proxy ## [3.57.9](https://github.com/eggjs/tegg/compare/v3.57.8...v3.57.9) (2025-05-29) **Note:** Version bump only for package @eggjs/mcp-proxy ## [3.57.8](https://github.com/eggjs/tegg/compare/v3.57.7...v3.57.8) (2025-05-29) **Note:** Version bump only for package @eggjs/mcp-proxy ## [3.57.7](https://github.com/eggjs/tegg/compare/v3.57.6...v3.57.7) (2025-05-28) **Note:** Version bump only for package @eggjs/mcp-proxy ## [3.57.6](https://github.com/eggjs/tegg/compare/v3.57.5...v3.57.6) (2025-05-27) ### Bug Fixes * sse new ctx ([#323](https://github.com/eggjs/tegg/issues/323)) ([5716e0a](https://github.com/eggjs/tegg/commit/5716e0a33f8f58249ebdc1a3b6fb7959394ef4ee)) ## [3.57.5](https://github.com/eggjs/tegg/compare/v3.57.4...v3.57.5) (2025-05-15) ### Bug Fixes * zod phantom dependency ([#322](https://github.com/eggjs/tegg/issues/322)) ([e92372e](https://github.com/eggjs/tegg/commit/e92372eb884d0f5d8227d340a3d7db01b51267cf)) ## [3.57.4](https://github.com/eggjs/tegg/compare/v3.57.3...v3.57.4) (2025-05-14) **Note:** Version bump only for package @eggjs/mcp-proxy ## [3.57.3](https://github.com/eggjs/tegg/compare/v3.57.2...v3.57.3) (2025-05-13) **Note:** Version bump only for package @eggjs/mcp-proxy ## [3.57.2](https://github.com/eggjs/tegg/compare/v3.57.1...v3.57.2) (2025-05-13) ### Bug Fixes * stream mcp wait ([#319](https://github.com/eggjs/tegg/issues/319)) ([47ef28b](https://github.com/eggjs/tegg/commit/47ef28b1fae06c57857a7b340d0703403a907859)) ## [3.57.1](https://github.com/eggjs/tegg/compare/v3.57.0...v3.57.1) (2025-05-13) ### Bug Fixes * mcp context proto ([#318](https://github.com/eggjs/tegg/issues/318)) ([4d8e107](https://github.com/eggjs/tegg/commit/4d8e107fad4414da9593dd07ab2ae888dfd6a335)) # [3.57.0](https://github.com/eggjs/tegg/compare/v3.56.3...v3.57.0) (2025-05-09) **Note:** Version bump only for package @eggjs/mcp-proxy ## [3.56.3](https://github.com/eggjs/tegg/compare/v3.56.2...v3.56.3) (2025-05-07) ### Bug Fixes * dep ([#313](https://github.com/eggjs/tegg/issues/313)) ([791feea](https://github.com/eggjs/tegg/commit/791feead91ad48adaa2dee4c0746bea382e61d34)) * mcp teggCtxLifecycleMiddleware ([#312](https://github.com/eggjs/tegg/issues/312)) ([5304384](https://github.com/eggjs/tegg/commit/53043840c3aaab0e485db50b7a2d9362266eef8c)) ## [3.56.2](https://github.com/eggjs/tegg/compare/v3.56.1...v3.56.2) (2025-05-01) ### Bug Fixes * mcp version check ([#311](https://github.com/eggjs/tegg/issues/311)) ([7f16c9c](https://github.com/eggjs/tegg/commit/7f16c9c361cfec8b5ffd4075b23cff317cc5207a)) ## [3.56.1](https://github.com/eggjs/tegg/compare/v3.56.0...v3.56.1) (2025-04-29) **Note:** Version bump only for package @eggjs/mcp-proxy # [3.56.0](https://github.com/eggjs/tegg/compare/v3.55.0...v3.56.0) (2025-04-29) ### Features * add mcp stateless ([#309](https://github.com/eggjs/tegg/issues/309)) ([b79d313](https://github.com/eggjs/tegg/commit/b79d313aa8f24adc91f88ea0732f2d98c0a8ead9)) # [3.55.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.55.0) (2025-04-29) ### Features * add mcp ([#307](https://github.com/eggjs/tegg/issues/307)) ([a9a57b4](https://github.com/eggjs/tegg/commit/a9a57b4d7102dd552e09d33c3f82fc15a245790a)) # [3.54.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.54.0) (2025-04-29) ### Features * add mcp ([#307](https://github.com/eggjs/tegg/issues/307)) ([a9a57b4](https://github.com/eggjs/tegg/commit/a9a57b4d7102dd552e09d33c3f82fc15a245790a)) # [3.53.0](https://github.com/eggjs/tegg/compare/v3.52.1...v3.53.0) (2025-04-21) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.52.1](https://github.com/eggjs/tegg/compare/v3.52.0...v3.52.1) (2025-03-14) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.52.0](https://github.com/eggjs/tegg/compare/v3.51.2...v3.52.0) (2024-12-30) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.51.2](https://github.com/eggjs/tegg/compare/v3.51.1...v3.51.2) (2024-12-09) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.51.1](https://github.com/eggjs/tegg/compare/v3.51.0...v3.51.1) (2024-11-06) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.51.0](https://github.com/eggjs/tegg/compare/v3.50.1...v3.51.0) (2024-10-30) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.50.1](https://github.com/eggjs/tegg/compare/v3.50.0...v3.50.1) (2024-10-23) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.50.0](https://github.com/eggjs/tegg/compare/v3.49.0...v3.50.0) (2024-10-22) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.49.0](https://github.com/eggjs/tegg/compare/v3.48.1...v3.49.0) (2024-10-21) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.48.1](https://github.com/eggjs/tegg/compare/v3.48.0...v3.48.1) (2024-10-14) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.48.0](https://github.com/eggjs/tegg/compare/v3.47.2...v3.48.0) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.47.2](https://github.com/eggjs/tegg/compare/v3.47.1...v3.47.2) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.47.1](https://github.com/eggjs/tegg/compare/v3.47.0...v3.47.1) (2024-10-10) ### Bug Fixes * fix aop in constructor inject type ([#247](https://github.com/eggjs/tegg/issues/247)) ([d169bb2](https://github.com/eggjs/tegg/commit/d169bb2fbbc86335315619866b4134a25296f552)) # [3.47.0](https://github.com/eggjs/tegg/compare/v3.46.4...v3.47.0) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.46.4](https://github.com/eggjs/tegg/compare/v3.46.3...v3.46.4) (2024-10-09) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.46.3](https://github.com/eggjs/tegg/compare/v3.46.2...v3.46.3) (2024-10-08) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.46.2](https://github.com/eggjs/tegg/compare/v3.46.1...v3.46.2) (2024-10-07) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.46.1](https://github.com/eggjs/tegg/compare/v3.46.0...v3.46.1) (2024-09-30) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.46.0](https://github.com/eggjs/tegg/compare/v3.45.0...v3.46.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.45.0](https://github.com/eggjs/tegg/compare/v3.44.1...v3.45.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.44.1](https://github.com/eggjs/tegg/compare/v3.44.0...v3.44.1) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.44.0](https://github.com/eggjs/tegg/compare/v3.43.2...v3.44.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.43.2](https://github.com/eggjs/tegg/compare/v3.43.1...v3.43.2) (2024-09-14) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.43.1](https://github.com/eggjs/tegg/compare/v3.43.0...v3.43.1) (2024-09-14) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.43.0](https://github.com/eggjs/tegg/compare/v3.42.0...v3.43.0) (2024-09-13) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.42.0](https://github.com/eggjs/tegg/compare/v3.41.0...v3.42.0) (2024-09-10) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.41.0](https://github.com/eggjs/tegg/compare/v3.40.1...v3.41.0) (2024-08-26) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.40.1](https://github.com/eggjs/tegg/compare/v3.40.0...v3.40.1) (2024-08-23) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.40.0](https://github.com/eggjs/tegg/compare/v3.39.5...v3.40.0) (2024-08-22) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.39.5](https://github.com/eggjs/tegg/compare/v3.39.4...v3.39.5) (2024-08-09) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.39.4](https://github.com/eggjs/tegg/compare/v3.39.3...v3.39.4) (2024-07-09) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.39.3](https://github.com/eggjs/tegg/compare/v3.39.2...v3.39.3) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.39.2](https://github.com/eggjs/tegg/compare/v3.39.1...v3.39.2) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.39.1](https://github.com/eggjs/tegg/compare/v3.39.0...v3.39.1) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.39.0](https://github.com/eggjs/tegg/compare/v3.38.0...v3.39.0) (2024-04-19) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.38.0](https://github.com/eggjs/tegg/compare/v3.37.3...v3.38.0) (2024-04-18) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.37.3](https://github.com/eggjs/tegg/compare/v3.37.2...v3.37.3) (2024-04-17) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.37.2](https://github.com/eggjs/tegg/compare/v3.37.1...v3.37.2) (2024-04-17) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.37.1](https://github.com/eggjs/tegg/compare/v3.37.0...v3.37.1) (2024-04-16) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.37.0](https://github.com/eggjs/tegg/compare/v3.36.3...v3.37.0) (2024-04-16) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.36.3](https://github.com/eggjs/tegg/compare/v3.36.2...v3.36.3) (2024-04-10) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.36.2](https://github.com/eggjs/tegg/compare/v3.36.1...v3.36.2) (2024-04-08) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.36.1](https://github.com/eggjs/tegg/compare/v3.36.0...v3.36.1) (2024-04-07) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.36.0](https://github.com/eggjs/tegg/compare/v3.35.1...v3.36.0) (2024-04-02) ### Features * impl ajv + typebox Validator ([#201](https://github.com/eggjs/tegg/issues/201)) ([9fd585d](https://github.com/eggjs/tegg/commit/9fd585de9b613466c96b73494a08a494db34ea57)) ## [3.35.1](https://github.com/eggjs/tegg/compare/v3.35.0...v3.35.1) (2024-03-26) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.35.0](https://github.com/eggjs/tegg/compare/v3.34.0...v3.35.0) (2024-03-26) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.34.0](https://github.com/eggjs/tegg/compare/v3.33.1...v3.34.0) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.33.1](https://github.com/eggjs/tegg/compare/v3.33.0...v3.33.1) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.33.0](https://github.com/eggjs/tegg/compare/v3.32.0...v3.33.0) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.32.0](https://github.com/eggjs/tegg/compare/v3.31.0...v3.32.0) (2024-02-19) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.31.0](https://github.com/eggjs/tegg/compare/v3.30.1...v3.31.0) (2024-01-31) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.30.1](https://github.com/eggjs/tegg/compare/v3.30.0...v3.30.1) (2024-01-25) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.30.0](https://github.com/eggjs/tegg/compare/v3.29.0...v3.30.0) (2024-01-17) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.29.0](https://github.com/eggjs/tegg/compare/v3.28.2...v3.29.0) (2023-12-26) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.28.2](https://github.com/eggjs/tegg/compare/v3.28.1...v3.28.2) (2023-12-12) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.28.1](https://github.com/eggjs/tegg/compare/v3.28.0...v3.28.1) (2023-12-11) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.28.0](https://github.com/eggjs/tegg/compare/v3.27.0...v3.28.0) (2023-12-10) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.27.0](https://github.com/eggjs/tegg/compare/v3.26.0...v3.27.0) (2023-11-23) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.26.0](https://github.com/eggjs/tegg/compare/v3.25.2...v3.26.0) (2023-11-17) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.25.2](https://github.com/eggjs/tegg/compare/v3.25.1...v3.25.2) (2023-11-06) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.25.1](https://github.com/eggjs/tegg/compare/v3.25.0...v3.25.1) (2023-11-03) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.25.0](https://github.com/eggjs/tegg/compare/v3.24.0...v3.25.0) (2023-11-03) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.24.0](https://github.com/eggjs/tegg/compare/v3.23.0...v3.24.0) (2023-10-26) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.23.0](https://github.com/eggjs/tegg/compare/v3.22.0...v3.23.0) (2023-09-20) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.20.0](https://github.com/eggjs/tegg/compare/v3.19.0...v3.20.0) (2023-09-07) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.19.0](https://github.com/eggjs/tegg/compare/v3.18.1...v3.19.0) (2023-08-31) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.18.1](https://github.com/eggjs/tegg/compare/v3.18.0...v3.18.1) (2023-08-29) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.18.0](https://github.com/eggjs/tegg/compare/v3.17.0...v3.18.0) (2023-08-29) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.17.0](https://github.com/eggjs/tegg/compare/v3.16.0...v3.17.0) (2023-08-24) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.16.0](https://github.com/eggjs/tegg/compare/v3.15.0...v3.16.0) (2023-08-24) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.14.3](https://github.com/eggjs/tegg/compare/v3.14.2...v3.14.3) (2023-08-14) ### Bug Fixes * fix aop plugin files ([#140](https://github.com/eggjs/tegg/issues/140)) ([f47eef6](https://github.com/eggjs/tegg/commit/f47eef634efd442ac5a8f68567e36c940247e48b)) ## [3.14.2](https://github.com/eggjs/tegg/compare/v3.14.1...v3.14.2) (2023-08-14) ### Bug Fixes * init all context advice if root proto miss ([#139](https://github.com/eggjs/tegg/issues/139)) ([0602ea8](https://github.com/eggjs/tegg/commit/0602ea81578bf717ee4b4c490ace8c1c133478c5)) ## [3.14.1](https://github.com/eggjs/tegg/compare/v3.14.0...v3.14.1) (2023-08-11) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.14.0](https://github.com/eggjs/tegg/compare/v3.13.0...v3.14.0) (2023-07-31) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.13.0](https://github.com/eggjs/tegg/compare/v3.12.0...v3.13.0) (2023-07-25) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.12.0](https://github.com/eggjs/tegg/compare/v3.11.1...v3.12.0) (2023-07-13) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.11.1](https://github.com/eggjs/tegg/compare/v3.11.0...v3.11.1) (2023-06-29) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.11.0](https://github.com/eggjs/tegg/compare/v3.10.0...v3.11.0) (2023-06-29) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.10.0](https://github.com/eggjs/tegg/compare/v3.9.0...v3.10.0) (2023-06-28) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.9.0](https://github.com/eggjs/tegg/compare/v3.8.0...v3.9.0) (2023-06-20) ### Features * implement advice params ([76ec8ad](https://github.com/eggjs/tegg/commit/76ec8ad7b7170a637e59d74d49c1f00d8a201321)) # [3.8.0](https://github.com/eggjs/tegg/compare/v3.7.0...v3.8.0) (2023-05-30) ### Features * impl EggObjectLifecycle hook in decorator ([#119](https://github.com/eggjs/tegg/issues/119)) ([cced8a2](https://github.com/eggjs/tegg/commit/cced8a2e009c33d5040fa21d00409fddef471b0e)) # [3.7.0](https://github.com/eggjs/tegg/compare/v3.6.3...v3.7.0) (2023-04-03) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.6.3](https://github.com/eggjs/tegg/compare/v3.6.2...v3.6.3) (2023-03-02) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.6.2](https://github.com/eggjs/tegg/compare/v3.6.1...v3.6.2) (2023-02-16) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.6.1](https://github.com/eggjs/tegg/compare/v3.6.0...v3.6.1) (2023-02-14) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.6.0](https://github.com/eggjs/tegg/compare/v3.5.2...v3.6.0) (2023-02-13) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.5.2](https://github.com/eggjs/tegg/compare/v3.5.1...v3.5.2) (2023-02-10) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.5.1](https://github.com/eggjs/tegg/compare/v3.5.0...v3.5.1) (2023-02-10) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.5.0](https://github.com/eggjs/tegg/compare/v3.4.1...v3.5.0) (2023-02-10) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.4.1](https://github.com/eggjs/tegg/compare/v3.4.0...v3.4.1) (2023-02-02) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.4.0](https://github.com/eggjs/tegg/compare/v3.3.4...v3.4.0) (2023-02-01) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.3.4](https://github.com/eggjs/tegg/compare/v3.3.3...v3.3.4) (2023-01-29) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.3.3](https://github.com/eggjs/tegg/compare/v3.3.2...v3.3.3) (2023-01-29) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.3.2](https://github.com/eggjs/tegg/compare/v3.3.1...v3.3.2) (2023-01-29) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.3.1](https://github.com/eggjs/tegg/compare/v3.3.0...v3.3.1) (2023-01-28) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.3.0](https://github.com/eggjs/tegg/compare/v3.2.4...v3.3.0) (2023-01-28) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.2.3](https://github.com/eggjs/tegg/compare/v3.2.2...v3.2.3) (2023-01-16) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.2.2](https://github.com/eggjs/tegg/compare/v3.2.1...v3.2.2) (2023-01-06) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [3.2.1](https://github.com/eggjs/tegg/compare/v3.2.0...v3.2.1) (2022-12-28) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.2.0](https://github.com/eggjs/tegg/compare/v3.1.0...v3.2.0) (2022-12-28) ### Features * impl mockModuleContextScope ([#73](https://github.com/eggjs/tegg/issues/73)) ([041881c](https://github.com/eggjs/tegg/commit/041881ca317ad81366172a35ac56b7b2dc0a0488)) # [3.1.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.1.0) (2022-12-27) ### Features * **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) # [3.0.0](https://github.com/eggjs/tegg/compare/v3.0.0-alpha.0...v3.0.0) (2022-12-26) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [3.0.0-alpha.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.0.0-alpha.0) (2022-12-22) ### Features * **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) ## [1.3.9](https://github.com/eggjs/tegg/compare/@eggjs/tegg-aop-plugin@1.3.8...@eggjs/tegg-aop-plugin@1.3.9) (2022-09-05) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [1.3.8](https://github.com/eggjs/tegg/compare/@eggjs/tegg-aop-plugin@1.3.7...@eggjs/tegg-aop-plugin@1.3.8) (2022-09-04) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [1.3.7](https://github.com/eggjs/tegg/compare/@eggjs/tegg-aop-plugin@1.3.6...@eggjs/tegg-aop-plugin@1.3.7) (2022-08-24) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [1.3.6](https://github.com/eggjs/tegg/compare/@eggjs/tegg-aop-plugin@1.3.5...@eggjs/tegg-aop-plugin@1.3.6) (2022-08-16) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [1.3.4](https://github.com/eggjs/tegg/compare/@eggjs/tegg-aop-plugin@1.3.3...@eggjs/tegg-aop-plugin@1.3.4) (2022-07-28) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [1.3.3](https://github.com/eggjs/tegg/compare/@eggjs/tegg-aop-plugin@1.3.2...@eggjs/tegg-aop-plugin@1.3.3) (2022-07-20) **Note:** Version bump only for package @eggjs/tegg-aop-plugin ## [1.3.2](https://github.com/eggjs/tegg/compare/@eggjs/tegg-aop-plugin@1.3.1...@eggjs/tegg-aop-plugin@1.3.2) (2022-07-20) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [1.3.0](https://github.com/eggjs/tegg/compare/v1.2.0...v1.3.0) (2022-07-01) **Note:** Version bump only for package @eggjs/tegg-aop-plugin # [0.2.0](https://github.com/eggjs/tegg/compare/v0.1.19...v0.2.0) (2022-01-20) ### Features * impl aop ([c53df00](https://github.com/eggjs/tegg/commit/c53df001d1455a0a105689694775d880541d9d2f)) ================================================ FILE: plugin/mcp-proxy/README.md ================================================ # @eggjs/mcp-Proxy ## Usage ```js // plugin.js export.mcpProxy = { enable: true, package: '@eggjs/mcp-Proxy', }; ``` ================================================ FILE: plugin/mcp-proxy/agent.ts ================================================ import { Application } from 'egg'; export default class AppHook { private readonly agent: Application; constructor(agent: Application) { this.agent = agent; } async didLoad() { if (this.agent.mcpProxy) { await (this.agent.mcpProxy as any)?.ready(); } } } ================================================ FILE: plugin/mcp-proxy/app/extend/agent.ts ================================================ import { MCPProxyApiClient } from '../../index'; import { Application } from 'egg'; const MCP_PROXY = Symbol('Application#mcpProxy'); export default { get mcpProxy() { if (!this[MCP_PROXY]) { this[MCP_PROXY] = new MCPProxyApiClient({ logger: (this as unknown as Application).logger, messenger: (this as unknown as Application).messenger, app: this as unknown as Application, isAgent: true, }); } return this[MCP_PROXY]; }, }; ================================================ FILE: plugin/mcp-proxy/app/extend/application.ts ================================================ import { MCPProxyApiClient } from '../../index'; import { Application } from 'egg'; const MCP_PROXY = Symbol('Application#mcpProxy'); export default { get mcpProxy() { if (!this[MCP_PROXY]) { this[MCP_PROXY] = new MCPProxyApiClient({ logger: (this as unknown as Application).logger, messenger: (this as unknown as Application).messenger, app: this as unknown as Application, }); } return this[MCP_PROXY]; }, }; ================================================ FILE: plugin/mcp-proxy/app.ts ================================================ import { Application } from 'egg'; import { MCPProxyHook } from './index'; import { MCPControllerRegister } from '@eggjs/tegg-controller-plugin/lib/impl/mcp/MCPControllerRegister'; export default class AppHook { private readonly agent: Application; constructor(agent: Application) { this.agent = agent; } configWillLoad() { MCPControllerRegister.addHook(MCPProxyHook); } async didLoad() { if (this.agent.mcpProxy) { await (this.agent.mcpProxy as any)?.ready(); } } } ================================================ FILE: plugin/mcp-proxy/config/config.default.ts ================================================ export default () => { const config = { mcp: { proxyPort: 17031, }, }; return config; }; ================================================ FILE: plugin/mcp-proxy/index.ts ================================================ import { APIClientBase } from 'cluster-client'; import awaitEvent from 'await-event'; import { MCPProxyDataClient } from './lib/MCPProxyDataClient'; import { Application, Context, EggLogger, Messenger } from 'egg'; import getRawBody from 'raw-body'; import contentType from 'content-type'; import { fetch, Agent } from 'undici'; import http from 'http'; import { EventSourceParserStream } from 'eventsource-parser/stream'; import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js'; import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js'; import { Readable } from 'node:stream'; import cluster from 'node:cluster'; import { MCPControllerRegister, MCPControllerHook } from '@eggjs/tegg-controller-plugin/lib/impl/mcp/MCPControllerRegister'; import querystring from 'node:querystring'; import url from 'node:url'; import compose from 'koa-compose'; import { MCPProtocols } from '@eggjs/tegg-types'; const MAXIMUM_MESSAGE_SIZE = '4mb'; export interface MCPProxyPayload { sessionId: string; message: unknown; } type ProxyAction = 'MCP_STDIO_PROXY' | 'MCP_SEE_PROXY' | 'MCP_STREAM_PROXY'; export interface ProxyMessageOptions { detail: ClientDetail; sessionId: string; type: MCPProtocols; } export interface ClientDetail { pid: number; port: number; } const IGNORE_HEADERS = [ 'connection', 'upgrade', 'keep-alive', 'proxy-connection', 'te', 'trailer', 'transfer-encoding', ]; export const MCPProxyHook: MCPControllerHook = { async preSSEInitHandle(ctx, transport, self) { const id = transport.sessionId; // cluster proxy await self.app.mcpProxy.registerClient(id, process.pid); self.app.mcpProxy.setProxyHandler(MCPProtocols.SSE, async (req, res) => { const sessionId = querystring.parse(url.parse(req.url!).query ?? '').sessionId as string; const ctx = self.app.createContext(req, res) as unknown as Context; if (MCPControllerRegister.hooks.length > 0) { for (const hook of MCPControllerRegister.hooks) { await hook.preProxy?.(ctx, req, res); } } let transport: SSEServerTransport; const existingTransport = self.transports[sessionId]; if (existingTransport instanceof SSEServerTransport) { transport = existingTransport; } else { // https://modelcontextprotocol.io/docs/concepts/architecture#error-handling res.writeHead(400).end(JSON.stringify({ jsonrpc: '2.0', error: { code: -32000, message: 'Bad Request: Session exists but uses a different transport protocol', }, id: null, })); return; } if (transport) { try { await self.transports[sessionId].handlePostMessage(req, res); } catch (error) { self.app.logger.error('Error handling MCP message', error); if (!ctx.res.headersSent) { ctx.status = 500; ctx.body = { jsonrpc: '2.0', error: { code: -32603, message: `Internal error: ${error.message}`, }, id: null, }; } } } else { res.writeHead(404).end(JSON.stringify({ jsonrpc: '2.0', error: { code: -32602, message: 'Bad Request: No transport found for sessionId', }, id: null, })); } }); ctx.res.once('close', () => { delete self.transports[id]; const connection = self.sseConnections.get(id); if (connection) { clearInterval(connection.intervalId); self.sseConnections.delete(id); } self.app.mcpProxy.unregisterClient(id); }); }, async onStreamSessionInitialized(_ctx, transport, server, self) { const sessionId = transport.sessionId!; self.streamTransports[sessionId] = transport; self.mcpServerMap[sessionId] = server; self.app.mcpProxy.setProxyHandler(MCPProtocols.STREAM, async (req: http.IncomingMessage, res: http.ServerResponse) => { let mw = self.app.middleware.teggCtxLifecycleMiddleware(); if (self.globalMiddlewares) { mw = compose([ mw, self.globalMiddlewares ]); } const ctx = self.app.createContext(req, res) as unknown as Context; if (MCPControllerRegister.hooks.length > 0) { for (const hook of MCPControllerRegister.hooks) { await hook.preProxy?.(ctx, req, res); } } const sessionId = req.headers['mcp-session-id'] as string | undefined; if (!sessionId) { res.writeHead(500, { 'content-type': 'application/json' }); res.end(JSON.stringify({ jsonrpc: '2.0', error: { code: -32603, message: 'session id not have and run in proxy', }, id: null, })); } else { let transport: StreamableHTTPServerTransport; const existingTransport = self.streamTransports[sessionId]; if (existingTransport instanceof StreamableHTTPServerTransport) { transport = existingTransport; } else { res.writeHead(400, { 'content-type': 'application/json' }); res.end(JSON.stringify({ jsonrpc: '2.0', error: { code: -32000, message: 'Bad Request: Session exists but uses a different transport protocol', }, id: null, })); return; } if (transport) { await self.app.ctxStorage.run(ctx, async () => { await mw(ctx, async () => { await transport.handleRequest(ctx.req, ctx.res); await awaitEvent(ctx.res, 'close'); }); }); } else { res.writeHead(400, { 'content-type': 'application/json' }); res.end(JSON.stringify({ jsonrpc: '2.0', error: { code: -32602, message: 'Bad Request: No transport found for sessionId', }, id: null, })); } } }); await self.app.mcpProxy.registerClient(sessionId, process.pid); transport.onclose = async () => { const sid = transport.sessionId; if (sid && self.streamTransports[sid]) { delete self.streamTransports[sid]; delete self.mcpServerMap[sid]; } await self.app.mcpProxy.unregisterClient(sid!); }; }, async checkAndRunProxy(ctx, type, sessionId) { const detail = await ctx.app.mcpProxy.getClient(sessionId); if (detail?.pid !== process.pid) { await ctx.app.mcpProxy.proxyMessage(ctx, { detail: detail!, sessionId, type, }); return true; } return false; }, }; export class MCPProxyApiClient extends APIClientBase { private _client: any; private logger: EggLogger; private proxyHandlerMap: { [P in ProxyAction]?: StreamableHTTPServerTransport['handleRequest']; } = {}; private port: number; private app: Application; private isAgent: boolean; constructor(options: { logger: EggLogger; messenger: Messenger; app: Application; isAgent?: boolean; }) { super(Object.assign({}, options, { initMethod: '_init' })); this.logger = options.logger; this.port = 0; this.app = options.app; this.isAgent = !!options.isAgent; } async _init() { if (!this.isAgent) { const server = http.createServer(async (req, res) => { const type = req.headers['mcp-proxy-type'] as ProxyAction; await this.proxyHandlerMap[type]?.(req, res); }); this.port = this.app.config.mcp?.proxyPort + (cluster.worker?.id ?? 0); await new Promise(resolve => server.listen(this.port, () => { // const address = server.address()! as AddressInfo; // this.port = address.port; resolve(null); })); } } setProxyHandler(type: MCPProtocols, handler: StreamableHTTPServerTransport['handleRequest'] | SSEServerTransport['handlePostMessage']) { let action: ProxyAction; switch (type) { case MCPProtocols.SSE: action = 'MCP_SEE_PROXY'; break; case MCPProtocols.STDIO: action = 'MCP_STDIO_PROXY'; break; default: action = 'MCP_STREAM_PROXY'; break; } this.proxyHandlerMap[action] = handler; } async registerClient(sessionId: string, pid: number): Promise { await this._client.registerClient(sessionId, { pid, port: this.port, }); } async unregisterClient(sessionId: string): Promise { await this._client.unregisterClient(sessionId); } async getClient(sessionId: string): Promise { return this._client.getClient(sessionId); } async proxyMessage(ctx: Context, options: ProxyMessageOptions): Promise { let body: string | unknown; const { detail, sessionId, type } = options; try { let encoding = 'utf-8'; if (ctx.req.headers['content-type']) { const ct = contentType.parse(ctx.req.headers['content-type'] ?? ''); if (ct.type !== 'application/json') { throw new Error(`Unsupported content-type: ${ct}`); } encoding = ct.parameters.charset; } // ctx.respond = false; body = await getRawBody(ctx.req, { limit: MAXIMUM_MESSAGE_SIZE, encoding, }); } catch (error) { this.logger.error(error); ctx.res.writeHead(400).end(JSON.stringify({ jsonrpc: '2.0', error: { code: -32602, message: `Bad Request: ${String(error)}`, }, id: null, })); return; } try { // const socketPath = `${this.app.baseDir}/mcpServer${pid}.sock`; let action: ProxyAction; switch (type) { case 'SSE': { action = 'MCP_SEE_PROXY'; ctx.req.headers['mcp-proxy-type'] = action; ctx.req.headers['mcp-proxy-sessionid'] = sessionId; const resp = await fetch(`http://localhost:${detail.port}/mcp/message?sessionId=${sessionId}`, { // dispatcher: new Agent({ // connect: { // socketPath, // }, // }), headers: ctx.req.headers as unknown as Record, body: body as string, method: ctx.req.method, }); const headers: Record = { 'mcp-proxy-arg': encodeURIComponent((body as Buffer).toString()), }; for (const [ key, value ] of resp.headers.entries()) { if (IGNORE_HEADERS.includes(key)) { continue; } headers[key] = value; } ctx.set(headers); ctx.res.statusCode = resp.status; ctx.res.statusMessage = resp.statusText; const resData = await resp.text(); ctx.body = resData; break; } case 'STDIO': action = 'MCP_STDIO_PROXY'; ctx.req.headers['mcp-proxy-type'] = action; ctx.req.headers['mcp-proxy-sessionid'] = sessionId; ctx.res.writeHead(400).end(JSON.stringify({ jsonrpc: '2.0', error: { code: -32602, message: 'Bad Request: STDIO IS NOT IMPL', }, id: null, })); break; default: { action = 'MCP_STREAM_PROXY'; ctx.respond = false; ctx.req.headers['mcp-proxy-type'] = action; ctx.req.headers['mcp-proxy-sessionid'] = sessionId; const dispatcher = new Agent({ bodyTimeout: 10 * 60 * 1000, headersTimeout: 30 * 1000, keepAliveTimeout: 10 * 1000, keepAliveMaxTimeout: 30 * 1000, }); try { const response = await fetch(`http://localhost:${detail.port}`, { dispatcher, headers: ctx.req.headers as unknown as Record, method: ctx.req.method, ...(ctx.req.method !== 'GET' ? { body: body as string, } : {}), }); const headers: Record = { 'mcp-proxy-arg': encodeURIComponent((body as Buffer).toString()), }; for (const [ key, value ] of response.headers.entries()) { if (IGNORE_HEADERS.includes(key)) { continue; } headers[key] = value; } ctx.set(headers); ctx.res.statusCode = response.status; const readable = Readable.fromWeb(response.body!); readable.on('error', err => { this.logger.error('[mcp-proxy] stream proxy error: %s', err.message); if (!ctx.res.writableEnded) { ctx.res.end(); } dispatcher.close(); }); readable.on('end', () => { dispatcher.close(); }); ctx.res.on('close', () => { dispatcher.close(); }); readable.pipe(ctx.res); } catch (err) { dispatcher.close(); this.logger.error('[mcp-proxy] stream proxy fetch error: %s', err.message); if (!ctx.res.headersSent) { ctx.res.writeHead(502, { 'content-type': 'application/json' }); } if (!ctx.res.writableEnded) { ctx.res.end(JSON.stringify({ jsonrpc: '2.0', error: { code: -32603, message: `Proxy error: ${err.message}`, }, id: null, })); } } break; } } } catch (error) { this.logger.error(error); ctx.res.writeHead(500, { 'content-type': 'application/json' }) .end(JSON.stringify({ jsonrpc: '2.0', error: { code: -32603, message: 'Internal error', }, id: null, })); return; } } handleSseStream(ctx: Context, stream: ReadableStream) { const processStream = async () => { try { const reader = stream // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore .pipeThrough(new TextDecoderStream()) // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore .pipeThrough(new EventSourceParserStream()) .getReader(); // eslint-disable-next-line no-constant-condition while (true) { const { value: event, done } = await reader.read(); if (done) { break; } let eventData = 'event: message\n'; if ((event as any)!.id) { eventData += `id: ${(event as any)!.id}\n`; } eventData += `data: ${JSON.stringify((event as any)!.data)}\n\n`; ctx.res.write(eventData); } ctx.res.write('event: terminate'); } catch (error) { this.logger.error('[mcp-proxy] sse stream error: %s', error); if (!ctx.res.writableEnded) { ctx.res.statusCode = 500; ctx.res.end(); } } }; processStream(); } get delegates() { return { registerClient: 'invoke', unregisterClient: 'invoke', getClient: 'invoke', }; } get DataClient() { return MCPProxyDataClient; } get clusterOptions() { return { name: 'MCPProxy', }; } } ================================================ FILE: plugin/mcp-proxy/lib/MCPProxyDataClient.ts ================================================ import { Base } from 'sdk-base'; import { EggLogger } from 'egg'; export class MCPProxyDataClient extends Base { private readonly clients: Map; private readonly logger: EggLogger; constructor(options: { logger: EggLogger; }) { const superOptions = Object.assign({}, { initMethod: '_init', }); super(superOptions); this.clients = new Map(); this.logger = options.logger; } // eslint-disable-next-line @typescript-eslint/no-empty-function async _init() {} async registerClient(sessionId: string, pid: number) { if (this.clients.has(sessionId)) { const oldPid = this.clients.get(sessionId)!; this.logger.info('[MCPClientManager] duplicate register client %s new pid %s old pid', sessionId, pid, oldPid); this.clients.set(sessionId, pid); } else { this.logger.info('[MCPClientManager] register client %s pid %s', sessionId, pid); this.clients.set(sessionId, pid); } } async getClient(sessionId: string): Promise { return this.clients.get(sessionId); } async unregisterClient(sessionId: string) { this.clients.delete(sessionId); } } ================================================ FILE: plugin/mcp-proxy/package.json ================================================ { "name": "@eggjs/mcp-proxy", "version": "3.78.15", "eggPlugin": { "name": "mcpProxy" }, "types": "typings/index.d.ts", "description": "tegg mcp proxy plugin", "keywords": [ "egg", "typescript", "decorator", "aop", "tegg" ], "files": [ "app.js", "app.d.ts", "agent.js", "agent.d.ts", "index.js", "index.d.ts", "types.js", "types.d.ts", "config/**/*.js", "config/**/*.d.ts", "lib/**/*.js", "lib/**/*.d.ts", "app/**/*.js", "app/**/*.d.ts", "typings/*.d.ts" ], "scripts": { "test": "cross-env NODE_ENV=test NODE_OPTIONS='--no-deprecation' mocha", "clean": "tsc -b --clean", "tsc": "ut run clean && tsc -p ./tsconfig.json", "tsc:pub": "ut run clean && tsc -p ./tsconfig.pub.json", "prepublishOnly": "ut tsc:pub" }, "homepage": "https://github.com/eggjs/tegg", "bugs": { "url": "https://github.com/eggjs/tegg/issues" }, "repository": { "type": "git", "url": "git@github.com:eggjs/tegg.git", "directory": "plugin/mcp-proxy" }, "engines": { "node": ">=18.0.0" }, "dependencies": { "@eggjs/core": "^6.5.0", "@eggjs/tegg-controller-plugin": "^3.78.15", "@eggjs/tegg-types": "^3.78.15", "@modelcontextprotocol/sdk": "^1.23.0", "await-event": "^2.1.0", "cluster-client": "^3.7.0", "content-type": "^1.0.5", "eventsource-parser": "^3.0.1", "raw-body": "^2.5.2", "sdk-base": "^5.0.1", "zod": "^4.0.0" }, "devDependencies": { "@eggjs/egg-module-common": "^3.78.15", "@eggjs/tegg": "^3.78.15", "@eggjs/tegg-aop-runtime": "^3.78.15", "@eggjs/tegg-config": "^3.78.15", "@eggjs/tegg-metadata": "^3.78.15", "@eggjs/tegg-plugin": "^3.78.15", "@types/mocha": "^10.0.1", "@types/node": "^20.2.4", "cross-env": "^7.0.3", "egg": "^3.9.1", "egg-mock": "^5.5.0", "eventsource": "^3.0.5", "mocha": "^10.2.0", "ts-node": "^10.9.1", "typescript": "^5.0.4", "undici": "^5.26.5" }, "publishConfig": { "access": "public" }, "gitHead": "240a3e9f40fda65ebb7589727d197db5ce17916c" } ================================================ FILE: plugin/mcp-proxy/test/fixtures/apps/mcp-proxy/app/controller/app.ts ================================================ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js'; import { StreamableHTTPServerTransport, EventStore } from '@modelcontextprotocol/sdk/server/streamableHttp.js'; import { Controller } from 'egg'; import * as z from 'zod/v4'; import url from 'url'; import querystring from 'querystring'; import getRawBody from 'raw-body'; import contentType from 'content-type'; import { isInitializeRequest, JSONRPCMessage } from '@modelcontextprotocol/sdk/types.js'; import { randomUUID } from 'node:crypto'; import { MCPProtocols } from '@eggjs/tegg-types'; const mcpServer = new McpServer({ name: 'tegg-mcp-demo-server', version: '1.0.0', }, { capabilities: { logging: {} } }); // Register a simple tool that sends notifications over time mcpServer.tool( 'start-notification-stream', 'Starts sending periodic notifications for testing resumability', { interval: z.number().describe('Interval in milliseconds between notifications').default(100), count: z.number().describe('Number of notifications to send (0 for 100)').default(50), }, async ({ interval, count }, { sendNotification }) => { const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); let counter = 0; while (count === 0 || counter < count) { counter++; try { await sendNotification({ method: 'notifications/message', params: { level: 'info', data: `Periodic notification #${counter}`, }, }); } catch (error) { console.error('Error sending notification:', error); } await sleep(interval); } return { content: [ { type: 'text', text: `Started sending periodic notifications every ${interval}ms`, }, ], }; }, ); export class InMemoryEventStore implements EventStore { private events: Map = new Map(); /** * Generates a unique event ID for a given stream ID */ private generateEventId(streamId: string): string { return `${streamId}_${Date.now()}_${Math.random().toString(36).substring(2, 10)}`; } /** * Extracts the stream ID from an event ID */ private getStreamIdFromEventId(eventId: string): string { const parts = eventId.split('_'); return parts.length > 0 ? parts[0] : ''; } /** * Stores an event with a generated event ID * Implements EventStore.storeEvent */ async storeEvent(streamId: string, message: JSONRPCMessage): Promise { const eventId = this.generateEventId(streamId); this.events.set(eventId, { streamId, message }); return eventId; } /** * Replays events that occurred after a specific event ID * Implements EventStore.replayEventsAfter */ async replayEventsAfter(lastEventId: string, { send }: { send: (eventId: string, message: JSONRPCMessage) => Promise }, ): Promise { if (!lastEventId || !this.events.has(lastEventId)) { return ''; } // Extract the stream ID from the event ID const streamId = this.getStreamIdFromEventId(lastEventId); if (!streamId) { return ''; } let foundLastEvent = false; // Sort events by eventId for chronological ordering const sortedEvents = [ ...this.events.entries() ].sort((a, b) => a[0].localeCompare(b[0])); for (const [ eventId, { streamId: eventStreamId, message }] of sortedEvents) { // Only include events from the same stream if (eventStreamId !== streamId) { continue; } // Start sending events after we find the lastEventId if (eventId === lastEventId) { foundLastEvent = true; continue; } if (foundLastEvent) { await send(eventId, message); } } return streamId; } } const transports: Record = {}; export default class App extends Controller { async ssePostHandler(req, res) { const sessionId = req.query?.sessionId ?? querystring.parse(url.parse(req.url).query ?? '').sessionId as string; let transport: SSEServerTransport; const existingTransport = transports[sessionId]; if (existingTransport instanceof SSEServerTransport) { transport = existingTransport; } else { res.status(400).json({ jsonrpc: '2.0', error: { code: -32000, message: 'Bad Request: Session exists but uses a different transport protocol', }, id: null, }); return; } if (transport) { await transport.handlePostMessage(req, res); } else { res.status(404).send({ jsonrpc: '2.0', error: { code: -32602, message: 'Bad Request: No transport found for sessionId', }, id: null, }); } } async init() { // sse stream this.ctx.respond = false; this.ctx.set({ 'content-type': 'text/event-stream', 'cache-control': 'no-cache', }); const self = this; const transport = new SSEServerTransport('/message', this.ctx.res); // register handler and client demo this.app.mcpProxy.setProxyHandler(MCPProtocols.SSE, async (req, res) => { return await self.ssePostHandler(req, res); }); transports[transport.sessionId] = transport; await this.app.mcpProxy.registerClient(transport.sessionId, process.pid); this.ctx.res.on('close', async () => { delete transports[transport.sessionId]; await this.app.mcpProxy.unregisterClient(transport.sessionId); }); // call const server = mcpServer; await server.connect(transport); } async message() { const detail = await this.app.mcpProxy.getClient(this.ctx.request.query.sessionId); if (detail?.pid !== process.pid) { await this.app.mcpProxy.proxyMessage(this.ctx, { detail: detail!, sessionId: this.ctx.request.query.sessionId, type: MCPProtocols.SSE, }); } else { await this.ssePostHandler(this.ctx.req, this.ctx.res); } } async streamPostHandler(req, res) { const sessionId = req.headers['mcp-session-id'] as string | undefined; if (!sessionId) { res.status(500).json({ jsonrpc: '2.0', error: { code: -32603, message: 'session id not have and run in proxy', }, id: null, }); } else { let transport: StreamableHTTPServerTransport; const existingTransport = transports[sessionId]; if (existingTransport instanceof StreamableHTTPServerTransport) { transport = existingTransport; } else { res.status(400).json({ jsonrpc: '2.0', error: { code: -32000, message: 'Bad Request: Session exists but uses a different transport protocol', }, id: null, }); return; } if (transport) { await transport.handleRequest(req, res); } else { res.status(404).send({ jsonrpc: '2.0', error: { code: -32602, message: 'Bad Request: No transport found for sessionId', }, id: null, }); } } } async allStream() { const sessionId = this.ctx.req.headers['mcp-session-id'] as string | undefined; if (!sessionId) { const ct = contentType.parse(this.ctx.req.headers['content-type'] ?? ''); const body = JSON.parse(await getRawBody(this.ctx.req, { limit: '4mb', encoding: ct.parameters.charset ?? 'utf-8', })); if (isInitializeRequest(body)) { this.ctx.respond = false; this.ctx.set({ 'content-type': 'text/event-stream', 'cache-control': 'no-cache', }); const eventStore = new InMemoryEventStore(); const self = this; const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: () => randomUUID(), eventStore, onsessioninitialized: async sessionId => { transports[sessionId] = transport; this.app.mcpProxy.setProxyHandler(MCPProtocols.STREAM, async (req, res) => { return await self.streamPostHandler(req, res); }); await this.app.mcpProxy.registerClient(sessionId, process.pid); }, }); transport.onclose = async () => { const sid = transport.sessionId; if (sid && transports[sid]) { delete transports[sid]; } await this.app.mcpProxy.unregisterClient(sid!); }; await mcpServer.connect(transport); await transport.handleRequest(this.ctx.req, this.ctx.res, body); } else { this.ctx.status = 400; this.ctx.body = { jsonrpc: '2.0', error: { code: -32000, message: 'Bad Request: No valid session ID provided', }, id: null, }; return; } } else if (sessionId) { const detail = await this.app.mcpProxy.getClient(sessionId); if (detail?.pid !== process.pid) { await this.app.mcpProxy.proxyMessage(this.ctx, { detail: detail!, sessionId, type: MCPProtocols.STREAM, }); } else { this.ctx.respond = false; this.ctx.set({ 'content-type': 'text/event-stream', 'cache-control': 'no-cache', }); const transport = transports[sessionId] as StreamableHTTPServerTransport; await transport.handleRequest(this.ctx.req, this.ctx.res); } } } } ================================================ FILE: plugin/mcp-proxy/test/fixtures/apps/mcp-proxy/app/router.ts ================================================ import { Application } from 'egg'; module.exports = (app: Application) => { app.router.all('/stream', app.controller.app.allStream); app.router.get('/init', app.controller.app.init); app.router.post('/message', app.controller.app.message); }; ================================================ FILE: plugin/mcp-proxy/test/fixtures/apps/mcp-proxy/config/config.default.js ================================================ 'use strict'; module.exports = function() { const config = { keys: 'test key', security: { csrf: { enable: false, }, }, bodyParser: { enable: false, }, }; return config; }; ================================================ FILE: plugin/mcp-proxy/test/fixtures/apps/mcp-proxy/config/plugin.js ================================================ 'use strict'; // eslint-disable-next-line @typescript-eslint/no-var-requires const path = require('node:path'); exports.tegg = { package: '@eggjs/tegg-plugin', enable: true, }; exports.teggConfig = { package: '@eggjs/tegg-config', enable: true, }; exports.mcpProxy = { enable: true, path: path.join(__dirname, '../../../../../'), }; exports.watcher = false; ================================================ FILE: plugin/mcp-proxy/test/fixtures/apps/mcp-proxy/package.json ================================================ { "name": "egg-app" } ================================================ FILE: plugin/mcp-proxy/test/proxy.test.ts ================================================ import mm from 'egg-mock'; import path from 'path'; import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js'; import { Client } from '@modelcontextprotocol/sdk/client/index.js'; import { CallToolRequest, CallToolResultSchema, ListToolsRequest, ListToolsResultSchema, LoggingMessageNotificationSchema } from '@modelcontextprotocol/sdk/types.js'; import assert from 'assert'; import { fetch } from 'urllib'; async function listTools(client: Client) { const toolsRequest: ListToolsRequest = { method: 'tools/list', params: {}, }; const toolsResult = await client.request(toolsRequest, ListToolsResultSchema); const tools: { name: string; description?: string; }[] = []; for (const tool of toolsResult.tools) { tools.push({ name: tool.name, description: tool.description, }); } return tools; } async function startNotificationTool(client: Client) { // Call the notification tool using reasonable defaults const request: CallToolRequest = { method: 'tools/call', params: { name: 'start-notification-stream', arguments: { interval: 1000, // 1 second between notifications count: 5, // Send 5 notifications }, }, }; const result = await client.request(request, CallToolResultSchema); const notifications: { text: string }[] = []; result.content.forEach(item => { if (item.type === 'text') { notifications.push({ text: item.text, }); } else { notifications.push({ text: (item as any).data!.toString(), }); } }); return notifications; } describe('plugin/mcp-proxy/test/proxy.test.ts', () => { if (parseInt(process.version.slice(1, 3)) > 17) { // eslint-disable-next-line @typescript-eslint/no-var-requires const { StreamableHTTPClientTransport } = require('@modelcontextprotocol/sdk/client/streamableHttp.js'); let app; after(async () => { await app.close(); }); afterEach(() => { // mm.restore(); }); before(async () => { mm(process.env, 'EGG_TYPESCRIPT', true); mm(process, 'cwd', () => { return path.join(__dirname, '..'); }); app = mm.cluster({ baseDir: path.join(__dirname, 'fixtures/apps/mcp-proxy'), framework: path.dirname(require.resolve('egg')), // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore workers: 3, sticky: false, opt: { env: { ...process.env, NODE_OPTIONS: '--require ts-node/register tsconfig-paths/register', }, }, }); await app.ready(); }); after(() => { return app.close(); }); it('sse should work', async () => { const sseClient = new Client({ name: 'sse-demo-client', version: '1.0.0', }); const baseUrl = await app.httpRequest() .get('/init').url; const sseTransport = new SSEClientTransport(new URL(baseUrl)); const sseNotifications: { level: string, data: string }[] = []; sseClient.setNotificationHandler(LoggingMessageNotificationSchema, notification => { sseNotifications.push({ level: notification.params.level, data: notification.params.data as string }); }); await sseClient.connect(sseTransport); const tools = await listTools(sseClient); const notificationResp = await startNotificationTool(sseClient); await new Promise(resolve => setTimeout(resolve, 5000)); await sseTransport.close(); assert.deepEqual(tools, [ { name: 'start-notification-stream', description: 'Starts sending periodic notifications for testing resumability', }, ]); assert.deepEqual(notificationResp, [{ text: 'Started sending periodic notifications every 1000ms' }]); assert.deepEqual(sseNotifications, [ { level: 'info', data: 'Periodic notification #1' }, { level: 'info', data: 'Periodic notification #2' }, { level: 'info', data: 'Periodic notification #3' }, { level: 'info', data: 'Periodic notification #4' }, { level: 'info', data: 'Periodic notification #5' }, ]); }); it('streamable should work', async () => { const streamableClient = new Client({ name: 'streamable-demo-client', version: '1.0.0', }); const baseUrl = await app.httpRequest() .post('/stream').url; const streamableTransport = new StreamableHTTPClientTransport(new URL(baseUrl), { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore fetch: async (...args) => { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore const res = await fetch(...args); assert.deepEqual(res.headers.has('content-length'), !res.headers.has('transfer-encoding')); return res; }, }); const streamableNotifications: { level: string, data: string }[] = []; streamableClient.setNotificationHandler(LoggingMessageNotificationSchema, notification => { streamableNotifications.push({ level: notification.params.level, data: notification.params.data as string }); }); await streamableClient.connect(streamableTransport); const tools = await listTools(streamableClient); const notificationResp = await startNotificationTool(streamableClient); await new Promise(resolve => setTimeout(resolve, 5000)); await streamableTransport.terminateSession(); await streamableClient.close(); assert.deepEqual(tools, [ { name: 'start-notification-stream', description: 'Starts sending periodic notifications for testing resumability', }, ]); assert.deepEqual(notificationResp, [{ text: 'Started sending periodic notifications every 1000ms' }]); assert.deepEqual(streamableNotifications, [ { level: 'info', data: 'Periodic notification #1' }, { level: 'info', data: 'Periodic notification #2' }, { level: 'info', data: 'Periodic notification #3' }, { level: 'info', data: 'Periodic notification #4' }, { level: 'info', data: 'Periodic notification #5' }, ]); }); } }); ================================================ FILE: plugin/mcp-proxy/tsconfig.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "baseUrl": "./" }, "exclude": [ "node_modules", "test" ] } ================================================ FILE: plugin/mcp-proxy/tsconfig.pub.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "baseUrl": "./" }, "exclude": [ "node_modules", "test" ] } ================================================ FILE: plugin/mcp-proxy/typings/index.d.ts ================================================ import 'egg'; import '@eggjs/tegg-plugin'; import { MCPProxyApiClient } from '../index'; export { MCPProtocols } from '../types' declare module 'egg' { export interface MCPProxyApp { mcpProxy: MCPProxyApiClient; } export interface Application extends MCPProxyApp { } } ================================================ FILE: plugin/orm/CHANGELOG.md ================================================ # Change Log All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. ## [3.78.15](https://github.com/eggjs/tegg/compare/v3.78.14...v3.78.15) (2026-04-23) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.78.14](https://github.com/eggjs/tegg/compare/v3.78.13...v3.78.14) (2026-04-21) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.78.13](https://github.com/eggjs/tegg/compare/v3.78.12...v3.78.13) (2026-04-21) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.78.12](https://github.com/eggjs/tegg/compare/v3.78.11...v3.78.12) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.78.11](https://github.com/eggjs/tegg/compare/v3.78.10...v3.78.11) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.78.10](https://github.com/eggjs/tegg/compare/v3.78.9...v3.78.10) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.78.9](https://github.com/eggjs/tegg/compare/v3.78.8...v3.78.9) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.78.8](https://github.com/eggjs/tegg/compare/v3.78.7...v3.78.8) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.78.7](https://github.com/eggjs/tegg/compare/v3.78.6...v3.78.7) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.78.6](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.6) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.78.5](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.5) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.78.4](https://github.com/eggjs/tegg/compare/v3.78.3...v3.78.4) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.78.3](https://github.com/eggjs/tegg/compare/v3.78.2...v3.78.3) (2026-04-15) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.78.2](https://github.com/eggjs/tegg/compare/v3.78.1...v3.78.2) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.78.1](https://github.com/eggjs/tegg/compare/v3.78.0...v3.78.1) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.78.0](https://github.com/eggjs/tegg/compare/v3.77.2...v3.78.0) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.77.2](https://github.com/eggjs/tegg/compare/v3.77.1...v3.77.2) (2026-04-07) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.77.1](https://github.com/eggjs/tegg/compare/v3.77.0...v3.77.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.77.0](https://github.com/eggjs/tegg/compare/v3.76.1...v3.77.0) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.76.1](https://github.com/eggjs/tegg/compare/v3.76.0...v3.76.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.76.0](https://github.com/eggjs/tegg/compare/v3.75.1...v3.76.0) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.75.1](https://github.com/eggjs/tegg/compare/v3.75.0...v3.75.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.75.0](https://github.com/eggjs/tegg/compare/v3.74.0...v3.75.0) (2026-03-30) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.74.0](https://github.com/eggjs/tegg/compare/v3.73.0...v3.74.0) (2026-03-30) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.73.0](https://github.com/eggjs/tegg/compare/v3.72.0...v3.73.0) (2026-03-25) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.72.0](https://github.com/eggjs/tegg/compare/v3.71.2...v3.72.0) (2026-02-04) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.71.2](https://github.com/eggjs/tegg/compare/v3.71.1...v3.71.2) (2026-01-30) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.71.1](https://github.com/eggjs/tegg/compare/v3.71.0...v3.71.1) (2026-01-28) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.71.0](https://github.com/eggjs/tegg/compare/v3.70.1...v3.71.0) (2026-01-26) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.70.1](https://github.com/eggjs/tegg/compare/v3.70.0...v3.70.1) (2026-01-14) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.70.0](https://github.com/eggjs/tegg/compare/v3.69.0...v3.70.0) (2026-01-07) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.69.0](https://github.com/eggjs/tegg/compare/v3.68.0...v3.69.0) (2026-01-06) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.68.0](https://github.com/eggjs/tegg/compare/v3.67.2...v3.68.0) (2025-12-29) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.67.2](https://github.com/eggjs/tegg/compare/v3.67.1...v3.67.2) (2025-12-24) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.67.1](https://github.com/eggjs/tegg/compare/v3.67.0...v3.67.1) (2025-12-23) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.67.0](https://github.com/eggjs/tegg/compare/v3.66.0...v3.67.0) (2025-12-18) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.66.0](https://github.com/eggjs/tegg/compare/v3.65.3...v3.66.0) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.65.3](https://github.com/eggjs/tegg/compare/v3.65.2...v3.65.3) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.65.2](https://github.com/eggjs/tegg/compare/v3.65.1...v3.65.2) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.65.1](https://github.com/eggjs/tegg/compare/v3.65.0...v3.65.1) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.65.0](https://github.com/eggjs/tegg/compare/v3.64.5...v3.65.0) (2025-12-10) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.64.5](https://github.com/eggjs/tegg/compare/v3.64.4...v3.64.5) (2025-12-08) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.64.4](https://github.com/eggjs/tegg/compare/v3.64.3...v3.64.4) (2025-12-06) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.64.3](https://github.com/eggjs/tegg/compare/v3.64.2...v3.64.3) (2025-12-06) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.64.2](https://github.com/eggjs/tegg/compare/v3.64.1...v3.64.2) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.64.1](https://github.com/eggjs/tegg/compare/v3.64.0...v3.64.1) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.64.0](https://github.com/eggjs/tegg/compare/v3.63.2...v3.64.0) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.63.2](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.2) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.63.1](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.1) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.63.0](https://github.com/eggjs/tegg/compare/v3.62.3...v3.63.0) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.62.3](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.3) (2025-11-05) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.62.2](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.2) (2025-11-05) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.62.1](https://github.com/eggjs/tegg/compare/v3.62.0...v3.62.1) (2025-11-03) ### Bug Fixes * multi mcp client ([#357](https://github.com/eggjs/tegg/issues/357)) ([f9e4728](https://github.com/eggjs/tegg/commit/f9e47289160dffc5b47e93b60128a25ffd94fb4e)) # [3.62.0](https://github.com/eggjs/tegg/compare/v3.61.0...v3.62.0) (2025-09-24) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.61.0](https://github.com/eggjs/tegg/compare/v3.60.3...v3.61.0) (2025-08-15) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.60.3](https://github.com/eggjs/tegg/compare/v3.60.2...v3.60.3) (2025-08-06) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.60.2](https://github.com/eggjs/tegg/compare/v3.60.1...v3.60.2) (2025-08-06) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.60.1](https://github.com/eggjs/tegg/compare/v3.60.0...v3.60.1) (2025-07-28) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.60.0](https://github.com/eggjs/tegg/compare/v3.59.1...v3.60.0) (2025-07-28) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.59.1](https://github.com/eggjs/tegg/compare/v3.59.0...v3.59.1) (2025-07-16) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.59.0](https://github.com/eggjs/tegg/compare/v3.58.0...v3.59.0) (2025-07-07) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.58.0](https://github.com/eggjs/tegg/compare/v3.57.14...v3.58.0) (2025-07-01) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.57.14](https://github.com/eggjs/tegg/compare/v3.57.13...v3.57.14) (2025-06-18) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.57.13](https://github.com/eggjs/tegg/compare/v3.57.12...v3.57.13) (2025-06-18) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.57.12](https://github.com/eggjs/tegg/compare/v3.57.11...v3.57.12) (2025-06-16) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.57.11](https://github.com/eggjs/tegg/compare/v3.57.10...v3.57.11) (2025-06-16) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.57.10](https://github.com/eggjs/tegg/compare/v3.57.9...v3.57.10) (2025-06-12) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.57.9](https://github.com/eggjs/tegg/compare/v3.57.8...v3.57.9) (2025-05-29) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.57.8](https://github.com/eggjs/tegg/compare/v3.57.7...v3.57.8) (2025-05-29) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.57.7](https://github.com/eggjs/tegg/compare/v3.57.6...v3.57.7) (2025-05-28) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.57.6](https://github.com/eggjs/tegg/compare/v3.57.5...v3.57.6) (2025-05-27) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.57.5](https://github.com/eggjs/tegg/compare/v3.57.4...v3.57.5) (2025-05-15) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.57.4](https://github.com/eggjs/tegg/compare/v3.57.3...v3.57.4) (2025-05-14) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.57.3](https://github.com/eggjs/tegg/compare/v3.57.2...v3.57.3) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.57.2](https://github.com/eggjs/tegg/compare/v3.57.1...v3.57.2) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.57.1](https://github.com/eggjs/tegg/compare/v3.57.0...v3.57.1) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.57.0](https://github.com/eggjs/tegg/compare/v3.56.3...v3.57.0) (2025-05-09) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.56.3](https://github.com/eggjs/tegg/compare/v3.56.2...v3.56.3) (2025-05-07) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.56.2](https://github.com/eggjs/tegg/compare/v3.56.1...v3.56.2) (2025-05-01) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.56.1](https://github.com/eggjs/tegg/compare/v3.56.0...v3.56.1) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.56.0](https://github.com/eggjs/tegg/compare/v3.55.0...v3.56.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.55.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.55.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.54.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.54.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.53.0](https://github.com/eggjs/tegg/compare/v3.52.1...v3.53.0) (2025-04-21) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.52.1](https://github.com/eggjs/tegg/compare/v3.52.0...v3.52.1) (2025-03-14) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.52.0](https://github.com/eggjs/tegg/compare/v3.51.2...v3.52.0) (2024-12-30) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.51.2](https://github.com/eggjs/tegg/compare/v3.51.1...v3.51.2) (2024-12-09) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.51.1](https://github.com/eggjs/tegg/compare/v3.51.0...v3.51.1) (2024-11-06) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.51.0](https://github.com/eggjs/tegg/compare/v3.50.1...v3.51.0) (2024-10-30) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.50.1](https://github.com/eggjs/tegg/compare/v3.50.0...v3.50.1) (2024-10-23) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.50.0](https://github.com/eggjs/tegg/compare/v3.49.0...v3.50.0) (2024-10-22) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.49.0](https://github.com/eggjs/tegg/compare/v3.48.1...v3.49.0) (2024-10-21) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.48.1](https://github.com/eggjs/tegg/compare/v3.48.0...v3.48.1) (2024-10-14) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.48.0](https://github.com/eggjs/tegg/compare/v3.47.2...v3.48.0) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.47.2](https://github.com/eggjs/tegg/compare/v3.47.1...v3.47.2) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.47.1](https://github.com/eggjs/tegg/compare/v3.47.0...v3.47.1) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.47.0](https://github.com/eggjs/tegg/compare/v3.46.4...v3.47.0) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.46.4](https://github.com/eggjs/tegg/compare/v3.46.3...v3.46.4) (2024-10-09) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.46.3](https://github.com/eggjs/tegg/compare/v3.46.2...v3.46.3) (2024-10-08) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.46.2](https://github.com/eggjs/tegg/compare/v3.46.1...v3.46.2) (2024-10-07) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.46.1](https://github.com/eggjs/tegg/compare/v3.46.0...v3.46.1) (2024-09-30) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.46.0](https://github.com/eggjs/tegg/compare/v3.45.0...v3.46.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.45.0](https://github.com/eggjs/tegg/compare/v3.44.1...v3.45.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.44.1](https://github.com/eggjs/tegg/compare/v3.44.0...v3.44.1) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.44.0](https://github.com/eggjs/tegg/compare/v3.43.2...v3.44.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.43.2](https://github.com/eggjs/tegg/compare/v3.43.1...v3.43.2) (2024-09-14) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.43.1](https://github.com/eggjs/tegg/compare/v3.43.0...v3.43.1) (2024-09-14) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.43.0](https://github.com/eggjs/tegg/compare/v3.42.0...v3.43.0) (2024-09-13) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.42.0](https://github.com/eggjs/tegg/compare/v3.41.0...v3.42.0) (2024-09-10) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.41.0](https://github.com/eggjs/tegg/compare/v3.40.1...v3.41.0) (2024-08-26) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.40.1](https://github.com/eggjs/tegg/compare/v3.40.0...v3.40.1) (2024-08-23) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.40.0](https://github.com/eggjs/tegg/compare/v3.39.5...v3.40.0) (2024-08-22) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.39.5](https://github.com/eggjs/tegg/compare/v3.39.4...v3.39.5) (2024-08-09) ### Bug Fixes * generate index name with column name ([#230](https://github.com/eggjs/tegg/issues/230)) ([82ec72d](https://github.com/eggjs/tegg/commit/82ec72d4fb8628c847b32d0ddf23a95119ca6ccf)) ## [3.39.4](https://github.com/eggjs/tegg/compare/v3.39.3...v3.39.4) (2024-07-09) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.39.3](https://github.com/eggjs/tegg/compare/v3.39.2...v3.39.3) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.39.2](https://github.com/eggjs/tegg/compare/v3.39.1...v3.39.2) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.39.1](https://github.com/eggjs/tegg/compare/v3.39.0...v3.39.1) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.39.0](https://github.com/eggjs/tegg/compare/v3.38.0...v3.39.0) (2024-04-19) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.38.0](https://github.com/eggjs/tegg/compare/v3.37.3...v3.38.0) (2024-04-18) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.37.3](https://github.com/eggjs/tegg/compare/v3.37.2...v3.37.3) (2024-04-17) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.37.2](https://github.com/eggjs/tegg/compare/v3.37.1...v3.37.2) (2024-04-17) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.37.1](https://github.com/eggjs/tegg/compare/v3.37.0...v3.37.1) (2024-04-16) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.37.0](https://github.com/eggjs/tegg/compare/v3.36.3...v3.37.0) (2024-04-16) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.36.3](https://github.com/eggjs/tegg/compare/v3.36.2...v3.36.3) (2024-04-10) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.36.2](https://github.com/eggjs/tegg/compare/v3.36.1...v3.36.2) (2024-04-08) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.36.1](https://github.com/eggjs/tegg/compare/v3.36.0...v3.36.1) (2024-04-07) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.36.0](https://github.com/eggjs/tegg/compare/v3.35.1...v3.36.0) (2024-04-02) ### Features * impl ajv + typebox Validator ([#201](https://github.com/eggjs/tegg/issues/201)) ([9fd585d](https://github.com/eggjs/tegg/commit/9fd585de9b613466c96b73494a08a494db34ea57)) ## [3.35.1](https://github.com/eggjs/tegg/compare/v3.35.0...v3.35.1) (2024-03-26) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.35.0](https://github.com/eggjs/tegg/compare/v3.34.0...v3.35.0) (2024-03-26) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.34.0](https://github.com/eggjs/tegg/compare/v3.33.1...v3.34.0) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.33.1](https://github.com/eggjs/tegg/compare/v3.33.0...v3.33.1) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.33.0](https://github.com/eggjs/tegg/compare/v3.32.0...v3.33.0) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.32.0](https://github.com/eggjs/tegg/compare/v3.31.0...v3.32.0) (2024-02-19) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.31.0](https://github.com/eggjs/tegg/compare/v3.30.1...v3.31.0) (2024-01-31) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.30.1](https://github.com/eggjs/tegg/compare/v3.30.0...v3.30.1) (2024-01-25) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.30.0](https://github.com/eggjs/tegg/compare/v3.29.0...v3.30.0) (2024-01-17) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.29.0](https://github.com/eggjs/tegg/compare/v3.28.2...v3.29.0) (2023-12-26) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.28.2](https://github.com/eggjs/tegg/compare/v3.28.1...v3.28.2) (2023-12-12) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.28.1](https://github.com/eggjs/tegg/compare/v3.28.0...v3.28.1) (2023-12-11) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.28.0](https://github.com/eggjs/tegg/compare/v3.27.0...v3.28.0) (2023-12-10) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.27.0](https://github.com/eggjs/tegg/compare/v3.26.0...v3.27.0) (2023-11-23) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.26.0](https://github.com/eggjs/tegg/compare/v3.25.2...v3.26.0) (2023-11-17) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.25.2](https://github.com/eggjs/tegg/compare/v3.25.1...v3.25.2) (2023-11-06) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.25.1](https://github.com/eggjs/tegg/compare/v3.25.0...v3.25.1) (2023-11-03) ### Bug Fixes * fix standalone import ConfigSource ([#163](https://github.com/eggjs/tegg/issues/163)) ([6922071](https://github.com/eggjs/tegg/commit/6922071219413a8a11387be3d05f0e3970ce4f48)) # [3.25.0](https://github.com/eggjs/tegg/compare/v3.24.0...v3.25.0) (2023-11-03) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.24.0](https://github.com/eggjs/tegg/compare/v3.23.0...v3.24.0) (2023-10-26) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.23.0](https://github.com/eggjs/tegg/compare/v3.22.0...v3.23.0) (2023-09-20) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.20.0](https://github.com/eggjs/tegg/compare/v3.19.0...v3.20.0) (2023-09-07) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.19.0](https://github.com/eggjs/tegg/compare/v3.18.1...v3.19.0) (2023-08-31) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.18.1](https://github.com/eggjs/tegg/compare/v3.18.0...v3.18.1) (2023-08-29) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.18.0](https://github.com/eggjs/tegg/compare/v3.17.0...v3.18.0) (2023-08-29) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.17.0](https://github.com/eggjs/tegg/compare/v3.16.0...v3.17.0) (2023-08-24) ### Features * impl MultiInstanceProto ([#145](https://github.com/eggjs/tegg/issues/145)) ([12fd5cf](https://github.com/eggjs/tegg/commit/12fd5cff4004578bcc737dcdf4f7e9d1159f5633)) # [3.16.0](https://github.com/eggjs/tegg/compare/v3.15.0...v3.16.0) (2023-08-24) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.14.3](https://github.com/eggjs/tegg/compare/v3.14.2...v3.14.3) (2023-08-14) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.14.2](https://github.com/eggjs/tegg/compare/v3.14.1...v3.14.2) (2023-08-14) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.14.1](https://github.com/eggjs/tegg/compare/v3.14.0...v3.14.1) (2023-08-11) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.14.0](https://github.com/eggjs/tegg/compare/v3.13.0...v3.14.0) (2023-07-31) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.13.0](https://github.com/eggjs/tegg/compare/v3.12.0...v3.13.0) (2023-07-25) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.12.0](https://github.com/eggjs/tegg/compare/v3.11.1...v3.12.0) (2023-07-13) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.11.1](https://github.com/eggjs/tegg/compare/v3.11.0...v3.11.1) (2023-06-29) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.11.0](https://github.com/eggjs/tegg/compare/v3.10.0...v3.11.0) (2023-06-29) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.10.0](https://github.com/eggjs/tegg/compare/v3.9.0...v3.10.0) (2023-06-28) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.9.0](https://github.com/eggjs/tegg/compare/v3.8.0...v3.9.0) (2023-06-20) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.8.0](https://github.com/eggjs/tegg/compare/v3.7.0...v3.8.0) (2023-05-30) ### Features * impl EggObjectLifecycle hook in decorator ([#119](https://github.com/eggjs/tegg/issues/119)) ([cced8a2](https://github.com/eggjs/tegg/commit/cced8a2e009c33d5040fa21d00409fddef471b0e)) # [3.7.0](https://github.com/eggjs/tegg/compare/v3.6.3...v3.7.0) (2023-04-03) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.6.3](https://github.com/eggjs/tegg/compare/v3.6.2...v3.6.3) (2023-03-02) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.6.2](https://github.com/eggjs/tegg/compare/v3.6.1...v3.6.2) (2023-02-16) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.6.1](https://github.com/eggjs/tegg/compare/v3.6.0...v3.6.1) (2023-02-14) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.6.0](https://github.com/eggjs/tegg/compare/v3.5.2...v3.6.0) (2023-02-13) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.5.2](https://github.com/eggjs/tegg/compare/v3.5.1...v3.5.2) (2023-02-10) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.5.1](https://github.com/eggjs/tegg/compare/v3.5.0...v3.5.1) (2023-02-10) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.5.0](https://github.com/eggjs/tegg/compare/v3.4.1...v3.5.0) (2023-02-10) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.4.1](https://github.com/eggjs/tegg/compare/v3.4.0...v3.4.1) (2023-02-02) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.4.0](https://github.com/eggjs/tegg/compare/v3.3.4...v3.4.0) (2023-02-01) ### Features * use singleton model insteadof context ([#89](https://github.com/eggjs/tegg/issues/89)) ([cfdfc05](https://github.com/eggjs/tegg/commit/cfdfc05f13048806274de1a35b1207c073a8519d)) ## [3.3.4](https://github.com/eggjs/tegg/compare/v3.3.3...v3.3.4) (2023-01-29) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.3.3](https://github.com/eggjs/tegg/compare/v3.3.2...v3.3.3) (2023-01-29) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.3.2](https://github.com/eggjs/tegg/compare/v3.3.1...v3.3.2) (2023-01-29) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.3.1](https://github.com/eggjs/tegg/compare/v3.3.0...v3.3.1) (2023-01-28) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.3.0](https://github.com/eggjs/tegg/compare/v3.2.4...v3.3.0) (2023-01-28) ### Features * export singleton orm client ([#82](https://github.com/eggjs/tegg/issues/82)) ([5320af7](https://github.com/eggjs/tegg/commit/5320af77d7e7c5c73b80560a576f2ce01fc21fff)) ## [3.2.3](https://github.com/eggjs/tegg/compare/v3.2.2...v3.2.3) (2023-01-16) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.2.2](https://github.com/eggjs/tegg/compare/v3.2.1...v3.2.2) (2023-01-06) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [3.2.1](https://github.com/eggjs/tegg/compare/v3.2.0...v3.2.1) (2022-12-28) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.2.0](https://github.com/eggjs/tegg/compare/v3.1.0...v3.2.0) (2022-12-28) ### Features * impl mockModuleContextScope ([#73](https://github.com/eggjs/tegg/issues/73)) ([041881c](https://github.com/eggjs/tegg/commit/041881ca317ad81366172a35ac56b7b2dc0a0488)) # [3.1.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.1.0) (2022-12-27) ### Features * **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) * impl Inject Model ([#43](https://github.com/eggjs/tegg/issues/43)) ([ced2ce2](https://github.com/eggjs/tegg/commit/ced2ce2134964dcb410410c0192a34f77507c42d)) * impl Schedule decorator ([#52](https://github.com/eggjs/tegg/issues/52)) ([7f95005](https://github.com/eggjs/tegg/commit/7f950050b548ca542addbd7b466675da4e81ce3f)) # [3.0.0](https://github.com/eggjs/tegg/compare/v3.0.0-alpha.0...v3.0.0) (2022-12-26) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [3.0.0-alpha.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.0.0-alpha.0) (2022-12-22) ### Features * **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) * impl Inject Model ([#43](https://github.com/eggjs/tegg/issues/43)) ([ced2ce2](https://github.com/eggjs/tegg/commit/ced2ce2134964dcb410410c0192a34f77507c42d)) * impl Schedule decorator ([#52](https://github.com/eggjs/tegg/issues/52)) ([7f95005](https://github.com/eggjs/tegg/commit/7f950050b548ca542addbd7b466675da4e81ce3f)) ## [2.2.1](https://github.com/eggjs/tegg/compare/@eggjs/tegg-orm-plugin@2.2.0...@eggjs/tegg-orm-plugin@2.2.1) (2022-09-05) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [2.2.0](https://github.com/eggjs/tegg/compare/@eggjs/tegg-orm-plugin@2.1.4...@eggjs/tegg-orm-plugin@2.2.0) (2022-09-04) ### Features * impl Schedule decorator ([#52](https://github.com/eggjs/tegg/issues/52)) ([7f95005](https://github.com/eggjs/tegg/commit/7f950050b548ca542addbd7b466675da4e81ce3f)) ## [2.1.4](https://github.com/eggjs/tegg/compare/@eggjs/tegg-orm-plugin@2.1.3...@eggjs/tegg-orm-plugin@2.1.4) (2022-08-16) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [2.1.2](https://github.com/eggjs/tegg/compare/@eggjs/tegg-orm-plugin@2.1.1...@eggjs/tegg-orm-plugin@2.1.2) (2022-07-28) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ## [2.1.1](https://github.com/eggjs/tegg/compare/@eggjs/tegg-orm-plugin@2.1.0...@eggjs/tegg-orm-plugin@2.1.1) (2022-07-20) **Note:** Version bump only for package @eggjs/tegg-orm-plugin # [2.1.0](https://github.com/eggjs/tegg/compare/@eggjs/tegg-orm-plugin@2.0.0...@eggjs/tegg-orm-plugin@2.1.0) (2022-07-20) ### Features * impl Inject Model ([#43](https://github.com/eggjs/tegg/issues/43)) ([ced2ce2](https://github.com/eggjs/tegg/commit/ced2ce2134964dcb410410c0192a34f77507c42d)) # [1.3.0](https://github.com/eggjs/tegg/compare/v1.2.0...v1.3.0) (2022-07-01) ### Features * support leoric hooks ([#41](https://github.com/eggjs/tegg/issues/41)) ([9bdbc2c](https://github.com/eggjs/tegg/commit/9bdbc2cbe96df9f66f96b4f8e208883e99957946)) # [0.2.0](https://github.com/eggjs/tegg/compare/v0.1.19...v0.2.0) (2022-01-20) **Note:** Version bump only for package @eggjs/tegg-orm-plugin ================================================ FILE: plugin/orm/README.md ================================================ # @eggjs/tegg-orm-plugin 使用注解的方式来开发 egg 中的 orm ## Install ```shell npm i --save @eggjs/tegg-orm-plugin ``` ## Config ```js // config/plugin.js exports.teggOrm = { package: '@eggjs/tegg-orm-plugin', enable: true, }; ``` ================================================ FILE: plugin/orm/app.ts ================================================ import { Application } from 'egg'; import { DataSourceManager } from './lib/DataSourceManager'; import { LeoricRegister } from './lib/LeoricRegister'; import { ModelProtoManager } from './lib/ModelProtoManager'; import { ModelProtoHook } from './lib/ModelProtoHook'; import { MODEL_PROTO_IMPL_TYPE } from '@eggjs/tegg-orm-decorator'; import SingletonModelProto from './lib/SingletonModelProto'; import { SingletonModelObject } from './lib/SingletonModelObject'; import { ORMLoadUnitHook } from './lib/ORMLoadUnitHook'; export default class OrmAppBootHook { private readonly app: Application; private readonly dataSourceManager: DataSourceManager; private readonly leoricRegister: LeoricRegister; private readonly modelProtoManager: ModelProtoManager; private readonly modelProtoHook: ModelProtoHook; private readonly ormLoadUnitHook: ORMLoadUnitHook; constructor(app) { this.app = app; this.dataSourceManager = new DataSourceManager(); this.modelProtoManager = new ModelProtoManager(); this.leoricRegister = new LeoricRegister(this.modelProtoManager, this.dataSourceManager); this.modelProtoHook = new ModelProtoHook(this.modelProtoManager); this.app.eggPrototypeCreatorFactory.registerPrototypeCreator(MODEL_PROTO_IMPL_TYPE, SingletonModelProto.createProto); this.app.leoricRegister = this.leoricRegister; this.ormLoadUnitHook = new ORMLoadUnitHook(); } configWillLoad() { this.app.eggPrototypeLifecycleUtil.registerLifecycle(this.modelProtoHook); this.app.eggObjectFactory.registerEggObjectCreateMethod(SingletonModelProto, SingletonModelObject.createObject); this.app.loadUnitLifecycleUtil.registerLifecycle(this.ormLoadUnitHook); } configDidLoad() { const config = this.app.config.orm; if (config.datasources) { for (const datasource of config.datasources) { this.dataSourceManager.addConfig(datasource); } } else { this.dataSourceManager.addDefaultConfig(config); } } async didLoad() { await this.app.moduleHandler.ready(); await this.leoricRegister.register(); } async beforeClose() { this.app.eggPrototypeLifecycleUtil.deleteLifecycle(this.modelProtoHook); } } ================================================ FILE: plugin/orm/lib/DataSourceManager.ts ================================================ // exports.orm = { // client: 'mysql', // database: 'test', // host: 'localhost', // port: 3306, // user: 'root', // // delegate: 'model', // baseDir: 'model', // migrations: 'database', // // define: { // underscored: true, // }, // // // or put your config into datasources array to connect multiple databases // // datasources: [], // }; export interface OrmConfig { client: string; database: string; host: string; port: number; user: string; define: object; options: object; } export class DataSourceManager { private readonly dataSourceConfigs: OrmConfig[]; private defaultDataSourceConfig?: OrmConfig; constructor() { this.dataSourceConfigs = []; } addDefaultConfig(config: OrmConfig) { this.defaultDataSourceConfig = config; } getDefaultConfig(): OrmConfig | undefined { return this.defaultDataSourceConfig; } addConfig(config: OrmConfig) { this.dataSourceConfigs.push(config); } getConfig(name: string): OrmConfig | undefined { if (this.defaultDataSourceConfig?.database === name) { return this.defaultDataSourceConfig; } return this.dataSourceConfigs.find(t => t.database === name); } } ================================================ FILE: plugin/orm/lib/LeoricRegister.ts ================================================ import Base from 'sdk-base'; import { ModelProtoManager } from './ModelProtoManager'; import { DataSourceManager, OrmConfig } from './DataSourceManager'; import Realm, { hookNames } from 'leoric'; import { ModelMetadata, ModelMetadataUtil } from '@eggjs/tegg-orm-decorator'; export class LeoricRegister extends Base { private readonly modelProtoManager: ModelProtoManager; private readonly dataSourceManager: DataSourceManager; readonly realmMap: Map; constructor(modelProtoManager: ModelProtoManager, dataSourceManager: DataSourceManager) { super(); this.modelProtoManager = modelProtoManager; this.dataSourceManager = dataSourceManager; this.realmMap = new Map(); } getConfig(datasource?: string) { let config: OrmConfig | undefined; if (!datasource) { config = this.dataSourceManager.getDefaultConfig(); } else { config = this.dataSourceManager.getConfig(datasource); } return config; } getRealm(config: OrmConfig | undefined): Realm | undefined { if (!config?.database) { return undefined; } const realm = this.realmMap.get(config.database); return realm; } getOrCreateRealm(datasource: string | undefined): any { const config = this.getConfig(datasource); let realm: Realm | undefined; if (config) { realm = this.getRealm(config); if (realm) { return realm; } } realm = new (Realm as any)({ ...config }); this.realmMap.set(config!.database, realm); return realm; } generateLeoricAttributes(metadata: ModelMetadata) { const attributes = {}; for (const attribute of metadata.attributes) { attributes[attribute.propertyName] = { columnName: attribute.attributeName, type: attribute.dataType, allowNull: attribute.allowNull, primaryKey: attribute.primary, unique: attribute.unique, autoIncrement: attribute.autoIncrement, }; } return attributes; } async register() { for (const { proto, clazz } of this.modelProtoManager.getProtos()) { const metadata = ModelMetadataUtil.getModelMetadata(clazz); if (!metadata) throw new Error(`not found metadata for model ${proto.id}`); const realm = this.getOrCreateRealm(metadata.dataSource); realm.models[clazz.name] = clazz; realm[clazz.name] = clazz; const attributes = this.generateLeoricAttributes(metadata); const hooks = {}; for (const hookName of hookNames) { if (clazz[hookName]) { hooks[hookName] = clazz[hookName]; } } (clazz as any).init(attributes, { tableName: metadata.tableName, hooks, }, {}); } await Promise.all(Array.from(this.realmMap.values()) .map(realm => realm.connect())); this.ready(true); } } ================================================ FILE: plugin/orm/lib/ModelProtoHook.ts ================================================ import { LifecycleHook } from '@eggjs/tegg'; import { EggPrototype, EggPrototypeLifecycleContext } from '@eggjs/tegg-metadata'; import { IS_MODEL, ModelMetaBuilder, ModelMetadataUtil } from '@eggjs/tegg-orm-decorator'; import { ModelProtoManager } from './ModelProtoManager'; export class ModelProtoHook implements LifecycleHook { private readonly modelProtoManager: ModelProtoManager; constructor(modelProtoManager: ModelProtoManager) { this.modelProtoManager = modelProtoManager; } async postCreate(ctx: EggPrototypeLifecycleContext, obj: EggPrototype): Promise { if (!obj.getMetaData(IS_MODEL)) { return; } const builder = new ModelMetaBuilder(ctx.clazz); const metadata = builder.build(); ModelMetadataUtil.setModelMetadata(ctx.clazz, metadata); this.modelProtoManager.addProto(ctx.clazz, obj); } } ================================================ FILE: plugin/orm/lib/ModelProtoManager.ts ================================================ import { EggPrototype } from '@eggjs/tegg-metadata'; import { EggProtoImplClass } from '@eggjs/tegg'; export interface ModelProtoPair { proto: EggPrototype; clazz: EggProtoImplClass; } export class ModelProtoManager { private readonly protos: Array = []; addProto(clazz: EggProtoImplClass, proto: EggPrototype) { this.protos.push({ proto, clazz }); } getProtos(): Array { return this.protos.slice(); } } ================================================ FILE: plugin/orm/lib/ORMLoadUnitHook.ts ================================================ import { LifecycleHook } from '@eggjs/tegg'; import { EggLoadUnitType, EggPrototypeCreatorFactory, EggPrototypeFactory, LoadUnit, LoadUnitLifecycleContext, } from '@eggjs/tegg-metadata'; import { Orm } from './SingletonORM'; const REGISTER_CLAZZ = [ Orm, ]; export class ORMLoadUnitHook implements LifecycleHook { async postCreate(_ctx: LoadUnitLifecycleContext, loadUnit: LoadUnit): Promise { if (loadUnit.type === EggLoadUnitType.APP) { for (const clazz of REGISTER_CLAZZ) { const protos = await EggPrototypeCreatorFactory.createProto(clazz, loadUnit); for (const proto of protos) { EggPrototypeFactory.instance.registerPrototype(proto, loadUnit); } } } } } ================================================ FILE: plugin/orm/lib/SingletonModelObject.ts ================================================ import { ContextHandler, EggObject, EggObjectStatus, } from '@eggjs/tegg-runtime'; import { EggPrototype } from '@eggjs/tegg-metadata'; import { EggPrototypeName, EggObjectName } from '@eggjs/tegg'; import { Id, IdenticalUtil } from '@eggjs/tegg-lifecycle'; import { Bone } from 'leoric'; import SingletonModelProto from './SingletonModelProto'; import { EGG_CONTEXT } from '@eggjs/egg-module-common'; export class SingletonModelObject implements EggObject { private status: EggObjectStatus = EggObjectStatus.PENDING; id: Id; readonly name: EggPrototypeName; private _obj: typeof Bone; readonly proto: SingletonModelProto; constructor(name: EggObjectName, proto: SingletonModelProto) { this.name = name; this.proto = proto; this.id = IdenticalUtil.createObjectId(this.proto.id); } async init() { const clazz = class ContextModelClass extends this.proto.model { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore static get name() { return super.name; } static get ctx() { const ctx = ContextHandler.getContext(); if (ctx) { return ctx.get(EGG_CONTEXT); } } get ctx() { return ContextModelClass.ctx; } }; this._obj = clazz; this.status = EggObjectStatus.READY; } injectProperty() { throw new Error('never call ModelObject#injectProperty'); } get isReady() { return this.status === EggObjectStatus.READY; } get obj() { return this._obj; } static async createObject(name: EggObjectName, proto: EggPrototype): Promise { const modelObject = new SingletonModelObject(name, proto as SingletonModelProto); await modelObject.init(); return modelObject; } } ================================================ FILE: plugin/orm/lib/SingletonModelProto.ts ================================================ import { EggPrototype, LoadUnit, EggPrototypeLifecycleContext } from '@eggjs/tegg-metadata'; import { AccessLevel, EggPrototypeName, ObjectInitType, QualifierInfo, QualifierUtil, MetadataUtil, MetaDataKey, QualifierAttribute, QualifierValue, } from '@eggjs/tegg'; import { Id, IdenticalUtil } from '@eggjs/tegg-lifecycle'; import { Bone } from 'leoric'; export default class SingletonModelProto implements EggPrototype { private readonly qualifiers: QualifierInfo[]; readonly accessLevel = AccessLevel.PUBLIC; id: Id; readonly initType = ObjectInitType.SINGLETON; readonly injectObjects = []; readonly loadUnitId: string; readonly moduleName: string; readonly name: EggPrototypeName; readonly model: typeof Bone; constructor(loadUnit: LoadUnit, model: typeof Bone) { this.model = model; this.id = IdenticalUtil.createProtoId(loadUnit.id, `leoric:${model.name}`); this.loadUnitId = loadUnit.id; this.moduleName = loadUnit.name; this.name = model.name; this.qualifiers = QualifierUtil.getProtoQualifiers(model); } constructEggObject(): object { return {}; } getMetaData(metadataKey: MetaDataKey): T | undefined { return MetadataUtil.getMetaData(metadataKey, this.model); } verifyQualifier(qualifier: QualifierInfo): boolean { const selfQualifiers = this.qualifiers.find(t => t.attribute === qualifier.attribute); return selfQualifiers?.value === qualifier.value; } verifyQualifiers(qualifiers: QualifierInfo[]): boolean { for (const qualifier of qualifiers) { if (!this.verifyQualifier(qualifier)) { return false; } } return true; } getQualifier(attribute: QualifierAttribute): QualifierValue | undefined { return this.qualifiers.find(t => t.attribute === attribute)?.value; } static createProto(ctx: EggPrototypeLifecycleContext): SingletonModelProto { return new SingletonModelProto(ctx.loadUnit, ctx.clazz as typeof Bone); } } ================================================ FILE: plugin/orm/lib/SingletonORM.ts ================================================ import { AccessLevel, Inject, SingletonProto, } from '@eggjs/tegg'; import { LeoricRegister } from './LeoricRegister'; import Realm from 'leoric'; @SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) export class Orm { @Inject() private leoricRegister: LeoricRegister; // default dataSource get client(): Realm { const defaultConfig = this.leoricRegister.getConfig(); return this.leoricRegister.getRealm(defaultConfig)!; } getClient(datasource: string): Realm { const config = this.leoricRegister.getConfig(datasource); if (!config) { throw new Error(`not found ${datasource} datasource`); } return this.leoricRegister.getOrCreateRealm(config.database)!; } } ================================================ FILE: plugin/orm/package.json ================================================ { "name": "@eggjs/tegg-orm-plugin", "eggPlugin": { "name": "teggOrm", "dependencies": [ "tegg" ] }, "version": "3.78.15", "description": "orm decorator for egg", "keywords": [ "egg", "plugin", "typescript", "module", "tegg", "orm", "leoric" ], "files": [ "app.js", "app.d.ts", "lib/**/*.js", "lib/**/*.d.ts", "app/**/*.js", "app/**/*.d.ts", "typings/*.d.ts" ], "types": "typings/index.d.ts", "scripts": { "test": "ut prepare-test && cross-env NODE_ENV=test NODE_OPTIONS='--no-deprecation' mocha", "clean": "tsc -b --clean", "tsc": "ut run clean && tsc -p ./tsconfig.json", "tsc:pub": "ut run clean && tsc -p ./tsconfig.pub.json", "prepublishOnly": "ut tsc:pub", "prepare-test": "node ./test/fixtures/prepare.js" }, "homepage": "https://github.com/eggjs/tegg", "bugs": { "url": "https://github.com/eggjs/tegg/issues" }, "repository": { "type": "git", "url": "git@github.com:eggjs/tegg.git", "directory": "plugin/orm" }, "engines": { "node": ">=14.0.0" }, "dependencies": { "@eggjs/egg-module-common": "^3.78.15", "@eggjs/tegg": "^3.78.15", "@eggjs/tegg-common-util": "^3.78.15", "@eggjs/tegg-lifecycle": "^3.78.15", "@eggjs/tegg-loader": "^3.78.15", "@eggjs/tegg-metadata": "^3.78.15", "@eggjs/tegg-orm-decorator": "^3.78.15", "@eggjs/tegg-runtime": "^3.78.15", "@types/koa-router": "^7.0.40", "koa-compose": "^3.2.1", "leoric": "2.13.8", "sdk-base": "^4.2.0" }, "devDependencies": { "@eggjs/module-test-util": "^3.78.15", "@eggjs/router": "^2.0.0", "@eggjs/tegg-config": "^3.78.15", "@eggjs/tegg-plugin": "^3.78.15", "@types/mocha": "^10.0.1", "@types/node": "^20.2.4", "cross-env": "^7.0.3", "egg": "^3.9.1", "egg-mock": "^5.5.0", "egg-tracer": "^2.0.0", "koa-router": "^8.0.8", "mocha": "^10.2.0", "mysql": "^2.18.1", "ts-node": "^10.9.1", "typescript": "^5.0.4" }, "publishConfig": { "access": "public" }, "gitHead": "240a3e9f40fda65ebb7589727d197db5ce17916c" } ================================================ FILE: plugin/orm/test/fixtures/apps/orm-app/app.ts ================================================ import { Application } from 'egg'; // @ts-expect-error: the library definition is wrong import { Logger } from 'leoric'; export default class OrmAppHook { private readonly app: Application; constructor(app: Application) { this.app = app; } async didLoad() { await this.app.leoricRegister.ready(); const app = this.app; for (const realm of this.app.leoricRegister.realmMap.values()) { realm.driver.logger = new Logger({ logQuery(sql, _, options) { const path = options.Model?.ctx?.path; app.logger.info('sql: %s path: %s', sql, path); }, }); } } } ================================================ FILE: plugin/orm/test/fixtures/apps/orm-app/config/config.default.js ================================================ 'use strict'; module.exports = function () { const config = { keys: 'test key', security: { csrf: { ignoreJSON: false, }, }, orm: { datasources: [ { client: 'mysql', database: 'test', host: '127.0.0.1', port: 3306, user: 'root', delegate: 'model', baseDir: 'model', migrations: 'database', define: { underscored: true, }, }, { client: 'mysql', database: 'apple', host: '127.0.0.1', port: 3306, user: 'root', delegate: 'model', baseDir: 'model', migrations: 'database', define: { underscored: true, }, }, { client: 'mysql', database: 'banana', host: '127.0.0.1', port: 3306, user: 'root', delegate: 'model', baseDir: 'model', migrations: 'database', define: { underscored: true, }, }, ], }, }; return config; }; ================================================ FILE: plugin/orm/test/fixtures/apps/orm-app/config/module.json ================================================ [ { "path": "../modules/orm-module" } ] ================================================ FILE: plugin/orm/test/fixtures/apps/orm-app/config/plugin.js ================================================ 'use strict'; exports.tracer = { package: 'egg-tracer', enable: true, }; exports.tegg = { package: '@eggjs/tegg-plugin', enable: true, }; exports.teggConfig = { package: '@eggjs/tegg-config', enable: true, }; ================================================ FILE: plugin/orm/test/fixtures/apps/orm-app/modules/orm-module/AppService.ts ================================================ import { Inject, SingletonProto } from '@eggjs/tegg'; import { Orm } from '@eggjs/tegg-orm-plugin/lib/SingletonORM'; import { App } from './model/App'; @SingletonProto() export class AppService { @Inject() App: typeof App; @Inject() private readonly orm: Orm; async createApp(data: { name: string; desc: string; }): Promise { const bone = await this.App.create(data as any); return bone as App; } async findApp(name: string): Promise { const app = await this.App.findOne({ name }); return app as App; } async rawQuery(dataSource: string, sql: string, values?: any[]) { return await this.orm.getClient(dataSource).query(sql, values); } async getClient(name: string) { return this.orm.getClient(name); } async getDefaultClient() { return this.orm.client; } } ================================================ FILE: plugin/orm/test/fixtures/apps/orm-app/modules/orm-module/CtxService.ts ================================================ import { ContextProto, Inject } from '@eggjs/tegg'; import { Pkg } from './model/Pkg'; @ContextProto() export class CtxService { @Inject() Pkg: typeof Pkg; async createCtxPkg(data: { name: string; desc: string; }): Promise { const bone = await this.Pkg.create(data as any); return bone as Pkg; } async findCtxPkg(name: string): Promise { const app = await this.Pkg.findOne({ name }); return app as Pkg; } } ================================================ FILE: plugin/orm/test/fixtures/apps/orm-app/modules/orm-module/PkgService.ts ================================================ import { Inject, SingletonProto } from '@eggjs/tegg'; import { Pkg } from './model/Pkg'; @SingletonProto() export class PkgService { @Inject() Pkg: typeof Pkg; async createPkg(data: { name: string; desc: string; }): Promise { const bone = await this.Pkg.create(data as any); return bone as Pkg; } async findPkg(name: string): Promise { const app = await this.Pkg.findOne({ name }); return app as Pkg; } } ================================================ FILE: plugin/orm/test/fixtures/apps/orm-app/modules/orm-module/model/App.ts ================================================ import { Attribute, Model } from '@eggjs/tegg-orm-decorator'; import { DataTypes, Bone } from 'leoric'; @Model({ dataSource: 'test', }) export class App extends Bone { @Attribute(DataTypes.STRING) name: string; @Attribute(DataTypes.STRING) desc: string; } ================================================ FILE: plugin/orm/test/fixtures/apps/orm-app/modules/orm-module/model/Pkg.ts ================================================ import { Attribute, Model } from '@eggjs/tegg-orm-decorator'; import { DataTypes, Bone } from 'leoric'; @Model({ dataSource: 'test', }) export class Pkg extends Bone { @Attribute(DataTypes.STRING) name: string; @Attribute(DataTypes.STRING) desc: string; static beforeCreate(instance: Pkg) { instance.name += '_before_create_hook'; } } ================================================ FILE: plugin/orm/test/fixtures/apps/orm-app/modules/orm-module/package.json ================================================ { "name": "orm-module", "eggModule": { "name": "ormModule" } } ================================================ FILE: plugin/orm/test/fixtures/apps/orm-app/package.json ================================================ { "name": "controller-app" } ================================================ FILE: plugin/orm/test/fixtures/prepare.js ================================================ 'use strict'; const mysql = require('mysql'); const os = require('os'); const config = { host: '127.0.0.1', port: 3306, password: '', user: 'root', database: '', }; let connection; function connect() { connection = mysql.createConnection(config); connection.connect(); } async function query(sql) { return new Promise((resolve, reject) => { console.log('prepare database: ', sql); connection.query(sql, (err, res) => { if (err) { return reject(err); } return resolve(res); }); }); } async function init() { await query('DROP DATABASE IF EXISTS `test`;'); await query('CREATE DATABASE test;'); await query('DROP DATABASE IF EXISTS `apple`;'); await query('CREATE DATABASE apple;'); await query('DROP DATABASE IF EXISTS `banana`;'); await query('CREATE DATABASE banana;'); await query('use test;'); await query('CREATE TABLE `apps` (\n' + ' `id` bigint unsigned NOT NULL AUTO_INCREMENT,\n' + ' `name` varchar(100) NOT NULL,\n' + ' `desc` varchar(100) NOT NULL,\n' + ' PRIMARY KEY (`id`)\n' + ');'); await query('CREATE TABLE `pkgs` (\n' + ' `id` bigint unsigned NOT NULL AUTO_INCREMENT,\n' + ' `name` varchar(100) NOT NULL,\n' + ' `desc` varchar(100) NOT NULL,\n' + ' PRIMARY KEY (`id`)\n' + ');'); } (async () => { try { // TODO win32 ci not support mysql if ([ 'darwin', 'win32' ].includes(os.platform())) { return; } connect(); await init(); console.log('prepare database done'); process.exit(0); } catch (e) { console.log(e); process.exit(1); } })(); ================================================ FILE: plugin/orm/test/index.test.ts ================================================ import assert from 'assert'; import path from 'path'; // @ts-expect-error: the library definition is wrong import { Logger } from 'leoric'; import mm, { MockApplication } from 'egg-mock'; import { AppService } from './fixtures/apps/orm-app/modules/orm-module/AppService'; import os from 'os'; import { PkgService } from './fixtures/apps/orm-app/modules/orm-module/PkgService'; import { Pkg } from './fixtures/apps/orm-app/modules/orm-module/model/Pkg'; import { App } from './fixtures/apps/orm-app/modules/orm-module/model/App'; import { CtxService } from './fixtures/apps/orm-app/modules/orm-module/CtxService'; import { EggContext } from '@eggjs/tegg'; describe('plugin/orm/test/orm.test.ts', () => { // TODO win32 ci not support mysql if ([ 'darwin', 'win32' ].includes(os.platform())) { return; } let app: MockApplication; let appService: AppService; afterEach(async () => { await Promise.all([ Pkg.truncate(), App.truncate(), ]); mm.restore(); }); before(async () => { mm(process.env, 'EGG_TYPESCRIPT', true); mm(process, 'cwd', () => { return path.join(__dirname, '../'); }); app = mm.app({ baseDir: path.join(__dirname, './fixtures/apps/orm-app'), framework: require.resolve('egg'), }); await app.ready(); }); after(() => { return app.close(); }); it('bone should work', async () => { const appService = await app.getEggObject(AppService); const appModel = await appService.createApp({ name: 'egg', desc: 'the framework', }); assert(appModel); assert(appModel.name === 'egg'); assert(appModel.desc === 'the framework'); const findModel = await appService.findApp('egg'); assert(findModel); assert(findModel.name === 'egg'); assert(findModel.desc === 'the framework'); }); it('hook should work', async () => { const pkgService = await app.getEggObject(PkgService); const pkgModel = await pkgService.createPkg({ name: 'egg', desc: 'the framework', }); assert(pkgModel); assert(pkgModel.name === 'egg_before_create_hook'); assert(pkgModel.desc === 'the framework'); const findModel = await pkgService.findPkg('egg_before_create_hook'); assert(findModel); assert(findModel.name === 'egg_before_create_hook'); assert(findModel.desc === 'the framework'); }); it('ctx should inject with Model', async () => { const appService = await app.getEggObject(AppService); app.mockLog(); await appService.findApp('egg'); app.expectLog(/sql: SELECT \* FROM `apps` WHERE `name` = 'egg' LIMIT 1/); // Model.ctx should be undefined in Singleton Service app.expectLog(/path: undefined/); }); it('singleton ORM client', async () => { appService = await app.getEggObject(AppService); describe('raw query', () => { before(async () => { const appModel = await appService.createApp({ name: 'egg', desc: 'the framework', }); assert(appModel); assert(appModel.name === 'egg'); assert(appModel.desc === 'the framework'); }); it('query success', async () => { const res = await appService.rawQuery('test', 'select * from apps where name = "egg"'); assert(res.rows.length === 1); assert(res.rows[0].name === 'egg'); }); it('query success for args', async () => { const res = await appService.rawQuery('test', 'select * from apps where name = ?', [ 'egg' ]); assert(res.rows.length === 1); assert(res.rows[0].name === 'egg'); }); }); describe('multi db', () => { it('should work for multi database', async () => { const appleClient = await appService.getClient('apple'); const bananaClient = await appService.getClient('banana'); assert(appleClient.options.database === 'apple'); assert(appleClient.options.database === 'apple'); assert(bananaClient.options.database === 'banana'); assert(bananaClient.options.database === 'banana'); }); it('should throw when invalid database', async () => { await assert.rejects(async () => { await appService.getClient('orange'); }, /not found orange datasource/); }); it('should return undefined when get default client', async () => { const defaultClient = await appService.getDefaultClient(); assert(defaultClient === undefined); }); }); }); describe('context proto', () => { let ctx: EggContext; let ctxService: CtxService; beforeEach(async () => { ctx = await app.mockModuleContext(); ctxService = await ctx.getEggObject(CtxService); }); afterEach(async () => { await app.destroyModuleContext(ctx); }); it('should work for ContextProto service', async () => { const ctxPkg = await ctxService.createCtxPkg({ name: 'egg', desc: 'the framework', }); assert(ctxPkg.name === 'egg_before_create_hook'); }); it('should query work', async () => { app.mockLog(); await ctxService.createCtxPkg({ name: 'egg', desc: 'the framework', }); const ctxPkg = await ctxService.findCtxPkg('egg_before_create_hook'); assert(ctxPkg); assert(ctxPkg.name === 'egg_before_create_hook'); app.expectLog(/sql: SELECT \* FROM `pkgs` WHERE `name` = 'egg_before_create_hook' LIMIT 1 path: \//); }); it('should tracer ctx set', async () => { let ctx; await (app as any).leoricRegister.ready(); for (const realm of app.leoricRegister.realmMap.values()) { realm.driver.logger = new Logger({ // eslint-disable-next-line no-loop-func logQuery(_: any, __: any, options: { Model: { ctx: any; }; }) { if (options.Model) { ctx = options.Model.ctx; } }, }); } await ctxService.createCtxPkg({ name: 'egg', desc: 'the framework', }); assert(ctx.originalUrl === '/'); assert(ctx.tracer.traceId); }); }); }); ================================================ FILE: plugin/orm/tsconfig.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "baseUrl": "./" }, "exclude": [ "node_modules", "test" ] } ================================================ FILE: plugin/orm/tsconfig.pub.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "baseUrl": "./" }, "exclude": [ "node_modules", "test" ] } ================================================ FILE: plugin/orm/typings/index.d.ts ================================================ import 'egg'; import '@eggjs/tegg-plugin'; import { DataType } from 'leoric'; import { AttributeOptions } from '@eggjs/tegg-orm-decorator'; import { LeoricRegister } from '../lib/LeoricRegister'; import { Orm } from '../lib/SingletonORM'; declare module '@eggjs/tegg-orm-decorator' { export declare function Attribute(dataType: DataType, options?: AttributeOptions): (target: any, propertyKey: PropertyKey) => void; } declare module 'egg' { export interface TeggOrmApplication { leoricRegister: LeoricRegister; orm: Orm; } interface Application extends TeggOrmApplication { } } ================================================ FILE: plugin/schedule/CHANGELOG.md ================================================ # Change Log All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. ## [3.78.15](https://github.com/eggjs/tegg/compare/v3.78.14...v3.78.15) (2026-04-23) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.78.14](https://github.com/eggjs/tegg/compare/v3.78.13...v3.78.14) (2026-04-21) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.78.13](https://github.com/eggjs/tegg/compare/v3.78.12...v3.78.13) (2026-04-21) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.78.12](https://github.com/eggjs/tegg/compare/v3.78.11...v3.78.12) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.78.11](https://github.com/eggjs/tegg/compare/v3.78.10...v3.78.11) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.78.10](https://github.com/eggjs/tegg/compare/v3.78.9...v3.78.10) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.78.9](https://github.com/eggjs/tegg/compare/v3.78.8...v3.78.9) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.78.8](https://github.com/eggjs/tegg/compare/v3.78.7...v3.78.8) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.78.7](https://github.com/eggjs/tegg/compare/v3.78.6...v3.78.7) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.78.6](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.6) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.78.5](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.5) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.78.4](https://github.com/eggjs/tegg/compare/v3.78.3...v3.78.4) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.78.3](https://github.com/eggjs/tegg/compare/v3.78.2...v3.78.3) (2026-04-15) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.78.2](https://github.com/eggjs/tegg/compare/v3.78.1...v3.78.2) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.78.1](https://github.com/eggjs/tegg/compare/v3.78.0...v3.78.1) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.78.0](https://github.com/eggjs/tegg/compare/v3.77.2...v3.78.0) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.77.2](https://github.com/eggjs/tegg/compare/v3.77.1...v3.77.2) (2026-04-07) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.77.1](https://github.com/eggjs/tegg/compare/v3.77.0...v3.77.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.77.0](https://github.com/eggjs/tegg/compare/v3.76.1...v3.77.0) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.76.1](https://github.com/eggjs/tegg/compare/v3.76.0...v3.76.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.76.0](https://github.com/eggjs/tegg/compare/v3.75.1...v3.76.0) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.75.1](https://github.com/eggjs/tegg/compare/v3.75.0...v3.75.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.75.0](https://github.com/eggjs/tegg/compare/v3.74.0...v3.75.0) (2026-03-30) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.74.0](https://github.com/eggjs/tegg/compare/v3.73.0...v3.74.0) (2026-03-30) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.73.0](https://github.com/eggjs/tegg/compare/v3.72.0...v3.73.0) (2026-03-25) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.72.0](https://github.com/eggjs/tegg/compare/v3.71.2...v3.72.0) (2026-02-04) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.71.2](https://github.com/eggjs/tegg/compare/v3.71.1...v3.71.2) (2026-01-30) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.71.1](https://github.com/eggjs/tegg/compare/v3.71.0...v3.71.1) (2026-01-28) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.71.0](https://github.com/eggjs/tegg/compare/v3.70.1...v3.71.0) (2026-01-26) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.70.1](https://github.com/eggjs/tegg/compare/v3.70.0...v3.70.1) (2026-01-14) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.70.0](https://github.com/eggjs/tegg/compare/v3.69.0...v3.70.0) (2026-01-07) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.69.0](https://github.com/eggjs/tegg/compare/v3.68.0...v3.69.0) (2026-01-06) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.68.0](https://github.com/eggjs/tegg/compare/v3.67.2...v3.68.0) (2025-12-29) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.67.2](https://github.com/eggjs/tegg/compare/v3.67.1...v3.67.2) (2025-12-24) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.67.1](https://github.com/eggjs/tegg/compare/v3.67.0...v3.67.1) (2025-12-23) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.67.0](https://github.com/eggjs/tegg/compare/v3.66.0...v3.67.0) (2025-12-18) ### Features * **schedule:** module schedule unregister on module destroy ([#377](https://github.com/eggjs/tegg/issues/377)) ([098e889](https://github.com/eggjs/tegg/commit/098e889db4f9c1e227d2e8c6390b83e7596b3e69)) # [3.66.0](https://github.com/eggjs/tegg/compare/v3.65.3...v3.66.0) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.65.3](https://github.com/eggjs/tegg/compare/v3.65.2...v3.65.3) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.65.2](https://github.com/eggjs/tegg/compare/v3.65.1...v3.65.2) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.65.1](https://github.com/eggjs/tegg/compare/v3.65.0...v3.65.1) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.65.0](https://github.com/eggjs/tegg/compare/v3.64.5...v3.65.0) (2025-12-10) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.64.5](https://github.com/eggjs/tegg/compare/v3.64.4...v3.64.5) (2025-12-08) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.64.4](https://github.com/eggjs/tegg/compare/v3.64.3...v3.64.4) (2025-12-06) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.64.3](https://github.com/eggjs/tegg/compare/v3.64.2...v3.64.3) (2025-12-06) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.64.2](https://github.com/eggjs/tegg/compare/v3.64.1...v3.64.2) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.64.1](https://github.com/eggjs/tegg/compare/v3.64.0...v3.64.1) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.64.0](https://github.com/eggjs/tegg/compare/v3.63.2...v3.64.0) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.63.2](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.2) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.63.1](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.1) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.63.0](https://github.com/eggjs/tegg/compare/v3.62.3...v3.63.0) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.62.3](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.3) (2025-11-05) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.62.2](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.2) (2025-11-05) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.62.1](https://github.com/eggjs/tegg/compare/v3.62.0...v3.62.1) (2025-11-03) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.62.0](https://github.com/eggjs/tegg/compare/v3.61.0...v3.62.0) (2025-09-24) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.61.0](https://github.com/eggjs/tegg/compare/v3.60.3...v3.61.0) (2025-08-15) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.60.3](https://github.com/eggjs/tegg/compare/v3.60.2...v3.60.3) (2025-08-06) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.60.2](https://github.com/eggjs/tegg/compare/v3.60.1...v3.60.2) (2025-08-06) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.60.1](https://github.com/eggjs/tegg/compare/v3.60.0...v3.60.1) (2025-07-28) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.60.0](https://github.com/eggjs/tegg/compare/v3.59.1...v3.60.0) (2025-07-28) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.59.1](https://github.com/eggjs/tegg/compare/v3.59.0...v3.59.1) (2025-07-16) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.59.0](https://github.com/eggjs/tegg/compare/v3.58.0...v3.59.0) (2025-07-07) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.58.0](https://github.com/eggjs/tegg/compare/v3.57.14...v3.58.0) (2025-07-01) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.57.14](https://github.com/eggjs/tegg/compare/v3.57.13...v3.57.14) (2025-06-18) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.57.13](https://github.com/eggjs/tegg/compare/v3.57.12...v3.57.13) (2025-06-18) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.57.12](https://github.com/eggjs/tegg/compare/v3.57.11...v3.57.12) (2025-06-16) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.57.11](https://github.com/eggjs/tegg/compare/v3.57.10...v3.57.11) (2025-06-16) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.57.10](https://github.com/eggjs/tegg/compare/v3.57.9...v3.57.10) (2025-06-12) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.57.9](https://github.com/eggjs/tegg/compare/v3.57.8...v3.57.9) (2025-05-29) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.57.8](https://github.com/eggjs/tegg/compare/v3.57.7...v3.57.8) (2025-05-29) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.57.7](https://github.com/eggjs/tegg/compare/v3.57.6...v3.57.7) (2025-05-28) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.57.6](https://github.com/eggjs/tegg/compare/v3.57.5...v3.57.6) (2025-05-27) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.57.5](https://github.com/eggjs/tegg/compare/v3.57.4...v3.57.5) (2025-05-15) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.57.4](https://github.com/eggjs/tegg/compare/v3.57.3...v3.57.4) (2025-05-14) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.57.3](https://github.com/eggjs/tegg/compare/v3.57.2...v3.57.3) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.57.2](https://github.com/eggjs/tegg/compare/v3.57.1...v3.57.2) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.57.1](https://github.com/eggjs/tegg/compare/v3.57.0...v3.57.1) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.57.0](https://github.com/eggjs/tegg/compare/v3.56.3...v3.57.0) (2025-05-09) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.56.3](https://github.com/eggjs/tegg/compare/v3.56.2...v3.56.3) (2025-05-07) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.56.2](https://github.com/eggjs/tegg/compare/v3.56.1...v3.56.2) (2025-05-01) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.56.1](https://github.com/eggjs/tegg/compare/v3.56.0...v3.56.1) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.56.0](https://github.com/eggjs/tegg/compare/v3.55.0...v3.56.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.55.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.55.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.54.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.54.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.53.0](https://github.com/eggjs/tegg/compare/v3.52.1...v3.53.0) (2025-04-21) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.52.1](https://github.com/eggjs/tegg/compare/v3.52.0...v3.52.1) (2025-03-14) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.52.0](https://github.com/eggjs/tegg/compare/v3.51.2...v3.52.0) (2024-12-30) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.51.2](https://github.com/eggjs/tegg/compare/v3.51.1...v3.51.2) (2024-12-09) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.51.1](https://github.com/eggjs/tegg/compare/v3.51.0...v3.51.1) (2024-11-06) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.51.0](https://github.com/eggjs/tegg/compare/v3.50.1...v3.51.0) (2024-10-30) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.50.1](https://github.com/eggjs/tegg/compare/v3.50.0...v3.50.1) (2024-10-23) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.50.0](https://github.com/eggjs/tegg/compare/v3.49.0...v3.50.0) (2024-10-22) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.49.0](https://github.com/eggjs/tegg/compare/v3.48.1...v3.49.0) (2024-10-21) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.48.1](https://github.com/eggjs/tegg/compare/v3.48.0...v3.48.1) (2024-10-14) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.48.0](https://github.com/eggjs/tegg/compare/v3.47.2...v3.48.0) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.47.2](https://github.com/eggjs/tegg/compare/v3.47.1...v3.47.2) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.47.1](https://github.com/eggjs/tegg/compare/v3.47.0...v3.47.1) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.47.0](https://github.com/eggjs/tegg/compare/v3.46.4...v3.47.0) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.46.4](https://github.com/eggjs/tegg/compare/v3.46.3...v3.46.4) (2024-10-09) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.46.3](https://github.com/eggjs/tegg/compare/v3.46.2...v3.46.3) (2024-10-08) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.46.2](https://github.com/eggjs/tegg/compare/v3.46.1...v3.46.2) (2024-10-07) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.46.1](https://github.com/eggjs/tegg/compare/v3.46.0...v3.46.1) (2024-09-30) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.46.0](https://github.com/eggjs/tegg/compare/v3.45.0...v3.46.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.45.0](https://github.com/eggjs/tegg/compare/v3.44.1...v3.45.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.44.1](https://github.com/eggjs/tegg/compare/v3.44.0...v3.44.1) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.44.0](https://github.com/eggjs/tegg/compare/v3.43.2...v3.44.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.43.2](https://github.com/eggjs/tegg/compare/v3.43.1...v3.43.2) (2024-09-14) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.43.1](https://github.com/eggjs/tegg/compare/v3.43.0...v3.43.1) (2024-09-14) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.43.0](https://github.com/eggjs/tegg/compare/v3.42.0...v3.43.0) (2024-09-13) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.42.0](https://github.com/eggjs/tegg/compare/v3.41.0...v3.42.0) (2024-09-10) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.41.0](https://github.com/eggjs/tegg/compare/v3.40.1...v3.41.0) (2024-08-26) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.40.1](https://github.com/eggjs/tegg/compare/v3.40.0...v3.40.1) (2024-08-23) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.40.0](https://github.com/eggjs/tegg/compare/v3.39.5...v3.40.0) (2024-08-22) ### Features * @Middleware support Advice ([#231](https://github.com/eggjs/tegg/issues/231)) ([613a89d](https://github.com/eggjs/tegg/commit/613a89da7ea6dd70d50e34aa9f4152358a622625)) ## [3.39.5](https://github.com/eggjs/tegg/compare/v3.39.4...v3.39.5) (2024-08-09) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.39.4](https://github.com/eggjs/tegg/compare/v3.39.3...v3.39.4) (2024-07-09) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.39.3](https://github.com/eggjs/tegg/compare/v3.39.2...v3.39.3) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.39.2](https://github.com/eggjs/tegg/compare/v3.39.1...v3.39.2) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.39.1](https://github.com/eggjs/tegg/compare/v3.39.0...v3.39.1) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.39.0](https://github.com/eggjs/tegg/compare/v3.38.0...v3.39.0) (2024-04-19) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.38.0](https://github.com/eggjs/tegg/compare/v3.37.3...v3.38.0) (2024-04-18) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.37.3](https://github.com/eggjs/tegg/compare/v3.37.2...v3.37.3) (2024-04-17) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.37.2](https://github.com/eggjs/tegg/compare/v3.37.1...v3.37.2) (2024-04-17) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.37.1](https://github.com/eggjs/tegg/compare/v3.37.0...v3.37.1) (2024-04-16) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.37.0](https://github.com/eggjs/tegg/compare/v3.36.3...v3.37.0) (2024-04-16) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.36.3](https://github.com/eggjs/tegg/compare/v3.36.2...v3.36.3) (2024-04-10) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.36.2](https://github.com/eggjs/tegg/compare/v3.36.1...v3.36.2) (2024-04-08) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.36.1](https://github.com/eggjs/tegg/compare/v3.36.0...v3.36.1) (2024-04-07) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.36.0](https://github.com/eggjs/tegg/compare/v3.35.1...v3.36.0) (2024-04-02) ### Features * impl ajv + typebox Validator ([#201](https://github.com/eggjs/tegg/issues/201)) ([9fd585d](https://github.com/eggjs/tegg/commit/9fd585de9b613466c96b73494a08a494db34ea57)) ## [3.35.1](https://github.com/eggjs/tegg/compare/v3.35.0...v3.35.1) (2024-03-26) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.35.0](https://github.com/eggjs/tegg/compare/v3.34.0...v3.35.0) (2024-03-26) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.34.0](https://github.com/eggjs/tegg/compare/v3.33.1...v3.34.0) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.33.1](https://github.com/eggjs/tegg/compare/v3.33.0...v3.33.1) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.33.0](https://github.com/eggjs/tegg/compare/v3.32.0...v3.33.0) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.32.0](https://github.com/eggjs/tegg/compare/v3.31.0...v3.32.0) (2024-02-19) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.31.0](https://github.com/eggjs/tegg/compare/v3.30.1...v3.31.0) (2024-01-31) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.30.1](https://github.com/eggjs/tegg/compare/v3.30.0...v3.30.1) (2024-01-25) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.30.0](https://github.com/eggjs/tegg/compare/v3.29.0...v3.30.0) (2024-01-17) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.29.0](https://github.com/eggjs/tegg/compare/v3.28.2...v3.29.0) (2023-12-26) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.28.2](https://github.com/eggjs/tegg/compare/v3.28.1...v3.28.2) (2023-12-12) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.28.1](https://github.com/eggjs/tegg/compare/v3.28.0...v3.28.1) (2023-12-11) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.28.0](https://github.com/eggjs/tegg/compare/v3.27.0...v3.28.0) (2023-12-10) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.27.0](https://github.com/eggjs/tegg/compare/v3.26.0...v3.27.0) (2023-11-23) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.26.0](https://github.com/eggjs/tegg/compare/v3.25.2...v3.26.0) (2023-11-17) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.25.2](https://github.com/eggjs/tegg/compare/v3.25.1...v3.25.2) (2023-11-06) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.25.1](https://github.com/eggjs/tegg/compare/v3.25.0...v3.25.1) (2023-11-03) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.25.0](https://github.com/eggjs/tegg/compare/v3.24.0...v3.25.0) (2023-11-03) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.24.0](https://github.com/eggjs/tegg/compare/v3.23.0...v3.24.0) (2023-10-26) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.23.0](https://github.com/eggjs/tegg/compare/v3.22.0...v3.23.0) (2023-09-20) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.20.0](https://github.com/eggjs/tegg/compare/v3.19.0...v3.20.0) (2023-09-07) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.19.0](https://github.com/eggjs/tegg/compare/v3.18.1...v3.19.0) (2023-08-31) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.18.1](https://github.com/eggjs/tegg/compare/v3.18.0...v3.18.1) (2023-08-29) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.18.0](https://github.com/eggjs/tegg/compare/v3.17.0...v3.18.0) (2023-08-29) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.17.0](https://github.com/eggjs/tegg/compare/v3.16.0...v3.17.0) (2023-08-24) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.16.0](https://github.com/eggjs/tegg/compare/v3.15.0...v3.16.0) (2023-08-24) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.14.3](https://github.com/eggjs/tegg/compare/v3.14.2...v3.14.3) (2023-08-14) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.14.2](https://github.com/eggjs/tegg/compare/v3.14.1...v3.14.2) (2023-08-14) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.14.1](https://github.com/eggjs/tegg/compare/v3.14.0...v3.14.1) (2023-08-11) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.14.0](https://github.com/eggjs/tegg/compare/v3.13.0...v3.14.0) (2023-07-31) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.13.0](https://github.com/eggjs/tegg/compare/v3.12.0...v3.13.0) (2023-07-25) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.12.0](https://github.com/eggjs/tegg/compare/v3.11.1...v3.12.0) (2023-07-13) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.11.1](https://github.com/eggjs/tegg/compare/v3.11.0...v3.11.1) (2023-06-29) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.11.0](https://github.com/eggjs/tegg/compare/v3.10.0...v3.11.0) (2023-06-29) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.10.0](https://github.com/eggjs/tegg/compare/v3.9.0...v3.10.0) (2023-06-28) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.9.0](https://github.com/eggjs/tegg/compare/v3.8.0...v3.9.0) (2023-06-20) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.8.0](https://github.com/eggjs/tegg/compare/v3.7.0...v3.8.0) (2023-05-30) ### Features * impl EggObjectLifecycle hook in decorator ([#119](https://github.com/eggjs/tegg/issues/119)) ([cced8a2](https://github.com/eggjs/tegg/commit/cced8a2e009c33d5040fa21d00409fddef471b0e)) # [3.7.0](https://github.com/eggjs/tegg/compare/v3.6.3...v3.7.0) (2023-04-03) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.6.3](https://github.com/eggjs/tegg/compare/v3.6.2...v3.6.3) (2023-03-02) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.6.2](https://github.com/eggjs/tegg/compare/v3.6.1...v3.6.2) (2023-02-16) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.6.1](https://github.com/eggjs/tegg/compare/v3.6.0...v3.6.1) (2023-02-14) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.6.0](https://github.com/eggjs/tegg/compare/v3.5.2...v3.6.0) (2023-02-13) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.5.2](https://github.com/eggjs/tegg/compare/v3.5.1...v3.5.2) (2023-02-10) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.5.1](https://github.com/eggjs/tegg/compare/v3.5.0...v3.5.1) (2023-02-10) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.5.0](https://github.com/eggjs/tegg/compare/v3.4.1...v3.5.0) (2023-02-10) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.4.1](https://github.com/eggjs/tegg/compare/v3.4.0...v3.4.1) (2023-02-02) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.4.0](https://github.com/eggjs/tegg/compare/v3.3.4...v3.4.0) (2023-02-01) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.3.4](https://github.com/eggjs/tegg/compare/v3.3.3...v3.3.4) (2023-01-29) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.3.3](https://github.com/eggjs/tegg/compare/v3.3.2...v3.3.3) (2023-01-29) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.3.2](https://github.com/eggjs/tegg/compare/v3.3.1...v3.3.2) (2023-01-29) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.3.1](https://github.com/eggjs/tegg/compare/v3.3.0...v3.3.1) (2023-01-28) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.3.0](https://github.com/eggjs/tegg/compare/v3.2.4...v3.3.0) (2023-01-28) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.2.3](https://github.com/eggjs/tegg/compare/v3.2.2...v3.2.3) (2023-01-16) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.2.2](https://github.com/eggjs/tegg/compare/v3.2.1...v3.2.2) (2023-01-06) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [3.2.1](https://github.com/eggjs/tegg/compare/v3.2.0...v3.2.1) (2022-12-28) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.2.0](https://github.com/eggjs/tegg/compare/v3.1.0...v3.2.0) (2022-12-28) ### Features * impl mockModuleContextScope ([#73](https://github.com/eggjs/tegg/issues/73)) ([041881c](https://github.com/eggjs/tegg/commit/041881ca317ad81366172a35ac56b7b2dc0a0488)) # [3.1.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.1.0) (2022-12-27) ### Bug Fixes * fix miss agent file ([#56](https://github.com/eggjs/tegg/issues/56)) ([cfb4dcc](https://github.com/eggjs/tegg/commit/cfb4dcc006ee1253733c7122f885a05da94f80b5)) * fix schedule import ([1fb5481](https://github.com/eggjs/tegg/commit/1fb54816fb3240c641824c2bc2b464c35652b655)) ### Features * **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) * impl Schedule decorator ([#52](https://github.com/eggjs/tegg/issues/52)) ([7f95005](https://github.com/eggjs/tegg/commit/7f950050b548ca542addbd7b466675da4e81ce3f)) * implement cork/uncork for eventbus ([#60](https://github.com/eggjs/tegg/issues/60)) ([38114bd](https://github.com/eggjs/tegg/commit/38114bd7ea3b46cc4a79556a005ef18b2ae11ec2)) # [3.0.0](https://github.com/eggjs/tegg/compare/v3.0.0-alpha.0...v3.0.0) (2022-12-26) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin # [3.0.0-alpha.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.0.0-alpha.0) (2022-12-22) ### Bug Fixes * fix miss agent file ([#56](https://github.com/eggjs/tegg/issues/56)) ([cfb4dcc](https://github.com/eggjs/tegg/commit/cfb4dcc006ee1253733c7122f885a05da94f80b5)) * fix schedule import ([1fb5481](https://github.com/eggjs/tegg/commit/1fb54816fb3240c641824c2bc2b464c35652b655)) ### Features * **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) * impl Schedule decorator ([#52](https://github.com/eggjs/tegg/issues/52)) ([7f95005](https://github.com/eggjs/tegg/commit/7f950050b548ca542addbd7b466675da4e81ce3f)) * implement cork/uncork for eventbus ([#60](https://github.com/eggjs/tegg/issues/60)) ([38114bd](https://github.com/eggjs/tegg/commit/38114bd7ea3b46cc4a79556a005ef18b2ae11ec2)) ## [2.2.3](https://github.com/eggjs/tegg/compare/@eggjs/tegg-schedule-plugin@2.2.2...@eggjs/tegg-schedule-plugin@2.2.3) (2022-09-05) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin ## [2.2.2](https://github.com/eggjs/tegg/compare/@eggjs/tegg-schedule-plugin@2.2.1...@eggjs/tegg-schedule-plugin@2.2.2) (2022-09-05) ### Bug Fixes * fix miss agent file ([#56](https://github.com/eggjs/tegg/issues/56)) ([cfb4dcc](https://github.com/eggjs/tegg/commit/cfb4dcc006ee1253733c7122f885a05da94f80b5)) ## [2.2.1](https://github.com/eggjs/tegg/compare/@eggjs/tegg-schedule-plugin@2.2.0...@eggjs/tegg-schedule-plugin@2.2.1) (2022-09-04) ### Bug Fixes * fix schedule import ([1fb5481](https://github.com/eggjs/tegg/commit/1fb54816fb3240c641824c2bc2b464c35652b655)) # 2.2.0 (2022-09-04) ### Features * impl Schedule decorator ([#52](https://github.com/eggjs/tegg/issues/52)) ([7f95005](https://github.com/eggjs/tegg/commit/7f950050b548ca542addbd7b466675da4e81ce3f)) ================================================ FILE: plugin/schedule/README.md ================================================ # @eggjs/tegg-schedule-plugin 使用注解的方式来开发 egg 中的 schedule ## Install ```shell npm i --save @eggjs/tegg-schedule-plugin ``` ## Config ```js // config/plugin.js exports.teggSchedule = { package: '@eggjs/tegg-schedule-plugin', enable: true, }; ``` ================================================ FILE: plugin/schedule/agent.ts ================================================ import { Application } from 'egg'; import { ScheduleSubscriberRegister } from './lib/ScheduleSubscriberRegister'; import { LoaderFactory } from '@eggjs/tegg-loader'; import { EggLoadUnitType } from '@eggjs/tegg-metadata'; import { ScheduleInfoUtil, ScheduleMetaBuilder } from '@eggjs/tegg/schedule'; export default class ScheduleAppBootHook { private readonly agent: Application; private readonly scheduleSubscriberRegister: ScheduleSubscriberRegister; constructor(agent) { this.agent = agent; this.scheduleSubscriberRegister = new ScheduleSubscriberRegister(this.agent); } configDidLoad() { // FIXME: tegg use lots singleton, in mm.app test case, agent/app in one process // if use start tegg in agent, the app will use the same singleton // so we should refactor tegg to not use singleton. for (const moduleConfig of this.agent.moduleReferences) { const loader = LoaderFactory.createLoader(moduleConfig.path, EggLoadUnitType.MODULE); const clazzList = loader.load(); for (const clazz of clazzList) { if (ScheduleInfoUtil.isSchedule(clazz)) { const builder = new ScheduleMetaBuilder(clazz); const metadata = builder.build(); this.scheduleSubscriberRegister.register(clazz, metadata); } } } } } ================================================ FILE: plugin/schedule/app.ts ================================================ import { Application } from 'egg'; import { ScheduleManager } from './lib/ScheduleManager'; import { ScheduleWorkerRegister } from './lib/ScheduleWorkerRegister'; import { ScheduleWorkerLoadUnitHook } from './lib/ScheduleWorkerLoadUnitHook'; import { SchedulePrototypeHook } from './lib/SchedulePrototypeHook'; export default class ScheduleAppBootHook { private readonly app: Application; private readonly scheduleManager: ScheduleManager; private readonly scheduleWorkerRegister: ScheduleWorkerRegister; private readonly scheduleWorkerLoadUnitHook: ScheduleWorkerLoadUnitHook; private readonly schedulePrototypeHook: SchedulePrototypeHook; constructor(app: Application) { this.app = app; this.scheduleManager = new ScheduleManager(this.app); this.scheduleWorkerRegister = new ScheduleWorkerRegister(this.scheduleManager); this.scheduleWorkerLoadUnitHook = new ScheduleWorkerLoadUnitHook(this.scheduleWorkerRegister); this.schedulePrototypeHook = new SchedulePrototypeHook(); } configWillLoad() { this.app.loadUnitLifecycleUtil.registerLifecycle(this.scheduleWorkerLoadUnitHook); this.app.eggPrototypeLifecycleUtil.registerLifecycle(this.schedulePrototypeHook); } beforeClose() { // Unregister all schedules before deleting lifecycle hooks this.scheduleManager.unregisterAll(); this.app.loadUnitLifecycleUtil.deleteLifecycle(this.scheduleWorkerLoadUnitHook); this.app.eggPrototypeLifecycleUtil.deleteLifecycle(this.schedulePrototypeHook); } } ================================================ FILE: plugin/schedule/lib/EggScheduleAdapter.ts ================================================ import { Context } from 'egg'; import { EggPrototype } from '@eggjs/tegg-metadata'; import { ScheduleMetadata, ScheduleSubscriber } from '@eggjs/tegg-schedule-decorator'; import { ROOT_PROTO } from '@eggjs/egg-module-common'; import { EggContainerFactory } from '@eggjs/tegg-runtime'; export type EggScheduleFunction = (ctx: Context, data: any) => Promise; export function eggScheduleAdapterFactory(proto: EggPrototype, metaData: ScheduleMetadata): EggScheduleFunction { return async function(ctx: Context, data: any) { (ctx as any)[ROOT_PROTO] = proto; await ctx.beginModuleScope(async () => { if (metaData.disable) return; const eggObject = await EggContainerFactory.getOrCreateEggObject(proto, proto.name); const subscriber = eggObject.obj as ScheduleSubscriber; await subscriber.subscribe(data); }); }; } ================================================ FILE: plugin/schedule/lib/EggScheduleMetadataConvertor.ts ================================================ import { ScheduleMetadata } from '@eggjs/tegg-schedule-decorator'; export class EggScheduleMetadataConvertor { static convertToEggSchedule(metadata: ScheduleMetadata): object { return { ...metadata.scheduleData, type: metadata.type, env: metadata.env, disable: metadata.disable, immediate: metadata.immediate, }; } } ================================================ FILE: plugin/schedule/lib/ScheduleManager.ts ================================================ import { Application } from 'egg'; import { EggPrototype } from '@eggjs/tegg-metadata'; import { PrototypeUtil } from '@eggjs/tegg'; /** * Manager class to track registered schedules and handle cleanup */ export class ScheduleManager { private readonly app: Application; // Map of schedule key to EggPrototype private readonly registeredSchedules: Map = new Map(); constructor(app: Application) { this.app = app; } /** * Register a schedule and track it */ register(proto: EggPrototype, scheduleItem: { schedule: object; task: any; key: string }) { const { key } = scheduleItem; this.registeredSchedules.set(key, proto); (this.app as any).scheduleWorker.registerSchedule(scheduleItem); } /** * Unregister a single schedule by prototype */ unregister(proto: EggPrototype) { const key = proto.getMetaData(PrototypeUtil.FILE_PATH) as string; if (this.registeredSchedules.has(key)) { (this.app as any).scheduleWorker.unregisterSchedule(key); this.registeredSchedules.delete(key); } } /** * Unregister all tracked schedules * Called during app beforeClose */ unregisterAll() { for (const key of this.registeredSchedules.keys()) { (this.app as any).scheduleWorker.unregisterSchedule(key); } this.registeredSchedules.clear(); } /** * Get the count of registered schedules */ get size(): number { return this.registeredSchedules.size; } } ================================================ FILE: plugin/schedule/lib/SchedulePrototypeHook.ts ================================================ import { LifecycleHook } from '@eggjs/tegg-lifecycle'; import { EggPrototype, EggPrototypeLifecycleContext } from '@eggjs/tegg-metadata'; import { ScheduleInfoUtil, ScheduleMetadataUtil, ScheduleMetaBuilder } from '@eggjs/tegg-schedule-decorator'; export class SchedulePrototypeHook implements LifecycleHook { async postCreate(ctx: EggPrototypeLifecycleContext): Promise { if (!ScheduleInfoUtil.isSchedule(ctx.clazz)) { return; } const builder = new ScheduleMetaBuilder(ctx.clazz); const metadata = builder.build(); if (metadata) { ScheduleMetadataUtil.setScheduleMetadata(ctx.clazz, metadata); } } } ================================================ FILE: plugin/schedule/lib/ScheduleSubscriberRegister.ts ================================================ import { Application, EggLogger } from 'egg'; import { PrototypeUtil, EggProtoImplClass } from '@eggjs/tegg'; import { ScheduleMetadata } from '@eggjs/tegg-schedule-decorator'; import { EggScheduleMetadataConvertor } from './EggScheduleMetadataConvertor'; export class ScheduleSubscriberRegister { private readonly agent: Application; private readonly logger: EggLogger; constructor(agent: Application) { this.agent = agent; this.logger = this.agent.logger; } register(clazz: EggProtoImplClass, metadata: ScheduleMetadata) { // bind subscriber const schedule = EggScheduleMetadataConvertor.convertToEggSchedule(metadata); const path = PrototypeUtil.getFilePath(clazz); if (!metadata.disable) this.logger.info('[egg-schedule]: register schedule %s', path); (this.agent as any).schedule.registerSchedule({ schedule, key: path, }); } } ================================================ FILE: plugin/schedule/lib/ScheduleWorkerLoadUnitHook.ts ================================================ import { LifecycleHook } from '@eggjs/tegg'; import { ScheduleWorkerRegister } from './ScheduleWorkerRegister'; import { IS_SCHEDULE, SCHEDULE_METADATA, ScheduleMetadata } from '@eggjs/tegg-schedule-decorator'; import { LoadUnit, LoadUnitLifecycleContext } from '@eggjs/tegg-metadata'; export class ScheduleWorkerLoadUnitHook implements LifecycleHook { private readonly scheduleWorkerRegister: ScheduleWorkerRegister; constructor(scheduleWorkerRegister: ScheduleWorkerRegister) { this.scheduleWorkerRegister = scheduleWorkerRegister; } async postCreate(_: LoadUnitLifecycleContext, obj: LoadUnit): Promise { const iterator = obj.iterateEggPrototype(); for (const proto of iterator) { if (!proto.getMetaData(IS_SCHEDULE)) { continue; } const metadata: ScheduleMetadata | undefined = proto.getMetaData(SCHEDULE_METADATA); if (!metadata) { continue; } this.scheduleWorkerRegister.register(proto, metadata); } } } ================================================ FILE: plugin/schedule/lib/ScheduleWorkerRegister.ts ================================================ import { PrototypeUtil } from '@eggjs/tegg'; import { EggPrototype } from '@eggjs/tegg-metadata'; import { ScheduleMetadata } from '@eggjs/tegg-schedule-decorator'; import { eggScheduleAdapterFactory } from './EggScheduleAdapter'; import { EggScheduleMetadataConvertor } from './EggScheduleMetadataConvertor'; import { ScheduleManager } from './ScheduleManager'; export class ScheduleWorkerRegister { private readonly scheduleManager: ScheduleManager; constructor(scheduleManager: ScheduleManager) { this.scheduleManager = scheduleManager; } register(proto: EggPrototype, metadata: ScheduleMetadata) { const task = eggScheduleAdapterFactory(proto, metadata); const schedule = EggScheduleMetadataConvertor.convertToEggSchedule(metadata); const key = proto.getMetaData(PrototypeUtil.FILE_PATH) as string; if (!key) { throw new Error(`schedule prototype: ${proto.name as string} missing FILE_PATH metadata`); } this.scheduleManager.register(proto, { schedule, task, key, }); } } ================================================ FILE: plugin/schedule/package.json ================================================ { "name": "@eggjs/tegg-schedule-plugin", "eggPlugin": { "name": "teggSchedule", "dependencies": [ "tegg", "schedule" ] }, "version": "3.78.15", "description": "schedule decorator for egg", "keywords": [ "egg", "plugin", "typescript", "module", "tegg", "schedule" ], "files": [ "app.js", "app.d.ts", "agent.js", "agent.d.ts", "lib/**/*.js", "lib/**/*.d.ts", "app/**/*.js", "app/**/*.d.ts", "typings/*.d.ts" ], "types": "typings/index.d.ts", "scripts": { "test": "cross-env NODE_ENV=test NODE_OPTIONS='--no-deprecation' mocha", "clean": "tsc -b --clean", "tsc": "ut run clean && tsc -p ./tsconfig.json", "tsc:pub": "ut run clean && tsc -p ./tsconfig.pub.json", "prepublishOnly": "ut tsc:pub" }, "homepage": "https://github.com/eggjs/tegg", "bugs": { "url": "https://github.com/eggjs/tegg/issues" }, "repository": { "type": "git", "url": "git@github.com:eggjs/tegg.git", "directory": "plugin/schedule" }, "engines": { "node": ">=14.0.0" }, "dependencies": { "@eggjs/egg-module-common": "^3.78.15", "@eggjs/tegg": "^3.78.15", "@eggjs/tegg-common-util": "^3.78.15", "@eggjs/tegg-lifecycle": "^3.78.15", "@eggjs/tegg-loader": "^3.78.15", "@eggjs/tegg-metadata": "^3.78.15", "@eggjs/tegg-runtime": "^3.78.15", "@eggjs/tegg-schedule-decorator": "^3.78.15" }, "devDependencies": { "@eggjs/module-test-util": "^3.78.15", "@eggjs/tegg-config": "^3.78.15", "@eggjs/tegg-plugin": "^3.78.15", "@types/mocha": "^10.0.1", "@types/node": "^20.2.4", "cross-env": "^7.0.3", "egg": "^3.9.1", "egg-mock": "^5.5.0", "egg-schedule": "^4.0.0", "mocha": "^10.2.0", "ts-node": "^10.9.1", "typescript": "^5.0.4" }, "publishConfig": { "access": "public" }, "gitHead": "240a3e9f40fda65ebb7589727d197db5ce17916c" } ================================================ FILE: plugin/schedule/test/ScheduleManager.test.ts ================================================ import assert from 'assert'; import path from 'path'; import { EggPrototypeLifecycleUtil, LoadUnitFactory, LoadUnitLifecycleUtil, } from '@eggjs/tegg-metadata'; import { LoaderFactory } from '@eggjs/tegg-loader'; import { EggLoadUnitType } from '@eggjs/tegg-types'; import { LoaderUtil } from '@eggjs/module-test-util'; import { ScheduleManager } from '../lib/ScheduleManager'; import { ScheduleWorkerRegister } from '../lib/ScheduleWorkerRegister'; import { ScheduleWorkerLoadUnitHook } from '../lib/ScheduleWorkerLoadUnitHook'; import { SchedulePrototypeHook } from '../lib/SchedulePrototypeHook'; describe('plugin/schedule/test/ScheduleManager.test.ts', () => { describe('ScheduleManager', () => { it('should register and unregister schedules', async () => { const registeredSchedules: string[] = []; const unregisteredSchedules: string[] = []; // Mock app with scheduleWorker const mockApp = { scheduleWorker: { registerSchedule(item: { key: string }) { registeredSchedules.push(item.key); }, unregisterSchedule(key: string) { unregisteredSchedules.push(key); }, }, } as any; const scheduleManager = new ScheduleManager(mockApp); const scheduleWorkerRegister = new ScheduleWorkerRegister(scheduleManager); const scheduleWorkerLoadUnitHook = new ScheduleWorkerLoadUnitHook(scheduleWorkerRegister); const schedulePrototypeHook = new SchedulePrototypeHook(); // Register lifecycle hooks LoadUnitLifecycleUtil.registerLifecycle(scheduleWorkerLoadUnitHook); EggPrototypeLifecycleUtil.registerLifecycle(schedulePrototypeHook); try { // Create load unit const modulePath = path.join(__dirname, 'fixtures/schedule-app/app/simple-schedule-module'); // Build global graph first LoaderUtil.buildGlobalGraph([ modulePath ]); const loader = LoaderFactory.createLoader(modulePath, EggLoadUnitType.MODULE); await LoadUnitFactory.createLoadUnit(modulePath, EggLoadUnitType.MODULE, loader); // Verify schedule was registered assert(registeredSchedules.length > 0, 'Schedule should be registered'); assert(scheduleManager.size > 0, 'ScheduleManager should track registered schedules'); const registeredKey = registeredSchedules[0]; // Simulate beforeClose - unregister all schedules scheduleManager.unregisterAll(); // Verify schedule was unregistered assert(unregisteredSchedules.length > 0, 'Schedule should be unregistered'); assert.strictEqual(unregisteredSchedules[0], registeredKey, 'Unregistered key should match registered key'); assert.strictEqual(scheduleManager.size, 0, 'ScheduleManager should be empty after unregisterAll'); } finally { // Cleanup LoadUnitLifecycleUtil.deleteLifecycle(scheduleWorkerLoadUnitHook); EggPrototypeLifecycleUtil.deleteLifecycle(schedulePrototypeHook); } }); it('should handle multiple schedules', async () => { const registeredSchedules: string[] = []; const unregisteredSchedules: string[] = []; const mockApp = { scheduleWorker: { registerSchedule(item: { key: string }) { registeredSchedules.push(item.key); }, unregisterSchedule(key: string) { unregisteredSchedules.push(key); }, }, } as any; const scheduleManager = new ScheduleManager(mockApp); // Mock register multiple schedules const mockProto1 = { getMetaData: () => '/path/to/schedule1.ts' } as any; const mockProto2 = { getMetaData: () => '/path/to/schedule2.ts' } as any; // eslint-disable-next-line @typescript-eslint/no-empty-function scheduleManager.register(mockProto1, { schedule: {}, task: () => {}, key: '/path/to/schedule1.ts' }); // eslint-disable-next-line @typescript-eslint/no-empty-function scheduleManager.register(mockProto2, { schedule: {}, task: () => {}, key: '/path/to/schedule2.ts' }); assert.strictEqual(scheduleManager.size, 2, 'Should have 2 registered schedules'); assert.strictEqual(registeredSchedules.length, 2, 'Should have called registerSchedule twice'); // Unregister all scheduleManager.unregisterAll(); assert.strictEqual(scheduleManager.size, 0, 'Should have 0 registered schedules after unregisterAll'); assert.strictEqual(unregisteredSchedules.length, 2, 'Should have called unregisterSchedule twice'); }); }); }); ================================================ FILE: plugin/schedule/test/fixtures/schedule-app/app/simple-schedule-module/SimpleSchedule.ts ================================================ import { IntervalParams, Schedule, ScheduleType, } from '@eggjs/tegg-schedule-decorator'; @Schedule({ type: ScheduleType.WORKER, scheduleData: { interval: 1000, }, }) export class SimpleSchedule { async subscribe() { // do nothing console.log('schedule called'); } } ================================================ FILE: plugin/schedule/test/fixtures/schedule-app/app/simple-schedule-module/package.json ================================================ { "name": "simple-schedule-module", "eggModule": { "name": "simpleScheduleModule" } } ================================================ FILE: plugin/schedule/test/fixtures/schedule-app/app/subscriber/Subscriber.ts ================================================ import { Inject } from '@eggjs/tegg'; import { EggLogger } from 'egg'; import { IntervalParams, Schedule, ScheduleType, } from '@eggjs/tegg-schedule-decorator'; @Schedule({ type: ScheduleType.WORKER, scheduleData: { interval: 100, }, }) export class FooSubscriber { @Inject() private readonly logger: EggLogger; async subscribe() { this.logger.info('schedule called'); } } ================================================ FILE: plugin/schedule/test/fixtures/schedule-app/app/subscriber/package.json ================================================ { "name": "subscriber", "eggModule": { "name": "subscriber" } } ================================================ FILE: plugin/schedule/test/fixtures/schedule-app/config/module.json ================================================ [ { "path": "../app/subscriber" } ] ================================================ FILE: plugin/schedule/test/fixtures/schedule-app/config/plugin.js ================================================ 'use strict'; const path = require('path'); exports.tegg = { package: '@eggjs/tegg-plugin', enable: true, }; exports.teggConfig = { package: '@eggjs/tegg-config', enable: true, }; exports.teggSchedule = { path: path.join(__dirname, '../../../..'), enable: true, }; ================================================ FILE: plugin/schedule/test/fixtures/schedule-app/package.json ================================================ { "name": "schedule-app" } ================================================ FILE: plugin/schedule/test/schedule.test.ts ================================================ import path from 'path'; import fs from 'fs/promises'; import assert from 'assert'; import mm from 'egg-mock'; import { TimerUtil } from '@eggjs/tegg-common-util'; describe('plugin/schedule/test/schedule.test.ts', () => { describe('cluster mode', () => { let app; afterEach(async () => { mm.restore(); }); before(async () => { mm(process.env, 'EGG_TYPESCRIPT', true); const cluster = mm.cluster as any; app = cluster({ baseDir: path.join(__dirname, './fixtures/schedule-app'), workers: 1, cache: false, framework: path.dirname(require.resolve('egg/package.json')), opt: { cwd: path.join(__dirname, '../'), execArgv: [ '-r', require.resolve('ts-node/register') ], }, }) .debug(true); await app.ready(); }); after(() => { return app.close(); }); it('schedule should work', async () => { await TimerUtil.sleep(1000); const scheduleLog = await getScheduleLogContent('schedule-app'); assert(/schedule called/.test(scheduleLog)); }); }); describe('app mode - schedule unregister', () => { let app; beforeEach(async () => { mm(process.env, 'EGG_TYPESCRIPT', true); app = mm.app({ baseDir: path.join(__dirname, './fixtures/schedule-app'), framework: path.dirname(require.resolve('egg/package.json')), } as any); await app.ready(); }); afterEach(async () => { if (app) { await app.close(); } mm.restore(); }); it('should unregister schedule when app closes', async () => { // Verify scheduleWorker exists and has registered schedules assert(app.scheduleWorker, 'scheduleWorker should exist'); const scheduleItems = app.scheduleWorker.scheduleItems; const scheduleKeysBefore = Object.keys(scheduleItems); assert(scheduleKeysBefore.length > 0, 'Should have registered schedules'); // Find the subscriber schedule key const subscriberKey = scheduleKeysBefore.find(key => key.includes('Subscriber')); assert(subscriberKey, 'Should have Subscriber schedule registered'); assert(scheduleItems[subscriberKey], 'Subscriber schedule should exist before close'); // Close the app (this triggers LoadUnit destruction) await app.close(); app = null; // Verify schedule was unregistered by checking the scheduleItems object directly // Note: We check the same object reference, not Object.keys() again assert.strictEqual( scheduleItems[subscriberKey], undefined, 'Schedule should be unregistered after app close', ); }); }); }); async function getScheduleLogContent(name: string) { const logPath = path.join(__dirname, 'fixtures', name, 'logs', name, `${name}-web.log`); return fs.readFile(logPath, 'utf8'); } ================================================ FILE: plugin/schedule/tsconfig.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "baseUrl": "./" }, "exclude": [ "node_modules", "test" ] } ================================================ FILE: plugin/schedule/tsconfig.pub.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "baseUrl": "./" }, "exclude": [ "node_modules", "test" ] } ================================================ FILE: plugin/schedule/typings/index.d.ts ================================================ import 'egg'; import '@eggjs/tegg-plugin'; ================================================ FILE: plugin/tegg/CHANGELOG.md ================================================ # Change Log All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. ## [3.78.15](https://github.com/eggjs/tegg/compare/v3.78.14...v3.78.15) (2026-04-23) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.78.14](https://github.com/eggjs/tegg/compare/v3.78.13...v3.78.14) (2026-04-21) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.78.13](https://github.com/eggjs/tegg/compare/v3.78.12...v3.78.13) (2026-04-21) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.78.12](https://github.com/eggjs/tegg/compare/v3.78.11...v3.78.12) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.78.11](https://github.com/eggjs/tegg/compare/v3.78.10...v3.78.11) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.78.10](https://github.com/eggjs/tegg/compare/v3.78.9...v3.78.10) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.78.9](https://github.com/eggjs/tegg/compare/v3.78.8...v3.78.9) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.78.8](https://github.com/eggjs/tegg/compare/v3.78.7...v3.78.8) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.78.7](https://github.com/eggjs/tegg/compare/v3.78.6...v3.78.7) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.78.6](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.6) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.78.5](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.5) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.78.4](https://github.com/eggjs/tegg/compare/v3.78.3...v3.78.4) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.78.3](https://github.com/eggjs/tegg/compare/v3.78.2...v3.78.3) (2026-04-15) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.78.2](https://github.com/eggjs/tegg/compare/v3.78.1...v3.78.2) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.78.1](https://github.com/eggjs/tegg/compare/v3.78.0...v3.78.1) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-plugin # [3.78.0](https://github.com/eggjs/tegg/compare/v3.77.2...v3.78.0) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.77.2](https://github.com/eggjs/tegg/compare/v3.77.1...v3.77.2) (2026-04-07) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.77.1](https://github.com/eggjs/tegg/compare/v3.77.0...v3.77.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-plugin # [3.77.0](https://github.com/eggjs/tegg/compare/v3.76.1...v3.77.0) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.76.1](https://github.com/eggjs/tegg/compare/v3.76.0...v3.76.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-plugin # [3.76.0](https://github.com/eggjs/tegg/compare/v3.75.1...v3.76.0) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.75.1](https://github.com/eggjs/tegg/compare/v3.75.0...v3.75.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-plugin # [3.75.0](https://github.com/eggjs/tegg/compare/v3.74.0...v3.75.0) (2026-03-30) **Note:** Version bump only for package @eggjs/tegg-plugin # [3.74.0](https://github.com/eggjs/tegg/compare/v3.73.0...v3.74.0) (2026-03-30) **Note:** Version bump only for package @eggjs/tegg-plugin # [3.73.0](https://github.com/eggjs/tegg/compare/v3.72.0...v3.73.0) (2026-03-25) **Note:** Version bump only for package @eggjs/tegg-plugin # [3.72.0](https://github.com/eggjs/tegg/compare/v3.71.2...v3.72.0) (2026-02-04) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.71.2](https://github.com/eggjs/tegg/compare/v3.71.1...v3.71.2) (2026-01-30) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.71.1](https://github.com/eggjs/tegg/compare/v3.71.0...v3.71.1) (2026-01-28) **Note:** Version bump only for package @eggjs/tegg-plugin # [3.71.0](https://github.com/eggjs/tegg/compare/v3.70.1...v3.71.0) (2026-01-26) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.70.1](https://github.com/eggjs/tegg/compare/v3.70.0...v3.70.1) (2026-01-14) **Note:** Version bump only for package @eggjs/tegg-plugin # [3.70.0](https://github.com/eggjs/tegg/compare/v3.69.0...v3.70.0) (2026-01-07) **Note:** Version bump only for package @eggjs/tegg-plugin # [3.69.0](https://github.com/eggjs/tegg/compare/v3.68.0...v3.69.0) (2026-01-06) **Note:** Version bump only for package @eggjs/tegg-plugin # [3.68.0](https://github.com/eggjs/tegg/compare/v3.67.2...v3.68.0) (2025-12-29) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.67.2](https://github.com/eggjs/tegg/compare/v3.67.1...v3.67.2) (2025-12-24) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.67.1](https://github.com/eggjs/tegg/compare/v3.67.0...v3.67.1) (2025-12-23) **Note:** Version bump only for package @eggjs/tegg-plugin # [3.67.0](https://github.com/eggjs/tegg/compare/v3.66.0...v3.67.0) (2025-12-18) **Note:** Version bump only for package @eggjs/tegg-plugin # [3.66.0](https://github.com/eggjs/tegg/compare/v3.65.3...v3.66.0) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.65.3](https://github.com/eggjs/tegg/compare/v3.65.2...v3.65.3) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.65.2](https://github.com/eggjs/tegg/compare/v3.65.1...v3.65.2) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.65.1](https://github.com/eggjs/tegg/compare/v3.65.0...v3.65.1) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-plugin # [3.65.0](https://github.com/eggjs/tegg/compare/v3.64.5...v3.65.0) (2025-12-10) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.64.5](https://github.com/eggjs/tegg/compare/v3.64.4...v3.64.5) (2025-12-08) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.64.4](https://github.com/eggjs/tegg/compare/v3.64.3...v3.64.4) (2025-12-06) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.64.3](https://github.com/eggjs/tegg/compare/v3.64.2...v3.64.3) (2025-12-06) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.64.2](https://github.com/eggjs/tegg/compare/v3.64.1...v3.64.2) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.64.1](https://github.com/eggjs/tegg/compare/v3.64.0...v3.64.1) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-plugin # [3.64.0](https://github.com/eggjs/tegg/compare/v3.63.2...v3.64.0) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.63.2](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.2) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.63.1](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.1) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg-plugin # [3.63.0](https://github.com/eggjs/tegg/compare/v3.62.3...v3.63.0) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.62.3](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.3) (2025-11-05) ### Bug Fixes * **typings:** fix getEggObjectFromName return type to Promise ([#358](https://github.com/eggjs/tegg/issues/358)) ([919b72b](https://github.com/eggjs/tegg/commit/919b72b48e7966226a5e970c7e297ee6c3d1f081)) ## [3.62.2](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.2) (2025-11-05) ### Bug Fixes * **typings:** fix getEggObjectFromName return type to Promise ([#358](https://github.com/eggjs/tegg/issues/358)) ([919b72b](https://github.com/eggjs/tegg/commit/919b72b48e7966226a5e970c7e297ee6c3d1f081)) ## [3.62.1](https://github.com/eggjs/tegg/compare/v3.62.0...v3.62.1) (2025-11-03) **Note:** Version bump only for package @eggjs/tegg-plugin # [3.62.0](https://github.com/eggjs/tegg/compare/v3.61.0...v3.62.0) (2025-09-24) **Note:** Version bump only for package @eggjs/tegg-plugin # [3.61.0](https://github.com/eggjs/tegg/compare/v3.60.3...v3.61.0) (2025-08-15) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.60.3](https://github.com/eggjs/tegg/compare/v3.60.2...v3.60.3) (2025-08-06) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.60.2](https://github.com/eggjs/tegg/compare/v3.60.1...v3.60.2) (2025-08-06) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.60.1](https://github.com/eggjs/tegg/compare/v3.60.0...v3.60.1) (2025-07-28) **Note:** Version bump only for package @eggjs/tegg-plugin # [3.60.0](https://github.com/eggjs/tegg/compare/v3.59.1...v3.60.0) (2025-07-28) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.59.1](https://github.com/eggjs/tegg/compare/v3.59.0...v3.59.1) (2025-07-16) **Note:** Version bump only for package @eggjs/tegg-plugin # [3.59.0](https://github.com/eggjs/tegg/compare/v3.58.0...v3.59.0) (2025-07-07) **Note:** Version bump only for package @eggjs/tegg-plugin # [3.58.0](https://github.com/eggjs/tegg/compare/v3.57.14...v3.58.0) (2025-07-01) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.57.14](https://github.com/eggjs/tegg/compare/v3.57.13...v3.57.14) (2025-06-18) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.57.13](https://github.com/eggjs/tegg/compare/v3.57.12...v3.57.13) (2025-06-18) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.57.12](https://github.com/eggjs/tegg/compare/v3.57.11...v3.57.12) (2025-06-16) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.57.11](https://github.com/eggjs/tegg/compare/v3.57.10...v3.57.11) (2025-06-16) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.57.10](https://github.com/eggjs/tegg/compare/v3.57.9...v3.57.10) (2025-06-12) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.57.9](https://github.com/eggjs/tegg/compare/v3.57.8...v3.57.9) (2025-05-29) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.57.8](https://github.com/eggjs/tegg/compare/v3.57.7...v3.57.8) (2025-05-29) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.57.7](https://github.com/eggjs/tegg/compare/v3.57.6...v3.57.7) (2025-05-28) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.57.6](https://github.com/eggjs/tegg/compare/v3.57.5...v3.57.6) (2025-05-27) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.57.5](https://github.com/eggjs/tegg/compare/v3.57.4...v3.57.5) (2025-05-15) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.57.4](https://github.com/eggjs/tegg/compare/v3.57.3...v3.57.4) (2025-05-14) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.57.3](https://github.com/eggjs/tegg/compare/v3.57.2...v3.57.3) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.57.2](https://github.com/eggjs/tegg/compare/v3.57.1...v3.57.2) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.57.1](https://github.com/eggjs/tegg/compare/v3.57.0...v3.57.1) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-plugin # [3.57.0](https://github.com/eggjs/tegg/compare/v3.56.3...v3.57.0) (2025-05-09) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.56.3](https://github.com/eggjs/tegg/compare/v3.56.2...v3.56.3) (2025-05-07) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.56.2](https://github.com/eggjs/tegg/compare/v3.56.1...v3.56.2) (2025-05-01) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.56.1](https://github.com/eggjs/tegg/compare/v3.56.0...v3.56.1) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-plugin # [3.56.0](https://github.com/eggjs/tegg/compare/v3.55.0...v3.56.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-plugin # [3.55.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.55.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-plugin # [3.54.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.54.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-plugin # [3.53.0](https://github.com/eggjs/tegg/compare/v3.52.1...v3.53.0) (2025-04-21) ### Bug Fixes * stream end ([#302](https://github.com/eggjs/tegg/issues/302)) ([7f1f4b3](https://github.com/eggjs/tegg/commit/7f1f4b396294af5609c9454f6882d213dc237512)) ### Features * add timeout metadata for http controller ([#301](https://github.com/eggjs/tegg/issues/301)) ([68980c2](https://github.com/eggjs/tegg/commit/68980c23de81dbc9bd86c1d8df7b3952f52aa5ce)) ## [3.52.1](https://github.com/eggjs/tegg/compare/v3.52.0...v3.52.1) (2025-03-14) ### Bug Fixes * add qualifier check ([#295](https://github.com/eggjs/tegg/issues/295)) ([6744088](https://github.com/eggjs/tegg/commit/674408810d77fe0f4b95b25790bcb3975e543e26)) # [3.52.0](https://github.com/eggjs/tegg/compare/v3.51.2...v3.52.0) (2024-12-30) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.51.2](https://github.com/eggjs/tegg/compare/v3.51.1...v3.51.2) (2024-12-09) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.51.1](https://github.com/eggjs/tegg/compare/v3.51.0...v3.51.1) (2024-11-06) **Note:** Version bump only for package @eggjs/tegg-plugin # [3.51.0](https://github.com/eggjs/tegg/compare/v3.50.1...v3.51.0) (2024-10-30) ### Features * add default inject init type qualifier ([#255](https://github.com/eggjs/tegg/issues/255)) ([538ae80](https://github.com/eggjs/tegg/commit/538ae8033ff102ac0b1d141c6495058a800e46f1)) * support optional inject ([#254](https://github.com/eggjs/tegg/issues/254)) ([260470b](https://github.com/eggjs/tegg/commit/260470b766d5fdb323c1bd72cc6260a90468a161)) ## [3.50.1](https://github.com/eggjs/tegg/compare/v3.50.0...v3.50.1) (2024-10-23) **Note:** Version bump only for package @eggjs/tegg-plugin # [3.50.0](https://github.com/eggjs/tegg/compare/v3.49.0...v3.50.0) (2024-10-22) **Note:** Version bump only for package @eggjs/tegg-plugin # [3.49.0](https://github.com/eggjs/tegg/compare/v3.48.1...v3.49.0) (2024-10-21) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.48.1](https://github.com/eggjs/tegg/compare/v3.48.0...v3.48.1) (2024-10-14) ### Bug Fixes * fix merge qualifier ([#250](https://github.com/eggjs/tegg/issues/250)) ([d5a8a93](https://github.com/eggjs/tegg/commit/d5a8a93abad570f69881f9fa42f39d7b5cd436be)) # [3.48.0](https://github.com/eggjs/tegg/compare/v3.47.2...v3.48.0) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.47.2](https://github.com/eggjs/tegg/compare/v3.47.1...v3.47.2) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.47.1](https://github.com/eggjs/tegg/compare/v3.47.0...v3.47.1) (2024-10-10) ### Bug Fixes * fix aop in constructor inject type ([#247](https://github.com/eggjs/tegg/issues/247)) ([d169bb2](https://github.com/eggjs/tegg/commit/d169bb2fbbc86335315619866b4134a25296f552)) # [3.47.0](https://github.com/eggjs/tegg/compare/v3.46.4...v3.47.0) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.46.4](https://github.com/eggjs/tegg/compare/v3.46.3...v3.46.4) (2024-10-09) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.46.3](https://github.com/eggjs/tegg/compare/v3.46.2...v3.46.3) (2024-10-08) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.46.2](https://github.com/eggjs/tegg/compare/v3.46.1...v3.46.2) (2024-10-07) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.46.1](https://github.com/eggjs/tegg/compare/v3.46.0...v3.46.1) (2024-09-30) ### Bug Fixes * fix miss MultiInstance proper qualifiers ([#241](https://github.com/eggjs/tegg/issues/241)) ([15666d3](https://github.com/eggjs/tegg/commit/15666d36c18b99eccc4f1a11d8e7702503694ee1)) # [3.46.0](https://github.com/eggjs/tegg/compare/v3.45.0...v3.46.0) (2024-09-29) ### Features * impl MultiInstance inject MultiInstance ([#240](https://github.com/eggjs/tegg/issues/240)) ([08e3b0c](https://github.com/eggjs/tegg/commit/08e3b0cc02f3d2dbba767298a6aec6c00147f9ed)) # [3.45.0](https://github.com/eggjs/tegg/compare/v3.44.1...v3.45.0) (2024-09-29) ### Features * impl MultiInstanceInfo decorator ([#239](https://github.com/eggjs/tegg/issues/239)) ([70d4d95](https://github.com/eggjs/tegg/commit/70d4d95bca4a0c3e11d0d7cc4f292b1315e49e81)) ## [3.44.1](https://github.com/eggjs/tegg/compare/v3.44.0...v3.44.1) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-plugin # [3.44.0](https://github.com/eggjs/tegg/compare/v3.43.2...v3.44.0) (2024-09-29) ### Features * support inject in constructor ([#237](https://github.com/eggjs/tegg/issues/237)) ([e68b1ed](https://github.com/eggjs/tegg/commit/e68b1ed6a90432f1cb35a6f562914b7b04cb5114)) ## [3.43.2](https://github.com/eggjs/tegg/compare/v3.43.1...v3.43.2) (2024-09-14) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.43.1](https://github.com/eggjs/tegg/compare/v3.43.0...v3.43.1) (2024-09-14) **Note:** Version bump only for package @eggjs/tegg-plugin # [3.43.0](https://github.com/eggjs/tegg/compare/v3.42.0...v3.43.0) (2024-09-13) **Note:** Version bump only for package @eggjs/tegg-plugin # [3.42.0](https://github.com/eggjs/tegg/compare/v3.41.0...v3.42.0) (2024-09-10) **Note:** Version bump only for package @eggjs/tegg-plugin # [3.41.0](https://github.com/eggjs/tegg/compare/v3.40.1...v3.41.0) (2024-08-26) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.40.1](https://github.com/eggjs/tegg/compare/v3.40.0...v3.40.1) (2024-08-23) **Note:** Version bump only for package @eggjs/tegg-plugin # [3.40.0](https://github.com/eggjs/tegg/compare/v3.39.5...v3.40.0) (2024-08-22) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.39.5](https://github.com/eggjs/tegg/compare/v3.39.4...v3.39.5) (2024-08-09) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.39.4](https://github.com/eggjs/tegg/compare/v3.39.3...v3.39.4) (2024-07-09) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.39.3](https://github.com/eggjs/tegg/compare/v3.39.2...v3.39.3) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.39.2](https://github.com/eggjs/tegg/compare/v3.39.1...v3.39.2) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.39.1](https://github.com/eggjs/tegg/compare/v3.39.0...v3.39.1) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-plugin # [3.39.0](https://github.com/eggjs/tegg/compare/v3.38.0...v3.39.0) (2024-04-19) **Note:** Version bump only for package @eggjs/tegg-plugin # [3.38.0](https://github.com/eggjs/tegg/compare/v3.37.3...v3.38.0) (2024-04-18) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.37.3](https://github.com/eggjs/tegg/compare/v3.37.2...v3.37.3) (2024-04-17) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.37.2](https://github.com/eggjs/tegg/compare/v3.37.1...v3.37.2) (2024-04-17) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.37.1](https://github.com/eggjs/tegg/compare/v3.37.0...v3.37.1) (2024-04-16) **Note:** Version bump only for package @eggjs/tegg-plugin # [3.37.0](https://github.com/eggjs/tegg/compare/v3.36.3...v3.37.0) (2024-04-16) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.36.3](https://github.com/eggjs/tegg/compare/v3.36.2...v3.36.3) (2024-04-10) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.36.2](https://github.com/eggjs/tegg/compare/v3.36.1...v3.36.2) (2024-04-08) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.36.1](https://github.com/eggjs/tegg/compare/v3.36.0...v3.36.1) (2024-04-07) **Note:** Version bump only for package @eggjs/tegg-plugin # [3.36.0](https://github.com/eggjs/tegg/compare/v3.35.1...v3.36.0) (2024-04-02) ### Features * impl ajv + typebox Validator ([#201](https://github.com/eggjs/tegg/issues/201)) ([9fd585d](https://github.com/eggjs/tegg/commit/9fd585de9b613466c96b73494a08a494db34ea57)) ## [3.35.1](https://github.com/eggjs/tegg/compare/v3.35.0...v3.35.1) (2024-03-26) **Note:** Version bump only for package @eggjs/tegg-plugin # [3.35.0](https://github.com/eggjs/tegg/compare/v3.34.0...v3.35.0) (2024-03-26) **Note:** Version bump only for package @eggjs/tegg-plugin # [3.34.0](https://github.com/eggjs/tegg/compare/v3.33.1...v3.34.0) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.33.1](https://github.com/eggjs/tegg/compare/v3.33.0...v3.33.1) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-plugin # [3.33.0](https://github.com/eggjs/tegg/compare/v3.32.0...v3.33.0) (2024-03-22) ### Features * impl dal ([#192](https://github.com/eggjs/tegg/issues/192)) ([1c7d145](https://github.com/eggjs/tegg/commit/1c7d1454bc8c600cd58c3ec7b9cda4e8a98c7287)) # [3.32.0](https://github.com/eggjs/tegg/compare/v3.31.0...v3.32.0) (2024-02-19) ### Features * set plugin module optional false if be enabled as a plugin ([#190](https://github.com/eggjs/tegg/issues/190)) ([57a1adc](https://github.com/eggjs/tegg/commit/57a1adcd4f0305cad690be229c59e103e7acf5cd)) # [3.31.0](https://github.com/eggjs/tegg/compare/v3.30.1...v3.31.0) (2024-01-31) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.30.1](https://github.com/eggjs/tegg/compare/v3.30.0...v3.30.1) (2024-01-25) **Note:** Version bump only for package @eggjs/tegg-plugin # [3.30.0](https://github.com/eggjs/tegg/compare/v3.29.0...v3.30.0) (2024-01-17) ### Features * scan framework dependencies as optional module ([#184](https://github.com/eggjs/tegg/issues/184)) ([a4908c6](https://github.com/eggjs/tegg/commit/a4908c6c640000c7068def57d32052cca15adf47)) # [3.29.0](https://github.com/eggjs/tegg/compare/v3.28.2...v3.29.0) (2023-12-26) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.28.2](https://github.com/eggjs/tegg/compare/v3.28.1...v3.28.2) (2023-12-12) ### Bug Fixes * clear all hooks after app close ([#175](https://github.com/eggjs/tegg/issues/175)) ([6fe12b9](https://github.com/eggjs/tegg/commit/6fe12b9bd2cc1c250d02ac851a6e2e172ab12514)) ## [3.28.1](https://github.com/eggjs/tegg/compare/v3.28.0...v3.28.1) (2023-12-11) **Note:** Version bump only for package @eggjs/tegg-plugin # [3.28.0](https://github.com/eggjs/tegg/compare/v3.27.0...v3.28.0) (2023-12-10) ### Features * inject moduleConfig read from tegg-config app.moduleConfigs config ([#169](https://github.com/eggjs/tegg/issues/169)) ([2d984ef](https://github.com/eggjs/tegg/commit/2d984efad0806b333aa2ea30daac2df859967750)) # [3.27.0](https://github.com/eggjs/tegg/compare/v3.26.0...v3.27.0) (2023-11-23) ### Features * impl getObjectFromName ([#167](https://github.com/eggjs/tegg/issues/167)) ([95843c7](https://github.com/eggjs/tegg/commit/95843c74c201ecdfeb7023e16e3f8348a1cb32ea)) # [3.26.0](https://github.com/eggjs/tegg/compare/v3.25.2...v3.26.0) (2023-11-17) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.25.2](https://github.com/eggjs/tegg/compare/v3.25.1...v3.25.2) (2023-11-06) ### Bug Fixes * verify isEggMultiInstancePrototype before proto exists ([#164](https://github.com/eggjs/tegg/issues/164)) ([db9a621](https://github.com/eggjs/tegg/commit/db9a62159886829de36b831f49f296fe05f0b228)) ## [3.25.1](https://github.com/eggjs/tegg/compare/v3.25.0...v3.25.1) (2023-11-03) **Note:** Version bump only for package @eggjs/tegg-plugin # [3.25.0](https://github.com/eggjs/tegg/compare/v3.24.0...v3.25.0) (2023-11-03) ### Features * getObject support MultiInstanceProto ([#161](https://github.com/eggjs/tegg/issues/161)) ([1a24e48](https://github.com/eggjs/tegg/commit/1a24e48cd9a38e906966a21c5f0d1304c4b40d7c)) * tegg plugin support ModuleConfig ([#162](https://github.com/eggjs/tegg/issues/162)) ([58bd9fa](https://github.com/eggjs/tegg/commit/58bd9fafdd0d56aabdde5f7c33f17c45568bada8)) # [3.24.0](https://github.com/eggjs/tegg/compare/v3.23.0...v3.24.0) (2023-10-26) **Note:** Version bump only for package @eggjs/tegg-plugin # [3.23.0](https://github.com/eggjs/tegg/compare/v3.22.0...v3.23.0) (2023-09-20) **Note:** Version bump only for package @eggjs/tegg-plugin # [3.20.0](https://github.com/eggjs/tegg/compare/v3.19.0...v3.20.0) (2023-09-07) **Note:** Version bump only for package @eggjs/tegg-plugin # [3.19.0](https://github.com/eggjs/tegg/compare/v3.18.1...v3.19.0) (2023-08-31) ### Features * add LoadUnitMultiInstanceProtoHook for tegg plugin ([#150](https://github.com/eggjs/tegg/issues/150)) ([b938580](https://github.com/eggjs/tegg/commit/b9385803383dceedfc26bd990e5d752cd33f0f67)) ## [3.18.1](https://github.com/eggjs/tegg/compare/v3.18.0...v3.18.1) (2023-08-29) ### Bug Fixes * fix use MultiInstanceProto from other modules ([#147](https://github.com/eggjs/tegg/issues/147)) ([b71af60](https://github.com/eggjs/tegg/commit/b71af60ce6d1da0d778f5e712633b8c15052bd70)) # [3.18.0](https://github.com/eggjs/tegg/compare/v3.17.0...v3.18.0) (2023-08-29) **Note:** Version bump only for package @eggjs/tegg-plugin # [3.17.0](https://github.com/eggjs/tegg/compare/v3.16.0...v3.17.0) (2023-08-24) ### Features * impl MultiInstanceProto ([#145](https://github.com/eggjs/tegg/issues/145)) ([12fd5cf](https://github.com/eggjs/tegg/commit/12fd5cff4004578bcc737dcdf4f7e9d1159f5633)) # [3.16.0](https://github.com/eggjs/tegg/compare/v3.15.0...v3.16.0) (2023-08-24) ### Features * implement RuntimeConfig ([#144](https://github.com/eggjs/tegg/issues/144)) ([0862655](https://github.com/eggjs/tegg/commit/0862655846f6765349d406ee697c036cec2a37bd)) ## [3.14.3](https://github.com/eggjs/tegg/compare/v3.14.2...v3.14.3) (2023-08-14) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.14.2](https://github.com/eggjs/tegg/compare/v3.14.1...v3.14.2) (2023-08-14) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.14.1](https://github.com/eggjs/tegg/compare/v3.14.0...v3.14.1) (2023-08-11) ### Bug Fixes * ensure ContextInitiator be called after ctx ready ([#138](https://github.com/eggjs/tegg/issues/138)) ([79e16da](https://github.com/eggjs/tegg/commit/79e16dae913b6114ac8d13bde8de60164d57dab3)) # [3.14.0](https://github.com/eggjs/tegg/compare/v3.13.0...v3.14.0) (2023-07-31) **Note:** Version bump only for package @eggjs/tegg-plugin # [3.13.0](https://github.com/eggjs/tegg/compare/v3.12.0...v3.13.0) (2023-07-25) **Note:** Version bump only for package @eggjs/tegg-plugin # [3.12.0](https://github.com/eggjs/tegg/compare/v3.11.1...v3.12.0) (2023-07-13) ### Bug Fixes * after call mockModuleContext, hasMockModuleContext should be true ([#134](https://github.com/eggjs/tegg/issues/134)) ([88b3caa](https://github.com/eggjs/tegg/commit/88b3caadd24f08221b8098c42733e26376338cae)) ## [3.11.1](https://github.com/eggjs/tegg/compare/v3.11.0...v3.11.1) (2023-06-29) **Note:** Version bump only for package @eggjs/tegg-plugin # [3.11.0](https://github.com/eggjs/tegg/compare/v3.10.0...v3.11.0) (2023-06-29) **Note:** Version bump only for package @eggjs/tegg-plugin # [3.10.0](https://github.com/eggjs/tegg/compare/v3.9.0...v3.10.0) (2023-06-28) **Note:** Version bump only for package @eggjs/tegg-plugin # [3.9.0](https://github.com/eggjs/tegg/compare/v3.8.0...v3.9.0) (2023-06-20) **Note:** Version bump only for package @eggjs/tegg-plugin # [3.8.0](https://github.com/eggjs/tegg/compare/v3.7.0...v3.8.0) (2023-05-30) ### Features * impl EggObjectLifecycle hook in decorator ([#119](https://github.com/eggjs/tegg/issues/119)) ([cced8a2](https://github.com/eggjs/tegg/commit/cced8a2e009c33d5040fa21d00409fddef471b0e)) # [3.7.0](https://github.com/eggjs/tegg/compare/v3.6.3...v3.7.0) (2023-04-03) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.6.3](https://github.com/eggjs/tegg/compare/v3.6.2...v3.6.3) (2023-03-02) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.6.2](https://github.com/eggjs/tegg/compare/v3.6.1...v3.6.2) (2023-02-16) ### Bug Fixes * should not cache ctx object ([#103](https://github.com/eggjs/tegg/issues/103)) ([be54083](https://github.com/eggjs/tegg/commit/be5408375261d98b60fbc97e18de9232581a9547)) ## [3.6.1](https://github.com/eggjs/tegg/compare/v3.6.0...v3.6.1) (2023-02-14) ### Bug Fixes * get app/ctx properties when load unit beforeCreate ([#102](https://github.com/eggjs/tegg/issues/102)) ([76ef679](https://github.com/eggjs/tegg/commit/76ef679d745deb235db9dcc3fa34984b511bd5c6)) # [3.6.0](https://github.com/eggjs/tegg/compare/v3.5.2...v3.6.0) (2023-02-13) ### Bug Fixes * egg qualifier should register after all file loaded ([#100](https://github.com/eggjs/tegg/issues/100)) ([5033b51](https://github.com/eggjs/tegg/commit/5033b51796b8a3329bd79884a8d8f18226193a1b)) ### Features * add backgroundTask.timeout config ([#101](https://github.com/eggjs/tegg/issues/101)) ([0b1eee0](https://github.com/eggjs/tegg/commit/0b1eee00d6feb9c6d4509023dffe85c0ada749c2)) ## [3.5.2](https://github.com/eggjs/tegg/compare/v3.5.1...v3.5.2) (2023-02-10) ### Bug Fixes * not create ctx logger proto ([#97](https://github.com/eggjs/tegg/issues/97)) ([100886b](https://github.com/eggjs/tegg/commit/100886ba90bdc7cccd07fa2f390defb5b0c53e22)) ## [3.5.1](https://github.com/eggjs/tegg/compare/v3.5.0...v3.5.1) (2023-02-10) ### Bug Fixes * remove useless init singleton proto ([#96](https://github.com/eggjs/tegg/issues/96)) ([097ac58](https://github.com/eggjs/tegg/commit/097ac58c675d43088c8785a12cf224b5d6adea17)) # [3.5.0](https://github.com/eggjs/tegg/compare/v3.4.1...v3.5.0) (2023-02-10) ### Features * append call stack for runInBackground ([#91](https://github.com/eggjs/tegg/issues/91)) ([ec7bc2c](https://github.com/eggjs/tegg/commit/ec7bc2c60ffb49b4a51feec82e391b1f6a88549a)) * remove context egg object factory ([#93](https://github.com/eggjs/tegg/issues/93)) ([e14bdb2](https://github.com/eggjs/tegg/commit/e14bdb257eaebc0b0a4c37c6073a5c3237718718)) * use SingletonProto for egg ctx object ([#92](https://github.com/eggjs/tegg/issues/92)) ([3385d57](https://github.com/eggjs/tegg/commit/3385d571b076d3148978f252188f29d9cf2c6781)) ## [3.4.1](https://github.com/eggjs/tegg/compare/v3.4.0...v3.4.1) (2023-02-02) ### Bug Fixes * BackgroundTaskHelper should support recursively call ([#90](https://github.com/eggjs/tegg/issues/90)) ([368ac03](https://github.com/eggjs/tegg/commit/368ac0343d0d4e96b3768e7fd169b721551d0e4b)) # [3.4.0](https://github.com/eggjs/tegg/compare/v3.3.4...v3.4.0) (2023-02-01) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.3.4](https://github.com/eggjs/tegg/compare/v3.3.3...v3.3.4) (2023-01-29) ### Bug Fixes * should not notify backgroundTaskHelper if teggContext not exists ([#88](https://github.com/eggjs/tegg/issues/88)) ([4cab68b](https://github.com/eggjs/tegg/commit/4cab68bfc08a3786bde9a67cd8687f152829d9a0)) ## [3.3.3](https://github.com/eggjs/tegg/compare/v3.3.2...v3.3.3) (2023-01-29) ### Bug Fixes * wait egg background task done before destroy tegg ctx ([#87](https://github.com/eggjs/tegg/issues/87)) ([deea4d8](https://github.com/eggjs/tegg/commit/deea4d8d75c43347c6ee09e0e97f5fa80dd68dd9)) ## [3.3.2](https://github.com/eggjs/tegg/compare/v3.3.1...v3.3.2) (2023-01-29) ### Bug Fixes * beginModuleScope should be reentrant ([#86](https://github.com/eggjs/tegg/issues/86)) ([648aeaf](https://github.com/eggjs/tegg/commit/648aeaf1f20ff5bc217bf6f16fac9d9181eb8447)) ## [3.3.1](https://github.com/eggjs/tegg/compare/v3.3.0...v3.3.1) (2023-01-28) **Note:** Version bump only for package @eggjs/tegg-plugin # [3.3.0](https://github.com/eggjs/tegg/compare/v3.2.4...v3.3.0) (2023-01-28) ### Features * add app.eggContextHandler ([#84](https://github.com/eggjs/tegg/issues/84)) ([2772624](https://github.com/eggjs/tegg/commit/277262418143956b2e75bd1db5f2e7dd9b75eb8b)) ## [3.2.3](https://github.com/eggjs/tegg/compare/v3.2.2...v3.2.3) (2023-01-16) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.2.2](https://github.com/eggjs/tegg/compare/v3.2.1...v3.2.2) (2023-01-06) **Note:** Version bump only for package @eggjs/tegg-plugin ## [3.2.1](https://github.com/eggjs/tegg/compare/v3.2.0...v3.2.1) (2022-12-28) **Note:** Version bump only for package @eggjs/tegg-plugin # [3.2.0](https://github.com/eggjs/tegg/compare/v3.1.0...v3.2.0) (2022-12-28) ### Features * impl mockModuleContextScope ([#73](https://github.com/eggjs/tegg/issues/73)) ([041881c](https://github.com/eggjs/tegg/commit/041881ca317ad81366172a35ac56b7b2dc0a0488)) # [3.1.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.1.0) (2022-12-27) ### Bug Fixes * inject context proto to singleton proto ([#72](https://github.com/eggjs/tegg/issues/72)) ([fcc0b2b](https://github.com/eggjs/tegg/commit/fcc0b2b48fc9bce580c1f2bcfcc38039ae909951)) * optimize backgroud output ([#47](https://github.com/eggjs/tegg/issues/47)) ([6d978c5](https://github.com/eggjs/tegg/commit/6d978c5d7c339c78a90b00d2c2622f0be85ab3ce)) ### Features * **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) # [3.0.0](https://github.com/eggjs/tegg/compare/v3.0.0-alpha.0...v3.0.0) (2022-12-26) **Note:** Version bump only for package @eggjs/tegg-plugin # [3.0.0-alpha.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.0.0-alpha.0) (2022-12-22) ### Bug Fixes * optimize backgroud output ([#47](https://github.com/eggjs/tegg/issues/47)) ([6d978c5](https://github.com/eggjs/tegg/commit/6d978c5d7c339c78a90b00d2c2622f0be85ab3ce)) ### Features * **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) ## [1.3.8](https://github.com/eggjs/tegg/compare/@eggjs/tegg-plugin@1.3.7...@eggjs/tegg-plugin@1.3.8) (2022-09-05) **Note:** Version bump only for package @eggjs/tegg-plugin ## [1.3.7](https://github.com/eggjs/tegg/compare/@eggjs/tegg-plugin@1.3.6...@eggjs/tegg-plugin@1.3.7) (2022-09-04) **Note:** Version bump only for package @eggjs/tegg-plugin ## [1.3.6](https://github.com/eggjs/tegg/compare/@eggjs/tegg-plugin@1.3.5...@eggjs/tegg-plugin@1.3.6) (2022-08-16) **Note:** Version bump only for package @eggjs/tegg-plugin ## [1.3.4](https://github.com/eggjs/tegg/compare/@eggjs/tegg-plugin@1.3.3...@eggjs/tegg-plugin@1.3.4) (2022-07-28) **Note:** Version bump only for package @eggjs/tegg-plugin ## [1.3.3](https://github.com/eggjs/tegg/compare/@eggjs/tegg-plugin@1.3.2...@eggjs/tegg-plugin@1.3.3) (2022-07-20) **Note:** Version bump only for package @eggjs/tegg-plugin ## [1.3.2](https://github.com/eggjs/tegg/compare/@eggjs/tegg-plugin@1.3.1...@eggjs/tegg-plugin@1.3.2) (2022-07-20) **Note:** Version bump only for package @eggjs/tegg-plugin # [1.3.0](https://github.com/eggjs/tegg/compare/v1.2.0...v1.3.0) (2022-07-01) **Note:** Version bump only for package @eggjs/tegg-plugin # [0.2.0](https://github.com/eggjs/tegg/compare/v0.1.19...v0.2.0) (2022-01-20) ### Features * impl aop ([c53df00](https://github.com/eggjs/tegg/commit/c53df001d1455a0a105689694775d880541d9d2f)) ================================================ FILE: plugin/tegg/README.md ================================================ # `@eggjs/tegg-plugin` ## Usage Please read [../../README.md](../..) ================================================ FILE: plugin/tegg/app/extend/application.ts ================================================ import { EggPrototypeCreatorFactory, EggPrototypeFactory, EggPrototypeLifecycleUtil, LoadUnitFactory, LoadUnitLifecycleUtil, } from '@eggjs/tegg-metadata'; import { AbstractEggContext, EggContainerFactory, EggObjectFactory, LoadUnitInstanceFactory, EggContextLifecycleUtil, EggObjectLifecycleUtil, LoadUnitInstanceLifecycleUtil, } from '@eggjs/tegg-runtime'; import { LoaderFactory } from '@eggjs/tegg-loader'; import { EggProtoImplClass, IdenticalUtil, RuntimeConfig, QualifierInfo } from '@eggjs/tegg'; import type { Application } from 'egg'; export default { // @eggjs/tegg-metadata should not depend by other egg plugins. // May make multi singleton instances. // So tegg-compatible should delegate the metadata factories // TODO delegate all the singleton get eggPrototypeCreatorFactory() { return EggPrototypeCreatorFactory; }, get eggPrototypeFactory() { return EggPrototypeFactory.instance; }, get loadUnitLifecycleUtil() { return LoadUnitLifecycleUtil; }, get loadUnitFactory() { return LoadUnitFactory; }, get eggObjectFactory() { return EggObjectFactory; }, get loadUnitInstanceFactory() { return LoadUnitInstanceFactory; }, get loadUnitInstanceLifecycleUtil() { return LoadUnitInstanceLifecycleUtil; }, get eggContainerFactory() { return EggContainerFactory; }, get loaderFactory() { return LoaderFactory; }, get eggPrototypeLifecycleUtil() { return EggPrototypeLifecycleUtil; }, get eggContextLifecycleUtil() { return EggContextLifecycleUtil; }, get eggObjectLifecycleUtil() { return EggObjectLifecycleUtil; }, get abstractEggContext() { return AbstractEggContext; }, get identicalUtil() { return IdenticalUtil; }, get runtimeConfig(): RuntimeConfig { const app = this as unknown as Application; const config = app.config; return { baseDir: config.baseDir, env: config.env, name: config.name, }; }, async getEggObject(clazz: EggProtoImplClass, name?: string, qualifiers?: QualifierInfo | QualifierInfo[]) { if (qualifiers) { qualifiers = Array.isArray(qualifiers) ? qualifiers : [ qualifiers ]; } const eggObject = await EggContainerFactory.getOrCreateEggObjectFromClazz(clazz, name, qualifiers); return eggObject.obj; }, async getEggObjectFromName(name: string, qualifiers?: QualifierInfo | QualifierInfo[]) { if (qualifiers) { qualifiers = Array.isArray(qualifiers) ? qualifiers : [ qualifiers ]; } const eggObject = await EggContainerFactory.getOrCreateEggObjectFromName(name, qualifiers); return eggObject.obj; }, }; ================================================ FILE: plugin/tegg/app/extend/application.unittest.ts ================================================ import { MockApplication } from 'egg-mock'; import { Context } from 'egg'; import { EggContextImpl } from '../../lib/EggContextImpl'; import { EggContext, EggContextLifecycleContext } from '@eggjs/tegg-runtime'; const TEGG_LIFECYCLE_CACHE: Map = new Map(); let hasMockModuleContext = false; export default { async mockModuleContext(this: MockApplication, data?: any): Promise { this.deprecate('app.mockModuleContext is deprecated, use mockModuleContextScope.'); if (hasMockModuleContext) { throw new Error('should not call mockModuleContext twice.'); } const ctx = this.mockContext(data); const teggCtx = new EggContextImpl(ctx); const lifecycle = {}; TEGG_LIFECYCLE_CACHE.set(teggCtx, lifecycle); if (teggCtx.init) { await teggCtx.init(lifecycle); } hasMockModuleContext = true; return ctx; }, async destroyModuleContext(ctx: Context) { hasMockModuleContext = false; const teggCtx = ctx.teggContext; if (!teggCtx) { return; } const lifecycle = TEGG_LIFECYCLE_CACHE.get(teggCtx); if (teggCtx.destroy && lifecycle) { await teggCtx.destroy(lifecycle); } }, async mockModuleContextScope(this: MockApplication, fn: (ctx: Context) => Promise, data?: any): Promise { if (hasMockModuleContext) { throw new Error('mockModuleContextScope can not use with mockModuleContext, should use mockModuleContextScope only.'); } return this.mockContextScope(async ctx => { const teggCtx = new EggContextImpl(ctx); const lifecycle = {}; if (teggCtx.init) { await teggCtx.init(lifecycle); } try { return await fn(ctx); } finally { await teggCtx.destroy(lifecycle); } }, data); }, }; ================================================ FILE: plugin/tegg/app/extend/context.ts ================================================ import { Context } from 'egg'; import { EggContext } from '@eggjs/tegg-runtime'; import { TEGG_CONTEXT } from '@eggjs/egg-module-common'; import ctxLifecycleMiddleware from '../../lib/ctx_lifecycle_middleware'; import { EggProtoImplClass, PrototypeUtil, QualifierInfo } from '@eggjs/tegg'; import { EggPrototype } from '@eggjs/tegg-metadata'; export interface TEggPluginContext extends Context { [TEGG_CONTEXT]: EggContext; } export default { async beginModuleScope(this: TEggPluginContext, func: () => Promise) { await ctxLifecycleMiddleware(this, func); }, get teggContext(): EggContext { if (!this[TEGG_CONTEXT]) { throw new Error('tegg context have not ready, should call after teggCtxLifecycleMiddleware'); } return this[TEGG_CONTEXT]; }, async getEggObject(this: Context, clazz: EggProtoImplClass, name?: string) { const protoObj = PrototypeUtil.getClazzProto(clazz); if (!protoObj) { throw new Error(`can not get proto for clazz ${clazz.name}`); } const proto = protoObj as EggPrototype; const eggObject = await this.app.eggContainerFactory.getOrCreateEggObject(proto, name ?? proto.name); return eggObject.obj; }, async getEggObjectFromName(this: Context, name: string, qualifiers?: QualifierInfo | QualifierInfo[]) { if (qualifiers) { qualifiers = Array.isArray(qualifiers) ? qualifiers : [ qualifiers ]; } const eggObject = await this.app.eggContainerFactory.getOrCreateEggObjectFromName(name, qualifiers); return eggObject.obj; }, }; ================================================ FILE: plugin/tegg/app/middleware/tegg_ctx_lifecycle_middleware.ts ================================================ import ctxLifecycleMiddleware from '../../lib/ctx_lifecycle_middleware'; export default function() { return ctxLifecycleMiddleware; } ================================================ FILE: plugin/tegg/app.ts ================================================ import './lib/AppLoadUnit'; import './lib/AppLoadUnitInstance'; import './lib/EggCompatibleObject'; import { Application } from 'egg'; import { EggContextCompatibleHook } from './lib/EggContextCompatibleHook'; import { CompatibleUtil } from './lib/CompatibleUtil'; import { ModuleHandler } from './lib/ModuleHandler'; import { EggContextHandler } from './lib/EggContextHandler'; import { hijackRunInBackground } from './lib/run_in_background'; import { EggQualifierProtoHook } from './lib/EggQualifierProtoHook'; import { LoadUnitMultiInstanceProtoHook } from '@eggjs/tegg-metadata'; import { ConfigSourceLoadUnitHook } from './lib/ConfigSourceLoadUnitHook'; export default class App { private readonly app: Application; private compatibleHook?: EggContextCompatibleHook; private eggContextHandler: EggContextHandler; private eggQualifierProtoHook: EggQualifierProtoHook; private loadUnitMultiInstanceProtoHook: LoadUnitMultiInstanceProtoHook; private configSourceEggPrototypeHook: ConfigSourceLoadUnitHook; constructor(app: Application) { this.app = app; } configWillLoad() { this.app.config.coreMiddleware.push('teggCtxLifecycleMiddleware'); } configDidLoad() { this.eggContextHandler = new EggContextHandler(this.app); this.app.eggContextHandler = this.eggContextHandler; this.eggContextHandler.register(); this.app.moduleHandler = new ModuleHandler(this.app); } async didLoad() { hijackRunInBackground(this.app); this.loadUnitMultiInstanceProtoHook = new LoadUnitMultiInstanceProtoHook(); this.app.loadUnitLifecycleUtil.registerLifecycle(this.loadUnitMultiInstanceProtoHook); // wait all file loaded, so app/ctx has all properties this.eggQualifierProtoHook = new EggQualifierProtoHook(this.app); this.app.loadUnitLifecycleUtil.registerLifecycle(this.eggQualifierProtoHook); this.configSourceEggPrototypeHook = new ConfigSourceLoadUnitHook(); this.app.loadUnitLifecycleUtil.registerLifecycle(this.configSourceEggPrototypeHook); // start load tegg objects await this.app.moduleHandler.init(); this.compatibleHook = new EggContextCompatibleHook(this.app.moduleHandler); this.app.eggContextLifecycleUtil.registerLifecycle(this.compatibleHook); } async beforeClose() { CompatibleUtil.clean(); await this.app.moduleHandler.destroy(); if (this.compatibleHook) { this.app.eggContextLifecycleUtil.deleteLifecycle(this.compatibleHook); } if (this.eggQualifierProtoHook) { this.app.loadUnitLifecycleUtil.deleteLifecycle(this.eggQualifierProtoHook); } if (this.configSourceEggPrototypeHook) { this.app.loadUnitLifecycleUtil.deleteLifecycle(this.configSourceEggPrototypeHook); } if (this.loadUnitMultiInstanceProtoHook) { this.app.loadUnitLifecycleUtil.deleteLifecycle(this.loadUnitMultiInstanceProtoHook); } LoadUnitMultiInstanceProtoHook.clear(); } } ================================================ FILE: plugin/tegg/lib/AppLoadUnit.ts ================================================ import { EggLoadUnitType, EggLoadUnitTypeLike, EggPrototype, EggPrototypeFactory, Loader, LoadUnit, LoadUnitFactory, LoadUnitLifecycleContext, EggPrototypeCreatorFactory, } from '@eggjs/tegg-metadata'; import { Id, IdenticalUtil, EggPrototypeName, QualifierInfo, PrototypeUtil, InitTypeQualifierAttribute, LoadUnitNameQualifierAttribute, QualifierUtil, } from '@eggjs/tegg'; import { MapUtil } from '@eggjs/tegg-common-util'; export class AppLoadUnit implements LoadUnit { private readonly loader: Loader; id: Id; readonly name: string; readonly type: EggLoadUnitTypeLike = EggLoadUnitType.APP; readonly unitPath: string; private protoMap: Map = new Map(); constructor(name: string, unitPath: string, loader: Loader) { this.id = IdenticalUtil.createLoadUnitId(name); this.name = name; this.unitPath = unitPath; this.loader = loader; } async init() { const clazzList = this.loader.load(); for (const clazz of clazzList) { // TODO duplicate code, same in ModuleLoadUnit const defaultQualifier = [{ attribute: InitTypeQualifierAttribute, value: PrototypeUtil.getInitType(clazz, { unitPath: this.unitPath, moduleName: this.name, })!, }, { attribute: LoadUnitNameQualifierAttribute, value: this.name, }]; defaultQualifier.forEach(qualifier => { QualifierUtil.addProtoQualifier(clazz, qualifier.attribute, qualifier.value); }); const protos = await EggPrototypeCreatorFactory.createProto(clazz, this); for (const proto of protos) { EggPrototypeFactory.instance.registerPrototype(proto, this); } } } containPrototype(proto: EggPrototype): boolean { return !!(this.protoMap.get(proto.name)?.find(t => t === proto)); } getEggPrototype(name: string, qualifiers: QualifierInfo[]): EggPrototype[] { const protos = this.protoMap.get(name); return protos?.filter(proto => proto.verifyQualifiers(qualifiers)) || []; } registerEggPrototype(proto: EggPrototype) { const protoList = MapUtil.getOrStore(this.protoMap, proto.name, []); protoList.push(proto); } deletePrototype(proto: EggPrototype) { const protos = this.protoMap.get(proto.name); if (protos) { const index = protos.indexOf(proto); if (index !== -1) { protos.splice(index, 1); } } } async destroy() { for (const namedProtos of this.protoMap.values()) { // Delete prototype will delete item // array iterator is not safe const protos = namedProtos.slice(); for (const proto of protos) { EggPrototypeFactory.instance.deletePrototype(proto, this); } } this.protoMap.clear(); } iterateEggPrototype(): IterableIterator { const protos: EggPrototype[] = Array.from(this.protoMap.values()) .reduce((p, c) => { p = p.concat(c); return p; }, []); return protos.values(); } static createModule(ctx: LoadUnitLifecycleContext): AppLoadUnit { return new AppLoadUnit('tegg-app', ctx.unitPath, ctx.loader); } } LoadUnitFactory.registerLoadUnitCreator(EggLoadUnitType.APP, AppLoadUnit.createModule); ================================================ FILE: plugin/tegg/lib/AppLoadUnitInstance.ts ================================================ import { EggLoadUnitType, EggPrototype, LoadUnit } from '@eggjs/tegg-metadata'; import { EggObjectName, EggPrototypeName, ObjectInitType, Id, IdenticalUtil } from '@eggjs/tegg'; import { MapUtil } from '@eggjs/tegg-common-util'; import { EggObject, EggObjectFactory, LoadUnitInstance, LoadUnitInstanceFactory, LoadUnitInstanceLifecycleContext, LoadUnitInstanceLifecycleUtil, } from '@eggjs/tegg-runtime'; export class AppLoadUnitInstance implements LoadUnitInstance { readonly loadUnit: LoadUnit; readonly id: string; readonly name: string; private protoToCreateMap: Map = new Map(); private eggObjectMap: Map> = new Map(); private eggObjectPromiseMap: Map>> = new Map(); constructor(loadUnit: LoadUnit) { this.loadUnit = loadUnit; this.name = loadUnit.name; const iterator = this.loadUnit.iterateEggPrototype(); for (const proto of iterator) { if (proto.initType === ObjectInitType.SINGLETON) { this.protoToCreateMap.set(proto.name, proto); } } this.id = IdenticalUtil.createLoadUnitInstanceId(loadUnit.id); } iterateProtoToCreate(): IterableIterator<[ EggObjectName, EggPrototype ]> { return this.protoToCreateMap.entries(); } addProtoToCreate(name: string, proto: EggPrototype) { this.protoToCreateMap.set(name, proto); } deleteProtoToCreate(name: string) { this.protoToCreateMap.delete(name); } async init(ctx: LoadUnitInstanceLifecycleContext): Promise { await LoadUnitInstanceLifecycleUtil.objectPreCreate(ctx, this); for (const [ name, proto ] of this.protoToCreateMap) { await this.getOrCreateEggObject(name, proto); } } async destroy(): Promise { const objs: EggObject[] = []; for (const protoObjMap of this.eggObjectMap.values()) { for (const protoObj of protoObjMap.values()) { objs.push(protoObj); } } this.eggObjectMap.clear(); await Promise.all(objs.map(async obj => { await EggObjectFactory.destroyObject(obj); })); } async getOrCreateEggObject(name: EggPrototypeName, proto: EggPrototype): Promise { if (!this.loadUnit.containPrototype(proto)) { throw new Error('load unit not contain proto'); } const protoObjMap = MapUtil.getOrStore(this.eggObjectMap, proto.id, new Map()); if (!protoObjMap.has(name)) { const protoObjPromiseMap = MapUtil.getOrStore(this.eggObjectPromiseMap, proto.id, new Map()); if (!protoObjPromiseMap.has(name)) { const objPromise = EggObjectFactory.createObject(name, proto); protoObjPromiseMap.set(name, objPromise); const obj = await objPromise; protoObjPromiseMap.delete(name); protoObjMap.set(name, obj); } else { await protoObjPromiseMap.get(name); } } return protoObjMap.get(name)!; } getEggObject(name: EggPrototypeName, proto: EggPrototype): EggObject { const protoObjMap = this.eggObjectMap.get(proto.id); if (!protoObjMap || !protoObjMap.has(name)) { throw new Error(`EggObject ${String(proto.name)} not found`); } return protoObjMap.get(name)!; } static createModuleLoadUnitInstance(ctx: LoadUnitInstanceLifecycleContext): LoadUnitInstance { return new AppLoadUnitInstance(ctx.loadUnit); } } LoadUnitInstanceFactory.registerLoadUnitInstanceClass(EggLoadUnitType.APP, AppLoadUnitInstance.createModuleLoadUnitInstance); ================================================ FILE: plugin/tegg/lib/CompatibleUtil.ts ================================================ import { Application, Context } from 'egg'; import { EggPrototype, EggPrototypeFactory } from '@eggjs/tegg-metadata'; import { InitTypeQualifierAttribute, ObjectInitType } from '@eggjs/tegg'; import { EggContainerFactory, LoadUnitInstance } from '@eggjs/tegg-runtime'; import { ProxyUtil } from '@eggjs/tegg-common-util'; export class CompatibleUtil { static singletonProtoCache: Map = new Map(); static requestProtoCache: Map = new Map(); static getSingletonProto(name: PropertyKey): EggPrototype { if (!this.singletonProtoCache.has(name)) { const proto = EggPrototypeFactory.instance.getPrototype(name, undefined, [{ attribute: InitTypeQualifierAttribute, value: ObjectInitType.SINGLETON, }]); this.singletonProtoCache.set(name, proto); } return this.singletonProtoCache.get(name)!; } static getRequestProto(name: PropertyKey): EggPrototype { if (!this.requestProtoCache.has(name)) { const proto = EggPrototypeFactory.instance.getPrototype(name, undefined, [{ attribute: InitTypeQualifierAttribute, value: ObjectInitType.CONTEXT, }]); this.requestProtoCache.set(name, proto); } return this.requestProtoCache.get(name)!; } private static singletonModuleProxyFactory(app: Application, loadUnitInstance: LoadUnitInstance) { let deprecated = false; return function(_, p: PropertyKey) { const proto = CompatibleUtil.getSingletonProto(p); const eggObj = EggContainerFactory.getEggObject(proto); if (!deprecated) { deprecated = true; app.deprecate( `[egg/module] Please use await app.getEggObject(clazzName) instead of app.${loadUnitInstance.name}.${String(p)}`); } return eggObj.obj; }; } static appCompatible(app: Application, loadUnitInstance: LoadUnitInstance) { const moduleLoadUnitProxy = ProxyUtil.safeProxy(loadUnitInstance, CompatibleUtil.singletonModuleProxyFactory(app, loadUnitInstance)); Reflect.defineProperty(app.module, loadUnitInstance.name, { configurable: true, value: moduleLoadUnitProxy, }); } static contextModuleProxyFactory(holder: object, ctx: Context, loadUnitInstance: LoadUnitInstance) { const cacheKey = `_${loadUnitInstance.name}Proxy`; if (!holder[cacheKey]) { let deprecated = false; const getter = function(_, p: PropertyKey) { const proto = CompatibleUtil.getRequestProto(p); const eggObj = EggContainerFactory.getEggObject(proto, p); if (!deprecated) { deprecated = true; ctx.app.deprecate( `[egg/module] Please use await ctx.getEggObject(clazzName) instead of ctx.${loadUnitInstance.name}.${String(p)}`); } return eggObj.obj; }; holder[cacheKey] = ProxyUtil.safeProxy(loadUnitInstance, getter); } return holder[cacheKey]; } static contextModuleCompatible(context: Context, loadUnitInstances: LoadUnitInstance[]) { const loadUnitInstanceMap: Record = loadUnitInstances.reduce((p, c) => { p[c.name] = c; return p; }, {}); Reflect.defineProperty(context, 'module', { configurable: true, enumerable: true, get(this: Context): any { // eslint-disable-next-line @typescript-eslint/no-this-alias const ctx: Context = this; if (!this._moduleProxy) { const ctxModule = Object.create(loadUnitInstanceMap); this._moduleProxy = ProxyUtil.safeProxy(ctxModule, (_, p: PropertyKey) => { const loadUnitInstance = Reflect.get(ctxModule, p); if (!loadUnitInstance) { return; } return CompatibleUtil.contextModuleProxyFactory(ctxModule, ctx, loadUnitInstance); }); } return this._moduleProxy; }, }); } static clean() { this.singletonProtoCache.clear(); this.requestProtoCache.clear(); } } ================================================ FILE: plugin/tegg/lib/ConfigSourceLoadUnitHook.ts ================================================ import { LoadUnit, LoadUnitLifecycleContext } from '@eggjs/tegg-metadata'; import { LifecycleHook, PrototypeUtil, QualifierUtil, ConfigSourceQualifier, ConfigSourceQualifierAttribute, } from '@eggjs/tegg'; /** * Copy from standalone/src/ConfigSourceLoadUnitHook * Hook for inject moduleConfig. * Add default qualifier value is current module name. */ export class ConfigSourceLoadUnitHook implements LifecycleHook { async preCreate(ctx: LoadUnitLifecycleContext, loadUnit: LoadUnit): Promise { const classList = ctx.loader.load(); for (const clazz of classList) { const injectObjects = PrototypeUtil.getInjectObjects(clazz); const moduleConfigObject = injectObjects.find(t => t.objName === 'moduleConfig'); const configSourceQualifier = QualifierUtil.getProperQualifier(clazz, 'moduleConfig', ConfigSourceQualifierAttribute); if (moduleConfigObject && !configSourceQualifier) { ConfigSourceQualifier(loadUnit.name)(clazz.prototype, moduleConfigObject.refName); } } } } ================================================ FILE: plugin/tegg/lib/EggAppLoader.ts ================================================ import { Application } from 'egg'; import { Loader, TeggError } from '@eggjs/tegg-metadata'; import { AccessLevel, EggProtoImplClass, EggQualifierAttribute, EggType, InitTypeQualifierAttribute, LoadUnitNameQualifierAttribute, ObjectInitType, PrototypeUtil, QualifierUtil, } from '@eggjs/tegg'; import { ObjectUtils } from '@eggjs/tegg-common-util'; import { COMPATIBLE_PROTO_IMPLE_TYPE } from './EggCompatibleProtoImpl'; import { BackgroundTaskHelper } from '@eggjs/tegg-background-task'; import { EggObjectFactory } from '@eggjs/tegg-dynamic-inject-runtime'; import { ModuleConfigLoader } from './ModuleConfigLoader'; export const APP_CLAZZ_BLACK_LIST = [ 'eggObjectFactory', 'moduleConfigs', ]; export const CONTEXT_CLAZZ_BLACK_LIST = [ // just use the app.logger, ctx logger is deprecated. 'logger', ]; export const DEFAULT_APP_CLAZZ: string[] = []; export const DEFAULT_CONTEXT_CLAZZ = [ 'user', ]; export class EggAppLoader implements Loader { private readonly app: Application; private readonly moduleConfigLoader: ModuleConfigLoader; constructor(app) { this.app = app; this.moduleConfigLoader = new ModuleConfigLoader(this.app); } private buildClazz(name: string, eggType: EggType): EggProtoImplClass { const app = this.app; let func: EggProtoImplClass; if (eggType === EggType.APP) { func = function() { return app[name]; } as any; } else { func = function() { const ctx = app.currentContext; if (!ctx) { // ctx has been destroyed, throw humanize error info throw TeggError.create(`Can not read property \`${name}\` because egg ctx has been destroyed`, 'read_after_ctx_destroyed'); } return ctx[name]; } as any; } Object.defineProperty(func, 'name', { value: name, writable: false, enumerable: false, configurable: true, }); PrototypeUtil.setIsEggPrototype(func); PrototypeUtil.setFilePath(func, 'mock_file_path'); PrototypeUtil.setProperty(func, { name, initType: ObjectInitType.SINGLETON, accessLevel: AccessLevel.PUBLIC, protoImplType: COMPATIBLE_PROTO_IMPLE_TYPE, }); QualifierUtil.addProtoQualifier(func, LoadUnitNameQualifierAttribute, 'app'); QualifierUtil.addProtoQualifier(func, InitTypeQualifierAttribute, ObjectInitType.SINGLETON); QualifierUtil.addProtoQualifier(func, EggQualifierAttribute, eggType); return func; } private buildAppLoggerClazz(name: string): EggProtoImplClass { const app = this.app; const func: EggProtoImplClass = function() { return app.getLogger(name); } as any; Object.defineProperty(func, 'name', { value: name, writable: false, enumerable: false, configurable: true, }); PrototypeUtil.setIsEggPrototype(func); PrototypeUtil.setFilePath(func, 'mock_file_path'); PrototypeUtil.setProperty(func, { name, initType: ObjectInitType.SINGLETON, accessLevel: AccessLevel.PUBLIC, protoImplType: COMPATIBLE_PROTO_IMPLE_TYPE, }); QualifierUtil.addProtoQualifier(func, LoadUnitNameQualifierAttribute, 'app'); QualifierUtil.addProtoQualifier(func, InitTypeQualifierAttribute, ObjectInitType.SINGLETON); QualifierUtil.addProtoQualifier(func, EggQualifierAttribute, EggType.APP); return func; } private getLoggerNames(ctxClazzNames: string[], singletonClazzNames: string[]): string[] { const loggerNames = Array.from(this.app.loggers.keys()); // filter logger/coreLogger return loggerNames.filter(t => !ctxClazzNames.includes(t) && !singletonClazzNames.includes(t)); } load(): EggProtoImplClass[] { const app = this.app; const appProperties = ObjectUtils.getProperties(app); const contextProperties = ObjectUtils.getProperties((app as any).context); // custom plugin may define property conflict with default list const allSingletonClazzNameSet = new Set([ ...appProperties, ...DEFAULT_APP_CLAZZ, ]); APP_CLAZZ_BLACK_LIST.forEach(t => allSingletonClazzNameSet.delete(t)); const allSingletonClazzNames = Array.from(allSingletonClazzNameSet); const allContextClazzNamesSet = new Set([ ...contextProperties, ...DEFAULT_CONTEXT_CLAZZ, ]); CONTEXT_CLAZZ_BLACK_LIST.forEach(t => allContextClazzNamesSet.delete(t)); const allContextClazzNames = Array.from(allContextClazzNamesSet); const loggerNames = this.getLoggerNames(allContextClazzNames, allSingletonClazzNames); const allSingletonClazzs = allSingletonClazzNames.map(name => this.buildClazz(name, EggType.APP)); const allContextClazzs = allContextClazzNames.map(name => this.buildClazz(name, EggType.CONTEXT)); const appLoggerClazzs = loggerNames.map(name => this.buildAppLoggerClazz(name)); const moduleConfigList = this.moduleConfigLoader.loadModuleConfigList(); return [ ...allSingletonClazzs, ...allContextClazzs, ...appLoggerClazzs, ...moduleConfigList, // inner helper class list // TODO: should auto the inner class BackgroundTaskHelper, EggObjectFactory, ]; } } ================================================ FILE: plugin/tegg/lib/EggCompatibleObject.ts ================================================ import { EggCompatibleProtoImpl } from './EggCompatibleProtoImpl'; import { EggObject, EggObjectFactory, } from '@eggjs/tegg-runtime'; import { IdenticalUtil, EggObjectName, EggType, EggQualifierAttribute } from '@eggjs/tegg'; import { EggPrototype } from '@eggjs/tegg-metadata'; const OBJ = Symbol('EggCompatibleObject#obj'); export class EggCompatibleObject implements EggObject { readonly isReady: boolean = true; private [OBJ]: object; readonly proto: EggCompatibleProtoImpl; readonly name: EggObjectName; readonly id: string; readonly isContext: boolean; constructor(name: EggObjectName, proto: EggCompatibleProtoImpl) { this.proto = proto; this.name = name; this.id = IdenticalUtil.createObjectId(this.proto.id); this.isContext = this.proto.verifyQualifier({ value: EggType.CONTEXT, attribute: EggQualifierAttribute, }); } // If the egg object is a getter, // access may have side effect. // So access egg object lazy. get obj() { if (this.isContext) { return this.proto.constructEggObject(); } if (!this[OBJ]) { this[OBJ] = this.proto.constructEggObject(); } return this[OBJ]; } injectProperty() { return; } static async createObject(name: EggObjectName, proto: EggPrototype): Promise { return new EggCompatibleObject(name, proto as EggCompatibleProtoImpl); } } EggObjectFactory.registerEggObjectCreateMethod(EggCompatibleProtoImpl, EggCompatibleObject.createObject); ================================================ FILE: plugin/tegg/lib/EggCompatibleProtoImpl.ts ================================================ import { AccessLevel, EggProtoImplClass, EggPrototypeName, MetaDataKey, MetadataUtil, ObjectInitTypeLike, QualifierInfo, QualifierUtil, Id, IdenticalUtil, QualifierValue, } from '@eggjs/tegg'; import { EggPrototype, InjectObjectProto, EggPrototypeLifecycleContext, } from '@eggjs/tegg-metadata'; export const COMPATIBLE_PROTO_IMPLE_TYPE = 'EGG_COMPATIBLE'; export class EggCompatibleProtoImpl implements EggPrototype { private readonly clazz: EggProtoImplClass; private readonly qualifiers: QualifierInfo[]; readonly id: string; readonly name: EggPrototypeName; readonly initType: ObjectInitTypeLike; readonly accessLevel: AccessLevel; readonly injectObjects: InjectObjectProto[]; readonly loadUnitId: Id; constructor( id: string, name: EggPrototypeName, clazz: EggProtoImplClass, initType: ObjectInitTypeLike, loadUnitId: Id, qualifiers: QualifierInfo[], ) { this.id = id; this.clazz = clazz; this.name = name; this.initType = initType; this.accessLevel = AccessLevel.PUBLIC; this.injectObjects = []; this.loadUnitId = loadUnitId; this.qualifiers = qualifiers; } verifyQualifiers(qualifiers: QualifierInfo[]): boolean { for (const qualifier of qualifiers) { if (!this.verifyQualifier(qualifier)) { return false; } } return true; } verifyQualifier(qualifier: QualifierInfo): boolean { const selfQualifiers = this.qualifiers.find(t => t.attribute === qualifier.attribute); return selfQualifiers?.value === qualifier.value; } getQualifier(attribute: string): QualifierValue | undefined { return this.qualifiers.find(t => t.attribute === attribute)?.value; } constructEggObject(): object { return Reflect.apply(this.clazz, null, []); } getMetaData(metadataKey: MetaDataKey): T | undefined { return MetadataUtil.getMetaData(metadataKey, this.clazz); } static create(ctx: EggPrototypeLifecycleContext): EggPrototype { const { clazz, loadUnit } = ctx; const name = ctx.prototypeInfo.name; const id = IdenticalUtil.createProtoId(loadUnit.id, name); const proto = new EggCompatibleProtoImpl( id, name, clazz, ctx.prototypeInfo.initType, loadUnit.id, QualifierUtil.mergeQualifiers( QualifierUtil.getProtoQualifiers(clazz), (ctx.prototypeInfo.qualifiers ?? []), ), ); return proto; } } ================================================ FILE: plugin/tegg/lib/EggContextCompatibleHook.ts ================================================ import { BackgroundTaskHelper, LifecycleHook, ObjectInitType, PrototypeUtil } from '@eggjs/tegg'; import { EggContainerFactory, EggContext, EggContextLifecycleContext } from '@eggjs/tegg-runtime'; import { EggPrototype } from '@eggjs/tegg-metadata'; import { ModuleHandler } from './ModuleHandler'; import { ROOT_PROTO } from '@eggjs/egg-module-common'; export class EggContextCompatibleHook implements LifecycleHook { private readonly moduleHandler: ModuleHandler; private requestProtoList: Array = []; constructor(moduleHandler: ModuleHandler) { this.moduleHandler = moduleHandler; for (const loadUnitInstance of this.moduleHandler.loadUnitInstances) { const iterator = loadUnitInstance.loadUnit.iterateEggPrototype(); for (const proto of iterator) { if (proto.initType === ObjectInitType.CONTEXT) { this.requestProtoList.push(proto); } } } } async preCreate(_, ctx: EggContext): Promise { // root proto added in ctxLifecycleMiddleware if (!ctx.get(ROOT_PROTO)) { for (const proto of this.requestProtoList) { ctx.addProtoToCreate(proto.name, proto); } } else { // Use for ctx.runInBackground. // BackgroundTaskHelper should get by sync, // or tegg context may be destroyed before background task run. // So create it in preCreate. const protoObj = PrototypeUtil.getClazzProto(BackgroundTaskHelper); await EggContainerFactory.getOrCreateEggObject(protoObj as EggPrototype); } } async postCreate(_, ctx: EggContext): Promise { const rootProto = ctx.get(ROOT_PROTO); if (rootProto) { // Ensure ContextInitiator be called. await EggContainerFactory.getOrCreateEggObject(rootProto as EggPrototype); } } } ================================================ FILE: plugin/tegg/lib/EggContextHandler.ts ================================================ import { Application } from 'egg'; import { ContextHandler, EggContext } from '@eggjs/tegg-runtime'; import { EGG_CONTEXT } from '@eggjs/egg-module-common'; export class EggContextHandler { private readonly app: Application; constructor(app: Application) { this.app = app; } getContextCallback(): EggContext { const ctx = this.app.currentContext; return ctx && ctx.teggContext; } async run(eggContext: EggContext, fn: () => Promise): Promise { const ctx = eggContext.get(EGG_CONTEXT); return await this.app.ctxStorage.run(ctx, fn); } register() { ContextHandler.getContextCallback = () => { return this.getContextCallback(); }; ContextHandler.runInContextCallback = async (context: EggContext, fn: () => Promise) => { return await this.run(context, fn); }; } } ================================================ FILE: plugin/tegg/lib/EggContextImpl.ts ================================================ import { Context } from 'egg'; import { AbstractEggContext } from '@eggjs/tegg-runtime'; import { IdenticalUtil } from '@eggjs/tegg'; import { EGG_CONTEXT, TEGG_CONTEXT } from '@eggjs/egg-module-common'; export class EggContextImpl extends AbstractEggContext { readonly id: string; constructor(context: Context) { super(); this.set(EGG_CONTEXT, context); (context as any)[TEGG_CONTEXT] = this; this.id = IdenticalUtil.createContextId(context.tracer?.traceId); } } ================================================ FILE: plugin/tegg/lib/EggModuleLoader.ts ================================================ import { EggLoadUnitType, LoadUnitFactory, GlobalGraph, ModuleDescriptorDumper, } from '@eggjs/tegg-metadata'; import { LoaderFactory } from '@eggjs/tegg-loader'; import { EggAppLoader } from './EggAppLoader'; import { Application } from 'egg'; export class EggModuleLoader { app: Application; globalGraph: GlobalGraph; constructor(app) { this.app = app; GlobalGraph.instance = this.globalGraph = this.buildAppGraph(); } private async loadApp() { const loader = new EggAppLoader(this.app); const loadUnit = await LoadUnitFactory.createLoadUnit(this.app.baseDir, EggLoadUnitType.APP, loader); this.app.moduleHandler.loadUnits.push(loadUnit); } private buildAppGraph() { for (const plugin of Object.values(this.app.plugins)) { if (!plugin.enable) continue; const modulePlugin = this.app.moduleReferences.find(t => t.path === plugin.path); if (modulePlugin) { modulePlugin.optional = false; } } const moduleDescriptors = LoaderFactory.loadApp(this.app.moduleReferences); for (const moduleDescriptor of moduleDescriptors) { ModuleDescriptorDumper.dump(moduleDescriptor, { dumpDir: this.app.baseDir, }).catch(e => { e.message = 'dump module descriptor failed: ' + e.message; this.app.logger.warn(e); }); } const graph = GlobalGraph.create(moduleDescriptors); return graph; } private async loadModule() { this.globalGraph.build(); this.globalGraph.sort(); const moduleConfigList = this.globalGraph.moduleConfigList; for (const moduleConfig of moduleConfigList) { const modulePath = moduleConfig.path; const loader = LoaderFactory.createLoader(modulePath, EggLoadUnitType.MODULE); const loadUnit = await LoadUnitFactory.createLoadUnit(modulePath, EggLoadUnitType.MODULE, loader); this.app.moduleHandler.loadUnits.push(loadUnit); } } async load() { await this.loadApp(); await this.loadModule(); } } ================================================ FILE: plugin/tegg/lib/EggQualifierProtoHook.ts ================================================ import { LoadUnitLifecycleContext, LoadUnit } from '@eggjs/tegg-metadata'; import { LifecycleHook, PrototypeUtil, QualifierUtil, EggQualifierAttribute, EggType, } from '@eggjs/tegg'; import { Application } from 'egg'; import { APP_CLAZZ_BLACK_LIST, CONTEXT_CLAZZ_BLACK_LIST, DEFAULT_APP_CLAZZ, DEFAULT_CONTEXT_CLAZZ, } from './EggAppLoader'; import { ObjectUtils } from '@eggjs/tegg-common-util'; export class EggQualifierProtoHook implements LifecycleHook { private readonly app: Application; constructor(app: Application) { this.app = app; } async preCreate(ctx: LoadUnitLifecycleContext): Promise { const clazzList = ctx.loader.load(); const appProperties = ObjectUtils.getProperties(this.app); const ctxProperties = ObjectUtils.getProperties(this.app.context); for (const clazz of clazzList) { for (const injectObject of PrototypeUtil.getInjectObjects(clazz) || []) { const propertyQualifiers = QualifierUtil.getProperQualifiers(clazz, injectObject.refName); const hasEggQualifier = propertyQualifiers.find(t => t.attribute === EggQualifierAttribute); if (hasEggQualifier) { continue; } if (this.isCtxObject(injectObject.objName, ctxProperties)) { QualifierUtil.addProperQualifier(clazz, injectObject.refName, EggQualifierAttribute, EggType.CONTEXT); } else if (this.isAppObject(injectObject.objName, appProperties)) { QualifierUtil.addProperQualifier(clazz, injectObject.refName, EggQualifierAttribute, EggType.APP); } } } } private isAppObject(name: PropertyKey, appProperties: string[]) { name = String(name); if (APP_CLAZZ_BLACK_LIST.includes(name)) { return false; } if (DEFAULT_APP_CLAZZ.includes(name)) { return true; } return appProperties.includes(name); } private isCtxObject(name: PropertyKey, ctxProperties: string[]) { name = String(name); if (CONTEXT_CLAZZ_BLACK_LIST.includes(name)) { return false; } if (DEFAULT_CONTEXT_CLAZZ.includes(name)) { return true; } return ctxProperties.includes(name); } } ================================================ FILE: plugin/tegg/lib/ModuleConfigLoader.ts ================================================ import { AccessLevel, EggProtoImplClass, EggQualifierAttribute, EggType, InitTypeQualifierAttribute, LoadUnitNameQualifierAttribute, ModuleConfigs, ObjectInitType, PrototypeUtil, QualifierUtil, ModuleConfigHolder, ConfigSourceQualifierAttribute, } from '@eggjs/tegg'; import { ModuleConfigUtil } from '@eggjs/tegg/helper'; import { COMPATIBLE_PROTO_IMPLE_TYPE } from './EggCompatibleProtoImpl'; import { Application } from 'egg'; import extend from 'extend2'; export class ModuleConfigLoader { constructor(readonly app: Application) { } private loadModuleConfigs(moduleConfigMap: Record): EggProtoImplClass { const moduleConfigs = new ModuleConfigs(moduleConfigMap); const func: EggProtoImplClass = function() { return moduleConfigs; } as any; const name = 'moduleConfigs'; Object.defineProperty(func, 'name', { value: name, writable: false, enumerable: false, configurable: true, }); PrototypeUtil.setIsEggPrototype(func); PrototypeUtil.setFilePath(func, 'mock_file_path'); PrototypeUtil.setProperty(func, { name, initType: ObjectInitType.SINGLETON, accessLevel: AccessLevel.PUBLIC, protoImplType: COMPATIBLE_PROTO_IMPLE_TYPE, }); QualifierUtil.addProtoQualifier(func, LoadUnitNameQualifierAttribute, 'app'); QualifierUtil.addProtoQualifier(func, InitTypeQualifierAttribute, ObjectInitType.SINGLETON); QualifierUtil.addProtoQualifier(func, EggQualifierAttribute, EggType.APP); return func; } loadModuleConfigList(): EggProtoImplClass[] { const result: EggProtoImplClass[] = []; const moduleConfigMap: Record = {}; for (const reference of this.app.moduleReferences) { const moduleName = ModuleConfigUtil.readModuleNameSync(reference.path); const defaultConfig = ModuleConfigUtil.loadModuleConfigSync(reference.path, undefined, this.app.config.env); // @eggjs/tegg-config moduleConfigs[module].config overwrite const config = extend(true, {}, defaultConfig, this.app.moduleConfigs[moduleName]?.config); moduleConfigMap[moduleName] = { name: moduleName, reference: { name: moduleName, path: reference.path, }, config, }; const func: EggProtoImplClass = function() { return config; } as any; const name = 'moduleConfig'; Object.defineProperty(func, 'name', { value: name, writable: false, enumerable: false, configurable: true, }); PrototypeUtil.setIsEggPrototype(func); PrototypeUtil.setFilePath(func, 'mock_file_path'); PrototypeUtil.setProperty(func, { name, initType: ObjectInitType.SINGLETON, accessLevel: AccessLevel.PUBLIC, protoImplType: COMPATIBLE_PROTO_IMPLE_TYPE, }); QualifierUtil.addProtoQualifier(func, LoadUnitNameQualifierAttribute, 'app'); QualifierUtil.addProtoQualifier(func, InitTypeQualifierAttribute, ObjectInitType.SINGLETON); QualifierUtil.addProtoQualifier(func, EggQualifierAttribute, EggType.APP); QualifierUtil.addProtoQualifier(func, ConfigSourceQualifierAttribute, moduleName); result.push(func); } const moduleConfigs = this.loadModuleConfigs(moduleConfigMap); result.push(moduleConfigs); return result; } } ================================================ FILE: plugin/tegg/lib/ModuleHandler.ts ================================================ import Base from 'sdk-base'; import { Application, Context } from 'egg'; import { EggLoadUnitType, LoadUnit, LoadUnitFactory, } from '@eggjs/tegg-metadata'; import { LoadUnitInstance, LoadUnitInstanceFactory } from '@eggjs/tegg-runtime'; import { EggModuleLoader } from './EggModuleLoader'; import { CompatibleUtil } from './CompatibleUtil'; import { COMPATIBLE_PROTO_IMPLE_TYPE, EggCompatibleProtoImpl } from './EggCompatibleProtoImpl'; export class ModuleHandler extends Base { loadUnits: LoadUnit[] = []; loadUnitInstances: LoadUnitInstance[] = []; private readonly loadUnitLoader: EggModuleLoader; private readonly app: Application; constructor(app: Application) { super(); this.app = app; this.loadUnitLoader = new EggModuleLoader(this.app); } async init() { try { this.app.eggPrototypeCreatorFactory.registerPrototypeCreator( COMPATIBLE_PROTO_IMPLE_TYPE, EggCompatibleProtoImpl.create); await this.loadUnitLoader.load(); const instances: LoadUnitInstance[] = []; // TODO fixtures dts broken the module defintion (this.app as any).module = {}; for (const loadUnit of this.loadUnits) { const instance = await LoadUnitInstanceFactory.createLoadUnitInstance(loadUnit); if (instance.loadUnit.type !== EggLoadUnitType.APP) { CompatibleUtil.appCompatible(this.app, instance); } instances.push(instance); } CompatibleUtil.contextModuleCompatible((this.app as any).context as Context, instances); this.loadUnitInstances = instances; this.ready(true); } catch (e) { this.ready(e); throw e; } } async destroy() { if (this.loadUnitInstances) { for (const instance of this.loadUnitInstances) { await LoadUnitInstanceFactory.destroyLoadUnitInstance(instance); } } if (this.loadUnits) { for (const loadUnit of this.loadUnits) { await LoadUnitFactory.destroyLoadUnit(loadUnit); } } } } ================================================ FILE: plugin/tegg/lib/Utils.ts ================================================ function prepareObjectStackTrace(_, stack) { return stack; } export function getCalleeFromStack(withLine: boolean, stackIndex?: number) { stackIndex = stackIndex === undefined ? 2 : stackIndex; const limit = Error.stackTraceLimit; const prep = Error.prepareStackTrace; Error.prepareStackTrace = prepareObjectStackTrace; Error.stackTraceLimit = 5; // capture the stack const obj: any = {}; Error.captureStackTrace(obj); let callSite = obj.stack[stackIndex]; let fileName; /* istanbul ignore else */ if (callSite) { // egg-mock will create a proxy // https://github.com/eggjs/egg-mock/blob/master/lib/app.js#L174 fileName = callSite.getFileName(); /* istanbul ignore if */ if (fileName && fileName.endsWith('egg-mock/lib/app.js')) { // TODO: add test callSite = obj.stack[stackIndex + 1]; fileName = callSite.getFileName(); } } Error.prepareStackTrace = prep; Error.stackTraceLimit = limit; /* istanbul ignore if */ if (!callSite || !fileName) return ''; if (!withLine) return fileName; return `${fileName}:${callSite.getLineNumber()}:${callSite.getColumnNumber()}`; } ================================================ FILE: plugin/tegg/lib/ctx_lifecycle_middleware.ts ================================================ import { TEggPluginContext } from '../app/extend/context'; import { EggContextImpl } from './EggContextImpl'; import { ROOT_PROTO, TEGG_CONTEXT } from '@eggjs/egg-module-common'; import { StreamUtil } from '@eggjs/tegg-common-util'; import awaitFirst from 'await-first'; export default async function ctxLifecycleMiddleware(ctx: TEggPluginContext, next) { // should not recreate teggContext if (ctx[TEGG_CONTEXT]) { await next(); return; } const lifecycle = {}; const teggCtx = new EggContextImpl(ctx); // rootProto is set by tegg-controller-plugin global middleware(teggRootProto) // is used in EggControllerHook const rootProto = (ctx as any)[ROOT_PROTO]; if (rootProto) { teggCtx.set(ROOT_PROTO, rootProto); } if (teggCtx.init) { await teggCtx.init(lifecycle); } async function doDestory() { if (StreamUtil.isStream(ctx.response.body)) { try { await awaitFirst(ctx.response.body, [ 'close', 'error' ]); } catch (error) { ctx.res.destroy(error); } } try { if (teggCtx.destroy) { await teggCtx.destroy(lifecycle); } } catch (e) { e.message = '[tegg/ctxLifecycleMiddleware] destroy tegg ctx failed:' + e.message; ctx.logger.error(e); } } try { await next(); } finally { doDestory(); } } ================================================ FILE: plugin/tegg/lib/run_in_background.ts ================================================ import { Application, Context } from 'egg'; import { BackgroundTaskHelper, PrototypeUtil } from '@eggjs/tegg'; import { EggPrototype } from '@eggjs/tegg-metadata'; import { TEGG_CONTEXT } from '@eggjs/egg-module-common'; import { TEggPluginContext } from '../app/extend/context'; import { getCalleeFromStack } from './Utils'; export const LONG_STACK_DELIMITER = '\n --------------------\n'; function addLongStackTrace(err: Error, causeError: Error) { const callSiteStack = causeError.stack; if (!callSiteStack || typeof callSiteStack !== 'string') { return; } const index = callSiteStack.indexOf('\n'); if (index !== -1) { err.stack += LONG_STACK_DELIMITER + callSiteStack.substring(index + 1); } } export function hijackRunInBackground(app: Application) { const eggRunInBackground = app.context.runInBackground; app.context.runInBackground = function runInBackground(this: TEggPluginContext, scope: (ctx: Context) => Promise) { if (!this[TEGG_CONTEXT]) { return Reflect.apply(eggRunInBackground, this, [ scope ]); } const caseError = new Error('cause'); let resolveBackgroundTask; const backgroundTaskPromise = new Promise(resolve => { resolveBackgroundTask = resolve; }); const newScope = async () => { try { await scope(this); } catch (e) { addLongStackTrace(e, caseError); throw e; } finally { resolveBackgroundTask(); } }; const taskName = (scope as any)._name || scope.name || getCalleeFromStack(true, 2); (scope as any)._name = taskName; Object.defineProperty(newScope, 'name', { value: taskName, enumerable: false, configurable: true, writable: false, }); Reflect.apply(eggRunInBackground, this, [ newScope ]); const proto = PrototypeUtil.getClazzProto(BackgroundTaskHelper); const eggObject = app.eggContainerFactory.getEggObject(proto as EggPrototype); const backgroundTaskHelper = eggObject.obj as BackgroundTaskHelper; backgroundTaskHelper.run(async () => { await backgroundTaskPromise; }); }; } ================================================ FILE: plugin/tegg/package.json ================================================ { "name": "@eggjs/tegg-plugin", "eggPlugin": { "name": "tegg", "dependencies": [ "teggConfig" ] }, "version": "3.78.15", "description": "module plugin for egg", "keywords": [ "egg", "plugin", "typescript", "module", "tegg" ], "files": [ "app.js", "app.d.ts", "lib/**/*.js", "lib/**/*.d.ts", "app/**/*.js", "app/**/*.d.ts", "typings/*.d.ts" ], "types": "typings/index.d.ts", "scripts": { "test": "cross-env NODE_ENV=test NODE_OPTIONS='--no-deprecation' mocha", "clean": "tsc -b --clean", "tsc": "ut run clean && tsc -p ./tsconfig.json", "tsc:pub": "ut run clean && tsc -p ./tsconfig.pub.json", "prepublishOnly": "ut tsc:pub" }, "homepage": "https://github.com/eggjs/tegg", "bugs": { "url": "https://github.com/eggjs/tegg/issues" }, "repository": { "type": "git", "url": "git@github.com:eggjs/tegg.git", "directory": "plugin/tegg" }, "engines": { "node": ">=14.0.0" }, "dependencies": { "@eggjs/egg-module-common": "^3.78.15", "@eggjs/tegg": "^3.78.15", "@eggjs/tegg-background-task": "^3.78.15", "@eggjs/tegg-common-util": "^3.78.15", "@eggjs/tegg-dynamic-inject-runtime": "^3.78.15", "@eggjs/tegg-loader": "^3.78.15", "@eggjs/tegg-metadata": "^3.78.15", "@eggjs/tegg-runtime": "^3.78.15", "await-event": "^2.1.0", "await-first": "^1.0.0", "extend2": "^1.0.0", "sdk-base": "^4.2.0" }, "devDependencies": { "@eggjs/tegg-config": "^3.78.15", "@types/mocha": "^10.0.1", "@types/node": "^20.2.4", "cross-env": "^7.0.3", "egg": "^3.9.1", "egg-logger": "^3.0.1", "egg-mock": "^5.5.0", "egg-schedule": "^4.0.0", "egg-tracer": "^2.0.0", "mocha": "^10.2.0", "ts-node": "^10.9.1", "typescript": "^5.0.4" }, "publishConfig": { "access": "public" }, "gitHead": "240a3e9f40fda65ebb7589727d197db5ce17916c" } ================================================ FILE: plugin/tegg/test/AccessLevelCheck.test.ts ================================================ import mm from 'egg-mock'; import assert from 'assert'; import path from 'path'; import MainService from './fixtures/apps/access-level-check/modules/module-main/MainService'; describe('plugin/tegg/test/AccessLevelCheck.test.ts', () => { let app; const fixtureDir = path.join(__dirname, 'fixtures/apps/access-level-check'); after(async () => { await app.close(); }); afterEach(() => { mm.restore(); }); before(async () => { mm(process.env, 'EGG_TYPESCRIPT', true); mm(process, 'cwd', () => { return path.join(__dirname, '..'); }); app = mm.app({ baseDir: fixtureDir, framework: require.resolve('egg'), }); await app.ready(); }); it('invoke moduleMain fooService method', async () => { await app.httpRequest() .get('/invokeFoo') .expect(200) .expect(ret => { assert(ret.body.ret, 'moduleMain-FooService-Method'); }); }); it('should work: private has some name', async () => { await app.mockModuleContextScope(async ctx => { const mainService: MainService = await ctx.getEggObject(MainService); assert(mainService); assert(mainService.invokeFoo() === 'moduleMain-FooService-Method'); }); }); it('should work: public/private has some name', async () => { await app.mockModuleContextScope(async ctx => { const mainService: MainService = await ctx.getEggObject(MainService); assert(mainService); assert(mainService.invokeBar() === 'moduleMain-BarService-Method'); }); }); }); ================================================ FILE: plugin/tegg/test/BackgroundTask.test.ts ================================================ import assert from 'assert'; import path from 'path'; import fs from 'fs'; import mm from 'egg-mock'; import { TimerUtil } from '@eggjs/tegg-common-util'; import { TEGG_CONTEXT } from '@eggjs/egg-module-common'; import { BackgroundTaskHelper } from '@eggjs/tegg'; import { EggContext, EggContextLifecycleUtil } from '@eggjs/tegg-runtime'; import { CountService } from './fixtures/apps/background-app/modules/multi-module-background/CountService'; describe('plugin/tegg/test/BackgroundTask.test.ts', () => { const appDir = path.join(__dirname, 'fixtures/apps/background-app'); let app; after(async () => { await app.close(); }); afterEach(() => { mm.restore(); }); before(async () => { mm(process.env, 'EGG_TYPESCRIPT', true); mm(process, 'cwd', () => { return path.join(__dirname, '..'); }); app = mm.app({ baseDir: appDir, framework: require.resolve('egg'), }); await app.ready(); }); it('background task should work', async () => { app.mockCsrf(); await app.httpRequest() .get('/background') .expect(200); const countService = await app.getEggObject(CountService); assert(countService.count === 0); await TimerUtil.sleep(1000); assert(countService.count === 1); }); it('background timeout with humanize error info', async () => { app.mockCsrf(); await app.httpRequest() .get('/backgroudTimeout') .expect(200); await TimerUtil.sleep(7000); const errorLog = fs.readFileSync(path.resolve(appDir, 'logs/egg-app/common-error.log'), 'utf-8'); assert(errorLog.includes('Can not read property `testObj` because egg ctx has been destroyed [')); }); it('should release', async () => { let teggCtx: EggContext; await app.mockModuleContextScope(async ctx => { teggCtx = ctx[TEGG_CONTEXT]; const backgroundTaskHelper = await ctx.getEggObject(BackgroundTaskHelper); backgroundTaskHelper.run(async () => { // do nothing }); }); const lifecycleList = EggContextLifecycleUtil.getObjectLifecycleList(teggCtx!); assert(lifecycleList.length === 0); }); it('config should work', async () => { await app.mockModuleContextScope(async ctx => { const backgroundTaskHelper = await ctx.getEggObject(BackgroundTaskHelper); assert(backgroundTaskHelper.timeout === Infinity); }); }); }); ================================================ FILE: plugin/tegg/test/ConstructorModuleConfig.test.ts ================================================ import mm from 'egg-mock'; import assert from 'assert'; import path from 'path'; import { Foo } from './fixtures/apps/constructor-module-config/modules/module-with-config/foo'; describe('plugin/tegg/test/ModuleConfig.test.ts', () => { let app; const fixtureDir = path.join(__dirname, 'fixtures/apps/constructor-module-config'); after(async () => { await app.close(); }); afterEach(() => { mm.restore(); }); before(async () => { mm(process.env, 'EGG_TYPESCRIPT', true); mm(process, 'cwd', () => { return path.join(__dirname, '..'); }); app = mm.app({ baseDir: fixtureDir, framework: require.resolve('egg'), }); await app.ready(); }); it('should work', async () => { await app.httpRequest() .get('/config') .expect(200) .expect(res => { assert.deepStrictEqual(res.body, { foo: 'bar', bar: 'foo', }); }); }); it('construct proxy should work', async () => { const foo: Foo = await app.getEggObject(Foo); foo.log(); }); }); ================================================ FILE: plugin/tegg/test/DynamicInject.test.ts ================================================ import mm from 'egg-mock'; import path from 'path'; import assert from 'assert'; describe('plugin/tegg/test/DynamicInject.test.ts', () => { let app; after(async () => { await app.close(); }); afterEach(() => { mm.restore(); }); before(async () => { mm(process.env, 'EGG_TYPESCRIPT', true); mm(process, 'cwd', () => { return path.join(__dirname, '..'); }); app = mm.app({ baseDir: path.join(__dirname, 'fixtures/apps/dynamic-inject-app'), framework: require.resolve('egg'), }); await app.ready(); }); it('dynamic inject should work', async () => { app.mockCsrf(); const res = await app.httpRequest() .get('/dynamicInject') .expect(200); assert.deepStrictEqual(res.body, [ 'hello, foo(context:0)', 'hello, bar(context:0)', 'hello, foo(singleton:0)', 'hello, bar(singleton:0)', ]); }); it('singleton dynamic inject should work', async () => { app.mockCsrf(); const res = await app.httpRequest() .get('/singletonDynamicInject') .expect(200); assert.deepStrictEqual(res.body, [ 'hello, foo(singleton:1)', 'hello, bar(singleton:1)', ]); }); }); ================================================ FILE: plugin/tegg/test/EggCompatible.test.ts ================================================ import mm from 'egg-mock'; import assert from 'assert'; import path from 'path'; import EggTypeService from './fixtures/apps/egg-app/modules/multi-module-service/EggTypeService'; import TraceService from './fixtures/apps/egg-app/modules/multi-module-service/TraceService'; describe('plugin/tegg/test/EggCompatible.test.ts', () => { let app; after(async () => { await app.close(); }); afterEach(() => { mm.restore(); }); before(async () => { mm(process.env, 'EGG_TYPESCRIPT', true); mm(process, 'cwd', () => { return path.join(__dirname, '..'); }); app = mm.app({ baseDir: path.join(__dirname, 'fixtures/apps/egg-app'), framework: require.resolve('egg'), }); await app.ready(); }); it('should work', async () => { app.mockCsrf(); await app.httpRequest() .post('/apps') .send({ name: 'foo', desc: 'mock-desc', }) .expect(200) .expect(res => { assert(res.body.success === true); assert(res.body.traceId); }); await app.httpRequest() .get('/apps?name=foo') .expect(200) .expect(res => { assert(res.body.traceId); assert.deepStrictEqual(res.body.app, { name: 'foo', desc: 'mock-desc', }); }); }); it('module api should work', async () => { app.mockCsrf(); await app.httpRequest() .post('/apps') .send({ name: 'foo', desc: 'mock-desc', }) .expect(200) .expect(res => { assert(res.body.success === true); assert(res.body.traceId); }); await app.httpRequest() .get('/apps2?name=foo') .expect(200) .expect(res => { assert(res.body.traceId); assert.deepStrictEqual(res.body.app, { name: 'foo', desc: 'mock-desc', }); }); }); it('inject config should work', async () => { await app.mockModuleContextScope(async () => { const baseDir = app.module.multiModuleService.configService.getBaseDir(); assert(baseDir); const runtimeConfig = app.module.multiModuleService.configService.getRuntimeConfig(); assert.deepEqual(runtimeConfig, { baseDir: path.join(__dirname, 'fixtures/apps/egg-app'), env: 'unittest', name: 'egg-app', }); }); }); it('inject user should work', async () => { await app.mockModuleContextScope(async () => { const userName = app.module.multiModuleService.configService.getCurrentUserName(); assert(userName); }, { user: { userName: 'mock_user', }, }); }); it('custom logger should work', async () => { await app.mockModuleContextScope(async ctx => { await app.module.multiModuleService.customLoggerService.printLog(); await app.destroyModuleContext(ctx); }); }); it('use singleton proto should work', async () => { await app.module.multiModuleRepo.globalAppRepo.insertApp({ name: 'foo', desc: 'desc', }); const appInfo = await app.module.multiModuleRepo.globalAppRepo.findApp('foo'); assert.deepStrictEqual(appInfo, { name: 'foo', desc: 'desc', }); }); it('module proxy cache should work', async () => { await app.mockModuleContextScope(async () => { const moduleMultiModuleService1 = app.module.multiModuleService; const moduleMultiModuleService2 = app.module.multiModuleService; assert(moduleMultiModuleService1 === moduleMultiModuleService2); }); }); it('should load egg object with no side effect', async () => { await app.mockModuleContextScope(async ctx => { assert(ctx.counter === 0); assert(ctx.counter === 1); }); }); it('should support EggQualifier', async () => { await app.mockModuleContextScope(async () => { const eggTypeService = await app.getEggObject(EggTypeService); const result = eggTypeService.testInject(); assert.deepStrictEqual(result, { app: { from: 'app' }, ctx: { from: 'ctx' } }); }); }); it('should support context property', async () => { mm(app.context, 'tracer', { traceId: 'mockTraceId', }); await app.mockModuleContextScope(async () => { const traceService: TraceService = await app.getEggObject(TraceService); assert(traceService.getTraceId() === 'mockTraceId'); }); mm(app.context, 'tracer', { traceId: 'mockTraceId2', }); await app.mockModuleContextScope(async () => { const traceService: TraceService = await app.getEggObject(TraceService); console.log('id: ', traceService.getTraceId()); assert(traceService.getTraceId() === 'mockTraceId2'); }); }); }); ================================================ FILE: plugin/tegg/test/Inject.test.ts ================================================ import mm from 'egg-mock'; import path from 'path'; import assert from 'assert'; import { BarService } from './fixtures/apps/optional-inject/app/modules/module-a/BarService'; import { FooService } from './fixtures/apps/optional-inject/app/modules/module-a/FooService'; import { BarService1 } from './fixtures/apps/same-name-singleton-and-context-proto/app/modules/module-bar/BarService1'; import { BarService2 } from './fixtures/apps/same-name-singleton-and-context-proto/app/modules/module-bar/BarService2'; import { BarConstructorService1, } from './fixtures/apps/same-name-singleton-and-context-proto/app/modules/module-bar/BarConstructorService1'; import { BarConstructorService2, } from './fixtures/apps/same-name-singleton-and-context-proto/app/modules/module-bar/BarConstructorService2'; describe('plugin/tegg/test/Inject.test.ts', () => { let app; beforeEach(async () => { mm(process.env, 'EGG_TYPESCRIPT', true); mm(process, 'cwd', () => { return path.join(__dirname, '..'); }); }); afterEach(async () => { await app.close(); mm.restore(); }); describe('optional', () => { beforeEach(async () => { app = mm.app({ baseDir: path.join(__dirname, 'fixtures/apps/optional-inject'), framework: require.resolve('egg'), }); await app.ready(); }); it('should work with property', async () => { const barService: BarService = await app.getEggObject(BarService); const res = barService.bar(); assert.deepStrictEqual(res, { nil1: 'Y', nil2: 'Y', }); }); it('should work with constructor', async () => { const fooService: FooService = await app.getEggObject(FooService); const res = fooService.foo(); assert.deepStrictEqual(res, { nil1: 'Y', nil2: 'Y', }); }); }); describe('default initType qualifier', async () => { beforeEach(async () => { app = mm.app({ baseDir: path.join(__dirname, 'fixtures/apps/same-name-singleton-and-context-proto'), framework: require.resolve('egg'), }); await app.ready(); }); it('should work with singletonProto', async () => { await app.mockModuleContextScope(async () => { const barService1: BarService1 = await app.getEggObject(BarService1); assert.strictEqual(barService1.type(), 'singleton'); }); }); it('should work with contextProto', async () => { await app.mockModuleContextScope(async () => { const barService2: BarService2 = await app.getEggObject(BarService2); assert.strictEqual(barService2.type(), 'context'); }); }); it('should work with singletonProto', async () => { await app.mockModuleContextScope(async () => { const barService1: BarConstructorService1 = await app.getEggObject(BarConstructorService1); assert.strictEqual(barService1.type(), 'singleton'); }); }); it('should work with contextProto', async () => { await app.mockModuleContextScope(async () => { const barService2: BarConstructorService2 = await app.getEggObject(BarConstructorService2); assert.strictEqual(barService2.type(), 'context'); }); }); }); it('should throw error if no proto found', async () => { app = mm.app({ baseDir: path.join(__dirname, 'fixtures/apps/invalid-inject'), framework: require.resolve('egg'), }); await assert.rejects( app.ready(), /EggPrototypeNotFound: Object doesNotExist not found in LOAD_UNIT:a/, ); }); }); ================================================ FILE: plugin/tegg/test/ModuleConfig.test.ts ================================================ import mm from 'egg-mock'; import assert from 'assert'; import path from 'path'; describe('plugin/tegg/test/ModuleConfig.test.ts', () => { let app; const fixtureDir = path.join(__dirname, 'fixtures/apps/inject-module-config'); after(async () => { await app.close(); }); afterEach(() => { mm.restore(); }); before(async () => { mm(process.env, 'EGG_TYPESCRIPT', true); mm(process, 'cwd', () => { return path.join(__dirname, '..'); }); app = mm.app({ baseDir: fixtureDir, framework: require.resolve('egg'), }); await app.ready(); }); it('should work', async () => { await app.httpRequest() .get('/config') .expect(200) .expect(res => { assert.deepStrictEqual(res.body, { moduleConfigs: { features: { dynamic: { foo: 'bar', bar: 'foo' } } }, moduleConfig: { features: { dynamic: { foo: 'bar', bar: 'foo' } } }, }); }); }); it('should work with overwrite', async () => { await app.httpRequest() .get('/overwrite_config') .expect(200) .expect(res => { assert.deepStrictEqual(res.body, { moduleConfigs: { features: { dynamic: { foo: 'bar', bar: 'overwrite foo' } } }, moduleConfig: { features: { dynamic: { foo: 'bar', bar: 'overwrite foo' } } }, }); }); }); }); ================================================ FILE: plugin/tegg/test/MultiInstanceInjectMultiInstance.test.ts ================================================ import mm from 'egg-mock'; import path from 'path'; import assert from 'assert'; import { App2 } from './fixtures/apps/app-multi-inject-multi/app/modules/app2/App'; import { App } from './fixtures/apps/app-multi-inject-multi/app/modules/app/App'; describe('plugin/tegg/test/MultiInstanceInjectMultiInstance.test.ts', () => { let app; after(async () => { await app.close(); }); afterEach(() => { mm.restore(); }); before(async () => { mm(process.env, 'EGG_TYPESCRIPT', true); mm(process, 'cwd', () => { return path.join(__dirname, '..'); }); app = mm.app({ baseDir: path.join(__dirname, 'fixtures/apps/app-multi-inject-multi'), framework: require.resolve('egg'), }); await app.ready(); }); it('dynamic inject should work', async () => { const app2Instance: App2 = await app.getEggObject(App2); const appInstance: App = await app.getEggObject(App); const app2Secret = app2Instance.secret.getSecret('mock'); const appName = appInstance.bizManager.name; const appSecret = appInstance.bizManager.secret; assert.equal(app2Secret, 'mock233'); assert.equal(appName, 'foo'); assert.equal(appSecret, 'foo233'); }); }); ================================================ FILE: plugin/tegg/test/NoModuleJson.test.ts ================================================ import mm from 'egg-mock'; import assert from 'assert'; import path from 'path'; describe('plugin/tegg/test/NoModuleJson.test.ts', () => { let app; const fixtureDir = path.join(__dirname, 'fixtures/apps/app-with-no-module-json'); after(async () => { await app.close(); }); afterEach(() => { mm.restore(); }); before(async () => { mm(process.env, 'EGG_TYPESCRIPT', true); mm(process, 'cwd', () => { return path.join(__dirname, '..'); }); app = mm.app({ baseDir: fixtureDir, framework: require.resolve('egg'), }); await app.ready(); }); it('should work', async () => { await app.httpRequest() .get('/config') .expect(200) .expect(res => { const baseDir = res.body.baseDir; assert(baseDir === fixtureDir); }); }); }); ================================================ FILE: plugin/tegg/test/OptionalModule.test.ts ================================================ import mm from 'egg-mock'; import assert from 'assert'; import path from 'path'; import { RootProto } from './fixtures/apps/optional-module/app/modules/root/Root'; import { UsedProto } from './fixtures/apps/optional-module/node_modules/used/Used'; import { UnusedProto } from './fixtures/apps/optional-module/node_modules/unused/Unused'; describe('plugin/tegg/test/OptionalModule.test.ts', () => { let app; const fixtureDir = path.join(__dirname, 'fixtures/apps/optional-module'); after(async () => { await app.close(); }); afterEach(() => { mm.restore(); }); before(async () => { mm(process.env, 'EGG_TYPESCRIPT', true); mm(process, 'cwd', () => { return path.join(__dirname, '..'); }); app = mm.app({ baseDir: fixtureDir, framework: require.resolve('egg'), }); await app.ready(); }); it('should work', async () => { await app.mockModuleContextScope(async ctx => { const rootProto = await ctx.getEggObject(RootProto); assert(rootProto); const usedProto = await ctx.getEggObject(UsedProto); assert(usedProto); await assert.rejects(async () => { await ctx.getEggObject(UnusedProto); }, /can not get proto for clazz UnusedProto/); }); }); }); ================================================ FILE: plugin/tegg/test/OptionalPluginModule.test.ts ================================================ import mm from 'egg-mock'; import assert from 'assert'; import path from 'path'; import { UsedProto } from './fixtures/apps/plugin-module/node_modules/foo-plugin/Used'; describe('plugin/tegg/test/OptionalPluginModule.test.ts', () => { let app; const fixtureDir = path.join(__dirname, 'fixtures/apps/plugin-module'); after(async () => { await app.close(); }); afterEach(() => { mm.restore(); }); before(async () => { mm(process.env, 'EGG_TYPESCRIPT', true); mm(process, 'cwd', () => { return path.join(__dirname, '..'); }); app = mm.app({ baseDir: fixtureDir, framework: require.resolve('egg'), }); await app.ready(); }); it('should work', async () => { await app.mockModuleContextScope(async ctx => { const usedProto = await ctx.getEggObject(UsedProto); assert(usedProto); }); }); }); ================================================ FILE: plugin/tegg/test/SameProtoName.test.ts ================================================ import mm from 'egg-mock'; import assert from 'assert'; import path from 'path'; import { BarService } from './fixtures/apps/same-name-protos/app/modules/module-a/BarService'; describe('plugin/tegg/test/SameProtoName.test.ts', () => { let app; const fixtureDir = path.join(__dirname, 'fixtures/apps/same-name-protos'); after(async () => { await app.close(); }); afterEach(() => { mm.restore(); }); before(async () => { mm(process.env, 'EGG_TYPESCRIPT', true); mm(process, 'cwd', () => { return path.join(__dirname, '..'); }); app = mm.app({ baseDir: fixtureDir, framework: require.resolve('egg'), }); await app.ready(); }); it('should work', async () => { await app.mockModuleContextScope(async ctx => { const barService = await ctx.getEggObject(BarService); assert(barService); assert(barService.fooService); }); }); }); ================================================ FILE: plugin/tegg/test/Subscription.test.ts ================================================ import mm from 'egg-mock'; import assert from 'assert'; import path from 'path'; import AppService from './fixtures/apps/schedule-app/modules/multi-module-service/AppService'; describe('plugin/tegg/test/Subscription.test.ts', () => { let app; after(async () => { await app.close(); }); afterEach(() => { mm.restore(); }); before(async () => { mm(process.env, 'EGG_TYPESCRIPT', true); mm(process, 'cwd', () => { return path.join(__dirname, '..'); }); app = mm.app({ baseDir: path.join(__dirname, 'fixtures/apps/schedule-app'), framework: require.resolve('egg'), }); await app.ready(); }); it('should work', async () => { let called = false; mm(AppService.prototype, 'findApp', () => { called = true; }); await app.runSchedule('foo'); assert(called); }); }); ================================================ FILE: plugin/tegg/test/app/extend/application.test.ts ================================================ import assert from 'assert'; import path from 'path'; import mm from 'egg-mock'; import { Application } from 'egg'; import AppService from '../../fixtures/apps/egg-app/modules/multi-module-service/AppService'; import PersistenceService from '../../fixtures/apps/egg-app/modules/multi-module-repo/PersistenceService'; describe('test/app/extend/application.test.ts', () => { let app: Application; after(async () => { await app.close(); }); afterEach(() => { mm.restore(); }); before(async () => { mm(process.env, 'EGG_TYPESCRIPT', true); mm(process, 'cwd', () => { return path.join(__dirname, '../../../'); }); app = mm.app({ baseDir: path.join(__dirname, '../../fixtures/apps/egg-app'), framework: require.resolve('egg'), }); await app.ready(); }); describe('getEggObject', () => { it('should work', async () => { const persistenceService = await app.getEggObject(PersistenceService); assert(persistenceService instanceof PersistenceService); await assert.rejects(() => { return app.getEggObject(AppService); }, /ctx is required/); }); }); describe('getEggObjectFromName', () => { it('should work with correct type inference', async () => { const persistenceService: PersistenceService = await app.getEggObjectFromName('persistenceService'); assert(persistenceService instanceof PersistenceService); }); }); }); ================================================ FILE: plugin/tegg/test/app/extend/application.unittest.test.ts ================================================ import mm from 'egg-mock'; import path from 'path'; import assert from 'assert'; describe('test/app/extend/application.unittest.test.ts', () => { let app; after(async () => { await app.close(); }); afterEach(() => { mm.restore(); }); before(async () => { mm(process.env, 'EGG_TYPESCRIPT', true); mm(process, 'cwd', () => { return path.join(__dirname, '../../../'); }); app = mm.app({ baseDir: path.join(__dirname, '../../fixtures/apps/egg-app'), framework: require.resolve('egg'), }); await app.ready(); }); it('should work', async function() { await app.mockModuleContextScope(async () => { const traceId = await app.module.multiModuleService.traceService.getTraceId(); assert(traceId); }); }); it('should not call mockModuleContext twice', async () => { const ctx = await app.mockModuleContext(); try { await assert.rejects( app.mockModuleContext(), /should not call mockModuleContext twice./, ); } finally { await app.destroyModuleContext(ctx); } }); }); ================================================ FILE: plugin/tegg/test/app/extend/context.test.ts ================================================ import assert from 'assert'; import path from 'path'; import mm from 'egg-mock'; import { Application } from 'egg'; import { TimerUtil } from '@eggjs/tegg-common-util'; import AppService from '../../fixtures/apps/egg-app/modules/multi-module-service/AppService'; import PersistenceService from '../../fixtures/apps/egg-app/modules/multi-module-repo/PersistenceService'; import { LONG_STACK_DELIMITER } from '../../../lib/run_in_background'; describe('test/app/extend/context.test.ts', () => { let app: Application; after(async () => { await app.close(); }); afterEach(() => { mm.restore(); }); before(async () => { mm(process.env, 'EGG_TYPESCRIPT', true); mm(process, 'cwd', () => { return path.join(__dirname, '../../../'); }); app = mm.app({ baseDir: path.join(__dirname, '../../fixtures/apps/egg-app'), framework: require.resolve('egg'), }); await app.ready(); }); describe('getEggObject', () => { it('should work', async () => { await app.mockModuleContextScope(async ctx => { const appService = await ctx.getEggObject(AppService); assert(appService instanceof AppService); const persistenceService = await ctx.getEggObject(PersistenceService); assert(persistenceService instanceof PersistenceService); }); }); }); describe('getEggObjectFromName', () => { it('should work', async () => { await app.mockModuleContextScope(async ctx => { const appService = await ctx.getEggObjectFromName('appService'); assert(appService instanceof AppService); const persistenceService = await ctx.getEggObjectFromName('persistenceService'); assert(persistenceService instanceof PersistenceService); }); }); }); describe('beginModuleScope', () => { it('should be reentrant', async () => { await app.mockModuleContextScope(async ctx => { await ctx.beginModuleScope(async () => { // ...do nothing }); assert(ctx.teggContext.destroyed === false); }); }); }); describe('runInBackground', () => { it('should notify background task helper', async () => { let backgroundIsDone = false; await app.mockModuleContextScope(async ctx => { ctx.runInBackground(async () => { await TimerUtil.sleep(100); backgroundIsDone = true; }); }); assert(backgroundIsDone); }); it('recursive runInBackground should work', async () => { let backgroundIsDone = false; await app.mockModuleContextScope(async ctx => { ctx.runInBackground(async () => { await TimerUtil.sleep(100); ctx.runInBackground(async () => { await TimerUtil.sleep(100); backgroundIsDone = true; }); }); }); assert(backgroundIsDone); }); it('stack should be continuous', async () => { let backgroundError; app.on('error', e => { backgroundError = e; }); await app.mockModuleContextScope(async ctx => { ctx.runInBackground(async () => { throw new Error('background'); }); await TimerUtil.sleep(1000); }); const stack: string = backgroundError.stack; // background // at ~/plugin/tegg/test/app/extend/context.test.ts:88:17 // at ~/plugin/tegg/test/app/extend/context.test.ts:82:21 (~/plugin/tegg/lib/run_in_background.ts:34:15) // at ~/node_modules/egg/app/extend/context.js:232:49 // -------------------- // at Object.runInBackground (~/plugin/tegg/lib/run_in_background.ts:27:23) // at ~/plugin/tegg/test/app/extend/context.test.ts:87:13 // at ~/plugin/tegg/app/extend/application.unittest.ts:49:22 // at async Proxy.mockContextScope (~/node_modules/egg-mock/app/extend/application.js:81:12) // at async Context. (~/plugin/tegg/test/app/extend/context.test.ts:86:7) assert(stack.includes(__filename)); assert(stack.includes(LONG_STACK_DELIMITER)); }); }); }); ================================================ FILE: plugin/tegg/test/close.test.ts ================================================ import mm from 'egg-mock'; import assert from 'assert'; import path from 'path'; describe('plugin/tegg/test/close.test.ts', () => { it('should clean lifecycle hooks', async () => { mm(process.env, 'EGG_TYPESCRIPT', true); mm(process, 'cwd', () => { return path.join(__dirname, '..'); }); const app = mm.app({ baseDir: path.join(__dirname, 'fixtures/apps/schedule-app'), framework: require.resolve('egg'), }); await app.ready(); await app.close(); assert.equal(app.loadUnitLifecycleUtil.getLifecycleList().length, 0); assert.equal(app.loadUnitInstanceLifecycleUtil.getLifecycleList().length, 0); assert.equal(app.eggContextLifecycleUtil.getLifecycleList().length, 0); assert.equal(app.eggPrototypeLifecycleUtil.getLifecycleList().length, 0); assert.equal(app.eggObjectLifecycleUtil.getLifecycleList().length, 0); }); }); ================================================ FILE: plugin/tegg/test/fixtures/apps/access-level-check/app/controller/app.ts ================================================ import { Controller } from 'egg'; export default class App extends Controller { async invokeFoo() { const ret = await this.ctx.app.module.moduleMain.mainService.invokeFoo(); this.ctx.body = { ret, }; } } ================================================ FILE: plugin/tegg/test/fixtures/apps/access-level-check/app/router.ts ================================================ import { Application } from 'egg'; module.exports = (app: Application) => { app.router.get('/invokeFoo', app.controller.app.invokeFoo); }; ================================================ FILE: plugin/tegg/test/fixtures/apps/access-level-check/config/config.default.js ================================================ 'use strict'; exports.keys = 'test key'; ================================================ FILE: plugin/tegg/test/fixtures/apps/access-level-check/config/module.json ================================================ [ { "path": "../modules/module-a" }, { "path": "../modules/module-main" } ] ================================================ FILE: plugin/tegg/test/fixtures/apps/access-level-check/config/plugin.js ================================================ 'use strict'; const path = require('path'); exports.tracer = { path: path.join(__dirname, '../../../../../node_modules/egg-tracer'), enable: true, }; exports.tegg = { path: path.join(__dirname, '../../../../..'), enable: true, }; exports.teggConfig = { package: '@eggjs/tegg-config', enable: true, }; exports.watcher = false; ================================================ FILE: plugin/tegg/test/fixtures/apps/access-level-check/modules/module-a/BarService.ts ================================================ import { AccessLevel, SingletonProto } from '@eggjs/tegg'; @SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) export default class BarService { public moduleABarServiceMethod() { return 'moduleA-BarService-Method'; } } ================================================ FILE: plugin/tegg/test/fixtures/apps/access-level-check/modules/module-a/FooService.ts ================================================ import { AccessLevel, SingletonProto, Inject } from '@eggjs/tegg'; import BarService from './BarService'; @SingletonProto({ accessLevel: AccessLevel.PRIVATE, }) export default class FooService { @Inject() barService: BarService; public moduleAFooServiceMethod() { return 'moduleA-FooService-Method'; } public moduleMainFooServiceInvokeBar() { return this.barService.moduleABarServiceMethod(); } } ================================================ FILE: plugin/tegg/test/fixtures/apps/access-level-check/modules/module-a/package.json ================================================ { "name": "module-a", "eggModule": { "name": "moduleA" } } ================================================ FILE: plugin/tegg/test/fixtures/apps/access-level-check/modules/module-main/BarService.ts ================================================ import { AccessLevel, SingletonProto } from '@eggjs/tegg'; @SingletonProto({ accessLevel: AccessLevel.PRIVATE, }) export default class BarService { public moduleMainBarServiceMethod() { return 'moduleMain-BarService-Method'; } } ================================================ FILE: plugin/tegg/test/fixtures/apps/access-level-check/modules/module-main/FooService.ts ================================================ import { AccessLevel, SingletonProto, Inject } from '@eggjs/tegg'; import BarService from './BarService'; @SingletonProto({ accessLevel: AccessLevel.PRIVATE, }) export default class FooService { @Inject() barService: BarService; public moduleMainFooServiceMethod() { return 'moduleMain-FooService-Method'; } public moduleMainFooServiceInvokeBar() { return this.barService.moduleMainBarServiceMethod(); } } ================================================ FILE: plugin/tegg/test/fixtures/apps/access-level-check/modules/module-main/MainService.ts ================================================ import { AccessLevel, SingletonProto, Inject } from '@eggjs/tegg'; import BarService from './BarService'; import FooService from './FooService'; @SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) export default class MainService { @Inject() fooService: FooService; @Inject() barService: BarService; public invokeFoo() { return this.fooService.moduleMainFooServiceMethod(); } public invokeBar() { return this.barService.moduleMainBarServiceMethod(); } } ================================================ FILE: plugin/tegg/test/fixtures/apps/access-level-check/modules/module-main/package.json ================================================ { "name": "module-main", "eggModule": { "name": "moduleMain" } } ================================================ FILE: plugin/tegg/test/fixtures/apps/access-level-check/modules/package.json ================================================ { "name": "egg-app" } ================================================ FILE: plugin/tegg/test/fixtures/apps/access-level-check/package.json ================================================ { "name": "egg-app" } ================================================ FILE: plugin/tegg/test/fixtures/apps/access-level-check/typings/index.d.ts ================================================ import 'egg'; import MainService from '../../modules/module-main/MainService'; declare module 'egg' { export interface EggModule { moduleMain: { mainService: MainService; } } } ================================================ FILE: plugin/tegg/test/fixtures/apps/app-multi-inject-multi/app/modules/app/App.ts ================================================ import { Inject, SingletonProto } from '@eggjs/core-decorator'; import { BizManager, BizManagerQualifier } from '../bar/BizManager'; @SingletonProto() export class App { @Inject() @BizManagerQualifier('foo') bizManager: BizManager; } ================================================ FILE: plugin/tegg/test/fixtures/apps/app-multi-inject-multi/app/modules/app/module.yml ================================================ BizManager: clients: foo: {} bar: {} secret: keys: - '1' - '2' ================================================ FILE: plugin/tegg/test/fixtures/apps/app-multi-inject-multi/app/modules/app/package.json ================================================ { "name": "app", "eggModule": { "name": "app" } } ================================================ FILE: plugin/tegg/test/fixtures/apps/app-multi-inject-multi/app/modules/app2/App.ts ================================================ import { Inject, SingletonProto } from '@eggjs/core-decorator'; import { Secret, SecretQualifier } from '../foo/Secret'; @SingletonProto() export class App2 { @Inject() @SecretQualifier('app2') secret: Secret; } ================================================ FILE: plugin/tegg/test/fixtures/apps/app-multi-inject-multi/app/modules/app2/module.yml ================================================ secret: keys: - '1' - '2' ================================================ FILE: plugin/tegg/test/fixtures/apps/app-multi-inject-multi/app/modules/app2/package.json ================================================ { "name": "app2", "eggModule": { "name": "app2" } } ================================================ FILE: plugin/tegg/test/fixtures/apps/app-multi-inject-multi/app/modules/bar/BizManager.ts ================================================ import { MultiInstanceProto, AccessLevel, Inject, ObjectInitType, ObjectInfo, MultiInstancePrototypeGetObjectsContext, MultiInstanceInfo, } from '@eggjs/tegg'; import { ModuleConfigUtil } from '@eggjs/tegg-common-util'; import { EggProtoImplClass, QualifierUtil } from '@eggjs/core-decorator'; import { Secret, SecretQualifierAttribute } from '../foo/Secret'; export const BizManagerQualifierAttribute = Symbol.for('Qualifier.BizManager'); export const BizManagerInjectName = 'bizManager'; export function BizManagerQualifier(chatModelName: string) { return function(target: any, propertyKey: PropertyKey) { QualifierUtil.addProperQualifier(target.constructor as EggProtoImplClass, propertyKey, BizManagerQualifierAttribute, chatModelName); }; } @MultiInstanceProto({ accessLevel: AccessLevel.PUBLIC, initType: ObjectInitType.SINGLETON, // 从 module.yml 中动态获取配置来决定需要初始化几个对象 getObjects(ctx: MultiInstancePrototypeGetObjectsContext) { const config = ModuleConfigUtil.loadModuleConfigSync(ctx.unitPath) as any; const name = ModuleConfigUtil.readModuleNameSync(ctx.unitPath); const clients = config?.BizManager?.clients; if (!clients) return []; return Object.keys(clients).map((clientName: string) => { return { name: BizManagerInjectName, qualifiers: [{ attribute: BizManagerQualifierAttribute, value: clientName, }], properQualifiers: { secret: [{ attribute: SecretQualifierAttribute, value: name, }], }, }; }); }, }) export class BizManager { readonly name: string; readonly secret: string; constructor( @Inject() secret: Secret, @MultiInstanceInfo([ BizManagerQualifierAttribute ]) objInfo: ObjectInfo, ) { this.name = objInfo.qualifiers.find(t => t.attribute === BizManagerQualifierAttribute)!.value as string; this.secret = secret.getSecret(this.name); } } ================================================ FILE: plugin/tegg/test/fixtures/apps/app-multi-inject-multi/app/modules/bar/package.json ================================================ { "name": "bar", "eggModule": { "name": "bar" } } ================================================ FILE: plugin/tegg/test/fixtures/apps/app-multi-inject-multi/app/modules/foo/Secret.ts ================================================ import { MultiInstanceProto, MultiInstancePrototypeGetObjectsContext, ObjectInitType, AccessLevel, QualifierUtil, } from '@eggjs/tegg'; import { ModuleConfigUtil } from '@eggjs/tegg/helper'; import { EggProtoImplClass } from '@eggjs/tegg-types'; export const SecretQualifierAttribute = Symbol.for('Qualifier.Secret'); export const SecretInjectName = 'secret'; export function SecretQualifier(chatModelName: string) { return function(target: any, propertyKey: PropertyKey) { QualifierUtil.addProperQualifier(target.constructor as EggProtoImplClass, propertyKey, SecretQualifierAttribute, chatModelName); }; } @MultiInstanceProto({ accessLevel: AccessLevel.PUBLIC, initType: ObjectInitType.SINGLETON, getObjects(ctx: MultiInstancePrototypeGetObjectsContext) { const config = ModuleConfigUtil.loadModuleConfigSync(ctx.unitPath) as any; const keys = config?.secret?.keys; if (!keys || keys.length === 0) return []; const name = ModuleConfigUtil.readModuleNameSync(ctx.unitPath); return [{ name: SecretInjectName, qualifiers: [{ attribute: SecretQualifierAttribute, value: name, }], }]; }, }) export class Secret { getSecret(key: string): string { return key + '233'; } } ================================================ FILE: plugin/tegg/test/fixtures/apps/app-multi-inject-multi/app/modules/foo/package.json ================================================ { "name": "foo", "eggModule": { "name": "foo" } } ================================================ FILE: plugin/tegg/test/fixtures/apps/app-multi-inject-multi/config/config.default.js ================================================ 'use strict'; const path = require('path'); module.exports = function(appInfo) { const config = { keys: 'test key', security: { csrf: { ignoreJSON: false, }, }, }; return config; }; ================================================ FILE: plugin/tegg/test/fixtures/apps/app-multi-inject-multi/config/plugin.js ================================================ 'use strict'; exports.tracer = { package: 'egg-tracer', enable: true, }; exports.teggConfig = { package: '@eggjs/tegg-config', enable: true, }; exports.watcher = false; ================================================ FILE: plugin/tegg/test/fixtures/apps/app-multi-inject-multi/package.json ================================================ { "name": "app-multi-inject-multi" } ================================================ FILE: plugin/tegg/test/fixtures/apps/app-with-no-module-json/app/controller/app.ts ================================================ import { Controller } from 'egg'; export default class App extends Controller { async baseDir() { const baseDir = await this.ctx.app.module.config.configService.getBaseDir(); this.ctx.body = { baseDir, }; } } ================================================ FILE: plugin/tegg/test/fixtures/apps/app-with-no-module-json/app/extend/application.unittest.ts ================================================ import { MockApplication } from 'egg-mock'; export default { mockUser(this: MockApplication) { this.mockContext({ user: { userName: 'mock_user', }, }); }, }; ================================================ FILE: plugin/tegg/test/fixtures/apps/app-with-no-module-json/app/extend/context.ts ================================================ const COUNTER = Symbol('Context#counter'); export default { get counter() { if (!this[COUNTER]) { this[COUNTER] = 0; } return this[COUNTER]++; }, get user() { return {}; }, }; ================================================ FILE: plugin/tegg/test/fixtures/apps/app-with-no-module-json/app/router.ts ================================================ import { Application } from 'egg'; module.exports = (app: Application) => { app.router.get('/config', app.controller.app.baseDir); }; ================================================ FILE: plugin/tegg/test/fixtures/apps/app-with-no-module-json/app/typings/index.d.ts ================================================ import 'egg'; import ConfigService from '../../modules/multi-module-service/ConfigService'; declare module 'egg' { export interface EggModule { config: { configService: ConfigService; } } } ================================================ FILE: plugin/tegg/test/fixtures/apps/app-with-no-module-json/config/config.default.js ================================================ 'use strict'; const path = require('path'); module.exports = function(appInfo) { const config = { keys: 'test key', customLogger: { xxLogger: { file: path.join(appInfo.root, 'logs/xx.log'), }, }, security: { csrf: { ignoreJSON: false, } }, }; return config; }; ================================================ FILE: plugin/tegg/test/fixtures/apps/app-with-no-module-json/config/plugin.js ================================================ 'use strict'; exports.tracer = { package: 'egg-tracer', enable: true, }; exports.teggConfig = { package: '@eggjs/tegg-config', enable: true, }; exports.watcher = false; ================================================ FILE: plugin/tegg/test/fixtures/apps/app-with-no-module-json/modules/config-module/ConfigService.ts ================================================ import { AccessLevel, SingletonProto, Inject } from '@eggjs/tegg'; import { EggAppConfig } from 'egg'; interface XSessionUser { userName: string; } @SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) export default class ConfigService { @Inject() user: XSessionUser; @Inject() config: EggAppConfig; getBaseDir(): string { return this.config.baseDir; } async getCurrentUserName(): Promise { return this.user.userName; } } ================================================ FILE: plugin/tegg/test/fixtures/apps/app-with-no-module-json/modules/config-module/package.json ================================================ { "name": "config-module", "eggModule": { "name": "config" } } ================================================ FILE: plugin/tegg/test/fixtures/apps/app-with-no-module-json/package.json ================================================ { "name": "egg-app" } ================================================ FILE: plugin/tegg/test/fixtures/apps/background-app/app/controller/app.ts ================================================ import { Controller } from 'egg'; import BackgroundService from '../../modules/multi-module-background/BackgroundService'; export default class App extends Controller { async background() { const backgroundService = await this.ctx.app.getEggObject(BackgroundService); await backgroundService.backgroundAdd(); this.ctx.status = 200; this.ctx.body = 'done'; } async backgroudTimeout() { const backgroundService = await this.ctx.app.getEggObject(BackgroundService); await backgroundService.backgroundAdd(6000); this.ctx.status = 200; this.ctx.body = 'done'; } } ================================================ FILE: plugin/tegg/test/fixtures/apps/background-app/app/router.ts ================================================ import { Application } from 'egg'; module.exports = (app: Application) => { app.router.get('/background', app.controller.app.background); app.router.get('/backgroudTimeout', app.controller.app.backgroudTimeout); }; ================================================ FILE: plugin/tegg/test/fixtures/apps/background-app/app/typings/index.d.ts ================================================ import 'egg'; import TraceService from '../../modules/multi-module-service/TraceService'; import AppService from '../../modules/multi-module-service/AppService'; declare module 'egg' { export interface EggModule { multiModuleService: { traceService: TraceService; appService: AppService; } } } ================================================ FILE: plugin/tegg/test/fixtures/apps/background-app/config/config.default.js ================================================ 'use strict'; const path = require('path'); module.exports = function(appInfo) { const config = { keys: 'test key', customLogger: { xxLogger: { file: path.join(appInfo.root, 'logs/xx.log'), }, }, security: { csrf: { ignoreJSON: false, } }, backgroundTask: { timeout: Infinity, }, }; return config; }; ================================================ FILE: plugin/tegg/test/fixtures/apps/background-app/config/module.json ================================================ [ { "path": "../modules/multi-module-background" } ] ================================================ FILE: plugin/tegg/test/fixtures/apps/background-app/config/plugin.js ================================================ 'use strict'; exports.tracer = { package: 'egg-tracer', enable: true, }; exports.teggConfig = { package: '@eggjs/tegg-config', enable: true, }; exports.watcher = false; ================================================ FILE: plugin/tegg/test/fixtures/apps/background-app/modules/multi-module-background/BackgroundService.ts ================================================ import assert from 'assert'; import { AccessLevel, SingletonProto, Inject, ContextProto } from '@eggjs/tegg'; import { BackgroundTaskHelper } from '@eggjs/tegg-background-task'; import { TimerUtil } from '@eggjs/tegg-common-util'; import { CountService } from './CountService'; @ContextProto() export class TestObj { ok = true; } @SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) export default class BackgroundService { @Inject() private readonly backgroundTaskHelper:BackgroundTaskHelper; @Inject() testObj: TestObj; @Inject() private readonly countService: CountService; async backgroundAdd(delay = 1000) { this.backgroundTaskHelper.timeout = 5000; this.backgroundTaskHelper.run(async () => { await TimerUtil.sleep(delay); assert(this.testObj.ok); this.countService.count += 1; }); } } ================================================ FILE: plugin/tegg/test/fixtures/apps/background-app/modules/multi-module-background/CountService.ts ================================================ import { SingletonProto } from '@eggjs/tegg'; @SingletonProto() export class CountService { count = 0; } ================================================ FILE: plugin/tegg/test/fixtures/apps/background-app/modules/multi-module-background/package.json ================================================ { "name": "backgroundModule", "eggModule": { "name": "backgroundModule" } } ================================================ FILE: plugin/tegg/test/fixtures/apps/background-app/package.json ================================================ { "name": "egg-app" } ================================================ FILE: plugin/tegg/test/fixtures/apps/constructor-module-config/app/controller/app.ts ================================================ import { Controller } from 'egg'; export default class App extends Controller { async baseDir() { this.ctx.body = { foo: this.app.module.constructorSimple.foo.foo, bar: this.app.module.constructorSimple.foo.bar, }; } } ================================================ FILE: plugin/tegg/test/fixtures/apps/constructor-module-config/app/extend/application.unittest.ts ================================================ import { MockApplication } from 'egg-mock'; export default { mockUser(this: MockApplication) { this.mockContext({ user: { userName: 'mock_user', }, }); }, }; ================================================ FILE: plugin/tegg/test/fixtures/apps/constructor-module-config/app/extend/context.ts ================================================ const COUNTER = Symbol('Context#counter'); export default { get counter() { if (!this[COUNTER]) { this[COUNTER] = 0; } return this[COUNTER]++; }, get user() { return {}; }, }; ================================================ FILE: plugin/tegg/test/fixtures/apps/constructor-module-config/app/router.ts ================================================ import { Application } from 'egg'; module.exports = (app: Application) => { app.router.get('/config', app.controller.app.baseDir); }; ================================================ FILE: plugin/tegg/test/fixtures/apps/constructor-module-config/app/typings/index.d.ts ================================================ import 'egg'; import { Foo } from '../../modules/module-with-config/foo'; import { Bar } from '../../modules/module-with-overwrite-config/bar'; declare module 'egg' { export interface EggModule { constructorSimple: { foo: Foo; }, } } ================================================ FILE: plugin/tegg/test/fixtures/apps/constructor-module-config/app.ts ================================================ import { Application } from 'egg'; export default class AppBoot { app: Application; constructor(app: Application) { this.app = app; } configWillLoad() { if (this.app.moduleConfigs?.overwrite?.config) { (this.app.moduleConfigs.overwrite.config as Record).features.dynamic.bar = 'overwrite foo'; } } } ================================================ FILE: plugin/tegg/test/fixtures/apps/constructor-module-config/config/config.default.js ================================================ 'use strict'; const path = require('path'); module.exports = function(appInfo) { const config = { keys: 'test key', customLogger: { xxLogger: { file: path.join(appInfo.root, 'logs/xx.log'), }, }, security: { csrf: { ignoreJSON: false, } }, }; return config; }; ================================================ FILE: plugin/tegg/test/fixtures/apps/constructor-module-config/config/plugin.js ================================================ 'use strict'; exports.tracer = { package: 'egg-tracer', enable: true, }; exports.teggConfig = { package: '@eggjs/tegg-config', enable: true, }; exports.watcher = false; ================================================ FILE: plugin/tegg/test/fixtures/apps/constructor-module-config/modules/module-with-config/foo.ts ================================================ import { AccessLevel, Inject, SingletonProto } from '@eggjs/tegg'; import { EggLogger } from 'egg-logger'; @SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) export class Foo { readonly foo: string; readonly bar: string; constructor( @Inject() moduleConfig: Record, @Inject() readonly logger: EggLogger, ) { this.foo = moduleConfig.features.dynamic.foo; this.bar = moduleConfig.features.dynamic.bar; } log() { this.logger.info('foo'); } } ================================================ FILE: plugin/tegg/test/fixtures/apps/constructor-module-config/modules/module-with-config/module.unittest.yml ================================================ features: dynamic: bar: 'foo' ================================================ FILE: plugin/tegg/test/fixtures/apps/constructor-module-config/modules/module-with-config/module.yml ================================================ features: dynamic: foo: 'bar' ================================================ FILE: plugin/tegg/test/fixtures/apps/constructor-module-config/modules/module-with-config/package.json ================================================ { "name": "constructorSimple", "eggModule": { "name": "constructorSimple" } } ================================================ FILE: plugin/tegg/test/fixtures/apps/constructor-module-config/package.json ================================================ { "name": "egg-app" } ================================================ FILE: plugin/tegg/test/fixtures/apps/dynamic-inject-app/app/controller/app.ts ================================================ import { Controller } from 'egg'; import { HelloService } from '../../modules/dynamic-inject-module/HelloService'; import { SingletonHelloService } from '../../modules/dynamic-inject-module/SingletonHelloService'; export default class App extends Controller { async dynamicInject() { const helloService: HelloService = await (this.ctx.module as any).dynamicInjectModule.helloService; const msgs = await helloService.hello(); this.ctx.status = 200; this.ctx.body = msgs; } async singletonDynamicInject() { const helloService: SingletonHelloService = await (this.app.module as any).dynamicInjectModule.singletonHelloService; const msgs = await helloService.hello(); this.ctx.status = 200; this.ctx.body = msgs; } } ================================================ FILE: plugin/tegg/test/fixtures/apps/dynamic-inject-app/app/router.ts ================================================ import { Application } from 'egg'; module.exports = (app: Application) => { app.router.get('/dynamicInject', app.controller.app.dynamicInject); app.router.get('/singletonDynamicInject', app.controller.app.singletonDynamicInject); }; ================================================ FILE: plugin/tegg/test/fixtures/apps/dynamic-inject-app/app/typings/index.d.ts ================================================ import 'egg'; import TraceService from '../../modules/multi-module-service/TraceService'; import AppService from '../../modules/multi-module-service/AppService'; declare module 'egg' { export interface EggModule { multiModuleService: { traceService: TraceService; appService: AppService; } } } ================================================ FILE: plugin/tegg/test/fixtures/apps/dynamic-inject-app/config/config.default.js ================================================ 'use strict'; const path = require('path'); module.exports = function(appInfo) { const config = { keys: 'test key', customLogger: { xxLogger: { file: path.join(appInfo.root, 'logs/xx.log'), }, }, security: { csrf: { ignoreJSON: false, } }, }; return config; }; ================================================ FILE: plugin/tegg/test/fixtures/apps/dynamic-inject-app/config/module.json ================================================ [ { "path": "../modules/dynamic-inject-module" } ] ================================================ FILE: plugin/tegg/test/fixtures/apps/dynamic-inject-app/config/plugin.js ================================================ 'use strict'; const path = require('path'); exports.tracer = { path: path.join(__dirname, '../../../../../node_modules/egg-tracer'), enable: true, }; exports.tegg = { path: path.join(__dirname, '../../../../..'), enable: true, }; exports.teggConfig = { package: '@eggjs/tegg-config', enable: true, }; exports.watcher = false; ================================================ FILE: plugin/tegg/test/fixtures/apps/dynamic-inject-app/modules/dynamic-inject-module/AbstractContextHello.ts ================================================ export abstract class AbstractContextHello { abstract hello(): string; } ================================================ FILE: plugin/tegg/test/fixtures/apps/dynamic-inject-app/modules/dynamic-inject-module/AbstractSingletonHello.ts ================================================ export abstract class AbstractSingletonHello { abstract hello(): string; } ================================================ FILE: plugin/tegg/test/fixtures/apps/dynamic-inject-app/modules/dynamic-inject-module/FooType.ts ================================================ export enum ContextHelloType { FOO = 'FOO', BAR = 'BAR', } export enum SingletonHelloType { FOO = 'FOO', BAR = 'BAR', } ================================================ FILE: plugin/tegg/test/fixtures/apps/dynamic-inject-app/modules/dynamic-inject-module/HelloService.ts ================================================ import { AccessLevel, ContextProto, Inject, EggObjectFactory } from '@eggjs/tegg'; import { ContextHelloType, SingletonHelloType } from './FooType'; import { AbstractContextHello } from './AbstractContextHello'; import { AbstractSingletonHello } from './AbstractSingletonHello'; @ContextProto({ accessLevel: AccessLevel.PUBLIC, }) export class HelloService { @Inject() private readonly eggObjectFactory: EggObjectFactory; async hello(): Promise { const helloImpls = await Promise.all([ this.eggObjectFactory.getEggObject(AbstractContextHello, ContextHelloType.FOO), this.eggObjectFactory.getEggObject(AbstractContextHello, ContextHelloType.BAR), this.eggObjectFactory.getEggObject(AbstractSingletonHello, SingletonHelloType.FOO), this.eggObjectFactory.getEggObject(AbstractSingletonHello, SingletonHelloType.BAR), ]); const msgs = helloImpls.map(helloImpl => helloImpl.hello()); return msgs; } } ================================================ FILE: plugin/tegg/test/fixtures/apps/dynamic-inject-app/modules/dynamic-inject-module/SingletonHelloService.ts ================================================ import { AccessLevel, Inject, EggObjectFactory, SingletonProto } from '@eggjs/tegg'; import { SingletonHelloType } from './FooType'; import { AbstractSingletonHello } from './AbstractSingletonHello'; @SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) export class SingletonHelloService { @Inject() private readonly eggObjectFactory: EggObjectFactory; async hello(): Promise { const helloImpls = await Promise.all([ this.eggObjectFactory.getEggObject(AbstractSingletonHello, SingletonHelloType.FOO), this.eggObjectFactory.getEggObject(AbstractSingletonHello, SingletonHelloType.BAR), ]); const msgs = helloImpls.map(helloImpl => helloImpl.hello()); return msgs; } } ================================================ FILE: plugin/tegg/test/fixtures/apps/dynamic-inject-app/modules/dynamic-inject-module/decorator/ContextHello.ts ================================================ import { ContextHelloType } from '../FooType'; import { ImplDecorator, QualifierImplDecoratorUtil } from '@eggjs/tegg'; import { AbstractContextHello } from '../AbstractContextHello'; export const CONTEXT_HELLO_ATTRIBUTE = 'CONTEXT_HELLO_ATTRIBUTE'; export const ContextHello: ImplDecorator = QualifierImplDecoratorUtil.generatorDecorator(AbstractContextHello, CONTEXT_HELLO_ATTRIBUTE); ================================================ FILE: plugin/tegg/test/fixtures/apps/dynamic-inject-app/modules/dynamic-inject-module/decorator/SingletonHello.ts ================================================ import { SingletonHelloType } from '../FooType'; import { ImplDecorator, QualifierImplDecoratorUtil } from '@eggjs/tegg'; import { AbstractSingletonHello } from '../AbstractSingletonHello'; export const SINGLETON_HELLO_ATTRIBUTE = 'SINGLETON_HELLO_ATTRIBUTE'; export const SingletonHello: ImplDecorator = QualifierImplDecoratorUtil.generatorDecorator(AbstractSingletonHello, SINGLETON_HELLO_ATTRIBUTE); ================================================ FILE: plugin/tegg/test/fixtures/apps/dynamic-inject-app/modules/dynamic-inject-module/impl/BarContextHello.ts ================================================ import { ContextProto } from '@eggjs/tegg'; import { ContextHello } from '../decorator/ContextHello'; import { ContextHelloType } from '../FooType'; import { AbstractContextHello } from '../AbstractContextHello'; @ContextProto() @ContextHello(ContextHelloType.BAR) export class BarContextHello extends AbstractContextHello { id = 0; hello(): string { return `hello, bar(context:${this.id++})`; } } ================================================ FILE: plugin/tegg/test/fixtures/apps/dynamic-inject-app/modules/dynamic-inject-module/impl/BarSingletonHello.ts ================================================ import { SingletonProto } from '@eggjs/tegg'; import { SingletonHelloType } from '../FooType'; import { SingletonHello } from '../decorator/SingletonHello'; import { AbstractContextHello } from '../AbstractContextHello'; @SingletonProto() @SingletonHello(SingletonHelloType.BAR) export class BarSingletonHello extends AbstractContextHello { id = 0; hello(): string { return `hello, bar(singleton:${this.id++})`; } } ================================================ FILE: plugin/tegg/test/fixtures/apps/dynamic-inject-app/modules/dynamic-inject-module/impl/FooContextHello.ts ================================================ import { ContextProto } from '@eggjs/tegg'; import { ContextHello } from '../decorator/ContextHello'; import { ContextHelloType } from '../FooType'; import { AbstractContextHello } from '../AbstractContextHello'; @ContextProto() @ContextHello(ContextHelloType.FOO) export class FooContextHello extends AbstractContextHello { id = 0; hello(): string { return `hello, foo(context:${this.id++})`; } } ================================================ FILE: plugin/tegg/test/fixtures/apps/dynamic-inject-app/modules/dynamic-inject-module/impl/FooSingletonHello.ts ================================================ import { SingletonProto } from '@eggjs/tegg'; import { SingletonHelloType } from '../FooType'; import { SingletonHello } from '../decorator/SingletonHello'; import { AbstractContextHello } from '../AbstractContextHello'; @SingletonProto() @SingletonHello(SingletonHelloType.FOO) export class FooSingletonHello extends AbstractContextHello { id = 0; hello(): string { return `hello, foo(singleton:${this.id++})`; } } ================================================ FILE: plugin/tegg/test/fixtures/apps/dynamic-inject-app/modules/dynamic-inject-module/package.json ================================================ { "name": "dynamic-inject-module", "eggModule": { "name": "dynamicInjectModule" } } ================================================ FILE: plugin/tegg/test/fixtures/apps/dynamic-inject-app/package.json ================================================ { "name": "egg-app" } ================================================ FILE: plugin/tegg/test/fixtures/apps/egg-app/app/controller/app.ts ================================================ import { Controller } from 'egg'; export default class App extends Controller { async find() { const traceId = await this.ctx.app.module.multiModuleService.traceService.getTraceId(); const app = await this.ctx.module.multiModuleService.appService.findApp(this.ctx.query.name); this.ctx.body = { traceId, app, }; } async find2() { const traceId = await this.ctx.app.module.multiModuleService.traceService.getTraceId(); const app = await this.ctx.module.multiModuleService.appService.findApp(this.ctx.query.name); this.ctx.body = { traceId, app, }; } async save() { const app = this.ctx.request.body; const traceId = await this.ctx.app.module.multiModuleService.traceService.getTraceId(); await this.ctx.module.multiModuleService.appService.save(app); this.ctx.body = { success: true, traceId, }; } } ================================================ FILE: plugin/tegg/test/fixtures/apps/egg-app/app/extend/application.ts ================================================ export default { get appDefineObject() { return { from: 'app', }; }, }; ================================================ FILE: plugin/tegg/test/fixtures/apps/egg-app/app/extend/application.unittest.ts ================================================ import { MockApplication } from 'egg-mock'; export default { mockUser(this: MockApplication) { this.mockContext({ user: { userName: 'mock_user', }, }); }, }; ================================================ FILE: plugin/tegg/test/fixtures/apps/egg-app/app/extend/context.ts ================================================ const COUNTER = Symbol('Context#counter'); export default { get counter() { if (!this[COUNTER]) { this[COUNTER] = 0; } return this[COUNTER]++; }, get user() { return {}; }, get appDefineObject() { return { from: 'ctx', }; }, }; ================================================ FILE: plugin/tegg/test/fixtures/apps/egg-app/app/router.ts ================================================ import { Application } from 'egg'; module.exports = (app: Application) => { app.router.get('/apps', app.controller.app.find); app.router.get('/apps2', app.controller.app.find2); app.router.post('/apps', app.controller.app.save); }; ================================================ FILE: plugin/tegg/test/fixtures/apps/egg-app/app/typings/index.d.ts ================================================ import 'egg'; import TraceService from '../../modules/multi-module-service/TraceService'; import AppService from '../../modules/multi-module-service/AppService'; declare module 'egg' { export interface EggModule { multiModuleService: { traceService: TraceService; appService: AppService; } } } ================================================ FILE: plugin/tegg/test/fixtures/apps/egg-app/config/config.default.js ================================================ 'use strict'; const path = require('path'); module.exports = function(appInfo) { const config = { keys: 'test key', customLogger: { xxLogger: { file: path.join(appInfo.root, 'logs/xx.log'), }, }, security: { csrf: { ignoreJSON: false, } }, }; return config; }; ================================================ FILE: plugin/tegg/test/fixtures/apps/egg-app/config/module.json ================================================ [ { "path": "../modules/multi-module-repo" }, { "path": "../modules/multi-module-service" } ] ================================================ FILE: plugin/tegg/test/fixtures/apps/egg-app/config/plugin.js ================================================ 'use strict'; exports.tracer = { package: 'egg-tracer', enable: true, }; exports.teggConfig = { package: '@eggjs/tegg-config', enable: true, }; exports.watcher = false; ================================================ FILE: plugin/tegg/test/fixtures/apps/egg-app/modules/multi-module-common/model/App.ts ================================================ export default class App { name: string; desc: string; } ================================================ FILE: plugin/tegg/test/fixtures/apps/egg-app/modules/multi-module-common/package.json ================================================ { "name": "multi-module-common", "eggModule": { "name": "multi-module-common" } } ================================================ FILE: plugin/tegg/test/fixtures/apps/egg-app/modules/multi-module-repo/AppRepo.ts ================================================ import PersistenceService from './PersistenceService'; import { AccessLevel, SingletonProto, Inject } from '@eggjs/tegg'; import App from '../multi-module-common/model/App'; @SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) export default class AppRepo { @Inject() persistenceService: PersistenceService; public async findApp(name): Promise { const raw = this.persistenceService.get(name); if (!raw) { return null; } return JSON.parse(raw); } public async insertApp(app: App): Promise { this.persistenceService.set(app.name, JSON.stringify(app)); } } ================================================ FILE: plugin/tegg/test/fixtures/apps/egg-app/modules/multi-module-repo/GlobalAppRepo.ts ================================================ import PersistenceService from './PersistenceService'; import { AccessLevel, Inject, SingletonProto } from '@eggjs/tegg'; import App from '../multi-module-common/model/App'; @SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) export default class GlobalAppRepo { @Inject() persistenceService: PersistenceService; public async findApp(name): Promise { const raw = this.persistenceService.get(name); if (!raw) { return null; } return JSON.parse(raw); } public async insertApp(app: App): Promise { this.persistenceService.set(app.name, JSON.stringify(app)); } } ================================================ FILE: plugin/tegg/test/fixtures/apps/egg-app/modules/multi-module-repo/PersistenceService.ts ================================================ import { AccessLevel, SingletonProto } from '@eggjs/tegg'; @SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) export default class PersistenceService { private store: Map = new Map(); public set(key: string, val: string) { this.store.set(key, val); } public get(key: string): string | undefined { return this.store.get(key); } } ================================================ FILE: plugin/tegg/test/fixtures/apps/egg-app/modules/multi-module-repo/package.json ================================================ { "name": "multi-module-repo", "eggModule": { "name": "multiModuleRepo" } } ================================================ FILE: plugin/tegg/test/fixtures/apps/egg-app/modules/multi-module-service/AppService.ts ================================================ import { AccessLevel, ContextProto, Inject } from '@eggjs/tegg'; import AppRepo from '../multi-module-repo/AppRepo'; import App from '../multi-module-common/model/App'; @ContextProto({ accessLevel: AccessLevel.PUBLIC, }) export default class AppService { @Inject() appRepo: AppRepo; findApp(name: string): Promise { return this.appRepo.findApp(name); } save(app: App) { return this.appRepo.insertApp(app); } } ================================================ FILE: plugin/tegg/test/fixtures/apps/egg-app/modules/multi-module-service/ConfigService.ts ================================================ import { AccessLevel, SingletonProto, Inject, RuntimeConfig } from '@eggjs/tegg'; import { EggAppConfig } from 'egg'; interface XSessionUser { userName: string; } @SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) export default class ConfigService { @Inject() private user: XSessionUser; @Inject() private config: EggAppConfig; @Inject() private runtimeConfig: RuntimeConfig; getBaseDir(): string { return this.config.baseDir; } async getCurrentUserName(): Promise { return this.user.userName; } getRuntimeConfig(): RuntimeConfig { return this.runtimeConfig; } } ================================================ FILE: plugin/tegg/test/fixtures/apps/egg-app/modules/multi-module-service/CustomLoggerService.ts ================================================ import { AccessLevel, Inject, SingletonProto, } from '@eggjs/tegg'; import { EggLogger } from 'egg-logger'; @SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) export default class CustomLoggerService { @Inject() xxLogger: EggLogger; @Inject() logger: EggLogger; @Inject() coreLogger: EggLogger; async printLog(): Promise { this.xxLogger.info('hello logger'); this.logger.info('hello logger'); this.coreLogger.info('hello logger'); } } ================================================ FILE: plugin/tegg/test/fixtures/apps/egg-app/modules/multi-module-service/EggTypeService.ts ================================================ import { AccessLevel, EggQualifier, EggType, Inject, SingletonProto } from '@eggjs/tegg'; import { EggLogger } from 'egg-logger'; interface AppDefObj { from: string; } @SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) export default class EggTypeService { @Inject({ name: 'appDefineObject', }) @EggQualifier(EggType.APP) appAppDefineObject: AppDefObj; @Inject({ name: 'appDefineObject', }) @EggQualifier(EggType.CONTEXT) ctxAppDefineObject: AppDefObj; @Inject() @EggQualifier(EggType.APP) logger: EggLogger; testInject() { return { app: this.appAppDefineObject, ctx: this.ctxAppDefineObject, }; } } ================================================ FILE: plugin/tegg/test/fixtures/apps/egg-app/modules/multi-module-service/SingletonFooService.ts ================================================ import { AccessLevel, EggQualifier, EggType, Inject, SingletonProto } from '@eggjs/tegg'; import { EggLogger } from 'egg-logger'; @SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) export default class SingletonFooService { @Inject() @EggQualifier(EggType.APP) logger: EggLogger; async printLog(): Promise { this.logger.info('hello logger'); } } ================================================ FILE: plugin/tegg/test/fixtures/apps/egg-app/modules/multi-module-service/TraceService.ts ================================================ import { AccessLevel, SingletonProto, Inject } from '@eggjs/tegg'; interface Tracer { traceId: string; } @SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) export default class TraceService { @Inject() tracer: Tracer; getTraceId(): string { return this.tracer.traceId; } } ================================================ FILE: plugin/tegg/test/fixtures/apps/egg-app/modules/multi-module-service/package.json ================================================ { "name": "multi-module-service", "eggModule": { "name": "multiModuleService" } } ================================================ FILE: plugin/tegg/test/fixtures/apps/egg-app/package.json ================================================ { "name": "egg-app" } ================================================ FILE: plugin/tegg/test/fixtures/apps/inject-module-config/app/controller/app.ts ================================================ import { Controller } from 'egg'; export default class App extends Controller { async baseDir() { const configs = await this.ctx.module.simple.foo.getConfig(); this.ctx.body = configs; } async overwriteConfig() { const configs = await this.ctx.module.overwrite.bar.getConfig(); this.ctx.body = configs; } } ================================================ FILE: plugin/tegg/test/fixtures/apps/inject-module-config/app/extend/application.unittest.ts ================================================ import { MockApplication } from 'egg-mock'; export default { mockUser(this: MockApplication) { this.mockContext({ user: { userName: 'mock_user', }, }); }, }; ================================================ FILE: plugin/tegg/test/fixtures/apps/inject-module-config/app/extend/context.ts ================================================ const COUNTER = Symbol('Context#counter'); export default { get counter() { if (!this[COUNTER]) { this[COUNTER] = 0; } return this[COUNTER]++; }, get user() { return {}; }, }; ================================================ FILE: plugin/tegg/test/fixtures/apps/inject-module-config/app/router.ts ================================================ import { Application } from 'egg'; module.exports = (app: Application) => { app.router.get('/config', app.controller.app.baseDir); app.router.get('/overwrite_config', app.controller.app.overwriteConfig); }; ================================================ FILE: plugin/tegg/test/fixtures/apps/inject-module-config/app/typings/index.d.ts ================================================ import 'egg'; import { Foo } from '../../modules/module-with-config/foo'; import { Bar } from '../../modules/module-with-overwrite-config/bar'; declare module 'egg' { export interface EggModule { simple: { foo: Foo; }, overwrite: { bar: Bar; }, } } ================================================ FILE: plugin/tegg/test/fixtures/apps/inject-module-config/app.ts ================================================ import { Application } from 'egg'; export default class AppBoot { app: Application; constructor(app: Application) { this.app = app; } configWillLoad() { if (this.app.moduleConfigs?.overwrite?.config) { (this.app.moduleConfigs.overwrite.config as Record).features.dynamic.bar = 'overwrite foo'; } } } ================================================ FILE: plugin/tegg/test/fixtures/apps/inject-module-config/config/config.default.js ================================================ 'use strict'; const path = require('path'); module.exports = function(appInfo) { const config = { keys: 'test key', customLogger: { xxLogger: { file: path.join(appInfo.root, 'logs/xx.log'), }, }, security: { csrf: { ignoreJSON: false, } }, }; return config; }; ================================================ FILE: plugin/tegg/test/fixtures/apps/inject-module-config/config/plugin.js ================================================ 'use strict'; exports.tracer = { package: 'egg-tracer', enable: true, }; exports.teggConfig = { package: '@eggjs/tegg-config', enable: true, }; exports.watcher = false; ================================================ FILE: plugin/tegg/test/fixtures/apps/inject-module-config/modules/module-with-config/foo.ts ================================================ import { AccessLevel, ContextProto, Inject, ModuleConfigs } from '@eggjs/tegg'; @ContextProto({ accessLevel: AccessLevel.PUBLIC, }) export class Foo { @Inject() moduleConfigs: ModuleConfigs; @Inject() moduleConfig: Record; async getConfig(): Promise { return { moduleConfigs: this.moduleConfigs.get('simple'), moduleConfig: this.moduleConfig, }; } } ================================================ FILE: plugin/tegg/test/fixtures/apps/inject-module-config/modules/module-with-config/module.unittest.yml ================================================ features: dynamic: bar: 'foo' ================================================ FILE: plugin/tegg/test/fixtures/apps/inject-module-config/modules/module-with-config/module.yml ================================================ features: dynamic: foo: 'bar' ================================================ FILE: plugin/tegg/test/fixtures/apps/inject-module-config/modules/module-with-config/package.json ================================================ { "name": "simple", "eggModule": { "name": "simple" } } ================================================ FILE: plugin/tegg/test/fixtures/apps/inject-module-config/modules/module-with-overwrite-config/bar.ts ================================================ import { AccessLevel, ContextProto, Inject, ModuleConfigs } from '@eggjs/tegg'; @ContextProto({ accessLevel: AccessLevel.PUBLIC, }) export class Bar { @Inject() moduleConfigs: ModuleConfigs; @Inject() moduleConfig: Record; async getConfig(): Promise { return { moduleConfigs: this.moduleConfigs.get('overwrite'), moduleConfig: this.moduleConfig, }; } } ================================================ FILE: plugin/tegg/test/fixtures/apps/inject-module-config/modules/module-with-overwrite-config/module.unittest.yml ================================================ features: dynamic: bar: 'foo' ================================================ FILE: plugin/tegg/test/fixtures/apps/inject-module-config/modules/module-with-overwrite-config/module.yml ================================================ features: dynamic: foo: 'bar' ================================================ FILE: plugin/tegg/test/fixtures/apps/inject-module-config/modules/module-with-overwrite-config/package.json ================================================ { "name": "overwrite", "eggModule": { "name": "overwrite" } } ================================================ FILE: plugin/tegg/test/fixtures/apps/inject-module-config/package.json ================================================ { "name": "egg-app" } ================================================ FILE: plugin/tegg/test/fixtures/apps/invalid-inject/app/modules/module-a/BarService.ts ================================================ import { SingletonProto, Inject } from '@eggjs/tegg'; @SingletonProto() export class BarService { @Inject() doesNotExist: object; bar() { console.log(this.doesNotExist); } } ================================================ FILE: plugin/tegg/test/fixtures/apps/invalid-inject/app/modules/module-a/package.json ================================================ { "name": "module-a", "eggModule": { "name": "a" } } ================================================ FILE: plugin/tegg/test/fixtures/apps/invalid-inject/config/config.default.js ================================================ 'use strict'; const path = require('path'); module.exports = function(appInfo) { const config = { keys: 'test key', customLogger: { xxLogger: { file: path.join(appInfo.root, 'logs/xx.log'), }, }, security: { csrf: { ignoreJSON: false, }, }, }; return config; }; ================================================ FILE: plugin/tegg/test/fixtures/apps/invalid-inject/config/plugin.js ================================================ 'use strict'; exports.tracer = { package: 'egg-tracer', enable: true, }; exports.teggConfig = { package: '@eggjs/tegg-config', enable: true, }; exports.watcher = false; ================================================ FILE: plugin/tegg/test/fixtures/apps/invalid-inject/package.json ================================================ { "name": "egg-app" } ================================================ FILE: plugin/tegg/test/fixtures/apps/optional-inject/app/modules/module-a/BarService.ts ================================================ import { SingletonProto, Inject, InjectOptional } from '@eggjs/tegg'; @SingletonProto() export class BarService { @Inject({ optional: true }) doesNotExist1?: object; @InjectOptional() doesNotExist2?: object; bar() { return { nil1: this.doesNotExist1 ? 'N' : 'Y', nil2: this.doesNotExist2 ? 'N' : 'Y', }; } } ================================================ FILE: plugin/tegg/test/fixtures/apps/optional-inject/app/modules/module-a/FooService.ts ================================================ import { SingletonProto, Inject, InjectOptional } from '@eggjs/tegg'; @SingletonProto() export class FooService { constructor( @Inject({ optional: true }) readonly doesNotExist1?: object, @InjectOptional() readonly doesNotExist2?: object, ) {} foo() { return { nil1: this.doesNotExist1 ? 'N' : 'Y', nil2: this.doesNotExist2 ? 'N' : 'Y', }; } } ================================================ FILE: plugin/tegg/test/fixtures/apps/optional-inject/app/modules/module-a/package.json ================================================ { "name": "module-a", "eggModule": { "name": "a" } } ================================================ FILE: plugin/tegg/test/fixtures/apps/optional-inject/config/config.default.js ================================================ 'use strict'; const path = require('path'); module.exports = function(appInfo) { const config = { keys: 'test key', customLogger: { xxLogger: { file: path.join(appInfo.root, 'logs/xx.log'), }, }, security: { csrf: { ignoreJSON: false, }, }, }; return config; }; ================================================ FILE: plugin/tegg/test/fixtures/apps/optional-inject/config/plugin.js ================================================ 'use strict'; exports.tracer = { package: 'egg-tracer', enable: true, }; exports.teggConfig = { package: '@eggjs/tegg-config', enable: true, }; exports.watcher = false; ================================================ FILE: plugin/tegg/test/fixtures/apps/optional-inject/package.json ================================================ { "name": "egg-app" } ================================================ FILE: plugin/tegg/test/fixtures/apps/optional-module/app/modules/root/Root.ts ================================================ import { SingletonProto, Inject } from '@eggjs/core-decorator'; import { UsedProto } from 'used/Used'; @SingletonProto() export class RootProto { @Inject() usedProto: UsedProto; } ================================================ FILE: plugin/tegg/test/fixtures/apps/optional-module/app/modules/root/package.json ================================================ { "name": "root", "eggModule": { "name": "root" } } ================================================ FILE: plugin/tegg/test/fixtures/apps/optional-module/config/config.default.js ================================================ 'use strict'; const path = require('path'); module.exports = function(appInfo) { const config = { keys: 'test key', customLogger: { xxLogger: { file: path.join(appInfo.root, 'logs/xx.log'), }, }, security: { csrf: { ignoreJSON: false, } }, }; return config; }; ================================================ FILE: plugin/tegg/test/fixtures/apps/optional-module/config/plugin.js ================================================ 'use strict'; exports.tracer = { package: 'egg-tracer', enable: true, }; exports.teggConfig = { package: '@eggjs/tegg-config', enable: true, }; exports.watcher = false; ================================================ FILE: plugin/tegg/test/fixtures/apps/optional-module/node_modules/foo/package.json ================================================ { "name": "foo", "dependencies": { "used": "*", "unused": "*" } } ================================================ FILE: plugin/tegg/test/fixtures/apps/optional-module/node_modules/unused/Unused.js ================================================ "use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.UnusedProto = void 0; const tegg_core_decorator_1 = require('../../../../../../../../core/core-decorator'); let UnusedProto = class UnusedProto { }; exports.UnusedProto = UnusedProto; exports.UnusedProto = UnusedProto = __decorate([ (0, tegg_core_decorator_1.SingletonProto)() ], UnusedProto); ================================================ FILE: plugin/tegg/test/fixtures/apps/optional-module/node_modules/unused/package.json ================================================ { "name": "unused", "eggModule": { "name": "unused" } } ================================================ FILE: plugin/tegg/test/fixtures/apps/optional-module/node_modules/used/Used.js ================================================ "use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.UsedProto = void 0; const tegg_core_decorator_1 = require('../../../../../../../../core/core-decorator'); let UsedProto = class UsedProto { }; exports.UsedProto = UsedProto; exports.UsedProto = UsedProto = __decorate([ (0, tegg_core_decorator_1.SingletonProto)({ accessLevel: tegg_core_decorator_1.AccessLevel.PUBLIC, }) ], UsedProto); ================================================ FILE: plugin/tegg/test/fixtures/apps/optional-module/node_modules/used/package.json ================================================ { "name": "used", "eggModule": { "name": "used" } } ================================================ FILE: plugin/tegg/test/fixtures/apps/optional-module/package.json ================================================ { "name": "egg-app", "egg": { "framework": "foo" } } ================================================ FILE: plugin/tegg/test/fixtures/apps/plugin-module/config/config.default.js ================================================ 'use strict'; const path = require('path'); module.exports = function(appInfo) { const config = { keys: 'test key', customLogger: { xxLogger: { file: path.join(appInfo.root, 'logs/xx.log'), }, }, security: { csrf: { ignoreJSON: false, } }, }; return config; }; ================================================ FILE: plugin/tegg/test/fixtures/apps/plugin-module/config/plugin.js ================================================ 'use strict'; exports.tracer = { package: 'egg-tracer', enable: true, }; exports.teggConfig = { package: '@eggjs/tegg-config', enable: true, }; exports.eggFooPlugin = { package: 'foo-plugin', enable: true, }; exports.watcher = false; ================================================ FILE: plugin/tegg/test/fixtures/apps/plugin-module/node_modules/foo/package.json ================================================ { "name": "foo", "dependencies": { "foo-plugin": "*" } } ================================================ FILE: plugin/tegg/test/fixtures/apps/plugin-module/node_modules/foo-plugin/Used.js ================================================ "use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.UsedProto = void 0; const tegg_core_decorator_1 = require('../../../../../../../../core/core-decorator'); let UsedProto = class UsedProto { }; exports.UsedProto = UsedProto; exports.UsedProto = UsedProto = __decorate([ (0, tegg_core_decorator_1.SingletonProto)({ accessLevel: tegg_core_decorator_1.AccessLevel.PUBLIC, }) ], UsedProto); ================================================ FILE: plugin/tegg/test/fixtures/apps/plugin-module/node_modules/foo-plugin/package.json ================================================ { "name": "egg-foo-plugin", "eggPlugin": { "name": "eggFooPlugin" }, "eggModule": { "name": "eggFooPlugin" } } ================================================ FILE: plugin/tegg/test/fixtures/apps/plugin-module/package.json ================================================ { "name": "egg-app", "egg": { "framework": "foo" } } ================================================ FILE: plugin/tegg/test/fixtures/apps/recursive-module-app/app/controller/app.ts ================================================ import { Controller } from 'egg'; export default class App extends Controller { async find() { const traceId = await this.ctx.app.module.multiModuleService.traceService.getTraceId(); const app = await this.ctx.app.module.multiModuleService.appService.findApp(this.ctx.query.name); this.ctx.body = { traceId, app, }; } async save() { const app = this.ctx.request.body; const traceId = await this.ctx.app.module.multiModuleService.traceService.getTraceId(); await this.ctx.app.module.multiModuleService.appService.save(app); this.ctx.body = { success: true, traceId, }; } } ================================================ FILE: plugin/tegg/test/fixtures/apps/recursive-module-app/app/router.ts ================================================ import { Application } from 'egg'; module.exports = (app: Application) => { app.router.get('/apps', app.controller.app.find); app.router.post('/apps', app.controller.app.save); }; ================================================ FILE: plugin/tegg/test/fixtures/apps/recursive-module-app/config/config.default.js ================================================ 'use strict'; exports.keys = 'test key'; ================================================ FILE: plugin/tegg/test/fixtures/apps/recursive-module-app/config/module.json ================================================ [ { "path": "../modules/multi-module-repo" }, { "path": "../modules/multi-module-service" } ] ================================================ FILE: plugin/tegg/test/fixtures/apps/recursive-module-app/config/plugin.js ================================================ 'use strict'; exports.tracer = { package: 'egg-tracer', enable: true, }; exports.teggConfig = { package: '@eggjs/tegg-config', enable: true, }; exports.watcher = false; ================================================ FILE: plugin/tegg/test/fixtures/apps/recursive-module-app/modules/multi-module-repo/AppRepo.ts ================================================ import { AccessLevel, SingletonProto, Inject } from '@eggjs/tegg'; import AppService from '../multi-module-service/AppService'; @SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) export default class AppRepo { @Inject() appService: AppService; public async findApp(): Promise> { return this.appService.findApp(); } } ================================================ FILE: plugin/tegg/test/fixtures/apps/recursive-module-app/modules/multi-module-repo/package.json ================================================ { "name": "multi-module-repo", "eggModule": { "name": "multi-module-repo" } } ================================================ FILE: plugin/tegg/test/fixtures/apps/recursive-module-app/modules/multi-module-service/AppService.ts ================================================ import { AccessLevel, SingletonProto, Inject } from '@eggjs/tegg'; import AppRepo from '../multi-module-repo/AppRepo'; @SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) export default class AppService { @Inject() appRepo: AppRepo; findApp(): Promise> { return this.appRepo.findApp(); } } ================================================ FILE: plugin/tegg/test/fixtures/apps/recursive-module-app/modules/multi-module-service/package.json ================================================ { "name": "multi-module-service", "eggModule": { "name": "multiModuleService" } } ================================================ FILE: plugin/tegg/test/fixtures/apps/recursive-module-app/package.json ================================================ { "name": "egg-app" } ================================================ FILE: plugin/tegg/test/fixtures/apps/same-name-protos/app/modules/module-a/BarService.ts ================================================ import { SingletonProto, Inject, ModuleQualifier } from '@eggjs/tegg'; import { FooService } from '../module-foo/FooService'; @SingletonProto() export class BarService { @Inject() @ModuleQualifier('foo') fooService: FooService; bar() { console.log(this.fooService); } } ================================================ FILE: plugin/tegg/test/fixtures/apps/same-name-protos/app/modules/module-a/package.json ================================================ { "name": "module-a", "eggModule": { "name": "a" } } ================================================ FILE: plugin/tegg/test/fixtures/apps/same-name-protos/app/modules/module-bar/FooService.ts ================================================ import { AccessLevel, SingletonProto } from '@eggjs/tegg'; @SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) export class FooService { } ================================================ FILE: plugin/tegg/test/fixtures/apps/same-name-protos/app/modules/module-bar/package.json ================================================ { "name": "module-bar", "eggModule": { "name": "bar" } } ================================================ FILE: plugin/tegg/test/fixtures/apps/same-name-protos/app/modules/module-foo/FooService.ts ================================================ import { AccessLevel, SingletonProto } from '@eggjs/tegg'; @SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) export class FooService { } ================================================ FILE: plugin/tegg/test/fixtures/apps/same-name-protos/app/modules/module-foo/package.json ================================================ { "name": "module-foo", "eggModule": { "name": "foo" } } ================================================ FILE: plugin/tegg/test/fixtures/apps/same-name-protos/config/config.default.js ================================================ 'use strict'; const path = require('path'); module.exports = function(appInfo) { const config = { keys: 'test key', customLogger: { xxLogger: { file: path.join(appInfo.root, 'logs/xx.log'), }, }, security: { csrf: { ignoreJSON: false, } }, }; return config; }; ================================================ FILE: plugin/tegg/test/fixtures/apps/same-name-protos/config/plugin.js ================================================ 'use strict'; exports.tracer = { package: 'egg-tracer', enable: true, }; exports.teggConfig = { package: '@eggjs/tegg-config', enable: true, }; exports.watcher = false; ================================================ FILE: plugin/tegg/test/fixtures/apps/same-name-protos/package.json ================================================ { "name": "egg-app" } ================================================ FILE: plugin/tegg/test/fixtures/apps/same-name-singleton-and-context-proto/app/modules/module-bar/BarConstructorService1.ts ================================================ import { Inject, SingletonProto } from '@eggjs/tegg'; import { FooService } from '../module-foo/FooService'; @SingletonProto() export class BarConstructorService1 { constructor( @Inject() readonly fooService: FooService, ) {} type() { return this.fooService.type; } } ================================================ FILE: plugin/tegg/test/fixtures/apps/same-name-singleton-and-context-proto/app/modules/module-bar/BarConstructorService2.ts ================================================ import { Inject, SingletonProto } from '@eggjs/tegg'; import { FooService } from './FooService'; @SingletonProto() export class BarConstructorService2 { constructor( @Inject() readonly fooService: FooService, ) {} type() { return this.fooService.type; } } ================================================ FILE: plugin/tegg/test/fixtures/apps/same-name-singleton-and-context-proto/app/modules/module-bar/BarService1.ts ================================================ import { Inject, SingletonProto } from '@eggjs/tegg'; import { FooService } from '../module-foo/FooService'; @SingletonProto() export class BarService1 { @Inject() fooService: FooService; type() { return this.fooService.type; } } ================================================ FILE: plugin/tegg/test/fixtures/apps/same-name-singleton-and-context-proto/app/modules/module-bar/BarService2.ts ================================================ import { Inject, SingletonProto } from '@eggjs/tegg'; import { FooService } from './FooService'; @SingletonProto() export class BarService2 { @Inject() fooService: FooService; type() { return this.fooService.type; } } ================================================ FILE: plugin/tegg/test/fixtures/apps/same-name-singleton-and-context-proto/app/modules/module-bar/FooService.ts ================================================ import { AccessLevel, ContextProto } from '@eggjs/tegg'; @ContextProto({ accessLevel: AccessLevel.PUBLIC }) export class FooService { type = 'context'; } ================================================ FILE: plugin/tegg/test/fixtures/apps/same-name-singleton-and-context-proto/app/modules/module-bar/package.json ================================================ { "name": "module-a", "eggModule": { "name": "a" } } ================================================ FILE: plugin/tegg/test/fixtures/apps/same-name-singleton-and-context-proto/app/modules/module-foo/FooService.ts ================================================ import { AccessLevel, SingletonProto } from '@eggjs/tegg'; @SingletonProto({ accessLevel: AccessLevel.PUBLIC }) export class FooService { type = 'singleton'; } ================================================ FILE: plugin/tegg/test/fixtures/apps/same-name-singleton-and-context-proto/app/modules/module-foo/package.json ================================================ { "name": "module-foo", "eggModule": { "name": "foo" } } ================================================ FILE: plugin/tegg/test/fixtures/apps/same-name-singleton-and-context-proto/config/config.default.js ================================================ 'use strict'; const path = require('path'); module.exports = function(appInfo) { const config = { keys: 'test key', customLogger: { xxLogger: { file: path.join(appInfo.root, 'logs/xx.log'), }, }, security: { csrf: { ignoreJSON: false, } }, }; return config; }; ================================================ FILE: plugin/tegg/test/fixtures/apps/same-name-singleton-and-context-proto/config/plugin.js ================================================ 'use strict'; exports.tracer = { package: 'egg-tracer', enable: true, }; exports.teggConfig = { package: '@eggjs/tegg-config', enable: true, }; exports.watcher = false; ================================================ FILE: plugin/tegg/test/fixtures/apps/same-name-singleton-and-context-proto/package.json ================================================ { "name": "egg-app" } ================================================ FILE: plugin/tegg/test/fixtures/apps/schedule-app/app/schedule/foo.ts ================================================ import { Subscription } from 'egg'; export default class Foo extends Subscription { static get schedule() { return { interval: '1m', type: 'all', }; } async subscribe() { await this.ctx.beginModuleScope(async () => { await this.ctx.app.module.multiModuleService.appService.findApp(); }); } } ================================================ FILE: plugin/tegg/test/fixtures/apps/schedule-app/config/config.default.js ================================================ 'use strict'; exports.keys = 'test key'; ================================================ FILE: plugin/tegg/test/fixtures/apps/schedule-app/config/module.json ================================================ [ { "path": "../modules/multi-module-service" }, { "path": "../modules/multi-module-repo" } ] ================================================ FILE: plugin/tegg/test/fixtures/apps/schedule-app/config/plugin.js ================================================ 'use strict'; exports.tracer = { package: 'egg-tracer', enable: true, }; exports.teggConfig = { package: '@eggjs/tegg-config', enable: true, }; exports.watcher = false; ================================================ FILE: plugin/tegg/test/fixtures/apps/schedule-app/modules/multi-module-repo/AppRepo.ts ================================================ import { AccessLevel, SingletonProto } from '@eggjs/tegg'; @SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) export default class AppRepo { public async findApp(): Promise> { return {}; } } ================================================ FILE: plugin/tegg/test/fixtures/apps/schedule-app/modules/multi-module-repo/package.json ================================================ { "name": "multi-module-repo", "eggModule": { "name": "multi-module-repo" } } ================================================ FILE: plugin/tegg/test/fixtures/apps/schedule-app/modules/multi-module-service/AppService.ts ================================================ import { AccessLevel, SingletonProto, Inject } from '@eggjs/tegg'; import AppRepo from '../multi-module-repo/AppRepo'; @SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) export default class AppService { @Inject() appRepo: AppRepo; findApp(): Promise> { return this.appRepo.findApp(); } } ================================================ FILE: plugin/tegg/test/fixtures/apps/schedule-app/modules/multi-module-service/package.json ================================================ { "name": "multi-module-service", "eggModule": { "name": "multiModuleService" } } ================================================ FILE: plugin/tegg/test/fixtures/apps/schedule-app/package.json ================================================ { "name": "egg-app" } ================================================ FILE: plugin/tegg/test/fixtures/apps/wrong-order-app/app/controller/app.ts ================================================ import { Controller } from 'egg'; export default class App extends Controller { async find() { const traceId = await this.ctx.app.module.multiModuleService.traceService.getTraceId(); const app = await this.ctx.app.module.multiModuleService.appService.findApp(this.ctx.query.name); this.ctx.body = { traceId, app, }; } async save() { const app = this.ctx.request.body; const traceId = await this.ctx.app.module.multiModuleService.traceService.getTraceId(); await this.ctx.app.module.multiModuleService.appService.save(app); this.ctx.body = { success: true, traceId, }; } } ================================================ FILE: plugin/tegg/test/fixtures/apps/wrong-order-app/app/router.ts ================================================ import { Application } from 'egg'; module.exports = (app: Application) => { app.router.get('/apps', app.controller.app.find); app.router.post('/apps', app.controller.app.save); }; ================================================ FILE: plugin/tegg/test/fixtures/apps/wrong-order-app/config/config.default.js ================================================ 'use strict'; exports.keys = 'test key'; ================================================ FILE: plugin/tegg/test/fixtures/apps/wrong-order-app/config/module.json ================================================ [ { "path": "../modules/multi-module-service" }, { "path": "../modules/multi-module-repo" } ] ================================================ FILE: plugin/tegg/test/fixtures/apps/wrong-order-app/config/plugin.js ================================================ 'use strict'; exports.tracer = { package: 'egg-tracer', enable: true, }; exports.teggConfig = { package: '@eggjs/tegg-config', enable: true, }; exports.watcher = false; ================================================ FILE: plugin/tegg/test/fixtures/apps/wrong-order-app/modules/multi-module-repo/AppRepo.ts ================================================ import { AccessLevel, SingletonProto } from '@eggjs/tegg'; @SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) export default class AppRepo { public async findApp(): Promise> { return {}; } } ================================================ FILE: plugin/tegg/test/fixtures/apps/wrong-order-app/modules/multi-module-repo/package.json ================================================ { "name": "multi-module-repo", "eggModule": { "name": "multi-module-repo" } } ================================================ FILE: plugin/tegg/test/fixtures/apps/wrong-order-app/modules/multi-module-service/AppService.ts ================================================ import { AccessLevel, SingletonProto, Inject } from '@eggjs/tegg'; import AppRepo from '../multi-module-repo/AppRepo'; @SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) export default class AppService { @Inject() appRepo: AppRepo; findApp(): Promise> { return this.appRepo.findApp(); } } ================================================ FILE: plugin/tegg/test/fixtures/apps/wrong-order-app/modules/multi-module-service/package.json ================================================ { "name": "multi-module-service", "eggModule": { "name": "multiModuleService" } } ================================================ FILE: plugin/tegg/test/fixtures/apps/wrong-order-app/package.json ================================================ { "name": "egg-app" } ================================================ FILE: plugin/tegg/test/lib/EggModuleLoader.test.ts ================================================ import mm from 'egg-mock'; import assert from 'assert'; import path from 'path'; describe('test/lib/EggModuleLoader.test.ts', () => { beforeEach(() => { mm(process.env, 'EGG_TYPESCRIPT', true); mm(process, 'cwd', () => { return path.join(__dirname, '../..'); }); }); afterEach(() => { mm.restore(); }); describe('has recursive dependency module', () => { it('should throw error', async () => { const app = mm.app({ baseDir: path.join(__dirname, '../fixtures/apps/recursive-module-app'), framework: require.resolve('egg'), }); await assert.rejects(() => app.ready(), /module has recursive deps/); return app.close(); }); }); describe('module config in wrong order', () => { it('should load module success', async () => { const app = mm.app({ baseDir: path.join(__dirname, '../fixtures/apps/wrong-order-app'), framework: require.resolve('egg'), }); await app.ready(); return app.close(); }); }); }); ================================================ FILE: plugin/tegg/tsconfig.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "baseUrl": "./" }, "exclude": [ "node_modules" ] } ================================================ FILE: plugin/tegg/tsconfig.pub.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "baseUrl": "./" }, "exclude": [ "node_modules", "test" ] } ================================================ FILE: plugin/tegg/typings/index.d.ts ================================================ import { AsyncLocalStorage } from 'async_hooks'; import { Context } from 'egg'; import '@eggjs/tegg-config'; import { EggPrototypeCreatorFactory } from '@eggjs/tegg-metadata'; import { EggPrototypeFactory, LoadUnitLifecycleUtil, LoadUnitFactory, EggPrototypeLifecycleUtil, } from '@eggjs/tegg-metadata'; import { EggContainerFactory, LoadUnitInstanceFactory, LoadUnitInstanceLifecycleUtil, EggContextLifecycleUtil, EggObjectLifecycleUtil, AbstractEggContext, EggObjectFactory, EggContext, } from '@eggjs/tegg-runtime'; import { LoaderFactory } from '@eggjs/tegg-loader'; import { IdenticalUtil, EggProtoImplClass, QualifierInfo } from '@eggjs/tegg'; import { ModuleHandler } from '../lib/ModuleHandler'; import { EggContextHandler } from '../lib/EggContextHandler'; declare module 'egg' { export interface EggModule { } export interface EggContextModule { } export interface EggApplicationModule { } export interface TEggApplication { eggPrototypeCreatorFactory: typeof EggPrototypeCreatorFactory; eggPrototypeFactory: EggPrototypeFactory; eggContainerFactory: typeof EggContainerFactory; loadUnitFactory: typeof LoadUnitFactory; eggObjectFactory: typeof EggObjectFactory; loadUnitInstanceFactory: typeof LoadUnitInstanceFactory; abstractEggContext: typeof AbstractEggContext; identicalUtil: typeof IdenticalUtil; loaderFactory: typeof LoaderFactory; loadUnitLifecycleUtil: typeof LoadUnitLifecycleUtil; loadUnitInstanceLifecycleUtil: typeof LoadUnitInstanceLifecycleUtil; eggPrototypeLifecycleUtil: typeof EggPrototypeLifecycleUtil; eggContextLifecycleUtil: typeof EggContextLifecycleUtil; eggObjectLifecycleUtil: typeof EggObjectLifecycleUtil; teggContext: EggContext; moduleHandler: ModuleHandler; eggContextHandler: EggContextHandler; mockModuleContext(data?: any): Promise; mockModuleContextScope(fn: (ctx: Context) => Promise, data?: any): Promise; destroyModuleContext(context: Context): Promise; // 兼容现有 module 的定义 module: EggModule & EggApplicationModule; getEggObject(clazz: EggProtoImplClass, name?: string, qualifiers?: QualifierInfo | QualifierInfo[]): Promise; getEggObjectFromName(name: string, qualifiers?: QualifierInfo | QualifierInfo[]): Promise; } export interface TEggContext { beginModuleScope(func: () => Promise): Promise; // 兼容现有 module 的定义 module: EggModule & EggContextModule; getEggObject(clazz: EggProtoImplClass, name?: string, qualifiers?: QualifierInfo | QualifierInfo[]): Promise; } interface Application extends TEggApplication { } interface Context extends TEggContext { } } ================================================ FILE: standalone/service-worker/CHANGELOG.md ================================================ # Change Log All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. ## [3.78.15](https://github.com/eggjs/tegg/compare/v3.78.14...v3.78.15) (2026-04-23) **Note:** Version bump only for package @eggjs/tegg-service-worker ## [3.78.14](https://github.com/eggjs/tegg/compare/v3.78.13...v3.78.14) (2026-04-21) ### Bug Fixes * mcp path ([3835288](https://github.com/eggjs/tegg/commit/3835288e9b78e3d2f422e91e3e56bf4ead0c4372)) ## [3.78.13](https://github.com/eggjs/tegg/compare/v3.78.12...v3.78.13) (2026-04-21) **Note:** Version bump only for package @eggjs/tegg-service-worker ## [3.78.12](https://github.com/eggjs/tegg/compare/v3.78.11...v3.78.12) (2026-04-18) ### Bug Fixes * del debug log ([a7d8e06](https://github.com/eggjs/tegg/commit/a7d8e0608d709a733ea463c75ca955184ae4c552)) ## [3.78.11](https://github.com/eggjs/tegg/compare/v3.78.10...v3.78.11) (2026-04-18) ### Bug Fixes * limit mcp version ([8627372](https://github.com/eggjs/tegg/commit/86273726bbf4f33e0856dc726aa3f7ff963e9e99)) ## [3.78.10](https://github.com/eggjs/tegg/compare/v3.78.9...v3.78.10) (2026-04-18) ### Bug Fixes * import ([c7ed1b7](https://github.com/eggjs/tegg/commit/c7ed1b78f9c0ee308c85029e79d5187fd7fd1bd4)) ## [3.78.9](https://github.com/eggjs/tegg/compare/v3.78.8...v3.78.9) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-service-worker ## [3.78.8](https://github.com/eggjs/tegg/compare/v3.78.7...v3.78.8) (2026-04-18) ### Bug Fixes * remove egg module ([#438](https://github.com/eggjs/tegg/issues/438)) ([c82c26e](https://github.com/eggjs/tegg/commit/c82c26ebfa8272e32477f9b5be51da85e70904a6)) ## [3.78.7](https://github.com/eggjs/tegg/compare/v3.78.6...v3.78.7) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-service-worker ## [3.78.6](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.6) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-service-worker ## [3.78.5](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.5) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-service-worker ## [3.78.4](https://github.com/eggjs/tegg/compare/v3.78.3...v3.78.4) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-service-worker ================================================ FILE: standalone/service-worker/index.ts ================================================ export * from './src/constants'; export * from './src/types'; export * from './src/ServiceWorkerApp'; export * from './src/hook/ContextProtoLoadUnitHook'; export * from './src/hook/ControllerPrototypeHook'; export * from './src/hook/ControllerLoadUnitHook'; export * from './src/controller/ControllerMetadataManager'; export * from './src/controller/ControllerRegister'; export * from './src/controller/ControllerRegisterFactory'; export * from './src/controller/RootProtoManager'; export * from './src/controller/ServiceWorkerContext'; export * from './src/http/FetchEventHandler'; export * from './src/http/FetchRouter'; export * from './src/http/HTTPControllerRegister'; export * from './src/http/HTTPMethodRegister'; export * from './src/http/ServiceWorkerFetchContext'; export * from './src/utils/RequestUtils'; export * from './src/utils/ResponseUtils'; export * from './src/mcp/AbstractControllerAdvice'; export * from './src/mcp/MCPControllerRegister'; export * from './src/mcp/MCPServerHelper'; ================================================ FILE: standalone/service-worker/package.json ================================================ { "name": "@eggjs/tegg-service-worker", "description": "tegg service worker", "version": "3.78.15", "keywords": [ "egg", "typescript", "tegg", "standalone", "service worker" ], "main": "dist/index.js", "files": [ "dist/**/*.js", "dist/**/*.d.ts" ], "typings": "dist/index.d.ts", "scripts": { "test": "node --eval \"process.exit(parseInt(process.versions.node) < 18 ? 0 : 1)\" || mocha", "clean": "tsc -b --clean", "tsc": "npm run clean && tsc -p ./tsconfig.json", "tsc:pub": "npm run clean && tsc -p ./tsconfig.pub.json", "prepublishOnly": "npm run tsc:pub" }, "homepage": "https://github.com/eggjs/tegg", "bugs": { "url": "https://github.com/eggjs/tegg/issues" }, "repository": { "type": "git", "url": "git+ssh://git@github.com/eggjs/tegg.git", "directory": "standalone/service-worker" }, "engines": { "node": ">=18.0.0" }, "author": "killagu ", "license": "MIT", "publishConfig": { "access": "public" }, "dependencies": { "@eggjs/router": "^3.0.6", "@eggjs/tegg-ajv-plugin": "^3.78.15", "@eggjs/tegg-aop-runtime": "^3.78.15", "@eggjs/tegg-dal-plugin": "^3.78.15", "@eggjs/tegg-dynamic-inject-runtime": "^3.78.15", "@eggjs/tegg-lifecycle": "^3.78.15", "@eggjs/tegg-metadata": "^3.78.15", "@eggjs/tegg-standalone": "^3.78.15", "@eggjs/tegg-types": "^3.78.15", "@modelcontextprotocol/sdk": "1.24.3", "egg-errors": "^2.3.0", "path-to-regexp": "^1.8.0", "type-is": "^1.6.18", "urllib": "^4.0.0" }, "peerDependencies": { "@eggjs/tegg": "^3" }, "devDependencies": { "@eggjs/module-test-util": "^3.78.15", "@eggjs/tegg": "^3.78.15", "@types/mocha": "^10.0.1", "@types/node": "^20.2.4", "@types/type-is": "^1.6.6", "cross-env": "^7.0.3", "mocha": "^10.2.0", "supertest": "^7.1.1", "ts-node": "^10.9.1", "typescript": "^5.0.4", "undici": "^5.26.5" }, "directories": { "test": "test" }, "type": "commonjs" } ================================================ FILE: standalone/service-worker/src/ServiceWorkerApp.ts ================================================ import path from 'node:path'; import { EggPrototypeLifecycleUtil, LoadUnitLifecycleUtil, } from '@eggjs/tegg-metadata'; import { Runner, RunnerOptions, StandaloneContext } from '@eggjs/tegg-standalone'; import type { Logger } from '@eggjs/tegg-types'; import { getDefaultHttpClient } from 'urllib'; import { ContextProtoProperty } from './constants'; import { FetchRouter } from './http/FetchRouter'; import { RootProtoManager } from './controller/RootProtoManager'; import { ControllerMetadataManager } from './controller/ControllerMetadataManager'; import { ControllerRegisterFactory } from './controller/ControllerRegisterFactory'; import { ContextProtoLoadUnitHook } from './hook/ContextProtoLoadUnitHook'; import { ControllerPrototypeHook } from './hook/ControllerPrototypeHook'; import { ControllerLoadUnitHook } from './hook/ControllerLoadUnitHook'; import { HTTPControllerRegister } from './http/HTTPControllerRegister'; import { MCPControllerRegister } from './mcp/MCPControllerRegister'; import { LoadUnitInnerClassHook } from './hook/LoadUnitInnerClassHook'; import { ServiceWorkerRunner } from './ServiceWorkerRunner'; import { StandaloneEggObjectFactory } from './StandaloneEggObjectFactory'; import { FetchEventHandler } from './http/FetchEventHandler'; export interface ServiceWorkerAppOptions { innerObjectHandlers?: RunnerOptions['innerObjectHandlers']; logger?: Logger; } export class ServiceWorkerApp { private readonly runner: Runner; private readonly contextProtoLoadUnitHook: ContextProtoLoadUnitHook; private readonly controllerPrototypeHook: ControllerPrototypeHook; private readonly controllerLoadUnitHook: ControllerLoadUnitHook; private readonly fetchRouter: FetchRouter; private readonly rootProtoManager: RootProtoManager; private readonly controllerMetadataManager: ControllerMetadataManager; private readonly controllerRegisterFactory: ControllerRegisterFactory; private readonly loadUnitInnerClassHook: LoadUnitInnerClassHook; constructor(cwd: string, options?: ServiceWorkerAppOptions & RunnerOptions) { // Create shared objects this.fetchRouter = new FetchRouter(); this.rootProtoManager = new RootProtoManager(); this.controllerMetadataManager = new ControllerMetadataManager(); this.controllerRegisterFactory = new ControllerRegisterFactory(); // Create lifecycle hooks this.contextProtoLoadUnitHook = new ContextProtoLoadUnitHook('serviceWorker'); this.controllerPrototypeHook = new ControllerPrototypeHook(); this.controllerLoadUnitHook = new ControllerLoadUnitHook( this.controllerRegisterFactory, this.rootProtoManager, this.controllerMetadataManager, this.fetchRouter, ); // Register lifecycle hooks LoadUnitLifecycleUtil.registerLifecycle(this.contextProtoLoadUnitHook); LoadUnitLifecycleUtil.registerLifecycle(this.controllerLoadUnitHook); EggPrototypeLifecycleUtil.registerLifecycle(this.controllerPrototypeHook); // Build dependencies list - include this package as a framework dependency const frameworkDep = { baseDir: path.join(__dirname, '..'), extraFilePattern: [ '!**/test' ] }; const deps = [ ...(options?.dependencies || []), frameworkDep ]; // Register FetchRouter and RootProtoManager as inner objects so they can be @Inject()-ed const innerObjectHandlers: RunnerOptions['innerObjectHandlers'] = { ...options?.innerObjectHandlers, fetchRouter: [{ obj: this.fetchRouter }], rootProtoManager: [{ obj: this.rootProtoManager }], }; // Provide default logger (fallback to console) and httpclient (urllib singleton) if (!innerObjectHandlers.logger) { innerObjectHandlers.logger = [{ obj: options?.logger || console }]; } if (!innerObjectHandlers.httpclient) { innerObjectHandlers.httpclient = [{ obj: getDefaultHttpClient() }]; } this.loadUnitInnerClassHook = new LoadUnitInnerClassHook([ StandaloneEggObjectFactory, ServiceWorkerRunner, FetchEventHandler ]); LoadUnitLifecycleUtil.registerLifecycle(this.loadUnitInnerClassHook); this.runner = new Runner(cwd, { ...options, dependencies: deps, innerObjectHandlers, }); } async init() { await this.runner.init(); } async handleEvent(event: Event) { const context = new StandaloneContext(); context.set(ContextProtoProperty.Event.contextKey, event); return await this.runner.run(context); } async destroy() { // Clean up static singletons HTTPControllerRegister.clean(); MCPControllerRegister.clean(); // Unregister lifecycle hooks LoadUnitLifecycleUtil.deleteLifecycle(this.contextProtoLoadUnitHook); LoadUnitLifecycleUtil.deleteLifecycle(this.controllerLoadUnitHook); LoadUnitLifecycleUtil.deleteLifecycle(this.loadUnitInnerClassHook); EggPrototypeLifecycleUtil.deleteLifecycle(this.controllerPrototypeHook); await this.runner.destroy(); } } ================================================ FILE: standalone/service-worker/src/ServiceWorkerRunner.ts ================================================ import { SingletonProto, Inject, EggObjectFactory } from '@eggjs/tegg'; import { Runner, AbstractEventHandler, FetchEventLike } from '@eggjs/tegg/standalone'; import { ContextHandler } from '@eggjs/tegg/helper'; import { ContextProtoProperty } from './constants'; @Runner() @SingletonProto() export class ServiceWorkerRunner { @Inject({ name: 'standaloneEggObjectFactory' }) private readonly eggObjectFactory: EggObjectFactory; async main(): Promise { const requestContext = ContextHandler.getContext(); if (!requestContext) { throw new Error('[tegg-standalone-framework] no request context in FetchRunner'); } const event = requestContext.get(ContextProtoProperty.Event.contextKey) as FetchEventLike | undefined; if (!event) { throw new Error('[tegg-standalone-framework] no fetch event on context'); } const handler = await this.eggObjectFactory.getEggObject(AbstractEventHandler, event.type); return await handler.handleEvent(event); } } ================================================ FILE: standalone/service-worker/src/StandaloneEggObjectFactory.ts ================================================ import { AccessLevel, SingletonProto } from '@eggjs/tegg'; import { EggObjectFactory } from '@eggjs/tegg-dynamic-inject-runtime'; import { EGG_OBJECT_FACTORY_PROTO_IMPLE_TYPE, } from '@eggjs/tegg-dynamic-inject-runtime'; @SingletonProto({ protoImplType: EGG_OBJECT_FACTORY_PROTO_IMPLE_TYPE, accessLevel: AccessLevel.PRIVATE, }) export class StandaloneEggObjectFactory extends EggObjectFactory {} ================================================ FILE: standalone/service-worker/src/constants.ts ================================================ import { ProtoMeta } from './types'; export class ContextProtoProperty { static readonly Event: ProtoMeta = { protoName: 'event', contextKey: Symbol('context#event'), }; } ================================================ FILE: standalone/service-worker/src/controller/ControllerMetadataManager.ts ================================================ import { ControllerMetadata, ControllerTypeLike } from '@eggjs/tegg'; import { MapUtil } from '@eggjs/tegg/helper'; export class ControllerMetadataManager { private readonly controllers = new Map(); addController(metadata: ControllerMetadata) { const typeControllers = MapUtil.getOrStore(this.controllers, metadata.type, []); // 1.check controller name // 2.check proto name const sameNameControllers = typeControllers.filter(c => c.controllerName === metadata.controllerName); if (sameNameControllers.length) { throw new Error(`duplicate controller name ${metadata.controllerName}`); } const sameProtoControllers = typeControllers.filter(c => c.protoName === metadata.protoName); if (sameProtoControllers.length) { throw new Error(`duplicate proto name ${String(metadata.protoName)}`); } typeControllers.push(metadata); } clear() { this.controllers.clear(); } } ================================================ FILE: standalone/service-worker/src/controller/ControllerRegister.ts ================================================ import { RootProtoManager } from './RootProtoManager'; export interface ControllerRegister { register(rootProtoManager: RootProtoManager): Promise; } ================================================ FILE: standalone/service-worker/src/controller/ControllerRegisterFactory.ts ================================================ import { ControllerMetadata, ControllerTypeLike } from '@eggjs/tegg'; import { EggPrototype } from '@eggjs/tegg/helper'; import { ControllerRegister } from './ControllerRegister'; export type RegisterCreator = (proto: EggPrototype, controllerMeta: ControllerMetadata) => ControllerRegister; export class ControllerRegisterFactory { #registerCreatorMap: Map; constructor() { this.#registerCreatorMap = new Map(); } registerControllerRegister(type: ControllerTypeLike, creator: RegisterCreator) { this.#registerCreatorMap.set(type, creator); } getControllerRegister(proto: EggPrototype, metadata: ControllerMetadata): ControllerRegister | undefined { const creator = this.#registerCreatorMap.get(metadata.type); if (!creator) { return; } return creator(proto, metadata); } } ================================================ FILE: standalone/service-worker/src/controller/RootProtoManager.ts ================================================ import { EggPrototype, MapUtil } from '@eggjs/tegg/helper'; import { EggContext } from '@eggjs/tegg'; export type GetRootProtoCallback = (ctx: EggContext) => EggPrototype | undefined; export class RootProtoManager { // protoMap: Map = new Map(); registerRootProto(method: string, cb: GetRootProtoCallback, host?: string) { host = host || ''; const cbList = MapUtil.getOrStore(this.protoMap, method + host, []); cbList.push(cb); } getRootProto(ctx: EggContext): EggPrototype | undefined { const hostCbList = this.protoMap.get(ctx.method + ctx.host); if (hostCbList) { for (const cb of hostCbList) { const proto = cb(ctx); if (proto) { return proto; } } } const cbList = this.protoMap.get(ctx.method); if (!cbList) { return; } for (const cb of cbList) { const proto = cb(ctx); if (proto) { return proto; } } } } ================================================ FILE: standalone/service-worker/src/controller/ServiceWorkerContext.ts ================================================ import { ServiceWorkerContext, ServiceWorkerContextInit } from '@eggjs/tegg-types/standalone'; export abstract class BaseServiceWorkerContextImpl implements ServiceWorkerContext { event: Event; #response: Response; constructor(init: ServiceWorkerContextInit) { this.event = init.event; } get response(): Response | undefined { return this.#response; } set response(response: Response) { this.#response = response; } abstract get body(): any | undefined; abstract set body(body: any); } ================================================ FILE: standalone/service-worker/src/hook/ContextProtoLoadUnitHook.ts ================================================ import assert from 'node:assert'; import { IdenticalUtil, LifecycleHook } from '@eggjs/tegg-lifecycle'; import { ContextHandler, EggPrototypeFactory, LoadUnit, LoadUnitLifecycleContext } from '@eggjs/tegg/helper'; import { StandaloneInnerObjectProto } from '@eggjs/tegg-standalone'; import { ObjectInitType } from '@eggjs/tegg-types'; import { ProtoMeta } from '../types'; import { ContextProtoProperty } from '../constants'; export class ContextProtoLoadUnitHook implements LifecycleHook { private readonly moduleName: string; constructor(moduleName: string) { this.moduleName = moduleName; } async preCreate(_: LoadUnitLifecycleContext, loadUnit: LoadUnit): Promise { if (loadUnit.name === this.moduleName) { // can `@Inject() event` ContextProtoLoadUnitHook.registerPrototype(ContextProtoProperty.Event, loadUnit); } } static registerPrototype(protoMeta: ProtoMeta, loadUnit: LoadUnit) { const proto = new StandaloneInnerObjectProto( IdenticalUtil.createProtoId(loadUnit.id, protoMeta.protoName), protoMeta.protoName, (() => { const ctx = ContextHandler.getContext(); assert(ctx, 'context should not be null'); return ctx.get(protoMeta.contextKey); }) as any, ObjectInitType.CONTEXT, loadUnit.id, [], ); EggPrototypeFactory.instance.registerPrototype(proto, loadUnit); } } ================================================ FILE: standalone/service-worker/src/hook/ControllerLoadUnitHook.ts ================================================ import { LifecycleHook } from '@eggjs/tegg-lifecycle'; import { EggPrototype, LoadUnit, LoadUnitLifecycleContext } from '@eggjs/tegg/helper'; import { CONTROLLER_META_DATA, ControllerMetadata, ControllerType } from '@eggjs/tegg'; import { ControllerRegisterFactory } from '../controller/ControllerRegisterFactory'; import { RootProtoManager } from '../controller/RootProtoManager'; import { ControllerMetadataManager } from '../controller/ControllerMetadataManager'; import { FetchRouter } from '../http/FetchRouter'; import { HTTPControllerRegister } from '../http/HTTPControllerRegister'; import { MCPControllerRegister } from '../mcp/MCPControllerRegister'; export class ControllerLoadUnitHook implements LifecycleHook { private readonly controllerRegisterFactory: ControllerRegisterFactory; private readonly rootProtoManager: RootProtoManager; private readonly controllerMetadataManager: ControllerMetadataManager; private readonly fetchRouter: FetchRouter; constructor( controllerRegisterFactory: ControllerRegisterFactory, rootProtoManager: RootProtoManager, controllerMetadataManager: ControllerMetadataManager, fetchRouter: FetchRouter, ) { this.controllerRegisterFactory = controllerRegisterFactory; this.rootProtoManager = rootProtoManager; this.controllerMetadataManager = controllerMetadataManager; this.fetchRouter = fetchRouter; // Register the HTTP controller register creator this.controllerRegisterFactory.registerControllerRegister(ControllerType.HTTP, (proto: EggPrototype, controllerMeta: ControllerMetadata) => { return HTTPControllerRegister.create(proto, controllerMeta, this.fetchRouter); }); // Register the MCP controller register creator this.controllerRegisterFactory.registerControllerRegister(ControllerType.MCP, (proto: EggPrototype, controllerMeta: ControllerMetadata) => { return MCPControllerRegister.create(proto, controllerMeta, this.fetchRouter); }); } async postCreate(_: LoadUnitLifecycleContext, loadUnit: LoadUnit): Promise { const iterator = loadUnit.iterateEggPrototype(); for (const proto of iterator) { const metadata: ControllerMetadata | undefined = proto.getMetaData(CONTROLLER_META_DATA); if (!metadata) { continue; } const register = this.controllerRegisterFactory.getControllerRegister(proto, metadata); if (!register) { throw new Error(`not find controller implement for ${String(proto.name)} which type is ${metadata.type}`); } this.controllerMetadataManager.addController(metadata); await register.register(this.rootProtoManager); } } } ================================================ FILE: standalone/service-worker/src/hook/ControllerPrototypeHook.ts ================================================ import { EggPrototype, EggPrototypeLifecycleContext } from '@eggjs/tegg-metadata'; import { ControllerMetaBuilderFactory, ControllerMetadataUtil, LifecycleHook, } from '@eggjs/tegg'; export class ControllerPrototypeHook implements LifecycleHook { async postCreate(ctx: EggPrototypeLifecycleContext): Promise { const metadata = ControllerMetaBuilderFactory.build(ctx.clazz); if (metadata) { ControllerMetadataUtil.setControllerMetadata(ctx.clazz, metadata); } } } ================================================ FILE: standalone/service-worker/src/hook/LoadUnitInnerClassHook.ts ================================================ import { EggProtoImplClass, LifecycleHook } from '@eggjs/tegg'; import { EggPrototypeCreatorFactory, EggPrototypeFactory } from '@eggjs/tegg/helper'; import type { LoadUnit, LoadUnitLifecycleContext } from '@eggjs/tegg/helper'; export class LoadUnitInnerClassHook implements LifecycleHook { private readonly innerClasses: EggProtoImplClass[]; constructor(innerClasses: EggProtoImplClass[]) { this.innerClasses = innerClasses; } async postCreate(_: LoadUnitLifecycleContext, loadUnit: LoadUnit): Promise { if (loadUnit.type === 'StandaloneLoadUnitType') { for (const clazz of this.innerClasses) { const protos = await EggPrototypeCreatorFactory.createProto(clazz, loadUnit); for (const proto of protos) { EggPrototypeFactory.instance.registerPrototype(proto, loadUnit); } } } } } ================================================ FILE: standalone/service-worker/src/http/FetchEventHandler.ts ================================================ import { MiddlewareFuncWithRouter } from '@eggjs/router'; import { FetchEvent } from '@eggjs/tegg-types/standalone'; import { AccessLevel, Inject, } from '@eggjs/tegg'; import { AbstractEventHandler, EventHandlerProto } from '@eggjs/tegg/standalone'; import { FetchRouter } from './FetchRouter'; import { ServiceWorkerFetchContext } from './ServiceWorkerFetchContext'; import { RootProtoManager } from '../controller/RootProtoManager'; import { HTTPControllerRegister } from './HTTPControllerRegister'; import { MCPControllerRegister } from '../mcp/MCPControllerRegister'; @EventHandlerProto('fetch', { accessLevel: AccessLevel.PUBLIC }) export class FetchEventHandler extends AbstractEventHandler { @Inject() private readonly fetchRouter: FetchRouter; @Inject() private readonly rootProtoManager: RootProtoManager; #routes: MiddlewareFuncWithRouter; #initialized = false; private async initRoutes() { if (!this.#initialized) { HTTPControllerRegister.instance?.doRegister(this.rootProtoManager); await MCPControllerRegister.instance?.doRegister(); this.#routes = this.fetchRouter.middleware(); this.#initialized = true; } } async handleEvent(event: FetchEvent): Promise { await this.initRoutes(); const ctx = new ServiceWorkerFetchContext({ event }); try { await this.#routes(ctx, async () => { /**/ }); if (ctx.response) { return ctx.response; } return new Response(null, { status: 404 }); } catch (e) { console.error('handle event failed:', e); return new Response(e.message, { status: 500 }); } } } ================================================ FILE: standalone/service-worker/src/http/FetchRouter.ts ================================================ import { KoaRouter } from '@eggjs/router'; export class FetchRouter extends KoaRouter {} ================================================ FILE: standalone/service-worker/src/http/HTTPControllerRegister.ts ================================================ import assert from 'assert'; import { Router as KoaRouter } from '@eggjs/router'; import { CONTROLLER_META_DATA, ControllerMetadata, ControllerType, HTTPControllerMeta, HTTPMethodMeta, } from '@eggjs/tegg'; import { EggPrototype } from '@eggjs/tegg/helper'; import { ControllerRegister } from '../controller/ControllerRegister'; import { HTTPMethodRegister } from './HTTPMethodRegister'; import { RootProtoManager } from '../controller/RootProtoManager'; export class HTTPControllerRegister implements ControllerRegister { static instance?: HTTPControllerRegister; private readonly router: KoaRouter; private readonly checkRouters: Map; private controllerProtos: EggPrototype[] = []; static create(proto: EggPrototype, controllerMeta: ControllerMetadata, router: KoaRouter) { assert(controllerMeta.type === ControllerType.HTTP, 'controller meta type is not HTTP'); if (!HTTPControllerRegister.instance) { HTTPControllerRegister.instance = new HTTPControllerRegister(router); } HTTPControllerRegister.instance.controllerProtos.push(proto); return HTTPControllerRegister.instance; } constructor(router: KoaRouter) { this.router = router; this.checkRouters = new Map(); this.checkRouters.set('default', router); } register(): Promise { // do noting return Promise.resolve(); } static clean() { if (this.instance) { this.instance.controllerProtos = []; this.instance.checkRouters.clear(); } this.instance = undefined; } doRegister(rootProtoManager: RootProtoManager) { const methodMap = new Map(); for (const proto of this.controllerProtos) { const metadata = proto.getMetaData(CONTROLLER_META_DATA) as HTTPControllerMeta; for (const method of metadata.methods) { methodMap.set(method, proto); } } const allMethods = Array.from(methodMap.keys()) .sort((a, b) => b.priority - a.priority); for (const method of allMethods) { const controllerProto = methodMap.get(method)!; const controllerMeta = controllerProto.getMetaData(CONTROLLER_META_DATA) as HTTPControllerMeta; const methodRegister = new HTTPMethodRegister( controllerProto, controllerMeta, method, this.router, this.checkRouters); methodRegister.checkDuplicate(); } for (const method of allMethods) { const controllerProto = methodMap.get(method)!; const controllerMeta = controllerProto.getMetaData(CONTROLLER_META_DATA) as HTTPControllerMeta; const methodRegister = new HTTPMethodRegister( controllerProto, controllerMeta, method, this.router, this.checkRouters); methodRegister.register(rootProtoManager); } } } ================================================ FILE: standalone/service-worker/src/http/HTTPMethodRegister.ts ================================================ import pathToRegexp from 'path-to-regexp'; import { FrameworkErrorFormater } from 'egg-errors'; import { Router as KoaRouter } from '@eggjs/router'; import { EggContext, HTTPControllerMeta, HTTPMethodMeta, HTTPParamType, IncomingHttpHeaders, Next, PathParamMeta, QueriesParamMeta, QueryParamMeta, } from '@eggjs/tegg'; import type { EggProtoImplClass } from '@eggjs/tegg-types'; import { CONTROLLER_AOP_MIDDLEWARES, METHOD_AOP_MIDDLEWARES } from '@eggjs/tegg-types/controller-decorator'; import { EggContainerFactory, EggPrototype } from '@eggjs/tegg/helper'; import type { AbstractControllerAdvice } from '../mcp/AbstractControllerAdvice'; import { RootProtoManager } from '../controller/RootProtoManager'; import { ServiceWorkerFetchContext } from './ServiceWorkerFetchContext'; import { RequestUtils } from '../utils/RequestUtils'; const noop = () => { /* noop */ }; export class HTTPMethodRegister { private readonly router: KoaRouter; private readonly checkRouters: Map; private readonly controllerMeta: HTTPControllerMeta; private readonly methodMeta: HTTPMethodMeta; private readonly proto: EggPrototype; constructor( proto: EggPrototype, controllerMeta: HTTPControllerMeta, methodMeta: HTTPMethodMeta, router: KoaRouter, checkRouters: Map, ) { this.proto = proto; this.controllerMeta = controllerMeta; this.router = router; this.methodMeta = methodMeta; this.checkRouters = checkRouters; } private createHandler(methodMeta: HTTPMethodMeta, host: string | undefined) { const argsLength = methodMeta.paramMap.size; const hasContext = methodMeta.contextParamIndex !== undefined; const contextIndex = methodMeta.contextParamIndex; const methodArgsLength = argsLength + (hasContext ? 1 : 0); const self = this; return async function(ctx: ServiceWorkerFetchContext, next: Next) { // if hosts is not empty and host is not matched, not execute if (host && host !== ctx.host) { return await next(); } // HTTP decorator core implement // use controller metadata map http request to function arguments const eggObj = await EggContainerFactory.getOrCreateEggObject(self.proto, self.proto.name); const realObj = eggObj.obj; const realMethod = realObj[methodMeta.name]; const args: Array = new Array(methodArgsLength); if (hasContext) { args[contextIndex!] = ctx; } for (const [ index, param ] of methodMeta.paramMap) { switch (param.type) { case HTTPParamType.BODY: { const request = ctx.event.request; args[index] = await RequestUtils.getRequestBody(request); break; } case HTTPParamType.PARAM: { const pathParam: PathParamMeta = param as PathParamMeta; args[index] = ctx.params[pathParam.name]; break; } case HTTPParamType.QUERY: { const queryParam: QueryParamMeta = param as QueryParamMeta; args[index] = ctx.url.searchParams.get(queryParam.name) as string; break; } case HTTPParamType.QUERIES: { const queryParam: QueriesParamMeta = param as QueriesParamMeta; args[index] = ctx.url.searchParams.getAll(queryParam.name); break; } case HTTPParamType.HEADERS: { const headers: IncomingHttpHeaders = {}; for (const [ k, v ] of ctx.event.request.headers.entries()) { headers[k] = v; } args[index] = headers; break; } case HTTPParamType.REQUEST: { args[index] = ctx.event.request; break; } default: throw new Error(`unknown param type ${param.type} in method ${self.controllerMeta.controllerName}.${methodMeta.name}`); } } const res = await Reflect.apply(realMethod, realObj, args); if (res instanceof Response) { ctx.response = res; } else { ctx.body = res; } }; } checkDuplicate() { // 1. check duplicate with egg controller this.checkDuplicateInRouter(this.router); // 2. check duplicate with host tegg controller let hostRouter; const hosts = this.controllerMeta.getMethodHosts(this.methodMeta) || []; hosts.forEach(h => { if (h) { hostRouter = this.checkRouters.get(h); if (!hostRouter) { hostRouter = new KoaRouter({ sensitive: true }); this.checkRouters.set(h, hostRouter); } } if (hostRouter) { this.checkDuplicateInRouter(hostRouter); this.registerToRouter(hostRouter); } }); } private registerToRouter(router: KoaRouter) { const routerFunc = router[this.methodMeta.method.toLowerCase()]; const methodRealPath = this.controllerMeta.getMethodRealPath(this.methodMeta); const methodName = this.controllerMeta.getMethodName(this.methodMeta); Reflect.apply(routerFunc, router, [ methodName, methodRealPath, noop ]); } private checkDuplicateInRouter(router: KoaRouter) { const methodRealPath = this.controllerMeta.getMethodRealPath(this.methodMeta); const matched = router.match(methodRealPath, this.methodMeta.method); const methodName = this.controllerMeta.getMethodName(this.methodMeta); if (matched.route) { const [ layer ] = matched.path; const err = new Error(`register http controller ${methodName} failed, ${this.methodMeta.method} ${methodRealPath} is conflict with exists rule ${layer.path}`); throw FrameworkErrorFormater.format(err); } } private getAopMiddlewares(): Array<(ctx: ServiceWorkerFetchContext, next: Next) => Promise> { // Controller-level AOP middlewares const controllerAopClasses = (this.proto.getMetaData(CONTROLLER_AOP_MIDDLEWARES) ?? []) as EggProtoImplClass[]; // Method-level AOP middlewares const methodAopMap = this.proto.getMetaData(METHOD_AOP_MIDDLEWARES) as Map[]> | undefined; const methodAopClasses = methodAopMap?.get(this.methodMeta.name) ?? []; const allAopClasses = [ ...controllerAopClasses, ...methodAopClasses ]; return allAopClasses.map(clazz => { return async (ctx: ServiceWorkerFetchContext, next: Next) => { const eggObj = await EggContainerFactory.getOrCreateEggObjectFromClazz(clazz); await (eggObj.obj as AbstractControllerAdvice).middleware(ctx, next); }; }); } register(rootProtoManager: RootProtoManager) { const methodRealPath = this.controllerMeta.getMethodRealPath(this.methodMeta); const methodName = this.controllerMeta.getMethodName(this.methodMeta); const routerFunc = this.router[this.methodMeta.method.toLowerCase()]; const methodMiddlewares = this.controllerMeta.getMethodMiddlewares(this.methodMeta); const aopMiddlewares = this.getAopMiddlewares(); const hosts = this.controllerMeta.getMethodHosts(this.methodMeta) || [ undefined ]; hosts.forEach(h => { const handler = this.createHandler(this.methodMeta, h); Reflect.apply(routerFunc, this.router, [ methodName, methodRealPath, ...aopMiddlewares, ...methodMiddlewares, handler ]); // https://github.com/eggjs/egg-core/blob/0af6178022e7734c4a8b17bb56d592b315207883/lib/egg.js#L279 const regExp = pathToRegexp(methodRealPath, { sensitive: true }); rootProtoManager.registerRootProto(this.methodMeta.method, (ctx: EggContext) => { if (regExp.test(ctx.path)) { return this.proto; } }, h || ''); }); } } ================================================ FILE: standalone/service-worker/src/http/ServiceWorkerFetchContext.ts ================================================ import { FetchEvent, ServiceWorkerContextInit } from '@eggjs/tegg-types/standalone'; import { BaseServiceWorkerContextImpl } from '../controller/ServiceWorkerContext'; import { ResponseUtils } from '../utils/ResponseUtils'; export class ServiceWorkerFetchContext extends BaseServiceWorkerContextImpl { url: URL; method: string; path: string; host: string; // params will be set in @eggjs/router params: Record = {}; #body?: any; constructor(init: ServiceWorkerContextInit) { super(init); this.url = new URL(this.event.request.url); this.method = this.event.request.method; this.path = this.url.pathname; this.host = this.url.hostname; } get response(): Response | undefined { return super.response; } set response(response: Response) { super.response = response; this.#body = response.body; } get body(): any | undefined { return this.#body; } set body(body: any) { this.response = ResponseUtils.createResponseByBody(body); this.#body = body; } } ================================================ FILE: standalone/service-worker/src/mcp/AbstractControllerAdvice.ts ================================================ import type { Next } from '@eggjs/tegg-types/controller-decorator'; import type { IAdvice, AdviceContext } from '@eggjs/tegg-types/aop'; import type { ServiceWorkerFetchContext } from '../http/ServiceWorkerFetchContext'; export abstract class AbstractControllerAdvice implements IAdvice { // Default no-op around to satisfy IAdvice structural type check async around(_ctx: AdviceContext, next: () => Promise): Promise { return next(); } abstract middleware(ctx: ServiceWorkerFetchContext, next: Next): Promise; } ================================================ FILE: standalone/service-worker/src/mcp/MCPControllerRegister.ts ================================================ import assert from 'node:assert'; import { IncomingMessage, OutgoingHttpHeader, OutgoingHttpHeaders, ServerResponse } from 'node:http'; import { Socket } from 'node:net'; import { Readable, PassThrough } from 'node:stream'; import { Router as KoaRouter } from '@eggjs/router'; import { CONTROLLER_META_DATA, ControllerMetadata, ControllerType, MCPControllerMeta, MCPPromptMeta, MCPResourceMeta, MCPToolMeta, } from '@eggjs/tegg'; import type { EggObject, EggObjectName, EggProtoImplClass, EggPrototype } from '@eggjs/tegg-types'; import { CONTROLLER_AOP_MIDDLEWARES } from '@eggjs/tegg-types/controller-decorator'; import { EggContainerFactory } from '@eggjs/tegg/helper'; import type { AbstractControllerAdvice } from './AbstractControllerAdvice'; import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js'; import { ControllerRegister } from '../controller/ControllerRegister'; import { ServiceWorkerFetchContext } from '../http/ServiceWorkerFetchContext'; import { MCPServerHelper } from './MCPServerHelper'; interface ServerRegisterRecord { getOrCreateEggObject: (proto: EggPrototype, name?: EggObjectName) => Promise; proto: EggPrototype; meta: T; } // Bridge ServerResponse to a PassThrough stream for Fetch API Response construction class ServiceWorkerMCPServerResponse extends ServerResponse { callback: (value: object) => void; #stream: PassThrough; constructor(req: IncomingMessage, callback: (value: object) => void) { super(req); this.callback = callback; this.#stream = new PassThrough(); } write(chunk: any, callback?: (error: Error | null | undefined) => void): boolean; write(chunk: any, encoding: BufferEncoding, callback?: (error: Error | null | undefined) => void): boolean; write( chunk: any, encoding?: BufferEncoding | ((error: Error | null | undefined) => void), callback?: (error: Error | null | undefined) => void, ): boolean { super.write(chunk, encoding as any, callback); return this.#stream.write(chunk, encoding as any, callback); } get stream() { return this.#stream; } writeHead( statusCode: number, headers?: OutgoingHttpHeaders | OutgoingHttpHeader[] ): this; writeHead( statusCode: number, statusMessage?: string, headers?: OutgoingHttpHeaders | OutgoingHttpHeader[], ): this; writeHead( statusCode: number, reason?: string | (OutgoingHttpHeaders | OutgoingHttpHeader[]), obj?: OutgoingHttpHeaders | OutgoingHttpHeader[], ): this { if (typeof reason === 'string') { super.writeHead(statusCode, reason, obj); this.callback({ status: statusCode, headers: { ...(obj ? obj : {}), 'X-Accel-Buffering': 'no', }, }); } else { super.writeHead(statusCode, reason); this.callback({ status: statusCode, headers: { ...(reason ? { ...reason, ...(reason['content-length'] ? {} : { 'transfer-encoding': 'chunked' }), } : {}), 'X-Accel-Buffering': 'no', }, }); } return this; } end(cb?: () => void): this; end(chunk: any, cb?: () => void): this; end(chunk: any, encoding: BufferEncoding, cb?: () => void): this; end(...args: any[]): this { this.#stream.end(...args); super.end(...args); return this; } } export class MCPControllerRegister implements ControllerRegister { static instance?: MCPControllerRegister; private readonly router: KoaRouter; private controllerProtos: EggPrototype[] = []; private registeredControllerProtos: EggPrototype[] = []; private controllerMeta: MCPControllerMeta; mcpServerHelperMap: Record MCPServerHelper> = {}; streamTransports: Record = {}; middlewaresMap: Record Promise) => Promise>> = {}; registerMap: Record[]; prompts: ServerRegisterRecord[]; resources: ServerRegisterRecord[]; }> = {}; static create(proto: EggPrototype, controllerMeta: ControllerMetadata, router: KoaRouter) { assert(controllerMeta.type === ControllerType.MCP, 'controller meta type is not MCP'); if (!MCPControllerRegister.instance) { MCPControllerRegister.instance = new MCPControllerRegister(controllerMeta as MCPControllerMeta, router); } MCPControllerRegister.instance.controllerProtos.push(proto); return MCPControllerRegister.instance; } constructor(controllerMeta: MCPControllerMeta, router: KoaRouter) { this.router = router; this.controllerMeta = controllerMeta; } static clean() { if (this.instance) { this.instance.controllerProtos = []; this.instance.registeredControllerProtos = []; this.instance.registerMap = {}; this.instance.mcpServerHelperMap = {}; this.instance.middlewaresMap = {}; } this.instance = undefined; } /** * Connect the long-lived stateless stream transport and prime it with an * initialize call, matching the chair service-worker pattern. Must be * called after register() so all tools are already on the helper. */ async connectStatelessStreamTransport(name?: string) { const inst = MCPControllerRegister.instance; if (!inst) return; const transport = inst.streamTransports[name ?? 'default']; const mcpServerHelper = this.mcpServerHelperMap[name ?? 'default'](); const registerEntry = this.registerMap[name ?? 'default']; if (registerEntry) { for (const tool of registerEntry.tools) { await mcpServerHelper.mcpToolRegister( tool.getOrCreateEggObject, tool.proto, tool.meta, ); } for (const resource of registerEntry.resources) { await mcpServerHelper.mcpResourceRegister( resource.getOrCreateEggObject, resource.proto, resource.meta, ); } for (const prompt of registerEntry.prompts) { await mcpServerHelper.mcpPromptRegister( prompt.getOrCreateEggObject, prompt.proto, prompt.meta, ); } } await mcpServerHelper.server.connect(transport); const socket = new Socket(); const req = new IncomingMessage(socket); const res = new ServerResponse(req); req.method = 'POST'; req.url = '/mcp/stream'; req.headers = { accept: 'application/json, text/event-stream', 'content-type': 'application/json', }; const initBody = { jsonrpc: '2.0', id: 0, method: 'initialize', params: { protocolVersion: '2024-11-05', capabilities: {}, clientInfo: { name: 'init-client', version: '1.0.0' }, }, }; await inst.streamTransports[name ?? 'default'].handleRequest(req, res, initBody); } private async mcpStatelessStreamServerInit(name?: string) { const postRouterFunc = this.router.post; // Create fresh transport and server per request (stateless mode) const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined, }); MCPControllerRegister.instance!.streamTransports[name ?? 'default'] = transport; const initHandler = async (ctx: ServiceWorkerFetchContext) => { const transport = MCPControllerRegister.instance!.streamTransports[name ?? 'default']; // Bridge Fetch Request → Node.js IncomingMessage const socket = new Socket(); const req = new IncomingMessage(socket); req.url = ctx.url.pathname + ctx.url.search; const headers: Record = {}; for (const [ key, value ] of ctx.event.request.headers.entries()) { headers[key] = value; req.rawHeaders.push(key); req.rawHeaders.push(value); } req.headers = headers; req.method = ctx.event.request.method; // Create bridge ServerResponse → PassThrough → Fetch Response let callback: (value: object) => void; const resPromise = new Promise(resolve => { callback = resolve; }); const response = new ServiceWorkerMCPServerResponse(req, callback!); // Parse body from Fetch Request const body = await ctx.event.request.json(); // Handle the request (don't await - handleRequest writes to response stream) transport.handleRequest(req, response, body); const init = await resPromise; ctx.response = new Response(Readable.toWeb(response.stream) as any, init) as any; }; const streamPath = `/mcp${name ? `/${name}` : ''}/stream`; const basePath = `/mcp${name ? `/${name}` : ''}`; const middlewares = this.middlewaresMap[name ?? 'default'] ?? []; const paths = [ streamPath, basePath ]; for (const path of paths) { Reflect.apply(postRouterFunc, this.router, [ 'mcpStatelessStreamInit', path, ...middlewares, initHandler, ]); } // Only POST is allowed for stateless streamable HTTP const notAllowedHandler = async (ctx: ServiceWorkerFetchContext) => { ctx.response = new Response( JSON.stringify({ jsonrpc: '2.0', error: { code: -32000, message: 'Method not allowed.', }, id: null, }), { status: 405, headers: { 'content-type': 'application/json', }, }, ); }; const getRouterFunc = this.router.get; const delRouterFunc = this.router.del; for (const path of paths) { Reflect.apply(getRouterFunc, this.router, [ 'mcpStatelessStreamNotAllowed', path, notAllowedHandler, ]); Reflect.apply(delRouterFunc, this.router, [ 'mcpStatelessStreamNotAllowed', path, notAllowedHandler, ]); } } async register() { for (const proto of this.controllerProtos) { if (this.registeredControllerProtos.includes(proto)) { continue; } const metadata = proto.getMetaData(CONTROLLER_META_DATA) as MCPControllerMeta; if (!this.mcpServerHelperMap[metadata.name ?? 'default']) { this.mcpServerHelperMap[metadata.name ?? 'default'] = () => { return new MCPServerHelper({ name: this.controllerMeta.name ?? `mcp-${metadata.name ?? 'default'}-server`, version: this.controllerMeta.version ?? '1.0.0', }); }; } if (!this.registerMap[metadata.name ?? 'default']) { this.registerMap[metadata.name ?? 'default'] = { prompts: [], resources: [], tools: [], }; } for (const tool of metadata.tools) { this.registerMap[metadata.name ?? 'default'].tools.push({ getOrCreateEggObject: EggContainerFactory.getOrCreateEggObject.bind( EggContainerFactory, ), proto, meta: tool, }); } for (const resource of metadata.resources) { this.registerMap[metadata.name ?? 'default'].resources.push({ getOrCreateEggObject: EggContainerFactory.getOrCreateEggObject.bind( EggContainerFactory, ), proto, meta: resource, }); } for (const prompt of metadata.prompts) { this.registerMap[metadata.name ?? 'default'].prompts.push({ getOrCreateEggObject: EggContainerFactory.getOrCreateEggObject.bind( EggContainerFactory, ), proto, meta: prompt, }); } // Collect middlewares for this server name const serverName = metadata.name ?? 'default'; if (!this.middlewaresMap[serverName]) { this.middlewaresMap[serverName] = []; } // Function-type middlewares from MCPControllerMeta const classMiddlewares = metadata.middlewares ?? []; for (const mw of classMiddlewares) { this.middlewaresMap[serverName].push(mw as unknown as (ctx: ServiceWorkerFetchContext, next: () => Promise) => Promise); } // AOP-type middlewares from class metadata const aopMiddlewareClasses = (proto.getMetaData(CONTROLLER_AOP_MIDDLEWARES) ?? []) as EggProtoImplClass[]; for (const clazz of aopMiddlewareClasses) { this.middlewaresMap[serverName].push(async (ctx: ServiceWorkerFetchContext, next: () => Promise) => { const eggObj = await EggContainerFactory.getOrCreateEggObjectFromClazz(clazz); await (eggObj.obj as AbstractControllerAdvice).middleware(ctx, next); }); } this.registeredControllerProtos.push(proto); } } async doRegister() { // Initialize MCP routes for each server name const names = Object.keys(this.registerMap); for (const name of names) { await this.mcpStatelessStreamServerInit(name === 'default' ? undefined : name); await this.connectStatelessStreamTransport(name); } } } ================================================ FILE: standalone/service-worker/src/mcp/MCPServerHelper.ts ================================================ import { McpServer, ReadResourceCallback, ToolCallback, PromptCallback, } from '@modelcontextprotocol/sdk/server/mcp.js'; import { CONTROLLER_META_DATA } from '@eggjs/tegg'; import type { EggObject, EggObjectName, EggPrototype } from '@eggjs/tegg-types'; import { MCPControllerMeta, MCPPromptMeta, MCPResourceMeta, MCPToolMeta } from '@eggjs/tegg'; export interface MCPServerHelperOptions { name: string; version: string; } export class MCPServerHelper { server: McpServer; constructor(opts: MCPServerHelperOptions) { this.server = new McpServer( { name: opts.name, version: opts.version, }, { capabilities: { logging: {} } }, ); } async mcpResourceRegister( getOrCreateEggObject: (proto: EggPrototype, name?: EggObjectName) => Promise, controllerProto: EggPrototype, resourceMeta: MCPResourceMeta, ) { const handler = async (...args) => { const eggObj = await getOrCreateEggObject( controllerProto, controllerProto.name, ); const realObj = eggObj.obj; const realMethod = realObj[resourceMeta.name]; return Reflect.apply( realMethod, realObj, args, ) as ReturnType; }; const name = resourceMeta.mcpName ?? resourceMeta.name; if (resourceMeta.uri) { this.server.registerResource(name, resourceMeta.uri, resourceMeta.metadata ?? {}, handler); } else if (resourceMeta.template) { this.server.registerResource(name, resourceMeta.template as unknown as any, resourceMeta.metadata ?? {}, handler); } else { throw new Error(`MCPResource ${name} must have uri or template`); } } async mcpToolRegister( getOrCreateEggObject: (proto: EggPrototype, name?: EggObjectName) => Promise, controllerProto: EggPrototype, toolMeta: MCPToolMeta, ) { const controllerMeta = controllerProto.getMetaData( CONTROLLER_META_DATA, ) as MCPControllerMeta; void controllerMeta; const name: string = toolMeta.mcpName ?? toolMeta.name; const description: string | undefined = toolMeta.description; let schema: NonNullable['argsSchema'] | undefined; if (toolMeta.detail?.argsSchema) { schema = toolMeta.detail?.argsSchema; } const handler = async (...args) => { const eggObj = await getOrCreateEggObject( controllerProto, controllerProto.name, ); const realObj = eggObj.obj; const realMethod = realObj[toolMeta.name]; let newArgs: any[] = []; if (schema && toolMeta.detail) { newArgs[toolMeta.detail.index] = args[0]; if (toolMeta.extra) { newArgs[toolMeta.extra] = args[1]; } } else if (toolMeta.extra) { newArgs[toolMeta.extra] = args[0]; } newArgs = [ ...newArgs, ...args ]; return Reflect.apply(realMethod, realObj, newArgs) as ReturnType; }; this.server.registerTool(name, { description, inputSchema: schema, }, handler); } async mcpPromptRegister( getOrCreateEggObject: (proto: EggPrototype, name?: EggObjectName) => Promise, controllerProto: EggPrototype, promptMeta: MCPPromptMeta, ) { const name: string = promptMeta.mcpName ?? promptMeta.name; const description: string | undefined = promptMeta.description; let schema: NonNullable['argsSchema'] | undefined; if (promptMeta.detail?.argsSchema) { schema = promptMeta.detail?.argsSchema; } const handler = async (...args) => { const eggObj = await getOrCreateEggObject(controllerProto, controllerProto.name); const realObj = eggObj.obj; const realMethod = realObj[promptMeta.name]; let newArgs: any[] = []; if (schema && promptMeta.detail) { newArgs[promptMeta.detail.index] = args[0]; if (promptMeta.extra) { newArgs[promptMeta.extra] = args[1]; } } else if (promptMeta.extra) { newArgs[promptMeta.extra] = args[0]; } newArgs = [ ...newArgs, ...args ]; return Reflect.apply(realMethod, realObj, newArgs) as ReturnType; }; this.server.registerPrompt(name, { title: promptMeta.title, description, argsSchema: schema, }, handler); } } ================================================ FILE: standalone/service-worker/src/types.ts ================================================ export interface ProtoMeta { protoName: PropertyKey; contextKey: string | symbol; } ================================================ FILE: standalone/service-worker/src/utils/RequestUtils.ts ================================================ import typeis from 'type-is'; export class RequestUtils { static ContentTypes = { json: [ 'application/json', 'application/json-patch+json', 'application/vnd.api+json', 'application/csp-report', 'application/scim+json', ], form: [ 'application/x-www-form-urlencoded' ], text: [ 'text/plain' ], }; static async getRequestBody(request: Request) { if (RequestUtils.matchContentTypes(request, RequestUtils.ContentTypes.json)) { return await request.json(); } if (RequestUtils.matchContentTypes(request, RequestUtils.ContentTypes.text)) { return await request.text(); } if (RequestUtils.matchContentTypes(request, RequestUtils.ContentTypes.form)) { return await request.formData(); } } static matchContentTypes(request: Request, types: string[]) { const contentType = request.headers.get('content-type'); const contentTypeValue = typeof contentType === 'string' // trim extra semicolon ? contentType.replace(/;$/, '') : contentType; return typeis.is(contentTypeValue!, types); } } ================================================ FILE: standalone/service-worker/src/utils/ResponseUtils.ts ================================================ export class ResponseUtils { static createResponseByBody(body: any) { if (typeof body === 'undefined' || body === null) { return new Response(null, { status: 204 }); } if (Buffer.isBuffer(body) || typeof body === 'string' || body instanceof ReadableStream) { return new Response(body as BodyInit, { status: 200 }); } return new Response(JSON.stringify(body), { status: 200, headers: { 'Content-Type': 'application/json', }, }); } } ================================================ FILE: standalone/service-worker/test/Utils.ts ================================================ import path from 'node:path'; import { ServiceWorkerApp, ServiceWorkerAppOptions } from '../src/ServiceWorkerApp'; import { StandaloneTestUtil } from '@eggjs/module-test-util/StandaloneTestUtil'; export class TestUtils { static baseDir(name: string) { return path.join(__dirname, 'fixtures', name); } static async createApp(name: string, init?: ServiceWorkerAppOptions) { const app = new ServiceWorkerApp(TestUtils.baseDir(name), { ...init, env: 'unittest', name, }); await app.init(); return app; } static async createFetchApp(name: string, init?: ServiceWorkerAppOptions) { const app = await TestUtils.createApp(name, init); // Use port 0 to let the OS assign a free port const server = await StandaloneTestUtil.startHTTPServer('127.0.0.1', 0, { listener: e => app.handleEvent(e), }); return { app, server }; } } ================================================ FILE: standalone/service-worker/test/fixtures/http/AopMiddlewareController.ts ================================================ import { HTTPMethodEnum, HTTPController, HTTPMethod, Middleware } from '@eggjs/tegg'; import { HttpTestAdvice } from './HttpTestAdvice'; @Middleware(HttpTestAdvice) @HTTPController() export class AopMiddlewareController { @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/middleware/aop' }) async aopMiddlewareTest() { return { msg: 'hello' }; } } ================================================ FILE: standalone/service-worker/test/fixtures/http/GetController.ts ================================================ import { HTTPMethodEnum, HTTPController, HTTPMethod, HTTPQuery } from '@eggjs/tegg'; @HTTPController() export class GetController { @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/hello' }) async hello() { return 'hello'; } @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/api/null-body' }) async nullBody(@HTTPQuery() nil: string) { return nil ? null : undefined; } @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/api/response' }) async response() { return new Response('full response', { status: 500, headers: { 'content-type': 'text/plain', 'x-custom-header': 'custom-value', }, }); } } ================================================ FILE: standalone/service-worker/test/fixtures/http/HttpTestAdvice.ts ================================================ import { ObjectInitType } from '@eggjs/tegg'; import { Advice } from '@eggjs/tegg/aop'; import { AbstractControllerAdvice } from '../../../src/mcp/AbstractControllerAdvice'; import type { ServiceWorkerFetchContext } from '../../../src/http/ServiceWorkerFetchContext'; // Track middleware execution for testing export const httpAdviceExecutionLog: string[] = []; @Advice({ initType: ObjectInitType.SINGLETON }) export class HttpTestAdvice extends AbstractControllerAdvice { async middleware(_ctx: ServiceWorkerFetchContext, next: () => Promise): Promise { httpAdviceExecutionLog.push('before'); await next(); httpAdviceExecutionLog.push('after'); } } ================================================ FILE: standalone/service-worker/test/fixtures/http/PostController.ts ================================================ import { HTTPMethodEnum, HTTPController, HTTPMethod, HTTPBody } from '@eggjs/tegg'; @HTTPController() export class PostController { @HTTPMethod({ method: HTTPMethodEnum.POST, path: '/echo' }) async hello(@HTTPBody() data: object) { return { success: true, data, }; } } ================================================ FILE: standalone/service-worker/test/fixtures/http/package.json ================================================ { "name": "http-app", "eggModule": { "name": "httpApp" } } ================================================ FILE: standalone/service-worker/test/fixtures/http-builtin/BuiltinController.ts ================================================ import { HTTPMethodEnum, HTTPController, HTTPMethod, Inject, Logger } from '@eggjs/tegg'; import { HttpClient } from 'urllib'; @HTTPController() export class BuiltinController { @Inject() private readonly logger: Logger; @Inject() private readonly httpclient: HttpClient; @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/log' }) async log() { this.logger.info('hello from controller'); return { ok: true }; } @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/httpclient' }) async httpclientCheck() { // Just verify httpclient is injected and has request method return { hasRequest: typeof this.httpclient.request === 'function' }; } } ================================================ FILE: standalone/service-worker/test/fixtures/http-builtin/package.json ================================================ { "name": "http-builtin-app", "eggModule": { "name": "httpBuiltinApp" } } ================================================ FILE: standalone/service-worker/test/fixtures/http-inject/UserController.ts ================================================ import { HTTPController, HTTPMethod, HTTPMethodEnum, HTTPParam, Inject, } from '@eggjs/tegg'; import { UserService } from './UserService'; @HTTPController({ path: '/api/users', }) export class UserController { @Inject() private readonly userService: UserService; @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/' }) async list() { return await this.userService.list(); } @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/:id' }) async findById(@HTTPParam() id: string) { return await this.userService.findById(id); } } ================================================ FILE: standalone/service-worker/test/fixtures/http-inject/UserService.ts ================================================ import { SingletonProto, AccessLevel } from '@eggjs/tegg'; @SingletonProto({ accessLevel: AccessLevel.PUBLIC }) export class UserService { async findById(id: string) { return { id, name: `user-${id}`, }; } async list() { return [ { id: '1', name: 'Alice' }, { id: '2', name: 'Bob' }, ]; } } ================================================ FILE: standalone/service-worker/test/fixtures/http-inject/package.json ================================================ { "name": "http-inject-app", "eggModule": { "name": "httpInjectApp" } } ================================================ FILE: standalone/service-worker/test/fixtures/http-params/ParamController.ts ================================================ import { HTTPBody, HTTPController, HTTPHeaders, HTTPMethod, HTTPMethodEnum, HTTPParam, HTTPQueries, HTTPQuery, HTTPRequest, Request, } from '@eggjs/tegg'; @HTTPController() export class ParamController { @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/query' }) async hello(@HTTPQuery() name: string, @HTTPQueries() type: string[]) { return { name, type, }; } @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/find/:id' }) async find(@HTTPParam() id: string) { return { id, }; } @HTTPMethod({ method: HTTPMethodEnum.POST, path: '/echo/body' }) async echoBody(@HTTPBody() body: object) { if (body.constructor.name === 'FormData') { const res = {}; for (const [ key, value ] of (body as FormData).entries()) { res[key] = value; } return { type: 'formData', body: res }; } return { body }; } @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/headers' }) async headers(@HTTPHeaders() headers: Record) { return { headers, }; } @HTTPMethod({ method: HTTPMethodEnum.POST, path: '/request' }) async request(@Request() req: HTTPRequest) { return { url: req.url, method: req.method, customHeaders: req.headers.get('x-custom'), body: await req.json(), }; } } ================================================ FILE: standalone/service-worker/test/fixtures/http-params/package.json ================================================ { "name": "http-params-app", "eggModule": { "name": "httpParamsApp" } } ================================================ FILE: standalone/service-worker/test/fixtures/http-priority/PriorityController.ts ================================================ import { HTTPController, HTTPMethod, HTTPMethodEnum, } from '@eggjs/tegg'; @HTTPController({ path: '/users', }) export class PriorityController { @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/*', }) async lowPriority() { return 'low priority'; } @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/group', }) async highPriority() { return 'high priority'; } } ================================================ FILE: standalone/service-worker/test/fixtures/http-priority/ViewController.ts ================================================ import { HTTPController, HTTPMethod, HTTPMethodEnum, } from '@eggjs/tegg'; @HTTPController() export class ViewController { @HTTPMethod({ method: HTTPMethodEnum.GET, path: '/*', }) async get() { return 'hello, view'; } } ================================================ FILE: standalone/service-worker/test/fixtures/http-priority/package.json ================================================ { "name": "http-priority-app", "eggModule": { "name": "httpPriorityApp" } } ================================================ FILE: standalone/service-worker/test/fixtures/mcp/MCPTestController.ts ================================================ import { MCPController, MCPTool, MCPToolResponse, ToolArgsSchema, ToolArgs, Middleware } from '@eggjs/tegg'; import * as z from 'zod/v4'; import { McpTestAdvice } from './McpTestAdvice'; const EchoArgs = { message: z.string().describe('The message to echo'), }; const AddArgs = { a: z.number().describe('First number'), b: z.number().describe('Second number'), }; @Middleware(McpTestAdvice) @MCPController({ name: 'test-server', version: '1.0.0' }) export class MCPTestController { @MCPTool({ description: 'Echo the input message' }) async echo(@ToolArgsSchema(EchoArgs) args: ToolArgs): Promise { return { content: [{ type: 'text', text: args.message }], }; } @MCPTool({ description: 'Add two numbers' }) async add(@ToolArgsSchema(AddArgs) args: ToolArgs): Promise { return { content: [{ type: 'text', text: String(args.a + args.b) }], }; } } ================================================ FILE: standalone/service-worker/test/fixtures/mcp/McpTestAdvice.ts ================================================ import { ObjectInitType } from '@eggjs/tegg'; import { Advice } from '@eggjs/tegg/aop'; import { AbstractControllerAdvice } from '../../../src/mcp/AbstractControllerAdvice'; import type { ServiceWorkerFetchContext } from '../../../src/http/ServiceWorkerFetchContext'; // Track middleware execution for testing export const adviceExecutionLog: string[] = []; @Advice({ initType: ObjectInitType.SINGLETON }) export class McpTestAdvice extends AbstractControllerAdvice { async middleware(_ctx: ServiceWorkerFetchContext, next: () => Promise): Promise { adviceExecutionLog.push('before'); await next(); adviceExecutionLog.push('after'); } } ================================================ FILE: standalone/service-worker/test/fixtures/mcp/package.json ================================================ { "name": "mcp-app", "eggModule": { "name": "mcpApp" } } ================================================ FILE: standalone/service-worker/test/http/builtin.test.ts ================================================ import { Server } from 'node:http'; import httpRequest from 'supertest'; import { ServiceWorkerApp } from '../../src/ServiceWorkerApp'; import { StandaloneTestUtil } from '@eggjs/module-test-util/StandaloneTestUtil'; import { TestUtils } from '../Utils'; describe('standalone/service-worker/test/http/builtin.test.ts', () => { let app: ServiceWorkerApp; let server: Server; before(async function() { if (StandaloneTestUtil.skipOnNode()) { return this.skip(); } ({ app, server } = await TestUtils.createFetchApp('http-builtin')); }); after(async () => { server?.close(); await app?.destroy(); }); it('should inject logger and log successfully', async () => { await httpRequest(server) .get('/log') .expect(200, { ok: true }); }); it('should inject httpclient', async () => { await httpRequest(server) .get('/httpclient') .expect(200, { hasRequest: true }); }); }); ================================================ FILE: standalone/service-worker/test/http/inject.test.ts ================================================ import { Server } from 'node:http'; import httpRequest from 'supertest'; import { ServiceWorkerApp } from '../../src/ServiceWorkerApp'; import { StandaloneTestUtil } from '@eggjs/module-test-util/StandaloneTestUtil'; import { TestUtils } from '../Utils'; describe('standalone/service-worker/test/http/inject.test.ts', () => { let app: ServiceWorkerApp; let server: Server; before(async function() { if (StandaloneTestUtil.skipOnNode()) { return this.skip(); } ({ app, server } = await TestUtils.createFetchApp('http-inject')); }); after(async () => { server?.close(); await app?.destroy(); }); it('should inject service and list users', async () => { await httpRequest(server) .get('/api/users/') .expect(200, [ { id: '1', name: 'Alice' }, { id: '2', name: 'Bob' }, ]); }); it('should inject service and find user by id', async () => { await httpRequest(server) .get('/api/users/42') .expect(200, { id: '42', name: 'user-42', }); }); }); ================================================ FILE: standalone/service-worker/test/http/params.test.ts ================================================ import assert from 'node:assert'; import { Server } from 'node:http'; import { AddressInfo } from 'node:net'; import httpRequest from 'supertest'; import { ServiceWorkerApp } from '../../src/ServiceWorkerApp'; import { TestUtils } from '../Utils'; import { StandaloneTestUtil } from '@eggjs/module-test-util/StandaloneTestUtil'; describe('standalone/service-worker/test/http/params.test.ts', () => { let app: ServiceWorkerApp; let server: Server; before(async function() { if (StandaloneTestUtil.skipOnNode()) { return this.skip(); } ({ app, server } = await TestUtils.createFetchApp('http-params')); }); after(async () => { server?.close(); await app?.destroy(); }); it('should query param work', async () => { await httpRequest(server) .get('/query?name=tegg&type=1&type=2') .expect(200, { name: 'tegg', type: [ '1', '2' ], }); }); it('should path param work', async () => { await httpRequest(server) .get('/find/123') .expect(200, { id: '123', }); }); describe('@HTTPBody()', () => { it('should json body param work', async () => { await httpRequest(server) .post('/echo/body') .type('json') .send({ foo: 'bar' }) .expect(200, { body: { foo: 'bar' } }); }); it('should formData body param work', async () => { await httpRequest(server) .post('/echo/body') .type('form') .send({ foo: 'bar' }) .expect(200, { type: 'formData', body: { foo: 'bar' } }); }); it('should text body param work', async () => { await httpRequest(server) .post('/echo/body') .type('text') .send('hello world') .expect(200, { body: 'hello world' }); }); }); it('should headers param work', async () => { const res = await httpRequest(server) .get('/headers') .set('x-custom-header', 'custom-value') .expect(200); assert.equal(res.body.headers['x-custom-header'], 'custom-value'); }); it('should request param work', async () => { const port = (server.address() as AddressInfo).port; const res = await httpRequest(server) .post('/request') .set('x-custom', 'custom-value') .send({ foo: 'bar' }) .expect(200); assert.equal(res.body.url, `http://127.0.0.1:${port}/request`); assert.equal(res.body.method, 'POST'); assert.equal(res.body.customHeaders, 'custom-value'); assert.deepStrictEqual(res.body.body, { foo: 'bar' }); }); }); ================================================ FILE: standalone/service-worker/test/http/priority.test.ts ================================================ import { Server } from 'node:http'; import httpRequest from 'supertest'; import { ServiceWorkerApp } from '../../src/ServiceWorkerApp'; import { StandaloneTestUtil } from '@eggjs/module-test-util/StandaloneTestUtil'; import { TestUtils } from '../Utils'; describe('standalone/service-worker/test/http/priority.test.ts', () => { let app: ServiceWorkerApp; let server: Server; before(async function() { if (StandaloneTestUtil.skipOnNode()) { return this.skip(); } ({ app, server } = await TestUtils.createFetchApp('http-priority')); }); after(async () => { server?.close(); await app?.destroy(); }); it('should /* work', async () => { await httpRequest(server) .get('/view/foo') .expect(200) .expect('hello, view'); }); it('should /users/group work', async () => { await httpRequest(server) .get('/users/group') .expect(200) .expect('high priority'); }); it('should /users/* work', async () => { await httpRequest(server) .get('/users/foo') .expect(200) .expect('low priority'); }); }); ================================================ FILE: standalone/service-worker/test/http/response.test.ts ================================================ import { Server } from 'node:http'; import httpRequest from 'supertest'; import { ServiceWorkerApp } from '../../src/ServiceWorkerApp'; import { StandaloneTestUtil } from '@eggjs/module-test-util/StandaloneTestUtil'; import { TestUtils } from '../Utils'; describe('standalone/service-worker/test/http/response.test.ts', () => { let app: ServiceWorkerApp; let server: Server; before(async function() { if (StandaloneTestUtil.skipOnNode()) { return this.skip(); } ({ app, server } = await TestUtils.createFetchApp('http')); }); after(async () => { server?.close(); await app?.destroy(); }); it('should return Response work', async () => { await httpRequest(server) .get('/api/response') .expect(500, 'full response') .expect('Content-Type', 'text/plain') .expect('x-custom-header', 'custom-value'); }); it('should return 204 with no content', async () => { await httpRequest(server) .get('/api/null-body') .expect(204, ''); await httpRequest(server) .get('/api/null-body?nil=1') .expect(204, ''); }); }); ================================================ FILE: standalone/service-worker/test/http/router.test.ts ================================================ import assert from 'node:assert'; import { Server } from 'node:http'; import httpRequest from 'supertest'; import { ServiceWorkerApp } from '../../src/ServiceWorkerApp'; import { StandaloneTestUtil } from '@eggjs/module-test-util/StandaloneTestUtil'; import { TestUtils } from '../Utils'; import { httpAdviceExecutionLog } from '../fixtures/http/HttpTestAdvice'; describe('standalone/service-worker/test/http/router.test.ts', () => { let app: ServiceWorkerApp; let server: Server; before(async function() { if (StandaloneTestUtil.skipOnNode()) { return this.skip(); } ({ app, server } = await TestUtils.createFetchApp('http')); }); after(async () => { server?.close(); await app?.destroy(); }); it('should get work', async () => { await httpRequest(server) .get('/hello') .expect(200, 'hello'); }); it('should post work', async () => { await httpRequest(server) .post('/echo') .send({ name: 'tegg' }) .expect(200, { success: true, data: { name: 'tegg' }, }); }); it('should return 404 with invalid path', async () => { await httpRequest(server) .get('/invalid-path') .expect(404); }); it('should execute AOP middleware for HTTP controller', async () => { httpAdviceExecutionLog.length = 0; await httpRequest(server) .get('/middleware/aop') .expect(200, { msg: 'hello' }); assert(httpAdviceExecutionLog.length > 0, 'middleware should have been executed'); assert(httpAdviceExecutionLog.includes('before'), 'middleware before should have been called'); assert(httpAdviceExecutionLog.includes('after'), 'middleware after should have been called'); }); }); ================================================ FILE: standalone/service-worker/test/mcp/mcp.test.ts ================================================ import assert from 'node:assert'; import { Server } from 'node:http'; import { Client } from '@modelcontextprotocol/sdk/client/index.js'; import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'; import { ServiceWorkerApp } from '../../src/ServiceWorkerApp'; import { StandaloneTestUtil } from '@eggjs/module-test-util/StandaloneTestUtil'; import { TestUtils } from '../Utils'; import { adviceExecutionLog } from '../fixtures/mcp/McpTestAdvice'; describe('standalone/service-worker/test/mcp/mcp.test.ts', () => { let app: ServiceWorkerApp; let server: Server; let client: Client; let baseUrl: string; before(async function() { if (StandaloneTestUtil.skipOnNode()) { return this.skip(); } ({ app, server } = await TestUtils.createFetchApp('mcp')); const address = server.address(); if (typeof address === 'object' && address) { baseUrl = `http://127.0.0.1:${address.port}`; } }); after(async () => { await client?.close(); server?.close(); await app?.destroy(); }); it('should list tools', async () => { client = new Client({ name: 'test-mcp-client', version: '1.0.0', }); const transport = new StreamableHTTPClientTransport( new URL(`${baseUrl}/mcp/test-server/stream`), ); await client.connect(transport); const result = await client.listTools(); const toolNames = result.tools.map(t => t.name); assert(toolNames.includes('echo'), 'should have echo tool'); assert(toolNames.includes('add'), 'should have add tool'); const echoTool = result.tools.find(t => t.name === 'echo'); assert.strictEqual(echoTool?.description, 'Echo the input message'); const addTool = result.tools.find(t => t.name === 'add'); assert.strictEqual(addTool?.description, 'Add two numbers'); }); it('should call echo tool', async () => { client = new Client({ name: 'test-mcp-client', version: '1.0.0', }); const transport = new StreamableHTTPClientTransport( new URL(`${baseUrl}/mcp/test-server/stream`), ); await client.connect(transport); const result = await client.callTool({ name: 'echo', arguments: { message: 'hello tegg' }, }); assert.deepStrictEqual(result, { content: [{ type: 'text', text: 'hello tegg' }], }); }); it('should call add tool', async () => { client = new Client({ name: 'test-mcp-client', version: '1.0.0', }); const transport = new StreamableHTTPClientTransport( new URL(`${baseUrl}/mcp/test-server/stream`), ); await client.connect(transport); const result = await client.callTool({ name: 'add', arguments: { a: 3, b: 5 }, }); assert.deepStrictEqual(result, { content: [{ type: 'text', text: '8' }], }); }); it('should connect via /mcp/${name} path', async () => { client = new Client({ name: 'test-mcp-client', version: '1.0.0', }); const transport = new StreamableHTTPClientTransport( new URL(`${baseUrl}/mcp/test-server`), ); await client.connect(transport); const result = await client.listTools(); const toolNames = result.tools.map(t => t.name); assert(toolNames.includes('echo'), 'should have echo tool'); assert(toolNames.includes('add'), 'should have add tool'); const echoResult = await client.callTool({ name: 'echo', arguments: { message: 'hello from base path' }, }); assert.deepStrictEqual(echoResult, { content: [{ type: 'text', text: 'hello from base path' }], }); }); it('should execute AOP middleware', async () => { adviceExecutionLog.length = 0; client = new Client({ name: 'test-mcp-client', version: '1.0.0', }); const transport = new StreamableHTTPClientTransport( new URL(`${baseUrl}/mcp/test-server/stream`), ); await client.connect(transport); await client.callTool({ name: 'echo', arguments: { message: 'middleware test' }, }); // The middleware should have been executed for both the connect and callTool requests assert(adviceExecutionLog.length > 0, 'middleware should have been executed'); assert(adviceExecutionLog.includes('before'), 'middleware before should have been called'); assert(adviceExecutionLog.includes('after'), 'middleware after should have been called'); }); }); ================================================ FILE: standalone/service-worker/tsconfig.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "dist", "baseUrl": "./" }, "exclude": [ "dist", "node_modules", "test" ] } ================================================ FILE: standalone/service-worker/tsconfig.pub.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "dist", "baseUrl": "./" }, "exclude": [ "dist", "node_modules", "test" ] } ================================================ FILE: standalone/standalone/CHANGELOG.md ================================================ # Change Log All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. ## [3.78.15](https://github.com/eggjs/tegg/compare/v3.78.14...v3.78.15) (2026-04-23) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.78.14](https://github.com/eggjs/tegg/compare/v3.78.13...v3.78.14) (2026-04-21) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.78.13](https://github.com/eggjs/tegg/compare/v3.78.12...v3.78.13) (2026-04-21) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.78.12](https://github.com/eggjs/tegg/compare/v3.78.11...v3.78.12) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.78.11](https://github.com/eggjs/tegg/compare/v3.78.10...v3.78.11) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.78.10](https://github.com/eggjs/tegg/compare/v3.78.9...v3.78.10) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.78.9](https://github.com/eggjs/tegg/compare/v3.78.8...v3.78.9) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.78.8](https://github.com/eggjs/tegg/compare/v3.78.7...v3.78.8) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.78.7](https://github.com/eggjs/tegg/compare/v3.78.6...v3.78.7) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.78.6](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.6) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.78.5](https://github.com/eggjs/tegg/compare/v3.78.4...v3.78.5) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.78.4](https://github.com/eggjs/tegg/compare/v3.78.3...v3.78.4) (2026-04-18) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.78.3](https://github.com/eggjs/tegg/compare/v3.78.2...v3.78.3) (2026-04-15) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.78.2](https://github.com/eggjs/tegg/compare/v3.78.1...v3.78.2) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.78.1](https://github.com/eggjs/tegg/compare/v3.78.0...v3.78.1) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-standalone # [3.78.0](https://github.com/eggjs/tegg/compare/v3.77.2...v3.78.0) (2026-04-09) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.77.2](https://github.com/eggjs/tegg/compare/v3.77.1...v3.77.2) (2026-04-07) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.77.1](https://github.com/eggjs/tegg/compare/v3.77.0...v3.77.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-standalone # [3.77.0](https://github.com/eggjs/tegg/compare/v3.76.1...v3.77.0) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.76.1](https://github.com/eggjs/tegg/compare/v3.76.0...v3.76.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-standalone # [3.76.0](https://github.com/eggjs/tegg/compare/v3.75.1...v3.76.0) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.75.1](https://github.com/eggjs/tegg/compare/v3.75.0...v3.75.1) (2026-04-01) **Note:** Version bump only for package @eggjs/tegg-standalone # [3.75.0](https://github.com/eggjs/tegg/compare/v3.74.0...v3.75.0) (2026-03-30) **Note:** Version bump only for package @eggjs/tegg-standalone # [3.74.0](https://github.com/eggjs/tegg/compare/v3.73.0...v3.74.0) (2026-03-30) **Note:** Version bump only for package @eggjs/tegg-standalone # [3.73.0](https://github.com/eggjs/tegg/compare/v3.72.0...v3.73.0) (2026-03-25) **Note:** Version bump only for package @eggjs/tegg-standalone # [3.72.0](https://github.com/eggjs/tegg/compare/v3.71.2...v3.72.0) (2026-02-04) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.71.2](https://github.com/eggjs/tegg/compare/v3.71.1...v3.71.2) (2026-01-30) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.71.1](https://github.com/eggjs/tegg/compare/v3.71.0...v3.71.1) (2026-01-28) **Note:** Version bump only for package @eggjs/tegg-standalone # [3.71.0](https://github.com/eggjs/tegg/compare/v3.70.1...v3.71.0) (2026-01-26) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.70.1](https://github.com/eggjs/tegg/compare/v3.70.0...v3.70.1) (2026-01-14) **Note:** Version bump only for package @eggjs/tegg-standalone # [3.70.0](https://github.com/eggjs/tegg/compare/v3.69.0...v3.70.0) (2026-01-07) **Note:** Version bump only for package @eggjs/tegg-standalone # [3.69.0](https://github.com/eggjs/tegg/compare/v3.68.0...v3.69.0) (2026-01-06) **Note:** Version bump only for package @eggjs/tegg-standalone # [3.68.0](https://github.com/eggjs/tegg/compare/v3.67.2...v3.68.0) (2025-12-29) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.67.2](https://github.com/eggjs/tegg/compare/v3.67.1...v3.67.2) (2025-12-24) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.67.1](https://github.com/eggjs/tegg/compare/v3.67.0...v3.67.1) (2025-12-23) **Note:** Version bump only for package @eggjs/tegg-standalone # [3.67.0](https://github.com/eggjs/tegg/compare/v3.66.0...v3.67.0) (2025-12-18) **Note:** Version bump only for package @eggjs/tegg-standalone # [3.66.0](https://github.com/eggjs/tegg/compare/v3.65.3...v3.66.0) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.65.3](https://github.com/eggjs/tegg/compare/v3.65.2...v3.65.3) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.65.2](https://github.com/eggjs/tegg/compare/v3.65.1...v3.65.2) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.65.1](https://github.com/eggjs/tegg/compare/v3.65.0...v3.65.1) (2025-12-16) **Note:** Version bump only for package @eggjs/tegg-standalone # [3.65.0](https://github.com/eggjs/tegg/compare/v3.64.5...v3.65.0) (2025-12-10) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.64.5](https://github.com/eggjs/tegg/compare/v3.64.4...v3.64.5) (2025-12-08) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.64.4](https://github.com/eggjs/tegg/compare/v3.64.3...v3.64.4) (2025-12-06) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.64.3](https://github.com/eggjs/tegg/compare/v3.64.2...v3.64.3) (2025-12-06) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.64.2](https://github.com/eggjs/tegg/compare/v3.64.1...v3.64.2) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.64.1](https://github.com/eggjs/tegg/compare/v3.64.0...v3.64.1) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-standalone # [3.64.0](https://github.com/eggjs/tegg/compare/v3.63.2...v3.64.0) (2025-11-27) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.63.2](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.2) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.63.1](https://github.com/eggjs/tegg/compare/v3.63.0...v3.63.1) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg-standalone # [3.63.0](https://github.com/eggjs/tegg/compare/v3.62.3...v3.63.0) (2025-11-18) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.62.3](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.3) (2025-11-05) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.62.2](https://github.com/eggjs/tegg/compare/v3.62.1...v3.62.2) (2025-11-05) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.62.1](https://github.com/eggjs/tegg/compare/v3.62.0...v3.62.1) (2025-11-03) **Note:** Version bump only for package @eggjs/tegg-standalone # [3.62.0](https://github.com/eggjs/tegg/compare/v3.61.0...v3.62.0) (2025-09-24) ### Bug Fixes * deduplicate modules reference ([#343](https://github.com/eggjs/tegg/issues/343)) ([aa5daf7](https://github.com/eggjs/tegg/commit/aa5daf7e8db49c8b273ba2102c127fd14a2de044)) # [3.61.0](https://github.com/eggjs/tegg/compare/v3.60.3...v3.61.0) (2025-08-15) ### Features * allow inject MysqlDataSourceManager ([#342](https://github.com/eggjs/tegg/issues/342)) ([d13b2d7](https://github.com/eggjs/tegg/commit/d13b2d7cd11dd36960647cb40bfc4bf92ce704fd)) ## [3.60.3](https://github.com/eggjs/tegg/compare/v3.60.2...v3.60.3) (2025-08-06) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.60.2](https://github.com/eggjs/tegg/compare/v3.60.1...v3.60.2) (2025-08-06) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.60.1](https://github.com/eggjs/tegg/compare/v3.60.0...v3.60.1) (2025-07-28) **Note:** Version bump only for package @eggjs/tegg-standalone # [3.60.0](https://github.com/eggjs/tegg/compare/v3.59.1...v3.60.0) (2025-07-28) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.59.1](https://github.com/eggjs/tegg/compare/v3.59.0...v3.59.1) (2025-07-16) **Note:** Version bump only for package @eggjs/tegg-standalone # [3.59.0](https://github.com/eggjs/tegg/compare/v3.58.0...v3.59.0) (2025-07-07) **Note:** Version bump only for package @eggjs/tegg-standalone # [3.58.0](https://github.com/eggjs/tegg/compare/v3.57.14...v3.58.0) (2025-07-01) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.57.14](https://github.com/eggjs/tegg/compare/v3.57.13...v3.57.14) (2025-06-18) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.57.13](https://github.com/eggjs/tegg/compare/v3.57.12...v3.57.13) (2025-06-18) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.57.12](https://github.com/eggjs/tegg/compare/v3.57.11...v3.57.12) (2025-06-16) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.57.11](https://github.com/eggjs/tegg/compare/v3.57.10...v3.57.11) (2025-06-16) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.57.10](https://github.com/eggjs/tegg/compare/v3.57.9...v3.57.10) (2025-06-12) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.57.9](https://github.com/eggjs/tegg/compare/v3.57.8...v3.57.9) (2025-05-29) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.57.8](https://github.com/eggjs/tegg/compare/v3.57.7...v3.57.8) (2025-05-29) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.57.7](https://github.com/eggjs/tegg/compare/v3.57.6...v3.57.7) (2025-05-28) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.57.6](https://github.com/eggjs/tegg/compare/v3.57.5...v3.57.6) (2025-05-27) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.57.5](https://github.com/eggjs/tegg/compare/v3.57.4...v3.57.5) (2025-05-15) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.57.4](https://github.com/eggjs/tegg/compare/v3.57.3...v3.57.4) (2025-05-14) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.57.3](https://github.com/eggjs/tegg/compare/v3.57.2...v3.57.3) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.57.2](https://github.com/eggjs/tegg/compare/v3.57.1...v3.57.2) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.57.1](https://github.com/eggjs/tegg/compare/v3.57.0...v3.57.1) (2025-05-13) **Note:** Version bump only for package @eggjs/tegg-standalone # [3.57.0](https://github.com/eggjs/tegg/compare/v3.56.3...v3.57.0) (2025-05-09) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.56.3](https://github.com/eggjs/tegg/compare/v3.56.2...v3.56.3) (2025-05-07) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.56.2](https://github.com/eggjs/tegg/compare/v3.56.1...v3.56.2) (2025-05-01) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.56.1](https://github.com/eggjs/tegg/compare/v3.56.0...v3.56.1) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-standalone # [3.56.0](https://github.com/eggjs/tegg/compare/v3.55.0...v3.56.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-standalone # [3.55.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.55.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-standalone # [3.54.0](https://github.com/eggjs/tegg/compare/v3.53.0...v3.54.0) (2025-04-29) **Note:** Version bump only for package @eggjs/tegg-standalone # [3.53.0](https://github.com/eggjs/tegg/compare/v3.52.1...v3.53.0) (2025-04-21) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.52.1](https://github.com/eggjs/tegg/compare/v3.52.0...v3.52.1) (2025-03-14) **Note:** Version bump only for package @eggjs/tegg-standalone # [3.52.0](https://github.com/eggjs/tegg/compare/v3.51.2...v3.52.0) (2024-12-30) ### Features * dal retry when init failed ([#260](https://github.com/eggjs/tegg/issues/260)) ([74e7c06](https://github.com/eggjs/tegg/commit/74e7c067c3ff7ae0ed705abaaa8a91f804e487e3)) ## [3.51.2](https://github.com/eggjs/tegg/compare/v3.51.1...v3.51.2) (2024-12-09) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.51.1](https://github.com/eggjs/tegg/compare/v3.51.0...v3.51.1) (2024-11-06) ### Bug Fixes * remove inner class hook ([#257](https://github.com/eggjs/tegg/issues/257)) ([faffd34](https://github.com/eggjs/tegg/commit/faffd3492f9edd411213034651d6863fb3f1a24d)) # [3.51.0](https://github.com/eggjs/tegg/compare/v3.50.1...v3.51.0) (2024-10-30) ### Features * support optional inject ([#254](https://github.com/eggjs/tegg/issues/254)) ([260470b](https://github.com/eggjs/tegg/commit/260470b766d5fdb323c1bd72cc6260a90468a161)) ## [3.50.1](https://github.com/eggjs/tegg/compare/v3.50.0...v3.50.1) (2024-10-23) ### Bug Fixes * disable dump in preload ([#253](https://github.com/eggjs/tegg/issues/253)) ([081912b](https://github.com/eggjs/tegg/commit/081912beb9cb945c863c73d91ef5be112c2940d9)) # [3.50.0](https://github.com/eggjs/tegg/compare/v3.49.0...v3.50.0) (2024-10-22) ### Features * add dump switcher ([#252](https://github.com/eggjs/tegg/issues/252)) ([80c312f](https://github.com/eggjs/tegg/commit/80c312f7862b4021180f3e587f63c6b0dd87202c)) # [3.49.0](https://github.com/eggjs/tegg/compare/v3.48.1...v3.49.0) (2024-10-21) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.48.1](https://github.com/eggjs/tegg/compare/v3.48.0...v3.48.1) (2024-10-14) **Note:** Version bump only for package @eggjs/tegg-standalone # [3.48.0](https://github.com/eggjs/tegg/compare/v3.47.2...v3.48.0) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.47.2](https://github.com/eggjs/tegg/compare/v3.47.1...v3.47.2) (2024-10-10) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.47.1](https://github.com/eggjs/tegg/compare/v3.47.0...v3.47.1) (2024-10-10) ### Bug Fixes * fix aop in constructor inject type ([#247](https://github.com/eggjs/tegg/issues/247)) ([d169bb2](https://github.com/eggjs/tegg/commit/d169bb2fbbc86335315619866b4134a25296f552)) # [3.47.0](https://github.com/eggjs/tegg/compare/v3.46.4...v3.47.0) (2024-10-10) ### Features * impl GlobalGraph build hook ([#246](https://github.com/eggjs/tegg/issues/246)) ([48fce45](https://github.com/eggjs/tegg/commit/48fce4512e99259ec26a9b032bfcc9f4046ad235)) ## [3.46.4](https://github.com/eggjs/tegg/compare/v3.46.3...v3.46.4) (2024-10-09) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.46.3](https://github.com/eggjs/tegg/compare/v3.46.2...v3.46.3) (2024-10-08) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.46.2](https://github.com/eggjs/tegg/compare/v3.46.1...v3.46.2) (2024-10-07) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.46.1](https://github.com/eggjs/tegg/compare/v3.46.0...v3.46.1) (2024-09-30) **Note:** Version bump only for package @eggjs/tegg-standalone # [3.46.0](https://github.com/eggjs/tegg/compare/v3.45.0...v3.46.0) (2024-09-29) ### Features * impl MultiInstance inject MultiInstance ([#240](https://github.com/eggjs/tegg/issues/240)) ([08e3b0c](https://github.com/eggjs/tegg/commit/08e3b0cc02f3d2dbba767298a6aec6c00147f9ed)) # [3.45.0](https://github.com/eggjs/tegg/compare/v3.44.1...v3.45.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.44.1](https://github.com/eggjs/tegg/compare/v3.44.0...v3.44.1) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-standalone # [3.44.0](https://github.com/eggjs/tegg/compare/v3.43.2...v3.44.0) (2024-09-29) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.43.2](https://github.com/eggjs/tegg/compare/v3.43.1...v3.43.2) (2024-09-14) ### Bug Fixes * add preload loadunit ([#236](https://github.com/eggjs/tegg/issues/236)) ([0e28972](https://github.com/eggjs/tegg/commit/0e2897200a9bc3bc6aa1028c8549bdbf45bbaaa3)) ## [3.43.1](https://github.com/eggjs/tegg/compare/v3.43.0...v3.43.1) (2024-09-14) **Note:** Version bump only for package @eggjs/tegg-standalone # [3.43.0](https://github.com/eggjs/tegg/compare/v3.42.0...v3.43.0) (2024-09-13) **Note:** Version bump only for package @eggjs/tegg-standalone # [3.42.0](https://github.com/eggjs/tegg/compare/v3.41.0...v3.42.0) (2024-09-10) ### Features * add LifecyclePreLoad ([#234](https://github.com/eggjs/tegg/issues/234)) ([2b72163](https://github.com/eggjs/tegg/commit/2b7216387f02cd045952447eaa21baa3a7ee04a3)) # [3.41.0](https://github.com/eggjs/tegg/compare/v3.40.1...v3.41.0) (2024-08-26) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.40.1](https://github.com/eggjs/tegg/compare/v3.40.0...v3.40.1) (2024-08-23) **Note:** Version bump only for package @eggjs/tegg-standalone # [3.40.0](https://github.com/eggjs/tegg/compare/v3.39.5...v3.40.0) (2024-08-22) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.39.5](https://github.com/eggjs/tegg/compare/v3.39.4...v3.39.5) (2024-08-09) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.39.4](https://github.com/eggjs/tegg/compare/v3.39.3...v3.39.4) (2024-07-09) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.39.3](https://github.com/eggjs/tegg/compare/v3.39.2...v3.39.3) (2024-04-28) ### Bug Fixes * mount clazzExtension/clazzExtension/tableSql to BaseDao ([#220](https://github.com/eggjs/tegg/issues/220)) ([ac322cf](https://github.com/eggjs/tegg/commit/ac322cfc4100841a1483b04b99e04d553af323eb)) ## [3.39.2](https://github.com/eggjs/tegg/compare/v3.39.1...v3.39.2) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.39.1](https://github.com/eggjs/tegg/compare/v3.39.0...v3.39.1) (2024-04-28) **Note:** Version bump only for package @eggjs/tegg-standalone # [3.39.0](https://github.com/eggjs/tegg/compare/v3.38.0...v3.39.0) (2024-04-19) ### Features * use app.loader.getTypeFiles to generate module config file names ([#213](https://github.com/eggjs/tegg/issues/213)) ([e0656a4](https://github.com/eggjs/tegg/commit/e0656a4d59beef103a5627461d9b9c87996928e3)) # [3.38.0](https://github.com/eggjs/tegg/compare/v3.37.3...v3.38.0) (2024-04-18) ### Features * impl dal transaction ([#214](https://github.com/eggjs/tegg/issues/214)) ([b8b67dd](https://github.com/eggjs/tegg/commit/b8b67dd7e0fb282d78de7e68e68834ff79d30732)) ## [3.37.3](https://github.com/eggjs/tegg/compare/v3.37.2...v3.37.3) (2024-04-17) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.37.2](https://github.com/eggjs/tegg/compare/v3.37.1...v3.37.2) (2024-04-17) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.37.1](https://github.com/eggjs/tegg/compare/v3.37.0...v3.37.1) (2024-04-16) **Note:** Version bump only for package @eggjs/tegg-standalone # [3.37.0](https://github.com/eggjs/tegg/compare/v3.36.3...v3.37.0) (2024-04-16) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.36.3](https://github.com/eggjs/tegg/compare/v3.36.2...v3.36.3) (2024-04-10) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.36.2](https://github.com/eggjs/tegg/compare/v3.36.1...v3.36.2) (2024-04-08) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.36.1](https://github.com/eggjs/tegg/compare/v3.36.0...v3.36.1) (2024-04-07) **Note:** Version bump only for package @eggjs/tegg-standalone # [3.36.0](https://github.com/eggjs/tegg/compare/v3.35.1...v3.36.0) (2024-04-02) ### Features * impl ajv + typebox Validator ([#201](https://github.com/eggjs/tegg/issues/201)) ([9fd585d](https://github.com/eggjs/tegg/commit/9fd585de9b613466c96b73494a08a494db34ea57)) * impl dal forkDb ([#202](https://github.com/eggjs/tegg/issues/202)) ([a411f04](https://github.com/eggjs/tegg/commit/a411f04e074425419b5b348a362f120bf8189541)) ## [3.35.1](https://github.com/eggjs/tegg/compare/v3.35.0...v3.35.1) (2024-03-26) **Note:** Version bump only for package @eggjs/tegg-standalone # [3.35.0](https://github.com/eggjs/tegg/compare/v3.34.0...v3.35.0) (2024-03-26) **Note:** Version bump only for package @eggjs/tegg-standalone # [3.34.0](https://github.com/eggjs/tegg/compare/v3.33.1...v3.34.0) (2024-03-22) ### Features * impl dal for standalone tegg ([#197](https://github.com/eggjs/tegg/issues/197)) ([56b259d](https://github.com/eggjs/tegg/commit/56b259d7215a9d9542b36e421996623819369846)) ## [3.33.1](https://github.com/eggjs/tegg/compare/v3.33.0...v3.33.1) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-standalone # [3.33.0](https://github.com/eggjs/tegg/compare/v3.32.0...v3.33.0) (2024-03-22) **Note:** Version bump only for package @eggjs/tegg-standalone # [3.32.0](https://github.com/eggjs/tegg/compare/v3.31.0...v3.32.0) (2024-02-19) **Note:** Version bump only for package @eggjs/tegg-standalone # [3.31.0](https://github.com/eggjs/tegg/compare/v3.30.1...v3.31.0) (2024-01-31) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.30.1](https://github.com/eggjs/tegg/compare/v3.30.0...v3.30.1) (2024-01-25) **Note:** Version bump only for package @eggjs/tegg-standalone # [3.30.0](https://github.com/eggjs/tegg/compare/v3.29.0...v3.30.0) (2024-01-17) ### Bug Fixes * config for env is not merged when default config is empty ([#178](https://github.com/eggjs/tegg/issues/178)) ([9c1de22](https://github.com/eggjs/tegg/commit/9c1de223e9c9befb0a803ac5a1bd843f74aa9493)) # [3.29.0](https://github.com/eggjs/tegg/compare/v3.28.2...v3.29.0) (2023-12-26) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.28.2](https://github.com/eggjs/tegg/compare/v3.28.1...v3.28.2) (2023-12-12) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.28.1](https://github.com/eggjs/tegg/compare/v3.28.0...v3.28.1) (2023-12-11) **Note:** Version bump only for package @eggjs/tegg-standalone # [3.28.0](https://github.com/eggjs/tegg/compare/v3.27.0...v3.28.0) (2023-12-10) **Note:** Version bump only for package @eggjs/tegg-standalone # [3.27.0](https://github.com/eggjs/tegg/compare/v3.26.0...v3.27.0) (2023-11-23) **Note:** Version bump only for package @eggjs/tegg-standalone # [3.26.0](https://github.com/eggjs/tegg/compare/v3.25.2...v3.26.0) (2023-11-17) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.25.2](https://github.com/eggjs/tegg/compare/v3.25.1...v3.25.2) (2023-11-06) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.25.1](https://github.com/eggjs/tegg/compare/v3.25.0...v3.25.1) (2023-11-03) ### Bug Fixes * fix standalone import ConfigSource ([#163](https://github.com/eggjs/tegg/issues/163)) ([6922071](https://github.com/eggjs/tegg/commit/6922071219413a8a11387be3d05f0e3970ce4f48)) # [3.25.0](https://github.com/eggjs/tegg/compare/v3.24.0...v3.25.0) (2023-11-03) ### Features * tegg plugin support ModuleConfig ([#162](https://github.com/eggjs/tegg/issues/162)) ([58bd9fa](https://github.com/eggjs/tegg/commit/58bd9fafdd0d56aabdde5f7c33f17c45568bada8)) # [3.24.0](https://github.com/eggjs/tegg/compare/v3.23.0...v3.24.0) (2023-10-26) **Note:** Version bump only for package @eggjs/tegg-standalone # [3.23.0](https://github.com/eggjs/tegg/compare/v3.22.0...v3.23.0) (2023-09-20) ### Features * add className property to EggPrototypeInfo ([#158](https://github.com/eggjs/tegg/issues/158)) ([bddac97](https://github.com/eggjs/tegg/commit/bddac97a9f575c9f13b794246a7e8346c58d1a09)) # [3.20.0](https://github.com/eggjs/tegg/compare/v3.19.0...v3.20.0) (2023-09-07) ### Features * support load module config with env ([#151](https://github.com/eggjs/tegg/issues/151)) ([c087226](https://github.com/eggjs/tegg/commit/c087226bd7764242fadce5622fccd9e9fee56322)) # [3.19.0](https://github.com/eggjs/tegg/compare/v3.18.1...v3.19.0) (2023-08-31) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.18.1](https://github.com/eggjs/tegg/compare/v3.18.0...v3.18.1) (2023-08-29) ### Bug Fixes * fix use MultiInstanceProto from other modules ([#147](https://github.com/eggjs/tegg/issues/147)) ([b71af60](https://github.com/eggjs/tegg/commit/b71af60ce6d1da0d778f5e712633b8c15052bd70)) # [3.18.0](https://github.com/eggjs/tegg/compare/v3.17.0...v3.18.0) (2023-08-29) ### Features * add aop runtime/dynamic inject runtime for standalone ([#149](https://github.com/eggjs/tegg/issues/149)) ([6091fc6](https://github.com/eggjs/tegg/commit/6091fc6be885976d72a6920d37ec685927b63d5d)) # [3.17.0](https://github.com/eggjs/tegg/compare/v3.16.0...v3.17.0) (2023-08-24) ### Features * impl MultiInstanceProto ([#145](https://github.com/eggjs/tegg/issues/145)) ([12fd5cf](https://github.com/eggjs/tegg/commit/12fd5cff4004578bcc737dcdf4f7e9d1159f5633)) # [3.16.0](https://github.com/eggjs/tegg/compare/v3.15.0...v3.16.0) (2023-08-24) ### Features * export EggModuleLoader in standalone ([#146](https://github.com/eggjs/tegg/issues/146)) ([9d1da9a](https://github.com/eggjs/tegg/commit/9d1da9a87dbd486930adc50cd43020c2fb478230)) * implement RuntimeConfig ([#144](https://github.com/eggjs/tegg/issues/144)) ([0862655](https://github.com/eggjs/tegg/commit/0862655846f6765349d406ee697c036cec2a37bd)) ## [3.14.3](https://github.com/eggjs/tegg/compare/v3.14.2...v3.14.3) (2023-08-14) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.14.2](https://github.com/eggjs/tegg/compare/v3.14.1...v3.14.2) (2023-08-14) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.14.1](https://github.com/eggjs/tegg/compare/v3.14.0...v3.14.1) (2023-08-11) **Note:** Version bump only for package @eggjs/tegg-standalone # [3.14.0](https://github.com/eggjs/tegg/compare/v3.13.0...v3.14.0) (2023-07-31) ### Features * impl ModuleConfigs for standalone ([#136](https://github.com/eggjs/tegg/issues/136)) ([7227492](https://github.com/eggjs/tegg/commit/7227492295b9c84e3660bfc006ca96e7a9652a25)) # [3.13.0](https://github.com/eggjs/tegg/compare/v3.12.0...v3.13.0) (2023-07-25) **Note:** Version bump only for package @eggjs/tegg-standalone # [3.12.0](https://github.com/eggjs/tegg/compare/v3.11.1...v3.12.0) (2023-07-13) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.11.1](https://github.com/eggjs/tegg/compare/v3.11.0...v3.11.1) (2023-06-29) ### Bug Fixes * export StandaloneInnerObject ([#131](https://github.com/eggjs/tegg/issues/131)) ([e4b87e0](https://github.com/eggjs/tegg/commit/e4b87e0a48e3232adaf43bad75f44d0ae775c984)) # [3.11.0](https://github.com/eggjs/tegg/compare/v3.10.0...v3.11.0) (2023-06-29) **Note:** Version bump only for package @eggjs/tegg-standalone # [3.10.0](https://github.com/eggjs/tegg/compare/v3.9.0...v3.10.0) (2023-06-28) ### Features * standalone Runner run support ctx ([#126](https://github.com/eggjs/tegg/issues/126)) ([0788c7d](https://github.com/eggjs/tegg/commit/0788c7dfb57f96c55e94cc6692c0b6e9ac1ee03c)) # [3.9.0](https://github.com/eggjs/tegg/compare/v3.8.0...v3.9.0) (2023-06-20) **Note:** Version bump only for package @eggjs/tegg-standalone # [3.8.0](https://github.com/eggjs/tegg/compare/v3.7.0...v3.8.0) (2023-05-30) ### Features * impl EggObjectLifecycle hook in decorator ([#119](https://github.com/eggjs/tegg/issues/119)) ([cced8a2](https://github.com/eggjs/tegg/commit/cced8a2e009c33d5040fa21d00409fddef471b0e)) # [3.7.0](https://github.com/eggjs/tegg/compare/v3.6.3...v3.7.0) (2023-04-03) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.6.3](https://github.com/eggjs/tegg/compare/v3.6.2...v3.6.3) (2023-03-02) **Note:** Version bump only for package @eggjs/tegg-standalone # [3.6.0](https://github.com/eggjs/tegg/compare/v3.5.2...v3.6.0) (2023-02-13) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.5.2](https://github.com/eggjs/tegg/compare/v3.5.1...v3.5.2) (2023-02-10) **Note:** Version bump only for package @eggjs/tegg-standalone # [3.5.0](https://github.com/eggjs/tegg/compare/v3.4.1...v3.5.0) (2023-02-10) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.4.1](https://github.com/eggjs/tegg/compare/v3.4.0...v3.4.1) (2023-02-02) **Note:** Version bump only for package @eggjs/tegg-standalone # [3.4.0](https://github.com/eggjs/tegg/compare/v3.3.4...v3.4.0) (2023-02-01) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.3.1](https://github.com/eggjs/tegg/compare/v3.3.0...v3.3.1) (2023-01-28) **Note:** Version bump only for package @eggjs/tegg-standalone # [3.3.0](https://github.com/eggjs/tegg/compare/v3.2.4...v3.3.0) (2023-01-28) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.2.3](https://github.com/eggjs/tegg/compare/v3.2.2...v3.2.3) (2023-01-16) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.2.2](https://github.com/eggjs/tegg/compare/v3.2.1...v3.2.2) (2023-01-06) **Note:** Version bump only for package @eggjs/tegg-standalone ## [3.2.1](https://github.com/eggjs/tegg/compare/v3.2.0...v3.2.1) (2022-12-28) **Note:** Version bump only for package @eggjs/tegg-standalone # [3.1.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.1.0) (2022-12-27) ### Features * **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) * standalone support context ([#65](https://github.com/eggjs/tegg/issues/65)) ([b35dc2d](https://github.com/eggjs/tegg/commit/b35dc2d40fff1331145abd3f04917dc64f80010b)) # [3.0.0](https://github.com/eggjs/tegg/compare/v3.0.0-alpha.0...v3.0.0) (2022-12-26) **Note:** Version bump only for package @eggjs/tegg-standalone # [3.0.0-alpha.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.0.0-alpha.0) (2022-12-22) ### Features * **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) * standalone support context ([#65](https://github.com/eggjs/tegg/issues/65)) ([b35dc2d](https://github.com/eggjs/tegg/commit/b35dc2d40fff1331145abd3f04917dc64f80010b)) ## [1.3.6](https://github.com/eggjs/tegg/compare/@eggjs/tegg-standalone@1.3.5...@eggjs/tegg-standalone@1.3.6) (2022-09-04) **Note:** Version bump only for package @eggjs/tegg-standalone ## [1.3.5](https://github.com/eggjs/tegg/compare/@eggjs/tegg-standalone@1.3.4...@eggjs/tegg-standalone@1.3.5) (2022-08-16) **Note:** Version bump only for package @eggjs/tegg-standalone ## [1.3.4](https://github.com/eggjs/tegg/compare/@eggjs/tegg-standalone@1.3.3...@eggjs/tegg-standalone@1.3.4) (2022-07-28) **Note:** Version bump only for package @eggjs/tegg-standalone ## [1.3.3](https://github.com/eggjs/tegg/compare/@eggjs/tegg-standalone@1.3.2...@eggjs/tegg-standalone@1.3.3) (2022-07-20) **Note:** Version bump only for package @eggjs/tegg-standalone ## [1.3.2](https://github.com/eggjs/tegg/compare/@eggjs/tegg-standalone@1.3.1...@eggjs/tegg-standalone@1.3.2) (2022-07-20) **Note:** Version bump only for package @eggjs/tegg-standalone # [1.3.0](https://github.com/eggjs/tegg/compare/v1.2.0...v1.3.0) (2022-07-01) **Note:** Version bump only for package @eggjs/tegg-standalone # [0.2.0](https://github.com/eggjs/tegg/compare/v0.1.19...v0.2.0) (2022-01-20) **Note:** Version bump only for package @eggjs/tegg-standalone ================================================ FILE: standalone/standalone/README.md ================================================ # `@eggjs/tegg-standalone` 通过 `@eggjs/tegg-standalone` 在一个独立环境去中运行 tegg 应用。 ## install ```sh npm i --save @eggjs/tegg-standalone ``` ## Usage 当一个类上有 Runner 注解时,会自动运行其 main 函数。注无需再使用 `ContextProto` 注解,因为独立运行跑完即销毁,不用再区分独立上下文。 ```ts import { Runner, MainRunner } from '@eggjs/tegg/standalone'; @Runner() @SingletonProto() export class Foo implements MainRunner { @Inject() hello: Hello; async main(): Promise { return this.hello.hello(); } } ``` 运行代码 - cwd 为当前应用工作目录 - options: - innerObjects: 当前运行环境中内置的对象 ``` await main(cwd, { innerObjects: { hello: { hello: () => { return 'hello, inner'; }, }, }, }); ``` ### 配置 module 支持通过 module.yml 来定义配置,在代码中可以通过注入 moduleConfigs 获取全局配置,通过注入 moduleConfig 来获取单 module 的配置。 ```yaml # module.yml # module 根目录中 features: dynamic: foo: 'bar' ``` ```ts @ContextProto() export class Foo { // 获取全局配置, 通过 get 方法来获取特定 module 的配置 @Inject() moduleConfigs: ModuleConfigs; // 注入当前 module 的配置 @Inject() moduleConfig: ModuleConfig; // 注入 "bar" module 的配置 @Inject({ name: 'moduleConfig', }) @ConfigSourceQualifier('bar') barModuleConfig: ModuleConfig; async main() { return { configs: this.moduleConfigs, foo: this.moduleConfig, bar: this.barModuleConfig, }; } } ``` ================================================ FILE: standalone/standalone/index.ts ================================================ export * from './src/EggModuleLoader'; export * from './src/Runner'; export * from './src/main'; export * from './src/StandaloneInnerObjectProto'; export * from './src/StandaloneContext'; export * from './src/StandaloneInnerObject'; ================================================ FILE: standalone/standalone/package.json ================================================ { "name": "@eggjs/tegg-standalone", "description": "tegg standalone", "version": "3.78.15", "keywords": [ "egg", "typescript", "background", "async", "tegg", "standalone" ], "main": "dist/index.js", "files": [ "dist/**/*.js", "dist/**/*.d.ts" ], "typings": "dist/index.d.ts", "scripts": { "test": "cross-env NODE_ENV=test NODE_OPTIONS='--no-deprecation' mocha", "clean": "tsc -b --clean", "tsc": "ut run clean && tsc -p ./tsconfig.json", "tsc:pub": "ut run clean && tsc -p ./tsconfig.pub.json", "prepublishOnly": "ut tsc:pub" }, "homepage": "https://github.com/eggjs/tegg", "bugs": { "url": "https://github.com/eggjs/tegg/issues" }, "repository": { "type": "git", "url": "git@github.com:eggjs/tegg.git", "directory": "standalone/standalone" }, "engines": { "node": ">=14.0.0" }, "author": "killagu ", "license": "MIT", "dependencies": { "@eggjs/egg-module-common": "^3.78.15", "@eggjs/tegg": "^3.78.15", "@eggjs/tegg-aop-runtime": "^3.78.15", "@eggjs/tegg-background-task": "^3.78.15", "@eggjs/tegg-common-util": "^3.78.15", "@eggjs/tegg-dal-plugin": "^3.78.15", "@eggjs/tegg-dynamic-inject-runtime": "^3.78.15", "@eggjs/tegg-lifecycle": "^3.78.15", "@eggjs/tegg-loader": "^3.78.15", "@eggjs/tegg-metadata": "^3.78.15", "@eggjs/tegg-runtime": "^3.78.15" }, "publishConfig": { "access": "public" }, "devDependencies": { "@eggjs/tegg-ajv-plugin": "^3.78.15", "@types/mocha": "^10.0.1", "@types/node": "^20.2.4", "cross-env": "^7.0.3", "mm": "^3.2.1", "mocha": "^10.2.0", "ts-node": "^10.9.1", "typescript": "^5.0.4" }, "gitHead": "240a3e9f40fda65ebb7589727d197db5ce17916c" } ================================================ FILE: standalone/standalone/src/ConfigSourceLoadUnitHook.ts ================================================ import { LoadUnit, LoadUnitLifecycleContext } from '@eggjs/tegg-metadata'; import { LifecycleHook, PrototypeUtil, QualifierUtil, ConfigSourceQualifier, ConfigSourceQualifierAttribute, } from '@eggjs/tegg'; /** * Hook for inject moduleConfig. * Add default qualifier value is current module name. */ export class ConfigSourceLoadUnitHook implements LifecycleHook { async preCreate(ctx: LoadUnitLifecycleContext, loadUnit: LoadUnit): Promise { const classList = ctx.loader.load(); for (const clazz of classList) { const injectObjects = PrototypeUtil.getInjectObjects(clazz); const moduleConfigObject = injectObjects.find(t => t.objName === 'moduleConfig'); const configSourceQualifier = QualifierUtil.getProperQualifier(clazz, 'moduleConfig', ConfigSourceQualifierAttribute); if (moduleConfigObject && !configSourceQualifier) { ConfigSourceQualifier(loadUnit.name)(clazz.prototype, moduleConfigObject.refName); } } } } ================================================ FILE: standalone/standalone/src/EggModuleLoader.ts ================================================ import { EggLoadUnitType, GlobalGraph, Loader, LoadUnit, LoadUnitFactory, ModuleDescriptorDumper, } from '@eggjs/tegg-metadata'; import { LoaderFactory } from '@eggjs/tegg-loader'; import { ModuleReference } from '@eggjs/tegg-common-util'; import { Logger } from '@eggjs/tegg'; export interface EggModuleLoaderOptions { logger: Logger; baseDir: string; dump?: boolean; } export class EggModuleLoader { private moduleReferences: readonly ModuleReference[]; private globalGraph: GlobalGraph; constructor(moduleReferences: readonly ModuleReference[], options: EggModuleLoaderOptions) { this.moduleReferences = moduleReferences; GlobalGraph.instance = this.globalGraph = EggModuleLoader.generateAppGraph(this.moduleReferences, options); } private static generateAppGraph(moduleReferences: readonly ModuleReference[], options: EggModuleLoaderOptions) { const moduleDescriptors = LoaderFactory.loadApp(moduleReferences); if (options.dump !== false) { for (const moduleDescriptor of moduleDescriptors) { ModuleDescriptorDumper.dump(moduleDescriptor, { dumpDir: options.baseDir, }).catch(e => { e.message = 'dump module descriptor failed: ' + e.message; options.logger.warn(e); }); } } const globalGraph = GlobalGraph.create(moduleDescriptors); return globalGraph; } async load(): Promise { const loadUnits: LoadUnit[] = []; this.globalGraph.build(); this.globalGraph.sort(); const moduleConfigList = GlobalGraph.instance!.moduleConfigList; for (const moduleConfig of moduleConfigList) { const modulePath = moduleConfig.path; const loader = LoaderFactory.createLoader(modulePath, EggLoadUnitType.MODULE); const loadUnit = await LoadUnitFactory.createLoadUnit(modulePath, EggLoadUnitType.MODULE, loader); loadUnits.push(loadUnit); } return loadUnits; } static async preLoad(moduleReferences: readonly ModuleReference[], options: EggModuleLoaderOptions): Promise { const loadUnits: LoadUnit[] = []; const loaderCache = new Map(); const globalGraph = GlobalGraph.instance = EggModuleLoader.generateAppGraph(moduleReferences, options); globalGraph.sort(); const moduleConfigList = globalGraph.moduleConfigList; for (const moduleConfig of moduleConfigList) { const modulePath = moduleConfig.path; const loader = loaderCache.get(modulePath)!; const loadUnit = await LoadUnitFactory.createPreloadLoadUnit(modulePath, EggLoadUnitType.MODULE, loader); loadUnits.push(loadUnit); } for (const load of loadUnits) { await load.preLoad?.(); } } } ================================================ FILE: standalone/standalone/src/ModuleConfig.ts ================================================ import 'egg'; // for declare merging declare module 'egg' { export interface ModuleConfig { // ... } } ================================================ FILE: standalone/standalone/src/Runner.ts ================================================ import { ModuleConfigUtil, ModuleReference, ReadModuleReferenceOptions, RuntimeConfig } from '@eggjs/tegg-common-util'; import { EggPrototype, EggPrototypeLifecycleUtil, GlobalGraph, LoadUnit, LoadUnitFactory, LoadUnitLifecycleUtil, LoadUnitMultiInstanceProtoHook, } from '@eggjs/tegg-metadata'; import { ContextHandler, EggContainerFactory, EggContext, EggObjectLifecycleUtil, LoadUnitInstance, LoadUnitInstanceFactory, ModuleLoadUnitInstance, } from '@eggjs/tegg-runtime'; import { EggProtoImplClass, PrototypeUtil, ModuleConfigHolder, ModuleConfigs, ConfigSourceQualifierAttribute, Logger, } from '@eggjs/tegg'; import { StandaloneUtil, MainRunner } from '@eggjs/tegg/standalone'; import { CrosscutAdviceFactory } from '@eggjs/tegg/aop'; import { crossCutGraphHook, EggObjectAopHook, EggPrototypeCrossCutHook, LoadUnitAopHook, pointCutGraphHook, } from '@eggjs/tegg-aop-runtime'; import { EggModuleLoader } from './EggModuleLoader'; import { InnerObject, StandaloneLoadUnit, StandaloneLoadUnitType } from './StandaloneLoadUnit'; import { StandaloneContext } from './StandaloneContext'; import { StandaloneContextHandler } from './StandaloneContextHandler'; import { ConfigSourceLoadUnitHook } from './ConfigSourceLoadUnitHook'; import { DalTableEggPrototypeHook } from '@eggjs/tegg-dal-plugin/lib/DalTableEggPrototypeHook'; import { DalModuleLoadUnitHook } from '@eggjs/tegg-dal-plugin/lib/DalModuleLoadUnitHook'; import { MysqlDataSourceManager } from '@eggjs/tegg-dal-plugin'; import { SqlMapManager } from '@eggjs/tegg-dal-plugin/lib/SqlMapManager'; import { TableModelManager } from '@eggjs/tegg-dal-plugin/lib/TableModelManager'; import { TransactionPrototypeHook } from '@eggjs/tegg-dal-plugin/lib/TransactionPrototypeHook'; export interface ModuleDependency extends ReadModuleReferenceOptions { baseDir: string; } export interface RunnerOptions { /** * @deprecated * use inner object handlers instead */ innerObjects?: Record; env?: string; name?: string; innerObjectHandlers?: Record; dependencies?: (string | ModuleDependency)[]; dump?: boolean; } export class Runner { readonly cwd: string; readonly moduleReferences: readonly ModuleReference[]; readonly moduleConfigs: Record; readonly env?: string; readonly name?: string; private loadUnitLoader: EggModuleLoader; private runnerProto: EggPrototype; private configSourceEggPrototypeHook: ConfigSourceLoadUnitHook; private loadUnitMultiInstanceProtoHook: LoadUnitMultiInstanceProtoHook; private dalTableEggPrototypeHook: DalTableEggPrototypeHook; private dalModuleLoadUnitHook: DalModuleLoadUnitHook; private transactionPrototypeHook: TransactionPrototypeHook; private readonly crosscutAdviceFactory: CrosscutAdviceFactory; private readonly loadUnitAopHook: LoadUnitAopHook; private readonly eggPrototypeCrossCutHook: EggPrototypeCrossCutHook; private readonly eggObjectAopHook: EggObjectAopHook; loadUnits: LoadUnit[] = []; loadUnitInstances: LoadUnitInstance[] = []; innerObjects: Record; constructor(cwd: string, options?: RunnerOptions) { this.cwd = cwd; this.env = options?.env; this.name = options?.name; this.moduleReferences = Runner.getModuleReferences(this.cwd, options?.dependencies); this.moduleConfigs = {}; this.innerObjects = { moduleConfigs: [{ obj: new ModuleConfigs(this.moduleConfigs), }], moduleConfig: [], mysqlDataSourceManager: [{ obj: MysqlDataSourceManager.instance, }], }; const runtimeConfig: Partial = { baseDir: this.cwd, name: this.name, env: this.env, }; // Inject runtimeConfig this.innerObjects.runtimeConfig = [{ obj: runtimeConfig, }]; // load module.yml and module.env.yml by default if (!ModuleConfigUtil.configNames) { ModuleConfigUtil.configNames = [ 'module.default', `module.${this.env}` ]; } for (const reference of this.moduleReferences) { const absoluteRef = { path: ModuleConfigUtil.resolveModuleDir(reference.path, this.cwd), name: reference.name, }; const moduleName = ModuleConfigUtil.readModuleNameSync(absoluteRef.path); this.moduleConfigs[moduleName] = { name: moduleName, reference: absoluteRef, config: ModuleConfigUtil.loadModuleConfigSync(absoluteRef.path), }; } for (const moduleConfig of Object.values(this.moduleConfigs)) { this.innerObjects.moduleConfig.push({ obj: moduleConfig.config, qualifiers: [{ attribute: ConfigSourceQualifierAttribute, value: moduleConfig.name, }], }); } if (options?.innerObjects) { for (const [ name, obj ] of Object.entries(options.innerObjects)) { this.innerObjects[name] = [{ obj, }]; } } else if (options?.innerObjectHandlers) { Object.assign(this.innerObjects, options.innerObjectHandlers); } this.loadUnitLoader = new EggModuleLoader(this.moduleReferences, { logger: ((this.innerObjects.logger && this.innerObjects.logger[0])?.obj as Logger) || console, baseDir: this.cwd, dump: options?.dump, }); GlobalGraph.instance!.registerBuildHook(crossCutGraphHook); GlobalGraph.instance!.registerBuildHook(pointCutGraphHook); const configSourceEggPrototypeHook = new ConfigSourceLoadUnitHook(); LoadUnitLifecycleUtil.registerLifecycle(configSourceEggPrototypeHook); // TODO refactor with egg module // aop runtime this.crosscutAdviceFactory = new CrosscutAdviceFactory(); this.loadUnitAopHook = new LoadUnitAopHook(this.crosscutAdviceFactory); this.eggPrototypeCrossCutHook = new EggPrototypeCrossCutHook(this.crosscutAdviceFactory); this.eggObjectAopHook = new EggObjectAopHook(); EggPrototypeLifecycleUtil.registerLifecycle(this.eggPrototypeCrossCutHook); LoadUnitLifecycleUtil.registerLifecycle(this.loadUnitAopHook); EggObjectLifecycleUtil.registerLifecycle(this.eggObjectAopHook); this.loadUnitMultiInstanceProtoHook = new LoadUnitMultiInstanceProtoHook(); LoadUnitLifecycleUtil.registerLifecycle(this.loadUnitMultiInstanceProtoHook); const loggerInnerObject = this.innerObjects.logger && this.innerObjects.logger[0]; const logger = (loggerInnerObject?.obj || console) as Logger; this.dalModuleLoadUnitHook = new DalModuleLoadUnitHook(this.env ?? '', this.moduleConfigs, logger); this.dalTableEggPrototypeHook = new DalTableEggPrototypeHook(logger); this.transactionPrototypeHook = new TransactionPrototypeHook(this.moduleConfigs, logger); EggPrototypeLifecycleUtil.registerLifecycle(this.dalTableEggPrototypeHook); EggPrototypeLifecycleUtil.registerLifecycle(this.transactionPrototypeHook); LoadUnitLifecycleUtil.registerLifecycle(this.dalModuleLoadUnitHook); } async load() { StandaloneContextHandler.register(); LoadUnitFactory.registerLoadUnitCreator(StandaloneLoadUnitType, () => { return new StandaloneLoadUnit(this.innerObjects); }); LoadUnitInstanceFactory.registerLoadUnitInstanceClass(StandaloneLoadUnitType, ModuleLoadUnitInstance.createModuleLoadUnitInstance); const standaloneLoadUnit = await LoadUnitFactory.createLoadUnit('MockStandaloneLoadUnitPath', StandaloneLoadUnitType, { load(): EggProtoImplClass[] { return []; }, }); const loadUnits = await this.loadUnitLoader.load(); return [ standaloneLoadUnit, ...loadUnits ]; } static getModuleReferences(cwd: string, dependencies?: RunnerOptions['dependencies']) { const moduleDirs = (dependencies || []).concat(cwd); const allModuleReferences = moduleDirs.reduce((list, baseDir) => { const module = typeof baseDir === 'string' ? { baseDir } : baseDir; return list.concat(...ModuleConfigUtil.readModuleReference(module.baseDir, module)); }, [] as readonly ModuleReference[]); // 去重模块引用,避免重复添加 return ModuleConfigUtil.deduplicateModules(allModuleReferences); } static async preLoad(cwd: string, dependencies?: RunnerOptions['dependencies']) { const moduleReferences = Runner.getModuleReferences(cwd, dependencies); await EggModuleLoader.preLoad(moduleReferences, { baseDir: cwd, logger: console, dump: false, }); } async init() { this.loadUnits = await this.load(); const instances: LoadUnitInstance[] = []; for (const loadUnit of this.loadUnits) { const instance = await LoadUnitInstanceFactory.createLoadUnitInstance(loadUnit); instances.push(instance); } this.loadUnitInstances = instances; const runnerClass = StandaloneUtil.getMainRunner(); if (!runnerClass) { throw new Error('not found runner class. Do you add @Runner decorator?'); } const proto = PrototypeUtil.getClazzProto(runnerClass); if (!proto) { throw new Error(`can not get proto for clazz ${runnerClass.name}`); } this.runnerProto = proto as EggPrototype; } async run(aCtx?: EggContext) { const lifecycle = {}; const ctx = aCtx || new StandaloneContext(); return await ContextHandler.run(ctx, async () => { if (ctx.init) { await ctx.init(lifecycle); } const eggObject = await EggContainerFactory.getOrCreateEggObject(this.runnerProto); const runner = eggObject.obj as MainRunner; try { return await runner.main(); } finally { if (ctx.destroy) { ctx.destroy(lifecycle).catch(e => { e.message = `[tegg/standalone] destroy tegg context failed: ${e.message}`; console.warn(e); }); } } }); } async destroy() { if (this.loadUnitInstances) { for (const instance of this.loadUnitInstances) { await LoadUnitInstanceFactory.destroyLoadUnitInstance(instance); } } if (this.loadUnits) { for (const loadUnit of this.loadUnits) { await LoadUnitFactory.destroyLoadUnit(loadUnit); } } if (this.configSourceEggPrototypeHook) { LoadUnitLifecycleUtil.deleteLifecycle(this.configSourceEggPrototypeHook); } if (this.eggPrototypeCrossCutHook) { EggPrototypeLifecycleUtil.deleteLifecycle(this.eggPrototypeCrossCutHook); } if (this.loadUnitAopHook) { LoadUnitLifecycleUtil.deleteLifecycle(this.loadUnitAopHook); } if (this.eggObjectAopHook) { EggObjectLifecycleUtil.deleteLifecycle(this.eggObjectAopHook); } if (this.loadUnitMultiInstanceProtoHook) { LoadUnitLifecycleUtil.deleteLifecycle(this.loadUnitMultiInstanceProtoHook); } if (this.dalTableEggPrototypeHook) { EggPrototypeLifecycleUtil.deleteLifecycle(this.dalTableEggPrototypeHook); } if (this.dalModuleLoadUnitHook) { LoadUnitLifecycleUtil.deleteLifecycle(this.dalModuleLoadUnitHook); } if (this.transactionPrototypeHook) { EggPrototypeLifecycleUtil.deleteLifecycle(this.transactionPrototypeHook); } MysqlDataSourceManager.instance.clear(); SqlMapManager.instance.clear(); TableModelManager.instance.clear(); // clear configNames ModuleConfigUtil.setConfigNames(undefined); } } ================================================ FILE: standalone/standalone/src/StandaloneContext.ts ================================================ import { AbstractEggContext } from '@eggjs/tegg-runtime'; import { IdenticalUtil } from '@eggjs/tegg-lifecycle'; export class StandaloneContext extends AbstractEggContext { id: string; constructor() { super(); this.id = IdenticalUtil.createContextId(); } } ================================================ FILE: standalone/standalone/src/StandaloneContextHandler.ts ================================================ import { AsyncLocalStorage } from 'async_hooks'; import { ContextHandler, EggContext } from '@eggjs/tegg-runtime'; export class StandaloneContextHandler { static storage = new AsyncLocalStorage(); static register() { ContextHandler.getContextCallback = () => { return StandaloneContextHandler.storage.getStore(); }; ContextHandler.runInContextCallback = (context, fn) => { return StandaloneContextHandler.storage.run(context, fn); }; } } ================================================ FILE: standalone/standalone/src/StandaloneContextImpl.ts ================================================ import { AbstractEggContext } from '@eggjs/tegg-runtime'; import { IdenticalUtil } from '@eggjs/tegg'; export class StandaloneContextImpl extends AbstractEggContext { readonly id: string; constructor() { super(); this.id = IdenticalUtil.createContextId(); } } ================================================ FILE: standalone/standalone/src/StandaloneInnerObject.ts ================================================ import { StandaloneInnerObjectProto } from './StandaloneInnerObjectProto'; import { EggObject, EggObjectFactory } from '@eggjs/tegg-runtime'; import { IdenticalUtil, EggObjectName } from '@eggjs/tegg'; import { EggPrototype } from '@eggjs/tegg-metadata'; const OBJ = Symbol('EggCompatibleObject#obj'); export class StandaloneInnerObject implements EggObject { readonly isReady: boolean = true; private [OBJ]: object; readonly proto: StandaloneInnerObjectProto; readonly name: EggObjectName; readonly id: string; constructor(name: EggObjectName, proto: StandaloneInnerObjectProto) { this.proto = proto; this.name = name; this.id = IdenticalUtil.createObjectId(this.proto.id); } get obj() { if (!this[OBJ]) { this[OBJ] = this.proto.constructEggObject(); } return this[OBJ]; } injectProperty() { return; } static async createObject(name: EggObjectName, proto: EggPrototype): Promise { return new StandaloneInnerObject(name, proto as StandaloneInnerObjectProto); } } EggObjectFactory.registerEggObjectCreateMethod(StandaloneInnerObjectProto, StandaloneInnerObject.createObject); ================================================ FILE: standalone/standalone/src/StandaloneInnerObjectProto.ts ================================================ import { AccessLevel, EggProtoImplClass, EggPrototypeName, MetaDataKey, MetadataUtil, ObjectInitTypeLike, QualifierInfo, QualifierUtil, Id, IdenticalUtil, QualifierValue, } from '@eggjs/tegg'; import { EggPrototype, InjectObjectProto, EggPrototypeLifecycleContext, } from '@eggjs/tegg-metadata'; export class StandaloneInnerObjectProto implements EggPrototype { private readonly clazz: EggProtoImplClass; private readonly qualifiers: QualifierInfo[]; readonly id: string; readonly name: EggPrototypeName; readonly initType: ObjectInitTypeLike; readonly accessLevel: AccessLevel; readonly injectObjects: InjectObjectProto[]; readonly loadUnitId: Id; constructor( id: string, name: EggPrototypeName, clazz: EggProtoImplClass, initType: ObjectInitTypeLike, loadUnitId: Id, qualifiers: QualifierInfo[], ) { this.id = id; this.clazz = clazz; this.name = name; this.initType = initType; this.accessLevel = AccessLevel.PUBLIC; this.injectObjects = []; this.loadUnitId = loadUnitId; this.qualifiers = qualifiers; } verifyQualifiers(qualifiers: QualifierInfo[]): boolean { for (const qualifier of qualifiers) { if (!this.verifyQualifier(qualifier)) { return false; } } return true; } verifyQualifier(qualifier: QualifierInfo): boolean { const selfQualifiers = this.qualifiers.find(t => t.attribute === qualifier.attribute); return selfQualifiers?.value === qualifier.value; } constructEggObject(): object { return Reflect.apply(this.clazz, null, []); } getMetaData(metadataKey: MetaDataKey): T | undefined { return MetadataUtil.getMetaData(metadataKey, this.clazz); } getQualifier(attribute: string): QualifierValue | undefined { return this.qualifiers.find(t => t.attribute === attribute)?.value; } static create(ctx: EggPrototypeLifecycleContext): EggPrototype { const { clazz, loadUnit } = ctx; const name = ctx.prototypeInfo.name; const id = IdenticalUtil.createProtoId(loadUnit.id, name); const proto = new StandaloneInnerObjectProto( id, name, clazz, ctx.prototypeInfo.initType, loadUnit.id, QualifierUtil.getProtoQualifiers(clazz), ); return proto; } } ================================================ FILE: standalone/standalone/src/StandaloneLoadUnit.ts ================================================ import { EggPrototype, EggPrototypeFactory, LoadUnit } from '@eggjs/tegg-metadata'; import { EggPrototypeName, ObjectInitType, QualifierInfo } from '@eggjs/tegg'; import { MapUtil } from '@eggjs/tegg-common-util'; import { IdenticalUtil } from '@eggjs/tegg-lifecycle'; import { StandaloneInnerObjectProto } from './StandaloneInnerObjectProto'; export const StandaloneLoadUnitType = 'StandaloneLoadUnitType'; export interface InnerObject { obj: object, qualifiers?: QualifierInfo[], } export class StandaloneLoadUnit implements LoadUnit { readonly id: string = 'StandaloneLoadUnit'; readonly name: string = 'StandaloneLoadUnit'; readonly unitPath: string = 'MockStandaloneLoadUnitPath'; readonly type = StandaloneLoadUnitType; private innerObject: Record; private protoMap: Map = new Map(); constructor(innerObject: Record) { this.innerObject = innerObject; } async init() { for (const [ name, objs ] of Object.entries(this.innerObject)) { for (const { obj, qualifiers } of objs) { const proto = new StandaloneInnerObjectProto( IdenticalUtil.createProtoId(this.id, name), name, (() => obj) as any, ObjectInitType.SINGLETON, this.id, qualifiers || [], ); EggPrototypeFactory.instance.registerPrototype(proto, this); } } } containPrototype(proto: EggPrototype): boolean { return !!(this.protoMap.get(proto.name)?.find(t => t === proto)); } getEggPrototype(name: string, qualifiers: QualifierInfo[]): EggPrototype[] { const protos = this.protoMap.get(name); return protos?.filter(proto => proto.verifyQualifiers(qualifiers)) || []; } registerEggPrototype(proto: EggPrototype) { const protoList = MapUtil.getOrStore(this.protoMap, proto.name, []); protoList.push(proto); } deletePrototype(proto: EggPrototype) { const protos = this.protoMap.get(proto.name); if (protos) { const index = protos.indexOf(proto); if (index !== -1) { protos.splice(index, 1); } } } async destroy() { for (const namedProtoMap of this.protoMap.values()) { for (const proto of namedProtoMap.values()) { EggPrototypeFactory.instance.deletePrototype(proto, this); } } this.protoMap.clear(); } iterateEggPrototype(): IterableIterator { const protos: EggPrototype[] = Array.from(this.protoMap.values()) .reduce((p, c) => { p = p.concat(c); return p; }, []); return protos.values(); } } ================================================ FILE: standalone/standalone/src/main.ts ================================================ import { Runner, RunnerOptions } from './Runner'; export async function preLoad(cwd: string, dependencies?: RunnerOptions['dependencies']) { try { await Runner.preLoad(cwd, dependencies); } catch (e) { e.message = `[tegg/standalone] bootstrap standalone preLoad failed: ${e.message}`; throw e; } } export async function main(cwd: string, options?: RunnerOptions): Promise { const runner = new Runner(cwd, options); try { await runner.init(); } catch (e) { e.message = `[tegg/standalone] bootstrap tegg failed: ${e.message}`; throw e; } try { return await runner.run(); } finally { runner.destroy().catch(e => { e.message = `[tegg/standalone] destroy tegg failed: ${e.message}`; console.warn(e); }); } } ================================================ FILE: standalone/standalone/test/fixtures/ajv-module/foo.ts ================================================ import { ContextProto, Inject } from '@eggjs/tegg'; import { Runner, MainRunner } from '@eggjs/tegg/standalone'; import { Ajv, Static, Type, TransformEnum } from '@eggjs/tegg/ajv'; const RequestBodySchema = Type.Object({ fullname: Type.String({ transform: [ TransformEnum.trim ], maxLength: 100, }), skipDependencies: Type.Boolean(), registryName: Type.Optional(Type.String()), }); type RequestBody = Static; @ContextProto() @Runner() export class Foo implements MainRunner { @Inject() private readonly ajv: Ajv; async main(): Promise { const body: RequestBody = {} as any; this.ajv.validate(RequestBodySchema, body); return JSON.stringify({ body, }); } } ================================================ FILE: standalone/standalone/test/fixtures/ajv-module/package.json ================================================ { "name": "simple", "eggModule": { "name": "simple" } } ================================================ FILE: standalone/standalone/test/fixtures/ajv-module-pass/foo.ts ================================================ import { ContextProto, Inject } from '@eggjs/tegg'; import { Runner, MainRunner } from '@eggjs/tegg/standalone'; import { Ajv, Static, Type, TransformEnum } from '@eggjs/tegg/ajv'; const RequestBodySchema = Type.Object({ fullname: Type.String({ transform: [ TransformEnum.trim ], maxLength: 100, }), skipDependencies: Type.Boolean(), registryName: Type.Optional(Type.String()), }); type RequestBody = Static; @ContextProto() @Runner() export class Foo implements MainRunner { @Inject() private readonly ajv: Ajv; async main(): Promise { const body: RequestBody = { fullname: 'mock fullname', skipDependencies: true, registryName: 'ok', }; this.ajv.validate(RequestBodySchema, body); return JSON.stringify({ body, }); } } ================================================ FILE: standalone/standalone/test/fixtures/ajv-module-pass/package.json ================================================ { "name": "simple", "eggModule": { "name": "simple" } } ================================================ FILE: standalone/standalone/test/fixtures/aop-module/Hello.ts ================================================ import { ContextProto, Inject, SingletonProto, } from '@eggjs/tegg'; import { Advice, AdviceContext, Crosscut, IAdvice, Pointcut, PointcutType, } from '@eggjs/tegg/aop'; import assert from 'assert'; export interface CallTraceMsg { className: string; methodName: string; id: number; name: string; result?: string; adviceParams?: any; } @SingletonProto() export class CallTrace { msgs: Array = []; addMsg(msg: CallTraceMsg) { this.msgs.push(msg); } } export const pointcutAdviceParams = { point: Math.random() .toString(), cut: Math.random() .toString(), }; @Advice() export class PointcutAdvice implements IAdvice { @Inject() callTrace: CallTrace; async beforeCall(ctx: AdviceContext): Promise { assert.ok(ctx.adviceParams); assert.deepStrictEqual(ctx.adviceParams, pointcutAdviceParams); this.callTrace.addMsg({ className: PointcutAdvice.name, methodName: 'beforeCall', id: ctx.that.id, name: ctx.args[0], adviceParams: ctx.adviceParams, }); } async afterReturn(ctx: AdviceContext, result: any): Promise { assert.ok(ctx.adviceParams); assert.deepStrictEqual(ctx.adviceParams, pointcutAdviceParams); this.callTrace.addMsg({ className: PointcutAdvice.name, methodName: 'afterReturn', id: ctx.that.id, name: ctx.args[0], result, adviceParams: ctx.adviceParams, }); } async afterThrow(ctx: AdviceContext, error: Error): Promise { assert.ok(ctx.adviceParams); assert.deepStrictEqual(ctx.adviceParams, pointcutAdviceParams); this.callTrace.addMsg({ className: PointcutAdvice.name, methodName: 'afterThrow', id: ctx.that.id, name: ctx.args[0], result: error.message, adviceParams: ctx.adviceParams, }); } async afterFinally(ctx: AdviceContext): Promise { assert.ok(ctx.adviceParams); assert.deepStrictEqual(ctx.adviceParams, pointcutAdviceParams); this.callTrace.addMsg({ className: PointcutAdvice.name, methodName: 'afterFinally', id: ctx.that.id, name: ctx.args[0], adviceParams: ctx.adviceParams, }); } async around(ctx: AdviceContext, next: () => Promise): Promise { assert.ok(ctx.adviceParams); assert.deepStrictEqual(ctx.adviceParams, pointcutAdviceParams); ctx.args[0] = `withPointAroundParam(${ctx.args[0]})`; const result = await next(); return `withPointAroundResult(${result}${JSON.stringify(pointcutAdviceParams)})`; } } @ContextProto() export class Hello { id = 233; @Pointcut(PointcutAdvice, { adviceParams: pointcutAdviceParams }) async hello(name: string) { return `hello ${name}`; } @Pointcut(PointcutAdvice, { adviceParams: pointcutAdviceParams }) async helloWithException(name: string) { throw new Error(`ops, exception for ${name}`); } } export const crosscutAdviceParams = { cross: Math.random() .toString(), cut: Math.random() .toString(), }; @Crosscut({ type: PointcutType.CLASS, clazz: Hello, methodName: 'hello', }, { adviceParams: crosscutAdviceParams }) @Advice() export class CrosscutAdvice implements IAdvice { @Inject() callTrace: CallTrace; async beforeCall(ctx: AdviceContext): Promise { assert.ok(ctx.adviceParams); assert.deepStrictEqual(ctx.adviceParams, crosscutAdviceParams); this.callTrace.addMsg({ className: CrosscutAdvice.name, methodName: 'beforeCall', id: ctx.that.id, name: ctx.args[0], adviceParams: ctx.adviceParams, }); } async afterReturn(ctx: AdviceContext, result: any): Promise { assert.ok(ctx.adviceParams); assert.deepStrictEqual(ctx.adviceParams, crosscutAdviceParams); this.callTrace.addMsg({ className: CrosscutAdvice.name, methodName: 'afterReturn', id: ctx.that.id, name: ctx.args[0], result, adviceParams: ctx.adviceParams, }); } async afterFinally(ctx: AdviceContext): Promise { assert.ok(ctx.adviceParams); assert.deepStrictEqual(ctx.adviceParams, crosscutAdviceParams); this.callTrace.addMsg({ className: CrosscutAdvice.name, methodName: 'afterFinally', id: ctx.that.id, name: ctx.args[0], adviceParams: ctx.adviceParams, }); } async around(ctx: AdviceContext, next: () => Promise): Promise { assert.ok(ctx.adviceParams); assert.deepStrictEqual(ctx.adviceParams, crosscutAdviceParams); ctx.args[0] = `withCrosscutAroundParam(${ctx.args[0]})`; const result = await next(); return `withCrossAroundResult(${result}${JSON.stringify(ctx.adviceParams)})`; } } ================================================ FILE: standalone/standalone/test/fixtures/aop-module/main.ts ================================================ import { ContextProto, Inject } from '@eggjs/tegg'; import { Runner, MainRunner } from '@eggjs/tegg/standalone'; import { Hello } from './Hello'; @Runner() @ContextProto() export class Foo implements MainRunner { @Inject() hello: Hello; async main(): Promise { return await this.hello.hello('aop'); } } ================================================ FILE: standalone/standalone/test/fixtures/aop-module/package.json ================================================ { "name": "aop-module", "eggModule": { "name": "aopModule" } } ================================================ FILE: standalone/standalone/test/fixtures/custom-context/foo.ts ================================================ import { SingletonProto } from '@eggjs/tegg'; import { Runner, MainRunner } from '@eggjs/tegg/standalone'; import { ContextHandler } from '@eggjs/tegg-runtime'; export interface Hello { hello(): string; } @Runner() @SingletonProto() export class Foo implements MainRunner { async main(): Promise { const ctx = ContextHandler.getContext(); return ctx?.get('foo'); } } ================================================ FILE: standalone/standalone/test/fixtures/custom-context/package.json ================================================ { "name": "innerobject", "eggModule": { "name": "innerobject" } } ================================================ FILE: standalone/standalone/test/fixtures/dal-module/module.yml ================================================ dataSource: foo: connectionLimit: 100 database: 'test_dal_standalone' host: '127.0.0.1' user: root port: 3306 timezone: '+08:00' forkDb: true ================================================ FILE: standalone/standalone/test/fixtures/dal-module/package.json ================================================ { "name": "dal", "eggModule": { "name": "dal" }, "dependencies": { "@eggjs/tegg-dal-plugin": "*" } } ================================================ FILE: standalone/standalone/test/fixtures/dal-module/src/Foo.ts ================================================ import { Column, ColumnType, Geometry, GeometryCollection, Index, IndexType, IndexStoreType, Line, MultiLine, MultiPoint, MultiPolygon, Point, Polygon, Table, } from '@eggjs/dal-decorator'; @Table({ name: 'egg_foo', comment: 'foo table', characterSet: 'utf8mb4', collate: 'utf8mb4_unicode_ci', }) @Index({ keys: [ 'name', 'col1' ], type: IndexType.UNIQUE, storeType: IndexStoreType.BTREE, comment: 'index comment\n', }) @Index({ keys: [ 'col1' ], type: IndexType.FULLTEXT, comment: 'index comment\n', }) export class Foo { @Column({ type: ColumnType.INT, }, { primaryKey: true, autoIncrement: true, comment: 'the primary key', }) id: number; @Column({ type: ColumnType.VARCHAR, length: 100, }, { uniqueKey: true, }) name: string; @Column({ type: ColumnType.VARCHAR, length: 100, }, { name: 'col1', }) col1: string; @Column({ type: ColumnType.BIT, length: 10, }) bitColumn: Buffer; @Column({ type: ColumnType.BOOL, }) boolColumn: 0 | 1; @Column({ type: ColumnType.TINYINT, length: 5, unsigned: true, zeroFill: true, }) tinyIntColumn: number; @Column({ type: ColumnType.SMALLINT, length: 5, unsigned: true, zeroFill: true, }) smallIntColumn: number; @Column({ type: ColumnType.MEDIUMINT, length: 5, unsigned: true, zeroFill: true, }) mediumIntColumn: number; @Column({ type: ColumnType.INT, length: 5, unsigned: true, zeroFill: true, }) intColumn: number; @Column({ type: ColumnType.BIGINT, length: 5, unsigned: true, zeroFill: true, }) bigIntColumn: string; @Column({ type: ColumnType.DECIMAL, length: 10, fractionalLength: 5, unsigned: true, zeroFill: true, }) decimalColumn: string; @Column({ type: ColumnType.FLOAT, length: 10, fractionalLength: 5, unsigned: true, zeroFill: true, }) floatColumn: number; @Column({ type: ColumnType.DOUBLE, length: 10, fractionalLength: 5, unsigned: true, zeroFill: true, }) doubleColumn: number; @Column({ type: ColumnType.DATE, }) dateColumn: Date; @Column({ type: ColumnType.DATETIME, precision: 3, }) dateTimeColumn: Date; @Column({ type: ColumnType.TIMESTAMP, precision: 3, }) timestampColumn: Date; @Column({ type: ColumnType.TIME, precision: 3, }) timeColumn: string; @Column({ type: ColumnType.YEAR, }) yearColumn: number; @Column({ type: ColumnType.VARCHAR, length: 100, characterSet: 'utf8mb4', collate: 'utf8mb4_unicode_ci', }) varCharColumn: string; @Column({ type: ColumnType.BINARY, }) binaryColumn: Buffer; @Column({ type: ColumnType.VARBINARY, length: 100, }) varBinaryColumn: Buffer; @Column({ type: ColumnType.TINYBLOB, }) tinyBlobColumn: Buffer; @Column({ type: ColumnType.TINYTEXT, characterSet: 'utf8mb4', collate: 'utf8mb4_unicode_ci', }) tinyTextColumn: string; @Column({ type: ColumnType.BLOB, length: 100, }) blobColumn: Buffer; @Column({ type: ColumnType.TEXT, length: 100, characterSet: 'utf8mb4', collate: 'utf8mb4_unicode_ci', }) textColumn: string; @Column({ type: ColumnType.MEDIUMBLOB, }) mediumBlobColumn: Buffer; @Column({ type: ColumnType.LONGBLOB, }) longBlobColumn: Buffer; @Column({ type: ColumnType.MEDIUMTEXT, characterSet: 'utf8mb4', collate: 'utf8mb4_unicode_ci', }) mediumTextColumn: string; @Column({ type: ColumnType.LONGTEXT, characterSet: 'utf8mb4', collate: 'utf8mb4_unicode_ci', }) longTextColumn: string; @Column({ type: ColumnType.ENUM, enums: [ 'A', 'B' ], characterSet: 'utf8mb4', collate: 'utf8mb4_unicode_ci', }) enumColumn: string; @Column({ type: ColumnType.SET, enums: [ 'A', 'B' ], characterSet: 'utf8mb4', collate: 'utf8mb4_unicode_ci', }) setColumn: string; @Column({ type: ColumnType.GEOMETRY, }) geometryColumn: Geometry; @Column({ type: ColumnType.POINT, }) pointColumn: Point; @Column({ type: ColumnType.LINESTRING, }) lineStringColumn: Line; @Column({ type: ColumnType.POLYGON, }) polygonColumn: Polygon; @Column({ type: ColumnType.MULTIPOINT, }) multipointColumn: MultiPoint; @Column({ type: ColumnType.MULTILINESTRING, }) multiLineStringColumn: MultiLine; @Column({ type: ColumnType.MULTIPOLYGON, }) multiPolygonColumn: MultiPolygon; @Column({ type: ColumnType.GEOMETRYCOLLECTION, }) geometryCollectionColumn: GeometryCollection; @Column({ type: ColumnType.JSON, }) jsonColumn: object; } ================================================ FILE: standalone/standalone/test/fixtures/dal-module/src/dal/dao/FooDAO.ts ================================================ import { SingletonProto, AccessLevel } from '@eggjs/tegg'; import { BaseFooDAO } from './base/BaseFooDAO'; import { Foo } from '../../Foo'; /** * FooDAO 类 * @class FooDAO * @classdesc 在此扩展关于 Foo 数据的一切操作 * @augments BaseFooDAO */ @SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) export default class FooDAO extends BaseFooDAO { async findByName(name: string): Promise { return this.dataSource.execute('findByName', { name, }); } } ================================================ FILE: standalone/standalone/test/fixtures/dal-module/src/dal/dao/base/BaseFooDAO.ts ================================================ import fs from 'node:fs'; import path from 'node:path'; import type { InsertResult, UpdateResult, DeleteResult } from '@eggjs/dal-decorator'; import { Inject } from '@eggjs/tegg'; import { Dao } from '@eggjs/tegg/dal'; import { DataSource, DataSourceInjectName, DataSourceQualifier, ColumnTsType } from '@eggjs/dal-decorator'; import { Foo } from '../../../Foo'; import FooExtension from '../../extension/FooExtension'; import Structure from '../../structure/Foo.json'; const SQL = Symbol('Dao#sql'); type Optional = Omit < T, K > & Partial ; /** * 自动生成的 FooDAO 基类 * @class BaseFooDAO * @classdesc 该文件由 @eggjs/tegg 自动生成,请**不要**修改它! */ /* istanbul ignore next */ @Dao() export class BaseFooDAO { static clazzModel = Foo; static clazzExtension = FooExtension; static tableStature = Structure; static get tableSql() { if (!this[SQL]) { this[SQL] = fs.readFileSync(path.join(__dirname, '../../structure/Foo.sql'), 'utf8'); } return this[SQL]; } @Inject({ name: DataSourceInjectName, }) @DataSourceQualifier('dal.foo.Foo') protected readonly dataSource: DataSource; public async insert(raw: Optional): Promise { const data: Record = {}; let tmp; tmp = raw.id; if (tmp !== undefined) { data.$id = tmp; } tmp = raw.name; if (tmp !== undefined) { data.$name = tmp; } tmp = raw.col1; if (tmp !== undefined) { data.$col1 = tmp; } tmp = raw.bitColumn; if (tmp !== undefined) { data.$bitColumn = tmp; } tmp = raw.boolColumn; if (tmp !== undefined) { data.$boolColumn = tmp; } tmp = raw.tinyIntColumn; if (tmp !== undefined) { data.$tinyIntColumn = tmp; } tmp = raw.smallIntColumn; if (tmp !== undefined) { data.$smallIntColumn = tmp; } tmp = raw.mediumIntColumn; if (tmp !== undefined) { data.$mediumIntColumn = tmp; } tmp = raw.intColumn; if (tmp !== undefined) { data.$intColumn = tmp; } tmp = raw.bigIntColumn; if (tmp !== undefined) { data.$bigIntColumn = tmp; } tmp = raw.decimalColumn; if (tmp !== undefined) { data.$decimalColumn = tmp; } tmp = raw.floatColumn; if (tmp !== undefined) { data.$floatColumn = tmp; } tmp = raw.doubleColumn; if (tmp !== undefined) { data.$doubleColumn = tmp; } tmp = raw.dateColumn; if (tmp !== undefined) { data.$dateColumn = tmp; } tmp = raw.dateTimeColumn; if (tmp !== undefined) { data.$dateTimeColumn = tmp; } tmp = raw.timestampColumn; if (tmp !== undefined) { data.$timestampColumn = tmp; } tmp = raw.timeColumn; if (tmp !== undefined) { data.$timeColumn = tmp; } tmp = raw.yearColumn; if (tmp !== undefined) { data.$yearColumn = tmp; } tmp = raw.varCharColumn; if (tmp !== undefined) { data.$varCharColumn = tmp; } tmp = raw.binaryColumn; if (tmp !== undefined) { data.$binaryColumn = tmp; } tmp = raw.varBinaryColumn; if (tmp !== undefined) { data.$varBinaryColumn = tmp; } tmp = raw.tinyBlobColumn; if (tmp !== undefined) { data.$tinyBlobColumn = tmp; } tmp = raw.tinyTextColumn; if (tmp !== undefined) { data.$tinyTextColumn = tmp; } tmp = raw.blobColumn; if (tmp !== undefined) { data.$blobColumn = tmp; } tmp = raw.textColumn; if (tmp !== undefined) { data.$textColumn = tmp; } tmp = raw.mediumBlobColumn; if (tmp !== undefined) { data.$mediumBlobColumn = tmp; } tmp = raw.longBlobColumn; if (tmp !== undefined) { data.$longBlobColumn = tmp; } tmp = raw.mediumTextColumn; if (tmp !== undefined) { data.$mediumTextColumn = tmp; } tmp = raw.longTextColumn; if (tmp !== undefined) { data.$longTextColumn = tmp; } tmp = raw.enumColumn; if (tmp !== undefined) { data.$enumColumn = tmp; } tmp = raw.setColumn; if (tmp !== undefined) { data.$setColumn = tmp; } tmp = raw.geometryColumn; if (tmp !== undefined) { data.$geometryColumn = tmp; } tmp = raw.pointColumn; if (tmp !== undefined) { data.$pointColumn = tmp; } tmp = raw.lineStringColumn; if (tmp !== undefined) { data.$lineStringColumn = tmp; } tmp = raw.polygonColumn; if (tmp !== undefined) { data.$polygonColumn = tmp; } tmp = raw.multipointColumn; if (tmp !== undefined) { data.$multipointColumn = tmp; } tmp = raw.multiLineStringColumn; if (tmp !== undefined) { data.$multiLineStringColumn = tmp; } tmp = raw.multiPolygonColumn; if (tmp !== undefined) { data.$multiPolygonColumn = tmp; } tmp = raw.geometryCollectionColumn; if (tmp !== undefined) { data.$geometryCollectionColumn = tmp; } tmp = raw.jsonColumn; if (tmp !== undefined) { data.$jsonColumn = tmp; } return this.dataSource.executeRawScalar('insert', data); } public async update(id: ColumnTsType['INT'], data: Partial): Promise { const newData: Record = { primary: { id, }, }; let tmp; tmp = data.id; if (tmp !== undefined) { newData.$id = tmp; } tmp = data.name; if (tmp !== undefined) { newData.$name = tmp; } tmp = data.col1; if (tmp !== undefined) { newData.$col1 = tmp; } tmp = data.bitColumn; if (tmp !== undefined) { newData.$bitColumn = tmp; } tmp = data.boolColumn; if (tmp !== undefined) { newData.$boolColumn = tmp; } tmp = data.tinyIntColumn; if (tmp !== undefined) { newData.$tinyIntColumn = tmp; } tmp = data.smallIntColumn; if (tmp !== undefined) { newData.$smallIntColumn = tmp; } tmp = data.mediumIntColumn; if (tmp !== undefined) { newData.$mediumIntColumn = tmp; } tmp = data.intColumn; if (tmp !== undefined) { newData.$intColumn = tmp; } tmp = data.bigIntColumn; if (tmp !== undefined) { newData.$bigIntColumn = tmp; } tmp = data.decimalColumn; if (tmp !== undefined) { newData.$decimalColumn = tmp; } tmp = data.floatColumn; if (tmp !== undefined) { newData.$floatColumn = tmp; } tmp = data.doubleColumn; if (tmp !== undefined) { newData.$doubleColumn = tmp; } tmp = data.dateColumn; if (tmp !== undefined) { newData.$dateColumn = tmp; } tmp = data.dateTimeColumn; if (tmp !== undefined) { newData.$dateTimeColumn = tmp; } tmp = data.timestampColumn; if (tmp !== undefined) { newData.$timestampColumn = tmp; } tmp = data.timeColumn; if (tmp !== undefined) { newData.$timeColumn = tmp; } tmp = data.yearColumn; if (tmp !== undefined) { newData.$yearColumn = tmp; } tmp = data.varCharColumn; if (tmp !== undefined) { newData.$varCharColumn = tmp; } tmp = data.binaryColumn; if (tmp !== undefined) { newData.$binaryColumn = tmp; } tmp = data.varBinaryColumn; if (tmp !== undefined) { newData.$varBinaryColumn = tmp; } tmp = data.tinyBlobColumn; if (tmp !== undefined) { newData.$tinyBlobColumn = tmp; } tmp = data.tinyTextColumn; if (tmp !== undefined) { newData.$tinyTextColumn = tmp; } tmp = data.blobColumn; if (tmp !== undefined) { newData.$blobColumn = tmp; } tmp = data.textColumn; if (tmp !== undefined) { newData.$textColumn = tmp; } tmp = data.mediumBlobColumn; if (tmp !== undefined) { newData.$mediumBlobColumn = tmp; } tmp = data.longBlobColumn; if (tmp !== undefined) { newData.$longBlobColumn = tmp; } tmp = data.mediumTextColumn; if (tmp !== undefined) { newData.$mediumTextColumn = tmp; } tmp = data.longTextColumn; if (tmp !== undefined) { newData.$longTextColumn = tmp; } tmp = data.enumColumn; if (tmp !== undefined) { newData.$enumColumn = tmp; } tmp = data.setColumn; if (tmp !== undefined) { newData.$setColumn = tmp; } tmp = data.geometryColumn; if (tmp !== undefined) { newData.$geometryColumn = tmp; } tmp = data.pointColumn; if (tmp !== undefined) { newData.$pointColumn = tmp; } tmp = data.lineStringColumn; if (tmp !== undefined) { newData.$lineStringColumn = tmp; } tmp = data.polygonColumn; if (tmp !== undefined) { newData.$polygonColumn = tmp; } tmp = data.multipointColumn; if (tmp !== undefined) { newData.$multipointColumn = tmp; } tmp = data.multiLineStringColumn; if (tmp !== undefined) { newData.$multiLineStringColumn = tmp; } tmp = data.multiPolygonColumn; if (tmp !== undefined) { newData.$multiPolygonColumn = tmp; } tmp = data.geometryCollectionColumn; if (tmp !== undefined) { newData.$geometryCollectionColumn = tmp; } tmp = data.jsonColumn; if (tmp !== undefined) { newData.$jsonColumn = tmp; } return this.dataSource.executeRawScalar('update', newData); } public async delete(id: ColumnTsType['INT']): Promise { return this.dataSource.executeRawScalar('delete', { id, }); } public async del(id: ColumnTsType['INT']): Promise { return this.dataSource.executeRawScalar('delete', { id, }); } public async findByCol1($col1: ColumnTsType['VARCHAR']): Promise { return this.dataSource.execute('findByCol1', { $col1, }); } public async findOneByCol1($col1: ColumnTsType['VARCHAR']): Promise { return this.dataSource.executeScalar('findOneByCol1', { $col1, }); } public async findByUkNameCol1($name: ColumnTsType['VARCHAR'], $col1: ColumnTsType['VARCHAR']): Promise { return this.dataSource.execute('findByUkNameCol1', { $name, $col1, }); } public async findOneByUkNameCol1($name: ColumnTsType['VARCHAR'], $col1: ColumnTsType['VARCHAR']): Promise { return this.dataSource.executeScalar('findOneByUkNameCol1', { $name, $col1, }); } public async findById($id: ColumnTsType['INT']): Promise { return this.dataSource.executeScalar('findById', { $id, }); } public async findByPrimary($id: ColumnTsType['INT']): Promise { return this.dataSource.executeScalar('findById', { $id, }); } } ================================================ FILE: standalone/standalone/test/fixtures/dal-module/src/dal/extension/FooExtension.ts ================================================ import { SqlMap } from '@eggjs/tegg/dal'; /** * Define Custom SQLs * * import { SqlMap, SqlType } from '@eggjs/tegg/dal'; * * export default { * findByName: { * type: SqlType.SELECT, * sql: 'SELECT {{ allColumns }} from foo where name = {{ name }}' * }, * } */ export default { findByName: { type: 'SELECT', sql: 'SELECT {{ allColumns }} FROM egg_foo WHERE name = {{ name }}', }, } as Record; ================================================ FILE: standalone/standalone/test/fixtures/dal-module/src/dal/structure/Foo.json ================================================ { "name": "egg_foo", "dataSourceName": "default", "columns": [ { "columnName": "id", "propertyName": "id", "type": { "type": "INT" }, "canNull": false, "comment": "the primary key", "autoIncrement": true, "primaryKey": true }, { "columnName": "name", "propertyName": "name", "type": { "type": "VARCHAR", "length": 100 }, "canNull": true, "uniqueKey": true }, { "columnName": "col1", "propertyName": "col1", "type": { "type": "VARCHAR", "length": 100 }, "canNull": true }, { "columnName": "bit_column", "propertyName": "bitColumn", "type": { "type": "BIT", "length": 10 }, "canNull": true }, { "columnName": "bool_column", "propertyName": "boolColumn", "type": { "type": "BOOL" }, "canNull": true }, { "columnName": "tiny_int_column", "propertyName": "tinyIntColumn", "type": { "type": "TINYINT", "length": 5, "unsigned": true, "zeroFill": true }, "canNull": true }, { "columnName": "small_int_column", "propertyName": "smallIntColumn", "type": { "type": "SMALLINT", "length": 5, "unsigned": true, "zeroFill": true }, "canNull": true }, { "columnName": "medium_int_column", "propertyName": "mediumIntColumn", "type": { "type": "MEDIUMINT", "length": 5, "unsigned": true, "zeroFill": true }, "canNull": true }, { "columnName": "int_column", "propertyName": "intColumn", "type": { "type": "INT", "length": 5, "unsigned": true, "zeroFill": true }, "canNull": true }, { "columnName": "big_int_column", "propertyName": "bigIntColumn", "type": { "type": "BIGINT", "length": 5, "unsigned": true, "zeroFill": true }, "canNull": true }, { "columnName": "decimal_column", "propertyName": "decimalColumn", "type": { "type": "DECIMAL", "length": 10, "fractionalLength": 5, "unsigned": true, "zeroFill": true }, "canNull": true }, { "columnName": "float_column", "propertyName": "floatColumn", "type": { "type": "FLOAT", "length": 10, "fractionalLength": 5, "unsigned": true, "zeroFill": true }, "canNull": true }, { "columnName": "double_column", "propertyName": "doubleColumn", "type": { "type": "DOUBLE", "length": 10, "fractionalLength": 5, "unsigned": true, "zeroFill": true }, "canNull": true }, { "columnName": "date_column", "propertyName": "dateColumn", "type": { "type": "DATE" }, "canNull": true }, { "columnName": "date_time_column", "propertyName": "dateTimeColumn", "type": { "type": "DATETIME", "precision": 3 }, "canNull": true }, { "columnName": "timestamp_column", "propertyName": "timestampColumn", "type": { "type": "TIMESTAMP", "precision": 3 }, "canNull": true }, { "columnName": "time_column", "propertyName": "timeColumn", "type": { "type": "TIME", "precision": 3 }, "canNull": true }, { "columnName": "year_column", "propertyName": "yearColumn", "type": { "type": "YEAR" }, "canNull": true }, { "columnName": "var_char_column", "propertyName": "varCharColumn", "type": { "type": "VARCHAR", "length": 100, "characterSet": "utf8mb4", "collate": "utf8mb4_unicode_ci" }, "canNull": true }, { "columnName": "binary_column", "propertyName": "binaryColumn", "type": { "type": "BINARY" }, "canNull": true }, { "columnName": "var_binary_column", "propertyName": "varBinaryColumn", "type": { "type": "VARBINARY", "length": 100 }, "canNull": true }, { "columnName": "tiny_blob_column", "propertyName": "tinyBlobColumn", "type": { "type": "TINYBLOB" }, "canNull": true }, { "columnName": "tiny_text_column", "propertyName": "tinyTextColumn", "type": { "type": "TINYTEXT", "characterSet": "utf8mb4", "collate": "utf8mb4_unicode_ci" }, "canNull": true }, { "columnName": "blob_column", "propertyName": "blobColumn", "type": { "type": "BLOB", "length": 100 }, "canNull": true }, { "columnName": "text_column", "propertyName": "textColumn", "type": { "type": "TEXT", "length": 100, "characterSet": "utf8mb4", "collate": "utf8mb4_unicode_ci" }, "canNull": true }, { "columnName": "medium_blob_column", "propertyName": "mediumBlobColumn", "type": { "type": "MEDIUMBLOB" }, "canNull": true }, { "columnName": "long_blob_column", "propertyName": "longBlobColumn", "type": { "type": "LONGBLOB" }, "canNull": true }, { "columnName": "medium_text_column", "propertyName": "mediumTextColumn", "type": { "type": "MEDIUMTEXT", "characterSet": "utf8mb4", "collate": "utf8mb4_unicode_ci" }, "canNull": true }, { "columnName": "long_text_column", "propertyName": "longTextColumn", "type": { "type": "LONGTEXT", "characterSet": "utf8mb4", "collate": "utf8mb4_unicode_ci" }, "canNull": true }, { "columnName": "enum_column", "propertyName": "enumColumn", "type": { "type": "ENUM", "enums": [ "A", "B" ], "characterSet": "utf8mb4", "collate": "utf8mb4_unicode_ci" }, "canNull": true }, { "columnName": "set_column", "propertyName": "setColumn", "type": { "type": "SET", "enums": [ "A", "B" ], "characterSet": "utf8mb4", "collate": "utf8mb4_unicode_ci" }, "canNull": true }, { "columnName": "geometry_column", "propertyName": "geometryColumn", "type": { "type": "GEOMETRY" }, "canNull": true }, { "columnName": "point_column", "propertyName": "pointColumn", "type": { "type": "POINT" }, "canNull": true }, { "columnName": "line_string_column", "propertyName": "lineStringColumn", "type": { "type": "LINESTRING" }, "canNull": true }, { "columnName": "polygon_column", "propertyName": "polygonColumn", "type": { "type": "POLYGON" }, "canNull": true }, { "columnName": "multipoint_column", "propertyName": "multipointColumn", "type": { "type": "MULTIPOINT" }, "canNull": true }, { "columnName": "multi_line_string_column", "propertyName": "multiLineStringColumn", "type": { "type": "MULTILINESTRING" }, "canNull": true }, { "columnName": "multi_polygon_column", "propertyName": "multiPolygonColumn", "type": { "type": "MULTIPOLYGON" }, "canNull": true }, { "columnName": "geometry_collection_column", "propertyName": "geometryCollectionColumn", "type": { "type": "GEOMETRYCOLLECTION" }, "canNull": true }, { "columnName": "json_column", "propertyName": "jsonColumn", "type": { "type": "JSON" }, "canNull": true } ], "indices": [ { "name": "idx_col1", "keys": [ { "propertyName": "col1", "columnName": "col1" } ], "type": "FULLTEXT", "comment": "index comment\n" }, { "name": "uk_name_col1", "keys": [ { "propertyName": "name", "columnName": "name" }, { "propertyName": "col1", "columnName": "col1" } ], "type": "UNIQUE", "storeType": "BTREE", "comment": "index comment\n" } ], "comment": "foo table", "characterSet": "utf8mb4", "collate": "utf8mb4_unicode_ci" } ================================================ FILE: standalone/standalone/test/fixtures/dal-module/src/dal/structure/Foo.sql ================================================ CREATE TABLE IF NOT EXISTS egg_foo ( id INT NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT 'the primary key', name VARCHAR(100) NULL UNIQUE KEY, col1 VARCHAR(100) NULL, bit_column BIT(10) NULL, bool_column BOOL NULL, tiny_int_column TINYINT(5) UNSIGNED ZEROFILL NULL, small_int_column SMALLINT(5) UNSIGNED ZEROFILL NULL, medium_int_column MEDIUMINT(5) UNSIGNED ZEROFILL NULL, int_column INT(5) UNSIGNED ZEROFILL NULL, big_int_column BIGINT(5) UNSIGNED ZEROFILL NULL, decimal_column DECIMAL(10,5) UNSIGNED ZEROFILL NULL, float_column FLOAT(10,5) UNSIGNED ZEROFILL NULL, double_column DOUBLE(10,5) UNSIGNED ZEROFILL NULL, date_column DATE NULL, date_time_column DATETIME(3) NULL, timestamp_column TIMESTAMP(3) NULL, time_column TIME(3) NULL, year_column YEAR NULL, var_char_column VARCHAR(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL, binary_column BINARY NULL, var_binary_column VARBINARY(100) NULL, tiny_blob_column TINYBLOB NULL, tiny_text_column TINYTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL, blob_column BLOB(100) NULL, text_column TEXT(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL, medium_blob_column MEDIUMBLOB NULL, long_blob_column LONGBLOB NULL, medium_text_column MEDIUMTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL, long_text_column LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL, enum_column ENUM('A','B') CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL, set_column SET('A','B') CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL, geometry_column GEOMETRY NULL, point_column POINT NULL, line_string_column LINESTRING NULL, polygon_column POLYGON NULL, multipoint_column MULTIPOINT NULL, multi_line_string_column MULTILINESTRING NULL, multi_polygon_column MULTIPOLYGON NULL, geometry_collection_column GEOMETRYCOLLECTION NULL, json_column JSON NULL, FULLTEXT KEY idx_col1 (col1) COMMENT 'index comment\n', UNIQUE KEY uk_name_col1 (name,col1) USING BTREE COMMENT 'index comment\n' ) DEFAULT CHARACTER SET utf8mb4, DEFAULT COLLATE utf8mb4_unicode_ci, COMMENT='foo table'; ================================================ FILE: standalone/standalone/test/fixtures/dal-module/src/main.ts ================================================ import { ContextProto, Inject } from '@eggjs/tegg'; import { MysqlDataSourceManager } from '@eggjs/tegg/dal'; import { Runner, MainRunner } from '@eggjs/tegg/standalone'; import FooDAO from './dal/dao/FooDAO'; import { Foo } from './Foo'; @Runner() @ContextProto() export class FooRunner implements MainRunner { @Inject() fooDAO: FooDAO; @Inject() mysqlDataSourceManager: MysqlDataSourceManager; async main(): Promise { const foo = new Foo(); foo.col1 = '2333'; foo.name = 'test_service_worker'; foo.bitColumn = Buffer.from([ 0, 0 ]); foo.boolColumn = 0; foo.tinyIntColumn = 0; foo.smallIntColumn = 1; foo.mediumIntColumn = 3; foo.intColumn = 3; foo.bigIntColumn = '00099'; foo.decimalColumn = '00002.33333'; foo.floatColumn = 2.3; foo.doubleColumn = 2.3; foo.dateColumn = new Date('2020-03-15T16:00:00.000Z'); foo.dateTimeColumn = new Date('2024-03-16T01:26:58.677Z'); foo.timestampColumn = new Date('2024-03-16T01:26:58.677Z'); foo.timeColumn = '838:59:50.123'; foo.yearColumn = 2024; foo.varCharColumn = 'var_char'; foo.binaryColumn = Buffer.from('b'); foo.varBinaryColumn = Buffer.from('var_binary'); foo.tinyBlobColumn = Buffer.from('tiny_blob'); foo.tinyTextColumn = 'text'; foo.blobColumn = Buffer.from('blob'); foo.textColumn = 'text'; foo.mediumBlobColumn = Buffer.from('medium_blob'); foo.longBlobColumn = Buffer.from('long_blob'); foo.mediumTextColumn = 'medium_text'; foo.longTextColumn = 'long_text'; foo.enumColumn = 'A'; foo.setColumn = 'B'; foo.geometryColumn = { x: 10, y: 10 }; foo.pointColumn = { x: 10, y: 10 }; foo.lineStringColumn = [ { x: 15, y: 15 }, { x: 20, y: 20 }, ]; foo.polygonColumn = [ [ { x: 0, y: 0 }, { x: 10, y: 0 }, { x: 10, y: 10 }, { x: 0, y: 10 }, { x: 0, y: 0 }, ], [ { x: 5, y: 5 }, { x: 7, y: 5 }, { x: 7, y: 7 }, { x: 5, y: 7 }, { x: 5, y: 5 }, ], ]; foo.multipointColumn = [ { x: 0, y: 0 }, { x: 20, y: 20 }, { x: 60, y: 60 }, ]; foo.multiLineStringColumn = [ [ { x: 10, y: 10 }, { x: 20, y: 20 }, ], [ { x: 15, y: 15 }, { x: 30, y: 15 }, ], ]; foo.multiPolygonColumn = [ [ [ { x: 0, y: 0 }, { x: 10, y: 0 }, { x: 10, y: 10 }, { x: 0, y: 10 }, { x: 0, y: 0 }, ], ], [ [ { x: 5, y: 5 }, { x: 7, y: 5 }, { x: 7, y: 7 }, { x: 5, y: 7 }, { x: 5, y: 5 }, ], ], ]; foo.geometryCollectionColumn = [ { x: 10, y: 10 }, { x: 30, y: 30 }, [ { x: 15, y: 15 }, { x: 20, y: 20 }, ], ]; foo.jsonColumn = { hello: 'json', }; await this.fooDAO.insert(foo); return this.fooDAO.findOneByCol1('2333'); } } ================================================ FILE: standalone/standalone/test/fixtures/dal-module/tsconfig.json ================================================ { "extends": "../../../tsconfig.json", "compilerOptions": { "skipLibCheck": true } } ================================================ FILE: standalone/standalone/test/fixtures/dal-transaction-module/module.yml ================================================ dataSource: foo: connectionLimit: 100 database: 'test_dal_standalone' host: '127.0.0.1' user: root port: 3306 timezone: '+08:00' forkDb: true ================================================ FILE: standalone/standalone/test/fixtures/dal-transaction-module/package.json ================================================ { "name": "dal", "eggModule": { "name": "dal" }, "dependencies": { "@eggjs/tegg-dal-plugin": "*" } } ================================================ FILE: standalone/standalone/test/fixtures/dal-transaction-module/src/Foo.ts ================================================ import { Column, ColumnType, Geometry, GeometryCollection, Index, IndexType, IndexStoreType, Line, MultiLine, MultiPoint, MultiPolygon, Point, Polygon, Table, } from '@eggjs/dal-decorator'; @Table({ name: 'egg_foo', comment: 'foo table', characterSet: 'utf8mb4', collate: 'utf8mb4_unicode_ci', }) @Index({ keys: [ 'name', 'col1' ], type: IndexType.UNIQUE, storeType: IndexStoreType.BTREE, comment: 'index comment\n', }) @Index({ keys: [ 'col1' ], type: IndexType.FULLTEXT, comment: 'index comment\n', }) export class Foo { @Column({ type: ColumnType.INT, }, { primaryKey: true, autoIncrement: true, comment: 'the primary key', }) id: number; @Column({ type: ColumnType.VARCHAR, length: 100, }, { uniqueKey: true, }) name: string; @Column({ type: ColumnType.VARCHAR, length: 100, }, { name: 'col1', }) col1: string; @Column({ type: ColumnType.BIT, length: 10, }) bitColumn: Buffer; @Column({ type: ColumnType.BOOL, }) boolColumn: 0 | 1; @Column({ type: ColumnType.TINYINT, length: 5, unsigned: true, zeroFill: true, }) tinyIntColumn: number; @Column({ type: ColumnType.SMALLINT, length: 5, unsigned: true, zeroFill: true, }) smallIntColumn: number; @Column({ type: ColumnType.MEDIUMINT, length: 5, unsigned: true, zeroFill: true, }) mediumIntColumn: number; @Column({ type: ColumnType.INT, length: 5, unsigned: true, zeroFill: true, }) intColumn: number; @Column({ type: ColumnType.BIGINT, length: 5, unsigned: true, zeroFill: true, }) bigIntColumn: string; @Column({ type: ColumnType.DECIMAL, length: 10, fractionalLength: 5, unsigned: true, zeroFill: true, }) decimalColumn: string; @Column({ type: ColumnType.FLOAT, length: 10, fractionalLength: 5, unsigned: true, zeroFill: true, }) floatColumn: number; @Column({ type: ColumnType.DOUBLE, length: 10, fractionalLength: 5, unsigned: true, zeroFill: true, }) doubleColumn: number; @Column({ type: ColumnType.DATE, }) dateColumn: Date; @Column({ type: ColumnType.DATETIME, precision: 3, }) dateTimeColumn: Date; @Column({ type: ColumnType.TIMESTAMP, precision: 3, }) timestampColumn: Date; @Column({ type: ColumnType.TIME, precision: 3, }) timeColumn: string; @Column({ type: ColumnType.YEAR, }) yearColumn: number; @Column({ type: ColumnType.VARCHAR, length: 100, characterSet: 'utf8mb4', collate: 'utf8mb4_unicode_ci', }) varCharColumn: string; @Column({ type: ColumnType.BINARY, }) binaryColumn: Buffer; @Column({ type: ColumnType.VARBINARY, length: 100, }) varBinaryColumn: Buffer; @Column({ type: ColumnType.TINYBLOB, }) tinyBlobColumn: Buffer; @Column({ type: ColumnType.TINYTEXT, characterSet: 'utf8mb4', collate: 'utf8mb4_unicode_ci', }) tinyTextColumn: string; @Column({ type: ColumnType.BLOB, length: 100, }) blobColumn: Buffer; @Column({ type: ColumnType.TEXT, length: 100, characterSet: 'utf8mb4', collate: 'utf8mb4_unicode_ci', }) textColumn: string; @Column({ type: ColumnType.MEDIUMBLOB, }) mediumBlobColumn: Buffer; @Column({ type: ColumnType.LONGBLOB, }) longBlobColumn: Buffer; @Column({ type: ColumnType.MEDIUMTEXT, characterSet: 'utf8mb4', collate: 'utf8mb4_unicode_ci', }) mediumTextColumn: string; @Column({ type: ColumnType.LONGTEXT, characterSet: 'utf8mb4', collate: 'utf8mb4_unicode_ci', }) longTextColumn: string; @Column({ type: ColumnType.ENUM, enums: [ 'A', 'B' ], characterSet: 'utf8mb4', collate: 'utf8mb4_unicode_ci', }) enumColumn: string; @Column({ type: ColumnType.SET, enums: [ 'A', 'B' ], characterSet: 'utf8mb4', collate: 'utf8mb4_unicode_ci', }) setColumn: string; @Column({ type: ColumnType.GEOMETRY, }) geometryColumn: Geometry; @Column({ type: ColumnType.POINT, }) pointColumn: Point; @Column({ type: ColumnType.LINESTRING, }) lineStringColumn: Line; @Column({ type: ColumnType.POLYGON, }) polygonColumn: Polygon; @Column({ type: ColumnType.MULTIPOINT, }) multipointColumn: MultiPoint; @Column({ type: ColumnType.MULTILINESTRING, }) multiLineStringColumn: MultiLine; @Column({ type: ColumnType.MULTIPOLYGON, }) multiPolygonColumn: MultiPolygon; @Column({ type: ColumnType.GEOMETRYCOLLECTION, }) geometryCollectionColumn: GeometryCollection; @Column({ type: ColumnType.JSON, }) jsonColumn: object; static buildObj() { const foo = new Foo(); foo.name = 'name'; foo.col1 = 'col1'; foo.bitColumn = Buffer.from([ 0, 0 ]); foo.boolColumn = 0; foo.tinyIntColumn = 0; foo.smallIntColumn = 1; foo.mediumIntColumn = 3; foo.intColumn = 3; foo.bigIntColumn = '00099'; foo.decimalColumn = '00002.33333'; foo.floatColumn = 2.3; foo.doubleColumn = 2.3; foo.dateColumn = new Date('2020-03-15T16:00:00.000Z'); foo.dateTimeColumn = new Date('2024-03-16T01:26:58.677Z'); foo.timestampColumn = new Date('2024-03-16T01:26:58.677Z'); foo.timeColumn = '838:59:50.123'; foo.yearColumn = 2024; foo.varCharColumn = 'var_char'; foo.binaryColumn = Buffer.from('b'); foo.varBinaryColumn = Buffer.from('var_binary'); foo.tinyBlobColumn = Buffer.from('tiny_blob'); foo.tinyTextColumn = 'text'; foo.blobColumn = Buffer.from('blob'); foo.textColumn = 'text'; foo.mediumBlobColumn = Buffer.from('medium_blob'); foo.longBlobColumn = Buffer.from('long_blob'); foo.mediumTextColumn = 'medium_text'; foo.longTextColumn = 'long_text'; foo.enumColumn = 'A'; foo.setColumn = 'B'; foo.geometryColumn = { x: 10, y: 10 }; foo.pointColumn = { x: 10, y: 10 }; foo.lineStringColumn = [ { x: 15, y: 15 }, { x: 20, y: 20 }, ]; foo.polygonColumn = [ [ { x: 0, y: 0 }, { x: 10, y: 0 }, { x: 10, y: 10 }, { x: 0, y: 10 }, { x: 0, y: 0 }, ], [ { x: 5, y: 5 }, { x: 7, y: 5 }, { x: 7, y: 7 }, { x: 5, y: 7 }, { x: 5, y: 5 }, ], ]; foo.multipointColumn = [ { x: 0, y: 0 }, { x: 20, y: 20 }, { x: 60, y: 60 }, ]; foo.multiLineStringColumn = [ [ { x: 10, y: 10 }, { x: 20, y: 20 }, ], [ { x: 15, y: 15 }, { x: 30, y: 15 }, ], ]; foo.multiPolygonColumn = [ [ [ { x: 0, y: 0 }, { x: 10, y: 0 }, { x: 10, y: 10 }, { x: 0, y: 10 }, { x: 0, y: 0 }, ], ], [ [ { x: 5, y: 5 }, { x: 7, y: 5 }, { x: 7, y: 7 }, { x: 5, y: 7 }, { x: 5, y: 5 }, ], ], ]; foo.geometryCollectionColumn = [ { x: 10, y: 10 }, { x: 30, y: 30 }, [ { x: 15, y: 15 }, { x: 20, y: 20 }, ], ]; foo.jsonColumn = { hello: 'json', }; return foo; } } ================================================ FILE: standalone/standalone/test/fixtures/dal-transaction-module/src/FooService.ts ================================================ import { AccessLevel, Inject, SingletonProto } from '@eggjs/tegg'; import { Transactional } from '@eggjs/tegg/transaction'; import FooDAO from './dal/dao/FooDAO'; import { Foo } from './Foo'; @SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) export class FooService { @Inject() private readonly fooDAO: FooDAO; @Transactional() async succeedTransaction() { const foo = Foo.buildObj(); foo.name = 'insert_succeed_transaction_1'; const foo2 = Foo.buildObj(); foo2.name = 'insert_succeed_transaction_2'; await this.fooDAO.insert(foo); await this.fooDAO.insert(foo2); } @Transactional() async failedTransaction() { const foo = Foo.buildObj(); foo.name = 'insert_failed_transaction_1'; const foo2 = Foo.buildObj(); foo2.name = 'insert_failed_transaction_2'; await this.fooDAO.insert(foo); await this.fooDAO.insert(foo2); throw new Error('mock error'); } } ================================================ FILE: standalone/standalone/test/fixtures/dal-transaction-module/src/dal/dao/FooDAO.ts ================================================ import { SingletonProto, AccessLevel } from '@eggjs/tegg'; import { BaseFooDAO } from './base/BaseFooDAO'; import { Foo } from '../../Foo'; /** * FooDAO 类 * @class FooDAO * @classdesc 在此扩展关于 Foo 数据的一切操作 * @augments BaseFooDAO */ @SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) export default class FooDAO extends BaseFooDAO { async findByName(name: string): Promise { return this.dataSource.execute('findByName', { name, }); } } ================================================ FILE: standalone/standalone/test/fixtures/dal-transaction-module/src/dal/dao/base/BaseFooDAO.ts ================================================ import fs from 'node:fs'; import path from 'node:path'; import type { InsertResult, UpdateResult, DeleteResult } from '@eggjs/dal-decorator'; import { Inject } from '@eggjs/tegg'; import { Dao } from '@eggjs/tegg/dal'; import { DataSource, DataSourceInjectName, DataSourceQualifier, ColumnTsType } from '@eggjs/dal-decorator'; import { Foo } from '../../../Foo'; import FooExtension from '../../extension/FooExtension'; import Structure from '../../structure/Foo.json'; const SQL = Symbol('Dao#sql'); type Optional = Omit < T, K > & Partial ; /** * 自动生成的 FooDAO 基类 * @class BaseFooDAO * @classdesc 该文件由 @eggjs/tegg 自动生成,请**不要**修改它! */ /* istanbul ignore next */ @Dao() export class BaseFooDAO { static clazzModel = Foo; static clazzExtension = FooExtension; static tableStature = Structure; static get tableSql() { if (!this[SQL]) { this[SQL] = fs.readFileSync(path.join(__dirname, '../../structure/Foo.sql'), 'utf8'); } return this[SQL]; } @Inject({ name: DataSourceInjectName, }) @DataSourceQualifier('dal.foo.Foo') protected readonly dataSource: DataSource; public async insert(raw: Optional): Promise { const data: Record = {}; let tmp; tmp = raw.id; if (tmp !== undefined) { data.$id = tmp; } tmp = raw.name; if (tmp !== undefined) { data.$name = tmp; } tmp = raw.col1; if (tmp !== undefined) { data.$col1 = tmp; } tmp = raw.bitColumn; if (tmp !== undefined) { data.$bitColumn = tmp; } tmp = raw.boolColumn; if (tmp !== undefined) { data.$boolColumn = tmp; } tmp = raw.tinyIntColumn; if (tmp !== undefined) { data.$tinyIntColumn = tmp; } tmp = raw.smallIntColumn; if (tmp !== undefined) { data.$smallIntColumn = tmp; } tmp = raw.mediumIntColumn; if (tmp !== undefined) { data.$mediumIntColumn = tmp; } tmp = raw.intColumn; if (tmp !== undefined) { data.$intColumn = tmp; } tmp = raw.bigIntColumn; if (tmp !== undefined) { data.$bigIntColumn = tmp; } tmp = raw.decimalColumn; if (tmp !== undefined) { data.$decimalColumn = tmp; } tmp = raw.floatColumn; if (tmp !== undefined) { data.$floatColumn = tmp; } tmp = raw.doubleColumn; if (tmp !== undefined) { data.$doubleColumn = tmp; } tmp = raw.dateColumn; if (tmp !== undefined) { data.$dateColumn = tmp; } tmp = raw.dateTimeColumn; if (tmp !== undefined) { data.$dateTimeColumn = tmp; } tmp = raw.timestampColumn; if (tmp !== undefined) { data.$timestampColumn = tmp; } tmp = raw.timeColumn; if (tmp !== undefined) { data.$timeColumn = tmp; } tmp = raw.yearColumn; if (tmp !== undefined) { data.$yearColumn = tmp; } tmp = raw.varCharColumn; if (tmp !== undefined) { data.$varCharColumn = tmp; } tmp = raw.binaryColumn; if (tmp !== undefined) { data.$binaryColumn = tmp; } tmp = raw.varBinaryColumn; if (tmp !== undefined) { data.$varBinaryColumn = tmp; } tmp = raw.tinyBlobColumn; if (tmp !== undefined) { data.$tinyBlobColumn = tmp; } tmp = raw.tinyTextColumn; if (tmp !== undefined) { data.$tinyTextColumn = tmp; } tmp = raw.blobColumn; if (tmp !== undefined) { data.$blobColumn = tmp; } tmp = raw.textColumn; if (tmp !== undefined) { data.$textColumn = tmp; } tmp = raw.mediumBlobColumn; if (tmp !== undefined) { data.$mediumBlobColumn = tmp; } tmp = raw.longBlobColumn; if (tmp !== undefined) { data.$longBlobColumn = tmp; } tmp = raw.mediumTextColumn; if (tmp !== undefined) { data.$mediumTextColumn = tmp; } tmp = raw.longTextColumn; if (tmp !== undefined) { data.$longTextColumn = tmp; } tmp = raw.enumColumn; if (tmp !== undefined) { data.$enumColumn = tmp; } tmp = raw.setColumn; if (tmp !== undefined) { data.$setColumn = tmp; } tmp = raw.geometryColumn; if (tmp !== undefined) { data.$geometryColumn = tmp; } tmp = raw.pointColumn; if (tmp !== undefined) { data.$pointColumn = tmp; } tmp = raw.lineStringColumn; if (tmp !== undefined) { data.$lineStringColumn = tmp; } tmp = raw.polygonColumn; if (tmp !== undefined) { data.$polygonColumn = tmp; } tmp = raw.multipointColumn; if (tmp !== undefined) { data.$multipointColumn = tmp; } tmp = raw.multiLineStringColumn; if (tmp !== undefined) { data.$multiLineStringColumn = tmp; } tmp = raw.multiPolygonColumn; if (tmp !== undefined) { data.$multiPolygonColumn = tmp; } tmp = raw.geometryCollectionColumn; if (tmp !== undefined) { data.$geometryCollectionColumn = tmp; } tmp = raw.jsonColumn; if (tmp !== undefined) { data.$jsonColumn = tmp; } return this.dataSource.executeRawScalar('insert', data); } public async update(id: ColumnTsType['INT'], data: Partial): Promise { const newData: Record = { primary: { id, }, }; let tmp; tmp = data.id; if (tmp !== undefined) { newData.$id = tmp; } tmp = data.name; if (tmp !== undefined) { newData.$name = tmp; } tmp = data.col1; if (tmp !== undefined) { newData.$col1 = tmp; } tmp = data.bitColumn; if (tmp !== undefined) { newData.$bitColumn = tmp; } tmp = data.boolColumn; if (tmp !== undefined) { newData.$boolColumn = tmp; } tmp = data.tinyIntColumn; if (tmp !== undefined) { newData.$tinyIntColumn = tmp; } tmp = data.smallIntColumn; if (tmp !== undefined) { newData.$smallIntColumn = tmp; } tmp = data.mediumIntColumn; if (tmp !== undefined) { newData.$mediumIntColumn = tmp; } tmp = data.intColumn; if (tmp !== undefined) { newData.$intColumn = tmp; } tmp = data.bigIntColumn; if (tmp !== undefined) { newData.$bigIntColumn = tmp; } tmp = data.decimalColumn; if (tmp !== undefined) { newData.$decimalColumn = tmp; } tmp = data.floatColumn; if (tmp !== undefined) { newData.$floatColumn = tmp; } tmp = data.doubleColumn; if (tmp !== undefined) { newData.$doubleColumn = tmp; } tmp = data.dateColumn; if (tmp !== undefined) { newData.$dateColumn = tmp; } tmp = data.dateTimeColumn; if (tmp !== undefined) { newData.$dateTimeColumn = tmp; } tmp = data.timestampColumn; if (tmp !== undefined) { newData.$timestampColumn = tmp; } tmp = data.timeColumn; if (tmp !== undefined) { newData.$timeColumn = tmp; } tmp = data.yearColumn; if (tmp !== undefined) { newData.$yearColumn = tmp; } tmp = data.varCharColumn; if (tmp !== undefined) { newData.$varCharColumn = tmp; } tmp = data.binaryColumn; if (tmp !== undefined) { newData.$binaryColumn = tmp; } tmp = data.varBinaryColumn; if (tmp !== undefined) { newData.$varBinaryColumn = tmp; } tmp = data.tinyBlobColumn; if (tmp !== undefined) { newData.$tinyBlobColumn = tmp; } tmp = data.tinyTextColumn; if (tmp !== undefined) { newData.$tinyTextColumn = tmp; } tmp = data.blobColumn; if (tmp !== undefined) { newData.$blobColumn = tmp; } tmp = data.textColumn; if (tmp !== undefined) { newData.$textColumn = tmp; } tmp = data.mediumBlobColumn; if (tmp !== undefined) { newData.$mediumBlobColumn = tmp; } tmp = data.longBlobColumn; if (tmp !== undefined) { newData.$longBlobColumn = tmp; } tmp = data.mediumTextColumn; if (tmp !== undefined) { newData.$mediumTextColumn = tmp; } tmp = data.longTextColumn; if (tmp !== undefined) { newData.$longTextColumn = tmp; } tmp = data.enumColumn; if (tmp !== undefined) { newData.$enumColumn = tmp; } tmp = data.setColumn; if (tmp !== undefined) { newData.$setColumn = tmp; } tmp = data.geometryColumn; if (tmp !== undefined) { newData.$geometryColumn = tmp; } tmp = data.pointColumn; if (tmp !== undefined) { newData.$pointColumn = tmp; } tmp = data.lineStringColumn; if (tmp !== undefined) { newData.$lineStringColumn = tmp; } tmp = data.polygonColumn; if (tmp !== undefined) { newData.$polygonColumn = tmp; } tmp = data.multipointColumn; if (tmp !== undefined) { newData.$multipointColumn = tmp; } tmp = data.multiLineStringColumn; if (tmp !== undefined) { newData.$multiLineStringColumn = tmp; } tmp = data.multiPolygonColumn; if (tmp !== undefined) { newData.$multiPolygonColumn = tmp; } tmp = data.geometryCollectionColumn; if (tmp !== undefined) { newData.$geometryCollectionColumn = tmp; } tmp = data.jsonColumn; if (tmp !== undefined) { newData.$jsonColumn = tmp; } return this.dataSource.executeRawScalar('update', newData); } public async delete(id: ColumnTsType['INT']): Promise { return this.dataSource.executeRawScalar('delete', { id, }); } public async del(id: ColumnTsType['INT']): Promise { return this.dataSource.executeRawScalar('delete', { id, }); } public async findByCol1($col1: ColumnTsType['VARCHAR']): Promise { return this.dataSource.execute('findByCol1', { $col1, }); } public async findOneByCol1($col1: ColumnTsType['VARCHAR']): Promise { return this.dataSource.executeScalar('findOneByCol1', { $col1, }); } public async findByUkNameCol1($name: ColumnTsType['VARCHAR'], $col1: ColumnTsType['VARCHAR']): Promise { return this.dataSource.execute('findByUkNameCol1', { $name, $col1, }); } public async findOneByUkNameCol1($name: ColumnTsType['VARCHAR'], $col1: ColumnTsType['VARCHAR']): Promise { return this.dataSource.executeScalar('findOneByUkNameCol1', { $name, $col1, }); } public async findById($id: ColumnTsType['INT']): Promise { return this.dataSource.executeScalar('findById', { $id, }); } public async findByPrimary($id: ColumnTsType['INT']): Promise { return this.dataSource.executeScalar('findById', { $id, }); } } ================================================ FILE: standalone/standalone/test/fixtures/dal-transaction-module/src/dal/extension/FooExtension.ts ================================================ import { SqlMap } from '@eggjs/tegg/dal'; /** * Define Custom SQLs * * import { SqlMap, SqlType } from '@eggjs/tegg/dal'; * * export default { * findByName: { * type: SqlType.SELECT, * sql: 'SELECT {{ allColumns }} from foo where name = {{ name }}' * }, * } */ export default { findByName: { type: 'SELECT', sql: 'SELECT {{ allColumns }} FROM egg_foo WHERE name = {{ name }}', }, } as Record; ================================================ FILE: standalone/standalone/test/fixtures/dal-transaction-module/src/dal/structure/Foo.json ================================================ { "name": "egg_foo", "dataSourceName": "default", "columns": [ { "columnName": "id", "propertyName": "id", "type": { "type": "INT" }, "canNull": false, "comment": "the primary key", "autoIncrement": true, "primaryKey": true }, { "columnName": "name", "propertyName": "name", "type": { "type": "VARCHAR", "length": 100 }, "canNull": true, "uniqueKey": true }, { "columnName": "col1", "propertyName": "col1", "type": { "type": "VARCHAR", "length": 100 }, "canNull": true }, { "columnName": "bit_column", "propertyName": "bitColumn", "type": { "type": "BIT", "length": 10 }, "canNull": true }, { "columnName": "bool_column", "propertyName": "boolColumn", "type": { "type": "BOOL" }, "canNull": true }, { "columnName": "tiny_int_column", "propertyName": "tinyIntColumn", "type": { "type": "TINYINT", "length": 5, "unsigned": true, "zeroFill": true }, "canNull": true }, { "columnName": "small_int_column", "propertyName": "smallIntColumn", "type": { "type": "SMALLINT", "length": 5, "unsigned": true, "zeroFill": true }, "canNull": true }, { "columnName": "medium_int_column", "propertyName": "mediumIntColumn", "type": { "type": "MEDIUMINT", "length": 5, "unsigned": true, "zeroFill": true }, "canNull": true }, { "columnName": "int_column", "propertyName": "intColumn", "type": { "type": "INT", "length": 5, "unsigned": true, "zeroFill": true }, "canNull": true }, { "columnName": "big_int_column", "propertyName": "bigIntColumn", "type": { "type": "BIGINT", "length": 5, "unsigned": true, "zeroFill": true }, "canNull": true }, { "columnName": "decimal_column", "propertyName": "decimalColumn", "type": { "type": "DECIMAL", "length": 10, "fractionalLength": 5, "unsigned": true, "zeroFill": true }, "canNull": true }, { "columnName": "float_column", "propertyName": "floatColumn", "type": { "type": "FLOAT", "length": 10, "fractionalLength": 5, "unsigned": true, "zeroFill": true }, "canNull": true }, { "columnName": "double_column", "propertyName": "doubleColumn", "type": { "type": "DOUBLE", "length": 10, "fractionalLength": 5, "unsigned": true, "zeroFill": true }, "canNull": true }, { "columnName": "date_column", "propertyName": "dateColumn", "type": { "type": "DATE" }, "canNull": true }, { "columnName": "date_time_column", "propertyName": "dateTimeColumn", "type": { "type": "DATETIME", "precision": 3 }, "canNull": true }, { "columnName": "timestamp_column", "propertyName": "timestampColumn", "type": { "type": "TIMESTAMP", "precision": 3 }, "canNull": true }, { "columnName": "time_column", "propertyName": "timeColumn", "type": { "type": "TIME", "precision": 3 }, "canNull": true }, { "columnName": "year_column", "propertyName": "yearColumn", "type": { "type": "YEAR" }, "canNull": true }, { "columnName": "var_char_column", "propertyName": "varCharColumn", "type": { "type": "VARCHAR", "length": 100, "characterSet": "utf8mb4", "collate": "utf8mb4_unicode_ci" }, "canNull": true }, { "columnName": "binary_column", "propertyName": "binaryColumn", "type": { "type": "BINARY" }, "canNull": true }, { "columnName": "var_binary_column", "propertyName": "varBinaryColumn", "type": { "type": "VARBINARY", "length": 100 }, "canNull": true }, { "columnName": "tiny_blob_column", "propertyName": "tinyBlobColumn", "type": { "type": "TINYBLOB" }, "canNull": true }, { "columnName": "tiny_text_column", "propertyName": "tinyTextColumn", "type": { "type": "TINYTEXT", "characterSet": "utf8mb4", "collate": "utf8mb4_unicode_ci" }, "canNull": true }, { "columnName": "blob_column", "propertyName": "blobColumn", "type": { "type": "BLOB", "length": 100 }, "canNull": true }, { "columnName": "text_column", "propertyName": "textColumn", "type": { "type": "TEXT", "length": 100, "characterSet": "utf8mb4", "collate": "utf8mb4_unicode_ci" }, "canNull": true }, { "columnName": "medium_blob_column", "propertyName": "mediumBlobColumn", "type": { "type": "MEDIUMBLOB" }, "canNull": true }, { "columnName": "long_blob_column", "propertyName": "longBlobColumn", "type": { "type": "LONGBLOB" }, "canNull": true }, { "columnName": "medium_text_column", "propertyName": "mediumTextColumn", "type": { "type": "MEDIUMTEXT", "characterSet": "utf8mb4", "collate": "utf8mb4_unicode_ci" }, "canNull": true }, { "columnName": "long_text_column", "propertyName": "longTextColumn", "type": { "type": "LONGTEXT", "characterSet": "utf8mb4", "collate": "utf8mb4_unicode_ci" }, "canNull": true }, { "columnName": "enum_column", "propertyName": "enumColumn", "type": { "type": "ENUM", "enums": [ "A", "B" ], "characterSet": "utf8mb4", "collate": "utf8mb4_unicode_ci" }, "canNull": true }, { "columnName": "set_column", "propertyName": "setColumn", "type": { "type": "SET", "enums": [ "A", "B" ], "characterSet": "utf8mb4", "collate": "utf8mb4_unicode_ci" }, "canNull": true }, { "columnName": "geometry_column", "propertyName": "geometryColumn", "type": { "type": "GEOMETRY" }, "canNull": true }, { "columnName": "point_column", "propertyName": "pointColumn", "type": { "type": "POINT" }, "canNull": true }, { "columnName": "line_string_column", "propertyName": "lineStringColumn", "type": { "type": "LINESTRING" }, "canNull": true }, { "columnName": "polygon_column", "propertyName": "polygonColumn", "type": { "type": "POLYGON" }, "canNull": true }, { "columnName": "multipoint_column", "propertyName": "multipointColumn", "type": { "type": "MULTIPOINT" }, "canNull": true }, { "columnName": "multi_line_string_column", "propertyName": "multiLineStringColumn", "type": { "type": "MULTILINESTRING" }, "canNull": true }, { "columnName": "multi_polygon_column", "propertyName": "multiPolygonColumn", "type": { "type": "MULTIPOLYGON" }, "canNull": true }, { "columnName": "geometry_collection_column", "propertyName": "geometryCollectionColumn", "type": { "type": "GEOMETRYCOLLECTION" }, "canNull": true }, { "columnName": "json_column", "propertyName": "jsonColumn", "type": { "type": "JSON" }, "canNull": true } ], "indices": [ { "name": "idx_col1", "keys": [ { "propertyName": "col1", "columnName": "col1" } ], "type": "FULLTEXT", "comment": "index comment\n" }, { "name": "uk_name_col1", "keys": [ { "propertyName": "name", "columnName": "name" }, { "propertyName": "col1", "columnName": "col1" } ], "type": "UNIQUE", "storeType": "BTREE", "comment": "index comment\n" } ], "comment": "foo table", "characterSet": "utf8mb4", "collate": "utf8mb4_unicode_ci" } ================================================ FILE: standalone/standalone/test/fixtures/dal-transaction-module/src/dal/structure/Foo.sql ================================================ CREATE TABLE IF NOT EXISTS egg_foo ( id INT NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT 'the primary key', name VARCHAR(100) NULL UNIQUE KEY, col1 VARCHAR(100) NULL, bit_column BIT(10) NULL, bool_column BOOL NULL, tiny_int_column TINYINT(5) UNSIGNED ZEROFILL NULL, small_int_column SMALLINT(5) UNSIGNED ZEROFILL NULL, medium_int_column MEDIUMINT(5) UNSIGNED ZEROFILL NULL, int_column INT(5) UNSIGNED ZEROFILL NULL, big_int_column BIGINT(5) UNSIGNED ZEROFILL NULL, decimal_column DECIMAL(10,5) UNSIGNED ZEROFILL NULL, float_column FLOAT(10,5) UNSIGNED ZEROFILL NULL, double_column DOUBLE(10,5) UNSIGNED ZEROFILL NULL, date_column DATE NULL, date_time_column DATETIME(3) NULL, timestamp_column TIMESTAMP(3) NULL, time_column TIME(3) NULL, year_column YEAR NULL, var_char_column VARCHAR(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL, binary_column BINARY NULL, var_binary_column VARBINARY(100) NULL, tiny_blob_column TINYBLOB NULL, tiny_text_column TINYTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL, blob_column BLOB(100) NULL, text_column TEXT(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL, medium_blob_column MEDIUMBLOB NULL, long_blob_column LONGBLOB NULL, medium_text_column MEDIUMTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL, long_text_column LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL, enum_column ENUM('A','B') CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL, set_column SET('A','B') CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL, geometry_column GEOMETRY NULL, point_column POINT NULL, line_string_column LINESTRING NULL, polygon_column POLYGON NULL, multipoint_column MULTIPOINT NULL, multi_line_string_column MULTILINESTRING NULL, multi_polygon_column MULTIPOLYGON NULL, geometry_collection_column GEOMETRYCOLLECTION NULL, json_column JSON NULL, FULLTEXT KEY idx_col1 (col1) COMMENT 'index comment\n', UNIQUE KEY uk_name_col1 (name,col1) USING BTREE COMMENT 'index comment\n' ) DEFAULT CHARACTER SET utf8mb4, DEFAULT COLLATE utf8mb4_unicode_ci, COMMENT='foo table'; ================================================ FILE: standalone/standalone/test/fixtures/dal-transaction-module/src/main.ts ================================================ import { ContextProto, Inject } from '@eggjs/tegg'; import { Runner, MainRunner } from '@eggjs/tegg/standalone'; import { Foo } from './Foo'; import { FooService } from './FooService'; import FooDAO from './dal/dao/FooDAO'; @Runner() @ContextProto() export class FooRunner implements MainRunner>> { @Inject() fooService: FooService; @Inject() private readonly fooDAO: FooDAO; async main(): Promise>> { await Promise.allSettled([ this.fooService.succeedTransaction(), this.fooService.failedTransaction(), ]); return await Promise.all([ this.fooDAO.findByName('insert_succeed_transaction_1'), this.fooDAO.findByName('insert_succeed_transaction_2'), this.fooDAO.findByName('insert_failed_transaction_1'), this.fooDAO.findByName('insert_failed_transaction_2'), ]); } } ================================================ FILE: standalone/standalone/test/fixtures/dal-transaction-module/tsconfig.json ================================================ { "extends": "../../../tsconfig.json", "compilerOptions": { "skipLibCheck": true } } ================================================ FILE: standalone/standalone/test/fixtures/dependency/foo.ts ================================================ import { ContextProto, Inject } from '@eggjs/tegg'; import { Runner, MainRunner } from '@eggjs/tegg/standalone'; import { Hello } from 'dependency-2/foo'; import { ConfigSourceQualifier } from '../../../../../core/core-decorator/src/decorator/ConfigSource'; @ContextProto() @Runner() export class Foo implements MainRunner { @Inject() hello: Hello; @Inject() @ConfigSourceQualifier('dependency2') moduleConfig: any; async main(): Promise { return this.hello.hello() + JSON.stringify(this.moduleConfig); } } ================================================ FILE: standalone/standalone/test/fixtures/dependency/node_modules/dependency-1/package.json ================================================ { "name": "dependency-1", "eggModule": { "name": "dependency-1" }, "dependencies": { "dependency-2": "*" } } ================================================ FILE: standalone/standalone/test/fixtures/dependency/node_modules/dependency-2/foo.js ================================================ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; const { SingletonProto, AccessLevel } = require('@eggjs/tegg'); class Hello { hello() { return 'hello!'; } } exports.Hello = __decorate([SingletonProto({ accessLevel: AccessLevel.PUBLIC })], Hello); ================================================ FILE: standalone/standalone/test/fixtures/dependency/node_modules/dependency-2/module.yml ================================================ features: dynamic: foo: 'bar' ================================================ FILE: standalone/standalone/test/fixtures/dependency/node_modules/dependency-2/package.json ================================================ { "name": "dependency-2", "eggModule": { "name": "dependency2" } } ================================================ FILE: standalone/standalone/test/fixtures/dependency/package.json ================================================ { "name": "entry", "eggModule": { "name": "entry" } } ================================================ FILE: standalone/standalone/test/fixtures/dynamic-inject-module/AbstractContextHello.ts ================================================ export abstract class AbstractContextHello { abstract hello(): string; } ================================================ FILE: standalone/standalone/test/fixtures/dynamic-inject-module/AbstractSingletonHello.ts ================================================ export abstract class AbstractSingletonHello { abstract hello(): string; } ================================================ FILE: standalone/standalone/test/fixtures/dynamic-inject-module/FooType.ts ================================================ export enum ContextHelloType { FOO = 'FOO', BAR = 'BAR', } export enum SingletonHelloType { FOO = 'FOO', BAR = 'BAR', } ================================================ FILE: standalone/standalone/test/fixtures/dynamic-inject-module/HelloService.ts ================================================ import { ContextProto, Inject, EggObjectFactory } from '@eggjs/tegg'; import { ContextHelloType, SingletonHelloType } from './FooType'; import { AbstractContextHello } from './AbstractContextHello'; import { AbstractSingletonHello } from './AbstractSingletonHello'; @ContextProto() export class HelloService { @Inject() private readonly eggObjectFactory: EggObjectFactory; async hello(): Promise { const helloImpls = await Promise.all([ this.eggObjectFactory.getEggObject(AbstractContextHello, ContextHelloType.FOO), this.eggObjectFactory.getEggObject(AbstractContextHello, ContextHelloType.BAR), this.eggObjectFactory.getEggObject(AbstractSingletonHello, SingletonHelloType.FOO), this.eggObjectFactory.getEggObject(AbstractSingletonHello, SingletonHelloType.BAR), ]); const msgs = helloImpls.map(helloImpl => helloImpl.hello()); return msgs; } } ================================================ FILE: standalone/standalone/test/fixtures/dynamic-inject-module/decorator/ContextHello.ts ================================================ import { ContextHelloType } from '../FooType'; import { ImplDecorator, QualifierImplDecoratorUtil } from '@eggjs/tegg'; import { AbstractContextHello } from '../AbstractContextHello'; export const CONTEXT_HELLO_ATTRIBUTE = 'CONTEXT_HELLO_ATTRIBUTE'; export const ContextHello: ImplDecorator = QualifierImplDecoratorUtil.generatorDecorator(AbstractContextHello, CONTEXT_HELLO_ATTRIBUTE); ================================================ FILE: standalone/standalone/test/fixtures/dynamic-inject-module/decorator/SingletonHello.ts ================================================ import { SingletonHelloType } from '../FooType'; import { ImplDecorator, QualifierImplDecoratorUtil } from '@eggjs/tegg'; import { AbstractSingletonHello } from '../AbstractSingletonHello'; export const SINGLETON_HELLO_ATTRIBUTE = 'SINGLETON_HELLO_ATTRIBUTE'; export const SingletonHello: ImplDecorator = QualifierImplDecoratorUtil.generatorDecorator(AbstractSingletonHello, SINGLETON_HELLO_ATTRIBUTE); ================================================ FILE: standalone/standalone/test/fixtures/dynamic-inject-module/impl/BarContextHello.ts ================================================ import { ContextProto } from '@eggjs/core-decorator'; import { ContextHello } from '../decorator/ContextHello'; import { ContextHelloType } from '../FooType'; import { AbstractContextHello } from '../AbstractContextHello'; @ContextProto() @ContextHello(ContextHelloType.BAR) export class BarContextHello extends AbstractContextHello { id = 0; hello(): string { return `hello, bar(context:${this.id++})`; } } ================================================ FILE: standalone/standalone/test/fixtures/dynamic-inject-module/impl/BarSingletonHello.ts ================================================ import { SingletonProto } from '@eggjs/core-decorator'; import { SingletonHelloType } from '../FooType'; import { SingletonHello } from '../decorator/SingletonHello'; import { AbstractContextHello } from '../AbstractContextHello'; @SingletonProto() @SingletonHello(SingletonHelloType.BAR) export class BarSingletonHello extends AbstractContextHello { id = 0; hello(): string { return `hello, bar(singleton:${this.id++})`; } } ================================================ FILE: standalone/standalone/test/fixtures/dynamic-inject-module/impl/FooContextHello.ts ================================================ import { ContextProto } from '@eggjs/core-decorator'; import { ContextHello } from '../decorator/ContextHello'; import { ContextHelloType } from '../FooType'; import { AbstractContextHello } from '../AbstractContextHello'; @ContextProto() @ContextHello(ContextHelloType.FOO) export class FooContextHello extends AbstractContextHello { id = 0; hello(): string { return `hello, foo(context:${this.id++})`; } } ================================================ FILE: standalone/standalone/test/fixtures/dynamic-inject-module/impl/FooSingletonHello.ts ================================================ import { SingletonProto } from '@eggjs/core-decorator'; import { SingletonHelloType } from '../FooType'; import { SingletonHello } from '../decorator/SingletonHello'; import { AbstractContextHello } from '../AbstractContextHello'; @SingletonProto() @SingletonHello(SingletonHelloType.FOO) export class FooSingletonHello extends AbstractContextHello { id = 0; hello(): string { return `hello, foo(singleton:${this.id++})`; } } ================================================ FILE: standalone/standalone/test/fixtures/dynamic-inject-module/main.ts ================================================ import { ContextProto, Inject } from '@eggjs/tegg'; import { Runner, MainRunner } from '@eggjs/tegg/standalone'; import { HelloService } from './HelloService'; @Runner() @ContextProto() export class Foo implements MainRunner { @Inject() helloService: HelloService; async main(): Promise { return await this.helloService.hello(); } } ================================================ FILE: standalone/standalone/test/fixtures/dynamic-inject-module/package.json ================================================ { "name": "dynamic-inject-module", "eggModule": { "name": "dynamicInjectModule" } } ================================================ FILE: standalone/standalone/test/fixtures/inner-object/foo.ts ================================================ import { Inject, SingletonProto } from '@eggjs/tegg'; import { Runner, MainRunner } from '@eggjs/tegg/standalone'; export interface Hello { hello(): string; } @Runner() @SingletonProto() export class Foo implements MainRunner { @Inject() hello: Hello; async main(): Promise { return this.hello.hello(); } } ================================================ FILE: standalone/standalone/test/fixtures/inner-object/package.json ================================================ { "name": "innerobject", "eggModule": { "name": "innerobject" } } ================================================ FILE: standalone/standalone/test/fixtures/invalid-inject/foo.ts ================================================ import { Inject, SingletonProto } from '@eggjs/tegg'; import { MainRunner, Runner } from '@eggjs/tegg/standalone'; @Runner() @SingletonProto() export class Foo implements MainRunner { @Inject() doesNotExist?: object; async main(): Promise { return !!this.doesNotExist; } } ================================================ FILE: standalone/standalone/test/fixtures/invalid-inject/package.json ================================================ { "name": "invalid-inject", "eggModule": { "name": "invalidInject" } } ================================================ FILE: standalone/standalone/test/fixtures/lifecycle/foo.ts ================================================ import { SingletonProto, LifecyclePreLoad, LifecyclePostConstruct, LifecyclePreInject, LifecyclePostInject, LifecycleInit, LifecyclePreDestroy, LifecycleDestroy, } from '@eggjs/tegg'; import { Runner, MainRunner } from '@eggjs/tegg/standalone'; @Runner() @SingletonProto() export class Foo implements MainRunner { static staticCalled: string[] = []; getLifecycleCalled() { return Foo.staticCalled; } @LifecyclePreLoad() static async _preLoad() { Foo.staticCalled.push('preLoad'); } constructor() { Foo.staticCalled.push('construct'); } @LifecyclePostConstruct() protected async _postConstruct() { Foo.staticCalled.push('postConstruct'); } @LifecyclePreInject() protected async _preInject() { Foo.staticCalled.push('preInject'); } @LifecyclePostInject() protected async _postInject() { Foo.staticCalled.push('postInject'); } protected async init() { Foo.staticCalled.push('init should not called'); } @LifecycleInit() protected async _init() { Foo.staticCalled.push('init'); } @LifecyclePreDestroy() protected async _preDestroy() { Foo.staticCalled.push('preDestroy'); } @LifecycleDestroy() protected async _destroy() { Foo.staticCalled.push('destroy'); } async main(): Promise { return Foo.staticCalled; } } ================================================ FILE: standalone/standalone/test/fixtures/lifecycle/package.json ================================================ { "name": "lifecycle", "eggModule": { "name": "lifecycle" } } ================================================ FILE: standalone/standalone/test/fixtures/module-with-config/foo.ts ================================================ import { ContextProto, Inject, SingletonProto, ModuleConfigs } from '@eggjs/tegg'; import { Runner, MainRunner } from '@eggjs/tegg/standalone'; @SingletonProto() export class Hello { hello() { return 'hello!'; } } @ContextProto() export class HelloContext { hello() { return 'hello from ctx'; } } @ContextProto() @Runner() export class Foo implements MainRunner { @Inject() moduleConfigs: ModuleConfigs; async main(): Promise { return this.moduleConfigs.get('simple')!; } } ================================================ FILE: standalone/standalone/test/fixtures/module-with-config/module.yml ================================================ features: dynamic: foo: 'bar' ================================================ FILE: standalone/standalone/test/fixtures/module-with-config/package.json ================================================ { "name": "simple", "eggModule": { "name": "simple" } } ================================================ FILE: standalone/standalone/test/fixtures/module-with-empty-config/foo.ts ================================================ import { ContextProto, Inject, SingletonProto, ModuleConfigs } from '@eggjs/tegg'; import { Runner, MainRunner } from '@eggjs/tegg/standalone'; @SingletonProto() export class Hello { hello() { return 'hello!'; } } @ContextProto() export class HelloContext { hello() { return 'hello from ctx'; } } @ContextProto() @Runner() export class Foo implements MainRunner { @Inject() moduleConfigs: ModuleConfigs; async main(): Promise { return this.moduleConfigs.get('simple')!; } } ================================================ FILE: standalone/standalone/test/fixtures/module-with-empty-config/module.yml ================================================ ================================================ FILE: standalone/standalone/test/fixtures/module-with-empty-config/package.json ================================================ { "name": "simple", "eggModule": { "name": "simple" } } ================================================ FILE: standalone/standalone/test/fixtures/module-with-empty-default-config/foo.ts ================================================ import { ContextProto, Inject, SingletonProto, ModuleConfigs } from '@eggjs/tegg'; import { Runner, MainRunner } from '@eggjs/tegg/standalone'; @SingletonProto() export class Hello { hello() { return 'hello!'; } } @ContextProto() export class HelloContext { hello() { return 'hello from ctx'; } } @ContextProto() @Runner() export class Foo implements MainRunner { @Inject() moduleConfigs: ModuleConfigs; async main(): Promise { return this.moduleConfigs.get('simple')!; } } ================================================ FILE: standalone/standalone/test/fixtures/module-with-empty-default-config/module.dev.yml ================================================ features: dynamic: foo: 'foo' ================================================ FILE: standalone/standalone/test/fixtures/module-with-empty-default-config/module.yml ================================================ ================================================ FILE: standalone/standalone/test/fixtures/module-with-empty-default-config/package.json ================================================ { "name": "simple", "eggModule": { "name": "simple" } } ================================================ FILE: standalone/standalone/test/fixtures/module-with-env-config/foo.ts ================================================ import { ContextProto, Inject, SingletonProto, ModuleConfigs } from '@eggjs/tegg'; import { Runner, MainRunner } from '@eggjs/tegg/standalone'; @SingletonProto() export class Hello { hello() { return 'hello!'; } } @ContextProto() export class HelloContext { hello() { return 'hello from ctx'; } } @ContextProto() @Runner() export class Foo implements MainRunner { @Inject() moduleConfigs: ModuleConfigs; async main(): Promise { return this.moduleConfigs.get('simple')!; } } ================================================ FILE: standalone/standalone/test/fixtures/module-with-env-config/module.dev.yml ================================================ features: dynamic: foo: 'foo' ================================================ FILE: standalone/standalone/test/fixtures/module-with-env-config/module.yml ================================================ features: dynamic: foo: 'bar' ================================================ FILE: standalone/standalone/test/fixtures/module-with-env-config/package.json ================================================ { "name": "simple", "eggModule": { "name": "simple" } } ================================================ FILE: standalone/standalone/test/fixtures/multi-callback-instance-module/biz/biz.ts ================================================ import { Inject, SingletonProto, AccessLevel } from '@eggjs/tegg'; import { DynamicLogger, LogPath } from '../logger/DynamicLogger'; @SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) export class Biz { @Inject({ name: 'dynamicLogger', }) @LogPath('fooBiz') fooDynamicLogger: DynamicLogger; @Inject({ name: 'dynamicLogger', }) @LogPath('barBiz') barDynamicLogger: DynamicLogger; async doSomething(): Promise { await this.fooDynamicLogger.info('hello, foo biz'); await this.barDynamicLogger.info('hello, bar biz'); } } ================================================ FILE: standalone/standalone/test/fixtures/multi-callback-instance-module/biz/module.yml ================================================ features: logger: - fooBiz - barBiz ================================================ FILE: standalone/standalone/test/fixtures/multi-callback-instance-module/biz/package.json ================================================ { "name": "multi-callback-instance-module-biz", "eggModule": { "name": "multiCallbackInstanceModuleBiz" } } ================================================ FILE: standalone/standalone/test/fixtures/multi-callback-instance-module/logger/DynamicLogger.ts ================================================ import { MultiInstanceProto, MultiInstancePrototypeGetObjectsContext, LifecycleInit, LifecycleDestroy, QualifierUtil, EggProtoImplClass, AccessLevel, } from '@eggjs/tegg'; import { EggObject, ModuleConfigUtil, EggObjectLifeCycleContext } from '@eggjs/tegg/helper'; import fs from 'node:fs'; import { Writable } from 'node:stream'; import path from 'node:path'; import { EOL } from 'node:os'; export const LOG_PATH_ATTRIBUTE = Symbol.for('LOG_PATH_ATTRIBUTE'); export function LogPath(name: string) { return function(target: any, propertyKey: PropertyKey) { QualifierUtil.addProperQualifier(target.constructor as EggProtoImplClass, propertyKey, LOG_PATH_ATTRIBUTE, name); }; } @MultiInstanceProto({ accessLevel: AccessLevel.PUBLIC, getObjects(ctx: MultiInstancePrototypeGetObjectsContext) { const config = ModuleConfigUtil.loadModuleConfigSync(ctx.unitPath); const logger = (config as any)?.features?.logger; if (!logger) { return []; } return logger.map(name => { return { name: 'dynamicLogger', qualifiers: [{ attribute: LOG_PATH_ATTRIBUTE, value: name, }], }; }); }, }) export class DynamicLogger { stream: Writable; loggerName: string; @LifecycleInit() async init(ctx: EggObjectLifeCycleContext, obj: EggObject) { const loggerName = obj.proto.getQualifier(LOG_PATH_ATTRIBUTE); this.loggerName = loggerName as string; this.stream = fs.createWriteStream(path.join(ctx.loadUnit.unitPath, `${loggerName}.log`)); } @LifecycleDestroy() async destroy() { return new Promise((resolve, reject) => { this.stream.end(err => { if (err) { return reject(err); } return resolve(); }); }); } info(msg: string) { return new Promise((resolve, reject) => { this.stream.write(msg + EOL, err => { if (err) { return reject(err); } return resolve(); }); }); } } ================================================ FILE: standalone/standalone/test/fixtures/multi-callback-instance-module/logger/package.json ================================================ { "name": "multi-callback-instance-module-logger", "eggModule": { "name": "multiCallbackInstanceModuleLogger" } } ================================================ FILE: standalone/standalone/test/fixtures/multi-callback-instance-module/main/foo.ts ================================================ import { Inject, SingletonProto } from '@eggjs/tegg'; import { Runner, MainRunner } from '@eggjs/tegg/standalone'; import { DynamicLogger, LogPath } from '../logger/DynamicLogger'; import { Biz } from '../biz/biz'; @SingletonProto() @Runner() export class Foo implements MainRunner { @Inject({ name: 'dynamicLogger', }) @LogPath('foo') fooDynamicLogger: DynamicLogger; @Inject({ name: 'dynamicLogger', }) @LogPath('bar') barDynamicLogger: DynamicLogger; @Inject() biz: Biz; async main(): Promise { await this.fooDynamicLogger.info('hello, foo'); await this.barDynamicLogger.info('hello, bar'); await this.biz.doSomething(); } } ================================================ FILE: standalone/standalone/test/fixtures/multi-callback-instance-module/main/module.yml ================================================ features: logger: - foo - bar ================================================ FILE: standalone/standalone/test/fixtures/multi-callback-instance-module/main/package.json ================================================ { "name": "multi-callback-instance-module-main", "eggModule": { "name": "multiCallbackInstanceModuleMain" } } ================================================ FILE: standalone/standalone/test/fixtures/multi-modules/bar/module.yml ================================================ features: dynamic: bar: 'bar' ================================================ FILE: standalone/standalone/test/fixtures/multi-modules/bar/package.json ================================================ { "name": "bar", "eggModule": { "name": "bar" } } ================================================ FILE: standalone/standalone/test/fixtures/multi-modules/foo/foo.ts ================================================ import { ContextProto, Inject, SingletonProto, ModuleConfigs, ConfigSourceQualifier } from '@eggjs/tegg'; import { Runner, MainRunner } from '@eggjs/tegg/standalone'; import { ModuleConfig } from 'egg'; @SingletonProto() export class Hello { hello() { return 'hello!'; } } @ContextProto() export class HelloContext { hello() { return 'hello from ctx'; } } @ContextProto() @Runner() export class Foo implements MainRunner { @Inject() moduleConfigs: ModuleConfigs; @Inject() moduleConfig: ModuleConfig; @Inject({ name: 'moduleConfig', }) @ConfigSourceQualifier('bar') barModuleConfig: ModuleConfig; async main(): Promise { return { configs: this.moduleConfigs, foo: this.moduleConfig, bar: this.barModuleConfig, }; } } ================================================ FILE: standalone/standalone/test/fixtures/multi-modules/foo/module.yml ================================================ features: dynamic: foo: 'bar' ================================================ FILE: standalone/standalone/test/fixtures/multi-modules/foo/package.json ================================================ { "name": "foo", "eggModule": { "name": "foo" } } ================================================ FILE: standalone/standalone/test/fixtures/optional-inject/bar.ts ================================================ import { Inject, SingletonProto } from '@eggjs/tegg'; import { InjectOptional } from '@eggjs/core-decorator'; @SingletonProto() export class Bar { constructor( @InjectOptional() readonly hello?: object, @Inject({ optional: true }) readonly world?: object, ) {} } ================================================ FILE: standalone/standalone/test/fixtures/optional-inject/foo.ts ================================================ import { Inject, InjectOptional, SingletonProto } from '@eggjs/tegg'; import { Runner, MainRunner } from '@eggjs/tegg/standalone'; import { Bar } from './bar'; @Runner() @SingletonProto() export class Foo implements MainRunner { @Inject({ optional: true }) hello?: object; @InjectOptional() world?: object; @Inject() bar: Bar; async main(): Promise { return !this.hello && !this.world && !this.bar.hello && !this.bar.world; } } ================================================ FILE: standalone/standalone/test/fixtures/optional-inject/package.json ================================================ { "name": "optional-inject", "eggModule": { "name": "optionalInject" } } ================================================ FILE: standalone/standalone/test/fixtures/runtime-config/foo.ts ================================================ import { Inject, SingletonProto } from '@eggjs/tegg'; import { RuntimeConfig } from '@eggjs/tegg-common-util'; import { Runner, MainRunner } from '@eggjs/tegg/standalone'; @Runner() @SingletonProto() export class Foo implements MainRunner { @Inject() runtimeConfig: RuntimeConfig; async main(): Promise { return this.runtimeConfig; } } ================================================ FILE: standalone/standalone/test/fixtures/runtime-config/package.json ================================================ { "name": "runtime-config", "eggModule": { "name": "runtime-config" } } ================================================ FILE: standalone/standalone/test/fixtures/simple/foo.ts ================================================ import { ContextProto, Inject, SingletonProto } from '@eggjs/tegg'; import { Runner, MainRunner } from '@eggjs/tegg/standalone'; @SingletonProto() export class Hello { hello() { return 'hello!'; } } @ContextProto() export class HelloContext { hello() { return 'hello from ctx'; } } @ContextProto() @Runner() export class Foo implements MainRunner { @Inject() hello: Hello; @Inject() helloContext: HelloContext; async main(): Promise { return this.hello.hello() + this.helloContext.hello(); } } ================================================ FILE: standalone/standalone/test/fixtures/simple/package.json ================================================ { "name": "simple", "eggModule": { "name": "simple" } } ================================================ FILE: standalone/standalone/test/index.test.ts ================================================ import { strict as assert, deepStrictEqual } from 'node:assert'; import path from 'node:path'; import fs from 'node:fs/promises'; import { setTimeout as sleep } from 'node:timers/promises'; import mm from 'mm'; import { ModuleConfig, ModuleConfigs, ModuleDescriptorDumper } from '@eggjs/tegg/helper'; import { main, StandaloneContext, Runner, preLoad } from '..'; import { crosscutAdviceParams, pointcutAdviceParams } from './fixtures/aop-module/Hello'; import { Foo } from './fixtures/dal-module/src/Foo'; describe('standalone/standalone/test/index.test.ts', () => { describe('simple runner', () => { const fixture = path.join(__dirname, './fixtures/simple'); beforeEach(() => { mm.restore(); mm.spy(ModuleDescriptorDumper, 'dump'); }); it('should work', async () => { const msg: string = await main(fixture); assert(msg === 'hello!hello from ctx'); await sleep(500); assert.equal((ModuleDescriptorDumper.dump as any).called, 1); }); it('should not dump', async () => { await main(fixture, { dump: false }); await sleep(500); assert.equal((ModuleDescriptorDumper.dump as any).called, undefined); }); }); describe('runner with dependency', () => { it('should work', async () => { const msg: string = await main(path.join(__dirname, './fixtures/dependency'), { dependencies: [ path.join(__dirname, './fixtures/dependency/node_modules/dependency-1'), ], }); assert(msg === 'hello!{"features":{"dynamic":{"foo":"bar"}}}'); }); }); describe('runner with inner object', () => { it('should work', async () => { const msg: string = await main(path.join(__dirname, './fixtures/inner-object'), { innerObjectHandlers: { hello: [{ obj: { hello() { return 'hello, inner'; }, }, }], }, }); assert(msg === 'hello, inner'); }); }); describe('runner with custom context', () => { it('should work', async () => { const runner = new Runner(path.join(__dirname, './fixtures/custom-context')); await runner.init(); const ctx = new StandaloneContext(); ctx.set('foo', 'foo'); const msg = await runner.run(ctx); await runner.destroy(); assert(msg === 'foo'); }); }); describe('module with config', () => { it('should work', async () => { const config = await main(path.join(__dirname, './fixtures/module-with-config')); assert.deepEqual(config, { features: { dynamic: { foo: 'bar', }, }, }); }); it('should work with env', async () => { const config = await main(path.join(__dirname, './fixtures/module-with-env-config'), { env: 'dev', }); assert.deepEqual(config, { features: { dynamic: { foo: 'foo', }, }, }); }); it('should empty config work', async () => { const config = await main(path.join(__dirname, './fixtures/module-with-empty-config')); assert.deepEqual(config, {}); }); it('should empty default config work', async () => { const config = await main(path.join(__dirname, './fixtures/module-with-empty-default-config'), { env: 'dev' }); assert.deepEqual(config, { features: { dynamic: { foo: 'foo', }, }, }); }); }); describe('@ConfigSource qualifier', () => { it('should work', async () => { const { configs, foo, bar } = (await main(path.join(__dirname, './fixtures/multi-modules'))) as { configs: ModuleConfigs, foo: ModuleConfig, bar: ModuleConfig, }; assert.deepEqual(configs.get('foo'), foo); assert.deepEqual(configs.get('bar'), bar); }); }); describe('runner with runtimeConfig', () => { it('should work', async () => { const msg = await main(path.join(__dirname, './fixtures/runtime-config')); assert.deepEqual(msg, { baseDir: path.join(__dirname, './fixtures/runtime-config'), env: undefined, name: undefined, }); }); it('should auto set name and env', async () => { const msg = await main(path.join(__dirname, './fixtures/runtime-config'), { name: 'foo', env: 'unittest', }); assert.deepEqual(msg, { baseDir: path.join(__dirname, './fixtures/runtime-config'), name: 'foo', env: 'unittest', }); }); }); describe('multi instance prototype runner', () => { const fixturePath = path.join(__dirname, './fixtures/multi-callback-instance-module'); afterEach(async () => { await fs.unlink(path.join(fixturePath, 'main', 'foo.log')); await fs.unlink(path.join(fixturePath, 'main', 'bar.log')); await fs.unlink(path.join(fixturePath, 'biz', 'fooBiz.log')); await fs.unlink(path.join(fixturePath, 'biz', 'barBiz.log')); }); it('should work', async () => { await main(fixturePath); const fooContent = await fs.readFile(path.join(fixturePath, 'main', 'foo.log'), 'utf8'); const barContent = await fs.readFile(path.join(fixturePath, 'main', 'bar.log'), 'utf8'); assert(fooContent.includes('hello, foo')); assert(barContent.includes('hello, bar')); const fooBizContent = await fs.readFile(path.join(fixturePath, 'biz', 'fooBiz.log'), 'utf8'); const barBizContent = await fs.readFile(path.join(fixturePath, 'biz', 'barBiz.log'), 'utf8'); assert(fooBizContent.includes('hello, foo biz')); assert(barBizContent.includes('hello, bar biz')); }); }); describe('dynamic inject', () => { const fixturePath = path.join(__dirname, './fixtures/dynamic-inject-module'); it('should work', async () => { const msgs = await main(fixturePath, { dependencies: [ { baseDir: path.join(__dirname, '..'), extraFilePattern: [ '!**/test' ] }, ], }); assert.deepEqual(msgs, [ 'hello, foo(context:0)', 'hello, bar(context:0)', 'hello, foo(singleton:0)', 'hello, bar(singleton:0)', ]); }); }); describe('inject', () => { it('should optional work', async () => { const fixturePath = path.join(__dirname, './fixtures/optional-inject'); const nil = await main(fixturePath); assert.equal(nil, true); }); it('should throw error if no proto found', async () => { const fixturePath = path.join(__dirname, './fixtures/invalid-inject'); const runner = new Runner(fixturePath); await assert.rejects( runner.init(), /EggPrototypeNotFound: Object doesNotExist not found in LOAD_UNIT:invalidInject/, ); await runner.destroy(); }); }); describe('aop runtime', () => { const fixturePath = path.join(__dirname, './fixtures/aop-module'); it('should work', async () => { const msg = await main(fixturePath); assert.deepEqual(msg, `withCrossAroundResult(withPointAroundResult(hello withPointAroundParam(withCrosscutAroundParam(aop))${JSON.stringify(pointcutAdviceParams)})${JSON.stringify(crosscutAdviceParams)})`); }); }); describe('load', () => { let runner: Runner; afterEach(async () => { if (runner) await runner.destroy(); }); it('should work', async () => { runner = new Runner(path.join(__dirname, './fixtures/simple')); const loadunits = await runner.load(); for (const loadunit of loadunits) { for (const proto of loadunit.iterateEggPrototype()) { if (proto.id.match(/:hello$/)) { assert.equal(proto.className, 'Hello'); } else if (proto.id.match(/:moduleConfigs$/)) { assert.equal(proto.className, undefined); } else if (proto.id.match(/:moduleConfig$/)) { assert.equal(proto.className, undefined); } } } }); it('should work with multi', async () => { runner = new Runner(path.join(__dirname, './fixtures/multi-callback-instance-module')); const loadunits = await runner.load(); for (const loadunit of loadunits) { for (const proto of loadunit.iterateEggPrototype()) { if (proto.id.match(/:dynamicLogger$/)) { assert.equal(proto.className, 'DynamicLogger'); } } } }); }); describe('dal runner', () => { it('should work', async () => { const foo: Foo = await main(path.join(__dirname, './fixtures/dal-module'), { env: 'unittest', }); assert(foo); assert.equal(foo.col1, '2333'); }); }); describe('dal transaction runner', () => { it('should work', async () => { const foo: Array> = await main(path.join(__dirname, './fixtures/dal-transaction-module'), { env: 'unittest', }); // insert_succeed_transaction_1 assert.equal(foo[0].length, 1); // insert_succeed_transaction_2 assert.equal(foo[1].length, 1); // insert_failed_transaction_1 assert.equal(foo[2].length, 0); // insert_failed_transaction_2 assert.equal(foo[3].length, 0); }); }); describe('ajv runner', () => { it('should throw AjvInvalidParamError', async () => { await assert.rejects(async () => { await main(path.join(__dirname, './fixtures/ajv-module'), { dependencies: [ path.dirname(require.resolve('@eggjs/tegg-ajv-plugin/package.json')), ], }); }, (err: any) => { assert.equal(err.name, 'AjvInvalidParamError'); assert.equal(err.message, 'Validation Failed'); assert.deepEqual(err.errorData, {}); assert.equal(err.currentSchema, '{"type":"object","properties":{"fullname":{"transform":["trim"],"maxLength":100,"type":"string"},"skipDependencies":{"type":"boolean"},"registryName":{"type":"string"}},"required":["fullname","skipDependencies"]}'); assert.deepEqual(err.errors, [ { instancePath: '', schemaPath: '#/required', keyword: 'required', params: { missingProperty: 'fullname', }, message: "must have required property 'fullname'", }, ]); return true; }); }); it('should pass', async () => { const result = await main(path.join(__dirname, './fixtures/ajv-module-pass'), { dependencies: [ path.dirname(require.resolve('@eggjs/tegg-ajv-plugin/package.json')), ], }); assert.equal(result, '{"body":{"fullname":"mock fullname","skipDependencies":true,"registryName":"ok"}}'); }); }); describe('lifecycle', () => { const fixturePath = path.join(__dirname, './fixtures/lifecycle'); let Foo; beforeEach(() => { mm.restore(); mm.spy(ModuleDescriptorDumper, 'dump'); delete require.cache[require.resolve(path.join(fixturePath, './foo'))]; // eslint-disable-next-line @typescript-eslint/no-var-requires Foo = require(path.join(fixturePath, './foo')).Foo; }); it('should work', async () => { await preLoad(fixturePath); await main(fixturePath); deepStrictEqual(Foo.staticCalled, [ 'preLoad', 'construct', 'postConstruct', 'preInject', 'postInject', 'init', ]); assert.equal((ModuleDescriptorDumper.dump as any).called, 1); }); }); }); ================================================ FILE: standalone/standalone/tsconfig.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "dist", "baseUrl": "./" }, "exclude": [ "dist", "node_modules", "test" ] } ================================================ FILE: standalone/standalone/tsconfig.pub.json ================================================ { "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "dist", "baseUrl": "./" }, "exclude": [ "dist", "node_modules", "test" ] } ================================================ FILE: tsconfig.json ================================================ { "compileOnSave": true, "compilerOptions": { "target": "ES2019", "module": "CommonJS", "strict": true, "noImplicitAny": false, "esModuleInterop": true, "useUnknownInCatchVariables": false, "allowJs": false, "pretty": true, "noEmitOnError": false, "noUnusedLocals": true, "noUnusedParameters": true, "allowUnreachableCode": false, "allowUnusedLabels": false, "strictPropertyInitialization": false, "noFallthroughCasesInSwitch": true, "skipLibCheck": true, "skipDefaultLibCheck": true, "inlineSourceMap": true, "declaration": true, "resolveJsonModule": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, "baseUrl": ".", "paths": { "@eggjs/tegg-metadata": ["./core/metadata/src"], "@eggjs/tegg-metadata/*": ["./core/metadata/src/*"], "@eggjs/tegg-loader": ["./core/loader/src"], "@eggjs/tegg-loader/*": ["./core/loader/src/*"] } }, "ts-node": { "files": true } }