feat(service-worker): standalone service worker runtime (#6022)
Summary
Adds a standalone service-worker host for running tegg HTTP controllers and MCP tools without starting an Egg application. The same controller metadata and registration core now serve both the Egg node-HTTP host and Fetch-compatible runtimes, with a runnable Node/Cloudflare example.
Architecture
- Extracts
@eggjs/controller-runtimeas the host-neutral controller library. Egg transport remains in@eggjs/controller-plugin; Fetch transport lives in@eggjs/service-worker-controller;@eggjs/service-workeris the host facade overStandaloneApp.- HTTP and MCP registration use the same collect-then-finalize model. Egg finalizes both from
@LoadUnitInstanceLifecycleProtoproviders whenCONTROLLER_LOAD_UNITis created; the Fetch host finalizes once while building routes on the first event.- Controller middleware supports dependency-injected Advice through the existing
@Middleware(AdviceClass)form. The controller runtime invokesaround()at the bound HTTP/MCP invocation boundary and preservesAbstractControllerAdvice.middleware()compatibility, while explicit@Pointcutremains owned by the independent AOP plugin.- Framework extension hooks are declarative and per app: controller graph weaving is registered by a scanned inner object, and mcp-proxy injects the current
EggMcpRouterinstead of writing to a static hook registry.Standalone runtime
- Supports Node
serve(), embeddedhandleEvent(), and Cloudflare module-worker entries.- Supports Fetch
Request/Response, parameter binding, binary and streaming bodies, multipleSet-Cookieheaders, request-scope stream draining, host-provided error mapping, and background tasks.- Exposes MCP stateless Streamable HTTP with fresh SDK server/transport instances per request, controller and method middleware, authentication, DNS-rebinding options, and host-registered transport providers.
- Keeps mutable runtime state isolated through each app’s
TeggScopebag.Bundle and module loading
- Promotes
TeggManifestto the shared tegg type surface and uses the sharedManifestLoaderFSoverlay in both Egg and standalone hosts.ModuleLoader.createModuleLoader()retains the selected file view in the current app scope, so later dynamic loaders such as DAL discovery read manifest-indexed files in a bundle without expanding multi-instance callback context.- Adds standalone support to
egg-bin bundle, including TypeScript metadata scanning in a loader-enabled child process and Cloudflare-compatible worker output.- Keeps the example minimal:
main.tsis the Node entry andworker.tsis the Cloudflare entry.Validation
- Focused controller graph, mcp-proxy registration, standalone HTTP, and standalone MCP suites: 46 tests.
- Typechecked
controller-runtime,controller-plugin,mcp-proxy-plugin, andservice-worker-controller.- The current pushed revision is green across CI on Linux/macOS/Windows with Node 22/24, CodeQL, examples/cnpmcore E2E, typecheck, and package checks.
Documentation
Updates the service-worker, controller Advice, module-plugin lifecycle, LoaderFS/bundler, local CI, package README, and minimal-example documentation to match the final runtime contracts.
Co-authored-by: Claude Fable 5 noreply@anthropic.com
版权所有:中国计算机学会技术支持:开源发展技术委员会
京ICP备13000930号-9
京公网安备 11010802047560号
English | 简体中文
Features
Quickstart
Follow the commands listed below.
Monorepo Structure
This project is structured as a utoo monorepo with the following packages:
packages/egg- Main Eggjs frameworkexamples/helloworld-commonjs- CommonJS example applicationexamples/helloworld-typescript- TypeScript example applicationsite- Documentation websiteThe monorepo uses utoo catalog mode for centralized dependency management, ensuring consistent versions across all packages.
Development Commands
Local External Services
Some DAL, ORM, Redis, and ecosystem benchmark paths need local MySQL and Redis services. Start the repository-aligned Docker services before running those tests on a clean machine:
This starts MySQL 8 and Redis 7, matching the main CI service versions, and creates the databases used by local DAL/ORM/e2e fixtures:
test,apple,banana,test_runtime_datasource,test_runtime_dao,test_dal_plugin,test_dal_standalone,cnpmcore, andcnpmcore_unittest.Useful commands:
The default host ports are
127.0.0.1:3306for MySQL and127.0.0.1:6379for Redis. If either port is already used, the start command stops before changing containers. Keep using the existing service if it is compatible with CI, or stop it and run the command again. You can change Docker host ports withEGG_DEV_SERVICES_MYSQL_PORTandEGG_DEV_SERVICES_REDIS_PORT; however, the full DAL/ORM/Redis local test path still expects the default host ports.Image overrides are available for compatibility checks:
Run
ut run dev:services:resetbefore switching MySQL image families, for example between MySQL 8 and MySQL 5.7, because MySQL data directories are not downgrade-compatible across major versions.Current hard-coded service assumptions:
plugins/redis/test/fixtures/apps/**/config.*use127.0.0.1:6379; skipped Redis plugin tests become runnable when that port is available.plugins/session/test/fixtures/redis-session/config/config.default.jsuse127.0.0.1:6379.tegg/core/dal-runtime/test/DataSource.test.tsandtegg/core/dal-runtime/test/DAO.test.tsuse local MySQL on port3306.tegg/plugin/dal/test/fixtures/apps/dal-app/modules/dal/module.ymlandtegg/standalone/standalone/test/fixtures/dal-*/module.ymluse local MySQL on port3306.tegg/plugin/orm/test/fixtures/prepare.jsandtegg/plugin/orm/test/fixtures/apps/orm-app/config/config.default.tsuse local MySQL on port3306.Documentations
Contributors
How to Contribute
Please let us know how can we help. Do check out issues for bug reports or suggestions first.
To become a contributor, please follow our contributing guide, and review the repository guidelines for day-to-day development tips.
Sponsors and Backers
License
MIT