## File: README.md

English | [简体中文](./README.zh-CN.md)


	


[](https://npmjs.org/package/egg)
[](http://packagequality.com/#?package=egg)
[](https://npmjs.org/package/egg)
[](https://nodejs.org/en/download/)
[](https://app.fossa.com/projects/git%2Bgithub.com%2Feggjs%2Fegg?ref=badge_shield)

[](https://github.com/eggjs/egg/actions?query=branch%3Amaster)
[](https://codecov.io/gh/eggjs/egg)
[](https://snyk.io/test/npm/egg)
[](https://opencollective.com/eggjs)

## Features

- Built-in Process Management
- Plugin System
- Framework Customization
- Lots of [plugins](https://github.com/search?q=topic%3Aegg-plugin&type=Repositories)

## Quickstart

Follow the commands listed below.

```bash
$ corepack enable utoo
$ mkdir showcase && cd showcase
$ ut create egg@beta
$ ut install
$ ut run dev
$ open http://localhost:7001
```

> Node.js >= 22.18.0 required.

## Monorepo Structure

This project is structured as a utoo monorepo with the following packages:

- `packages/egg` - Main Eggjs framework
- `examples/helloworld-commonjs` - CommonJS example application
- `examples/helloworld-typescript` - TypeScript example application
- `site` - Documentation website

The monorepo uses **utoo catalog mode** for centralized dependency management, ensuring consistent versions across all packages.

### Development Commands

```bash
# Install dependencies for all packages
ut install --from pnpm

# Build all packages
ut run build

# Test all packages
ut run test

# Run specific package commands
ut --filter=egg run test
ut --filter=@examples/helloworld-typescript run dev
ut --filter=site run dev
```

### 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:

```bash
ut run dev:services:start
```

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`, and `cnpmcore_unittest`.

Useful commands:

```bash
ut run dev:services:status
ut run dev:services:stop
ut run dev:services:reset
```

The default host ports are `127.0.0.1:3306` for MySQL and `127.0.0.1:6379` for 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 with `EGG_DEV_SERVICES_MYSQL_PORT` and `EGG_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:

```bash
EGG_DEV_SERVICES_MYSQL_IMAGE=mysql:5.7 ut run dev:services:start
EGG_DEV_SERVICES_REDIS_IMAGE=redis:7 ut run dev:services:start
```

Run `ut run dev:services:reset` before 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:

- Redis plugin fixtures under `plugins/redis/test/fixtures/apps/**/config.*` use `127.0.0.1:6379`; skipped Redis plugin tests become runnable when that port is available.
- Session Redis fixtures under `plugins/session/test/fixtures/redis-session/config/config.default.js` use `127.0.0.1:6379`.
- DAL runtime tests in `tegg/core/dal-runtime/test/DataSource.test.ts` and `tegg/core/dal-runtime/test/DAO.test.ts` use local MySQL on port `3306`.
- DAL module fixtures in `tegg/plugin/dal/test/fixtures/apps/dal-app/modules/dal/module.yml` and `tegg/standalone/standalone/test/fixtures/dal-*/module.yml` use local MySQL on port `3306`.
- ORM fixtures in `tegg/plugin/orm/test/fixtures/prepare.js` and `tegg/plugin/orm/test/fixtures/apps/orm-app/config/config.default.ts` use local MySQL on port `3306`.

## Documentations

- [Documentations](https://eggjs.org/)
- [Plugins](https://github.com/search?q=topic%3Aegg-plugin&type=Repositories)
- [Frameworks](https://github.com/search?q=topic%3Aegg-framework&type=Repositories)
- [Examples](https://github.com/eggjs/examples)

## Contributors

[](https://github.com/eggjs/egg/graphs/contributors)

## How to Contribute

Please let us know how can we help. Do check out [issues](https://github.com/eggjs/egg/issues) for bug reports or suggestions first.

To become a contributor, please follow our [contributing guide](CONTRIBUTING.md), and review the [repository guidelines](AGENTS.md) for day-to-day development tips.

## Sponsors and Backers

[](https://opencollective.com/eggjs#support)
[](https://opencollective.com/eggjs#support)

## License

[MIT](LICENSE)

[](https://app.fossa.com/projects/git%2Bgithub.com%2Feggjs%2Fegg?ref=badge_large)

---

## File: benchmark/ci-test/README.md

# CI Test Benchmark

`ut run benchmark:ci-test` runs the reusable CI test benchmark harness and writes a Markdown report, a JSON summary, and the raw Vitest JSON reporter output.

## Environment Setup

- Use Node.js `>=22.18.0`.
- Make sure the Utoo CLI is available as `ut`. The CI workflow uses `utooland/setup-utoo` before dependency installation.
- Install workspace dependencies from the repository root with `ut install --from pnpm`, matching the CI workflow.
- Run commands from the repository root so workspace paths and `vitest.config.ts` defaults can be detected.
- Keep Redis and MySQL available when benchmarking suites that require them. The CI test job uses Redis 7 on the default Redis port and MySQL 8 with a `test` database; the benchmark harness mirrors the CI Vitest flags but does not start external services.
- For CI-like metadata, set the relevant environment variables before running the command, for example `CI=1`, `GITHUB_SHA`, `RUNNER_OS`, or worker-related `VITEST_*` variables.

## Usage

```sh
# Run the suite and benchmark it
ut run benchmark:ci-test
ut run benchmark:ci-test -- --coverage
ut run benchmark:ci-test -- --output-dir .tmp/ci-benchmark -- ut execute vitest run packages/extend2/test/index.test.ts

# Summarize an existing Vitest JSON without running tests (the CI path)
ut run benchmark:ci-test -- --report-only --vitest-json benchmark/ci-test/ci-run/vitest-results.json
```

The default output directory is `benchmark/ci-test/<timestamp>`. Use `--output-dir` for a deterministic path when collecting artifacts.

## Parallelism metrics

Tests run with `isolate: false` (full parallelism; the tegg `TeggScope` per-app isolation keeps concurrent multi-app boots safe). The report reconstructs the concurrency timeline from each file's Vitest interval (`min test start .. max test end`) and reports:

- **Execution window** — `max(end) - min(start)`, the wall-clock span of test execution.
- **Busy time** — sum of per-file spans (the area under the concurrency curve).
- **Avg concurrency** — `busy time ÷ execution window`, the time-weighted mean number of files running at once.
- **Peak concurrency** — the maximum number of files whose intervals overlap (sweep line). The robust headline signal.
- **Parallel efficiency** — `avg concurrency ÷ worker ceiling` (the ceiling mirrors `vitest.config.ts`: Windows CI caps workers, otherwise the machine's available parallelism).
- **Critical path** — the longest single-file span (the wall-clock floor with unlimited workers).

> **Interval caveat:** the Vitest 4 JSON reporter derives a file's `startTime`/`endTime` from test-level timings only, so the span covers test bodies and per-test `beforeEach`/`afterEach` but **excludes suite-level `beforeAll`/`afterAll` (where egg boots its apps — often the dominant per-file cost) and module transform/import**. That excluded time still occupies the worker threads, so **avg concurrency and parallel efficiency are lower bounds** on real worker utilization; for `beforeAll`-heavy suites avg can read below 1 while peak is high. Use **peak concurrency** as the primary signal. Fully-skipped files (no test timings) are dropped from the calculation.

## CI integration

The `test` gating job in `.github/workflows/ci.yml` is instrumented without changing gate semantics:

1. `vitest.config.ts` adds a `json` reporter when `CI` is set, writing `benchmark/ci-test/ci-run/vitest-results.json` during the gating `ut run ci` run.
2. A `Report parallelism metrics` step (`if: always()`) runs the harness in `--report-only` mode against that JSON.
3. When `GITHUB_STEP_SUMMARY` is set, the Markdown report (including the parallelism table) is appended to the GitHub Actions job summary, so the metrics are visible on the run page per OS/Node matrix entry.

Gating still comes entirely from `ut run ci`; the metrics step is informational and exits `0` even when the JSON is missing.

## Outputs

- `report.md`: human-readable benchmark report (run, environment, parameters, parallelism, long-tail tables).
- `report.json`: structured report containing environment, command, wall time, Vitest summary, parallelism metrics, long-tail file/project durations, and coverage/worker/isolate parameters.
- `vitest-results.json`: raw Vitest JSON reporter output.

---

## File: packages/utils/README.md

# @eggjs/utils

[![NPM version][npm-image]][npm-url]
[![npm download][download-image]][download-url]
[](https://nodejs.org/en/download/)

[npm-image]: https://img.shields.io/npm/v/@eggjs/utils.svg?style=flat-square
[npm-url]: https://npmjs.org/package/@eggjs/utils
[download-image]: https://img.shields.io/npm/dm/@eggjs/utils.svg?style=flat-square
[download-url]: https://npmjs.org/package/@eggjs/utils

Utils for all egg projects.

## Installation

```bash
npm i @eggjs/utils
```

## API

### `getPlugins(options)`

- {String} baseDir - the current directory of application
- {String} framework - the directory of framework
- {String} env - egg environment

### `getLoadUnits(options)`

- {String} baseDir - the current directory of application
- {String} framework - the directory of framework
- {String} env - egg environment

### `getConfig(options)`

- {String} baseDir - the current directory of application
- {String} framework - the directory of framework
- {String} env - egg environment

### `getFrameworkPath(options)`

- {String} baseDir - the current directory of application
- {String} framework - the directory of framework

### `setBundleModuleLoader(loader)`

Register a module loader hook for bundled Egg apps. The hook runs before the
normal `importModule()` resolution path.

- {Function | undefined} loader - a synchronous function that receives the
  original `filepath` argument passed to `importModule()` after POSIX separator
  normalization, or a virtual specifier. It does not receive the resolved
  absolute file path from `importResolve()`. Return `undefined` to fall back to
  the normal import path.

The bundle loader is stored on `globalThis`, so bundled and external copies of
`@eggjs/utils` share the same loader. Non-`undefined` results follow the same
default export unwrapping rules as `importModule()`, including
`importDefaultOnly`.

### Bundle / snapshot module-loading hooks

`importModule()` resolves a module through the following hooks, in order. The
first one that produces a value wins; otherwise it falls back to the native
`import()` / `require()` path:

1. **`globalThis.__EGG_BUNDLE_MODULE_LOADER__`** — set via
   `setBundleModuleLoader()`. Looks up a module already inlined into the bundle
   (typically a static bundle map emitted by `egg-bundler`). Runs before on-disk
   resolution and receives the POSIX-normalized `importModule()` filepath or a
   virtual specifier. Return `undefined` to fall through.
2. **Snapshot module loader** — set via `setSnapshotModuleLoader()`. This is a
   module-local hook (not a `globalThis` global) used by the V8 snapshot entry
   generator to serve pre-bundled modules synchronously, keyed by the resolved
   path. Once registered it handles every load that reaches it, so the importer
   below is not consulted while it is active.
3. **`globalThis.__EGG_MODULE_IMPORTER__`** — an async (or sync, since the value
   is awaited) importer that receives the resolved file path (the
   `importResolve()` result, with OS-native separators — not normalized). When
   set, and the two hooks above did not resolve the module, it replaces the
   native `await import(filePath)`.

The bundle loader and importer globals are typed in `@eggjs/typings`
(`BundleModuleLoader` / `ModuleImporter`); import `@eggjs/typings/global` to pick
up the `declare global` augmentation. These hooks are the contract that
`egg-bundler`'s generated entry relies on. `@eggjs/core`'s `ManifestLoaderFS`
consults `__EGG_BUNDLE_MODULE_LOADER__` directly; its importer/native fallback is
reached through `@eggjs/loader-fs`, which calls back into `importModule()`. The
tegg loader (`LoaderUtil.loadFile`) consults both globals directly, passing the
loader filepath with separators normalized to POSIX.

`__EGG_MODULE_IMPORTER__` has two main uses:

- **Bundler-based test runners (e.g. Vitest):** route module loading through the
  runner's own module graph so the loader and the test file share a single
  module instance (otherwise `ctx.getEggObject(ClassRef)` fails with
  "can not get proto").
- **V8 startup-snapshot restore:** the deserialized main function runs without a
  host dynamic-import callback, so native `import()` throws. The snapshot entry
  installs a synchronous `require()`-based importer (`createRequire()` over the
  bundle output dir); `require()` can load ESM on Node >= 22, so modules resolve
  without dynamic import.

## License

[MIT](LICENSE)

## Contributors

[](https://github.com/eggjs/egg/graphs/contributors)

Made with [contributors-img](https://contrib.rocks).

---

## File: packages/tsconfig/README.md

# @eggjs/tsconfig

[![NPM version][npm-image]][npm-url]
[![npm download][download-image]][download-url]
[](https://nodejs.org/en/download/)
[](https://makeapullrequest.com)


[npm-image]: https://img.shields.io/npm/v/@eggjs/tsconfig.svg?style=flat-square
[npm-url]: https://npmjs.org/package/@eggjs/tsconfig
[download-image]: https://img.shields.io/npm/dm/@eggjs/tsconfig.svg?style=flat-square
[download-url]: https://npmjs.org/package/@eggjs/tsconfig

Base tsconfig file for egg project

## Install

```shell
npm i --save-dev @eggjs/tsconfig
```

## Usage

```json
// tsconfig.json
{
  "extends": "@eggjs/tsconfig",
  // custom config
  "compilerOptions": {
    // override @eggjs/tsconfig options here
  }
}
```

## License

[MIT](LICENSE)

## Contributors

[](https://github.com/eggjs/egg/graphs/contributors)

Made with [contributors-img](https://contrib.rocks).

---

## File: packages/supertest/README.md

# @eggjs/supertest

[![NPM version][npm-image]][npm-url]
[](https://makeapullrequest.com)
[![MIT License][license-badge]][license]
[![npm download][download-image]][download-url]
[](https://nodejs.org/en/download/)

[npm-image]: https://img.shields.io/npm/v/@eggjs/supertest.svg?style=flat-square
[npm-url]: https://npmjs.org/package/@eggjs/supertest
[license-badge]: https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square
[license]: https://github.com/eggjs/supertest/blob/master/LICENSE
[download-image]: https://img.shields.io/npm/dm/@eggjs/supertest.svg?style=flat-square
[download-url]: https://npmjs.org/package/@eggjs/supertest

> HTTP assertions made easy via [superagent](http://github.com/ladjs/superagent). Maintained for [Forward Email](https://github.com/forwardemail) and [Lad](https://github.com/ladjs).
> Forked for TypeScript friendly

Document see [SuperTest](https://ladjs.github.io/superagent/)

## About

The motivation with this module is to provide a high-level abstraction for testing
HTTP, while still allowing you to drop down to the [lower-level API](https://ladjs.github.io/superagent/) provided by superagent.

## Getting Started

Install SuperTest as an npm module and save it to your package.json file as a development dependency:

```bash
npm install @eggjs/supertest --save-dev
```

Once installed it can now be referenced by simply calling `require('supertest');`

## Example

You may pass an `http.Server`, or a `Function` to `request()` - if the server is not
already listening for connections then it is bound to an ephemeral port for you so
there is no need to keep track of ports.

SuperTest works with any test framework, here is an example without using any
test framework at all:

```js
const { request } = require('@eggjs/supertest');
const express = require('express');

const app = express();

app.get('/user', function (req, res) {
  res.status(200).json({ name: 'john' });
});

request(app)
  .get('/user')
  .expect('Content-Type', /json/)
  .expect('Content-Length', '15')
  .expect(200)
  .end(function (err, res) {
    if (err) throw err;
  });
```

To enable http2 protocol, simply append an options to `request` or `request.agent`:

```js
const { request } = require('@eggjs/supertest');
const express = require('express');

const app = express();

app.get('/user', function (req, res) {
  res.status(200).json({ name: 'john' });
});

request(app, { http2: true })
  .get('/user')
  .expect('Content-Type', /json/)
  .expect('Content-Length', '15')
  .expect(200)
  .end(function (err, res) {
    if (err) throw err;
  });

request
  .agent(app, { http2: true })
  .get('/user')
  .expect('Content-Type', /json/)
  .expect('Content-Length', '15')
  .expect(200)
  .end(function (err, res) {
    if (err) throw err;
  });
```

Here's an example with mocha, note how you can pass `done` straight to any of the `.expect()` calls:

```js
describe('GET /user', function () {
  it('responds with json', function (done) {
    request(app).get('/user').set('Accept', 'application/json').expect('Content-Type', /json/).expect(200, done);
  });
});
```

You can use `auth` method to pass HTTP username and password in the same way as in the [superagent](http://ladjs.github.io/superagent/#authentication):

```js
describe('GET /user', function () {
  it('responds with json', function (done) {
    request(app)
      .get('/user')
      .auth('username', 'password')
      .set('Accept', 'application/json')
      .expect('Content-Type', /json/)
      .expect(200, done);
  });
});
```

One thing to note with the above statement is that superagent now sends any HTTP
error (anything other than a 2XX response code) to the callback as the first argument if
you do not add a status code expect (i.e. `.expect(302)`).

If you are using the `.end()` method `.expect()` assertions that fail will
not throw - they will return the assertion as an error to the `.end()` callback. In
order to fail the test case, you will need to rethrow or pass `err` to `done()`, as follows:

```js
describe('POST /users', function () {
  it('responds with json', function (done) {
    request(app)
      .post('/users')
      .send({ name: 'john' })
      .set('Accept', 'application/json')
      .expect('Content-Type', /json/)
      .expect(200)
      .end(function (err, res) {
        if (err) return done(err);
        return done();
      });
  });
});
```

You can also use promises:

```js
describe('GET /users', function () {
  it('responds with json', function () {
    return request(app)
      .get('/users')
      .set('Accept', 'application/json')
      .expect('Content-Type', /json/)
      .expect(200)
      .then((response) => {
        expect(response.body.email).toEqual('foo@bar.com');
      });
  });
});
```

Or async/await syntax:

```js
describe('GET /users', function () {
  it('responds with json', async function () {
    const response = await request(app).get('/users').set('Accept', 'application/json');
    expect(response.headers['Content-Type']).toMatch(/json/);
    expect(response.status).toEqual(200);
    expect(response.body.email).toEqual('foo@bar.com');
  });
});
```

Expectations are run in the order of definition. This characteristic can be used
to modify the response body or headers before executing an assertion.

```js
describe('POST /user', function () {
  it('user.name should be an case-insensitive match for "john"', function (done) {
    request(app)
      .post('/user')
      .send('name=john') // x-www-form-urlencoded upload
      .set('Accept', 'application/json')
      .expect(function (res) {
        res.body.id = 'some fixed id';
        res.body.name = res.body.name.toLowerCase();
      })
      .expect(
        200,
        {
          id: 'some fixed id',
          name: 'john',
        },
        done,
      );
  });
});
```

Anything you can do with superagent, you can do with supertest - for example multipart file uploads!

```js
request(app)
  .post('/')
  .field('name', 'my awesome avatar')
  .field('complex_object', '{"attribute": "value"}', {contentType: 'application/json'})
  .attach('avatar', 'test/fixtures/avatar.jpg')
  ...
```

Passing the app or url each time is not necessary, if you're testing
the same host you may simply re-assign the request variable with the
initialization app or url, a new `Test` is created per `request.VERB()` call.

```js
t = request('http://localhost:5555');

t.get('/').expect(200, function (err) {
  console.log(err);
});

t.get('/').expect('heya', function (err) {
  console.log(err);
});
```

Here's an example with mocha that shows how to persist a request and its cookies:

```js
const { agent } = require('@eggjs/supertest');
const should = require('should');
const express = require('express');
const cookieParser = require('cookie-parser');

describe('request.agent(app)', function () {
  const app = express();
  app.use(cookieParser());

  app.get('/', function (req, res) {
    res.cookie('cookie', 'hey');
    res.send();
  });

  app.get('/return', function (req, res) {
    if (req.cookies.cookie) res.send(req.cookies.cookie);
    else res.send(':(');
  });

  const testAgent = agent(app);

  it('should save cookies', function (done) {
    testAgent.get('/').expect('set-cookie', 'cookie=hey; Path=/', done);
  });

  it('should send cookies', function (done) {
    testAgent.get('/return').expect('hey', done);
  });
});
```

There is another example that is introduced by the file [agency.js](https://github.com/ladjs/superagent/blob/master/test/node/agency.js)

Here is an example where 2 cookies are set on the request.

```js
agent(app)
  .get('/api/content')
  .set('Cookie', ['nameOne=valueOne;nameTwo=valueTwo'])
  .send()
  .expect(200)
  .end((err, res) => {
    if (err) {
      return done(err);
    }
    expect(res.text).to.be.equal('hey');
    return done();
  });
```

## API

You may use any [superagent](http://github.com/ladjs/superagent) methods,
including `.write()`, `.pipe()` etc and perform assertions in the `.end()` callback
for lower-level needs.

### .expect(status[, fn])

Assert response `status` code.

### .expect(status, body[, fn])

Assert response `status` code and `body`.

### .expect(body[, fn])

Assert response `body` text with a string, regular expression, or
parsed body object.

### .expect(field, value[, fn])

Assert header `field` `value` with a string or regular expression.

### .expect(function(res) {})

Pass a custom assertion function. It'll be given the response object to check. If the check fails, throw an error.

```js
request(app).get('/').expect(hasPreviousAndNextKeys).end(done);

function hasPreviousAndNextKeys(res) {
  if (!('next' in res.body)) throw new Error('missing next key');
  if (!('prev' in res.body)) throw new Error('missing prev key');
}
```

### .end(fn)

Perform the request and invoke `fn(err, res)`.

## Notes

Inspired by [api-easy](https://github.com/flatiron/api-easy) minus vows coupling.

## License

[MIT](LICENSE)

## Contributors

[](https://github.com/eggjs/egg/graphs/contributors)

Made with [contributors-img](https://contrib.rocks).

---

## File: packages/router/README.md

# @eggjs/router

[](https://npmjs.org/package/@eggjs/router)
[](https://npmjs.org/package/@eggjs/router)
[](https://snyk.io/test/npm/@eggjs/router)
[](https://nodejs.org/en/download/)
[](https://makeapullrequest.com)


Router core component for [Egg.js](https://github.com/eggjs).

> **This repository is a fork of [koa-router](https://github.com/alexmingoia/koa-router).** with some additional features.
> And thanks for the great work of @alexmingoia and the original team.

## API Reference

- [@eggjs/router](#eggjsrouter)
  - [API Reference](#api-reference)
    - [Router ⏏](#router-)
      - [new Router(\[opts\])](#new-routeropts)
      - [router.get|put|post|patch|delete|del ⇒ Router](#routergetputpostpatchdeletedel--router)
      - [Named routes](#named-routes)
      - [Multiple middleware](#multiple-middleware)
    - [Nested routers](#nested-routers)
      - [Router prefixes](#router-prefixes)
      - [URL parameters](#url-parameters)
      - [router.routes ⇒ function](#routerroutes--function)
      - [router.use(\[path\], middleware) ⇒ Router](#routerusepath-middleware--router)
      - [router.prefix(prefix) ⇒ Router](#routerprefixprefix--router)
      - [router.allowedMethods(\[options\]) ⇒ function](#routerallowedmethodsoptions--function)
      - [router.redirect(source, destination, \[code\]) ⇒ Router](#routerredirectsource-destination-code--router)
      - [router.route(name) ⇒ Layer | false](#routerroutename--layer--false)
      - [router.url(name, params, \[options\]) ⇒ String | Error](#routerurlname-params-options--string--error)
      - [router.param(param, middleware) ⇒ Router](#routerparamparam-middleware--router)
      - [Router.url(path, params \[, options\]) ⇒ String](#routerurlpath-params--options--string)
  - [Tests](#tests)
  - [Breaking changes on v3](#breaking-changes-on-v3)
  - [License](#license)
  - [Contributors](#contributors)

<a name="exp_module_egg-router--Router"></a>

### Router ⏏

**Kind**: Exported class
<a name="new_module_egg-router--Router_new"></a>

#### new Router([opts])

Create a new router.

| Param         | Type                | Description         |
| ------------- | ------------------- | ------------------- |
| [opts]        | `Object` |                     |
| [opts.prefix] | `String` | prefix router paths |

**Example**
Basic usage:

```ts
import Koa from '@eggjs/koa';
import Router from '@eggjs/router';

const app = new Koa();
const router = new Router();

router.get('/', async (ctx, next) => {
  // ctx.router available
});

app.use(router.routes()).use(router.allowedMethods());
```

<a name="module_egg-router--Router+get|put|post|patch|delete|del"></a>

#### router.get|put|post|patch|delete|del ⇒ `Router`

Create `router.verb()` methods, where _verb_ is one of the HTTP verbs such
as `router.get()` or `router.post()`.

Match URL patterns to callback functions or controller actions using `router.verb()`,
where **verb** is one of the HTTP verbs such as `router.get()` or `router.post()`.

Additionaly, `router.all()` can be used to match against all methods.

```ts
router
  .get('/', (ctx, next) => {
    ctx.body = 'Hello World!';
  })
  .post('/users', (ctx, next) => {
    // ...
  })
  .put('/users/:id', (ctx, next) => {
    // ...
  })
  .del('/users/:id', (ctx, next) => {
    // ...
  })
  .all('/users/:id', (ctx, next) => {
    // ...
  });
```

When a route is matched, its path is available at `ctx.routePath` and if named,
the name is available at `ctx.routeName`

Route paths will be translated to regular expressions using
[path-to-regexp](https://github.com/pillarjs/path-to-regexp).

Query strings will not be considered when matching requests.

#### Named routes

Routes can optionally have names. This allows generation of URLs and easy
renaming of URLs during development.

```ts
router.get('user', '/users/:id', (ctx, next) => {
  // ...
});

router.url('user', 3);
// => "/users/3"
```

#### Multiple middleware

Multiple middleware may be given:

```ts
router.get(
  '/users/:id',
  (ctx, next) => {
    return User.findOne(ctx.params.id).then(function (user) {
      ctx.user = user;
      next();
    });
  },
  (ctx) => {
    console.log(ctx.user);
    // => { id: 17, name: "Alex" }
  },
);
```

### Nested routers

Nesting routers is supported:

```ts
const forums = new Router();
const posts = new Router();

posts.get('/', (ctx, next) => {...});
posts.get('/:pid', (ctx, next) => {...});
forums.use('/forums/:fid/posts', posts.routes(), posts.allowedMethods());

// responds to "/forums/123/posts" and "/forums/123/posts/123"
app.use(forums.routes());
```

#### Router prefixes

Route paths can be prefixed at the router level:

```ts
const router = new Router({
  prefix: '/users'
});

router.get('/', ...); // responds to "/users"
router.get('/:id', ...); // responds to "/users/:id"
```

#### URL parameters

Named route parameters are captured and added to `ctx.params`.

```ts
router.get('/:category/:title', (ctx, next) => {
  console.log(ctx.params);
  // => { category: 'programming', title: 'how-to-node' }
});
```

The [path-to-regexp](https://github.com/pillarjs/path-to-regexp) module is
used to convert paths to regular expressions.

**Kind**: instance property of `[Router](#exp_module_egg-router--Router)`

| Param        | Type                  | Description         |
| ------------ | --------------------- | ------------------- |
| path         | `String`   |                     |
| [middleware] | `function` | route middleware(s) |
| callback     | `function` | route callback      |

<a name="module_egg-router--Router+routes"></a>

#### router.routes ⇒ `function`

Returns router middleware which dispatches a route matching the request.

**Kind**: instance property of `[Router](#exp_module_egg-router--Router)`
<a name="module_egg-router--Router+use"></a>

#### router.use([path], middleware) ⇒ `Router`

Use given middleware.

Middleware run in the order they are defined by `.use()`. They are invoked
sequentially, requests start at the first middleware and work their way
"down" the middleware stack.

**Kind**: instance method of `[Router](#exp_module_egg-router--Router)`

| Param      | Type                  |
| ---------- | --------------------- |
| [path]     | `String`   |
| middleware | `function` |
| [...]      | `function` |

**Example**

```ts
// session middleware will run before authorize
router.use(session()).use(authorize());

// use middleware only with given path
router.use('/users', userAuth());

// or with an array of paths
router.use(['/users', '/admin'], userAuth());

app.use(router.routes());
```

<a name="module_egg-router--Router+prefix"></a>

#### router.prefix(prefix) ⇒ `Router`

Set the path prefix for a Router instance that was already initialized.

**Kind**: instance method of `[Router](#exp_module_egg-router--Router)`

| Param  | Type                |
| ------ | ------------------- |
| prefix | `String` |

**Example**

```ts
router.prefix('/things/:thing_id');
```

<a name="module_egg-router--Router+allowedMethods"></a>

#### router.allowedMethods([options]) ⇒ `function`

Returns separate middleware for responding to `OPTIONS` requests with
an `Allow` header containing the allowed methods, as well as responding
with `405 Method Not Allowed` and `501 Not Implemented` as appropriate.

**Kind**: instance method of `[Router](#exp_module_egg-router--Router)`

| Param                      | Type                  | Description                                                             |
| -------------------------- | --------------------- | ----------------------------------------------------------------------- |
| [options]                  | `Object`   |                                                                         |
| [options.throw]            | `Boolean`  | throw error instead of setting status and header                        |
| [options.notImplemented]   | `function` | throw the returned value in place of the default NotImplemented error   |
| [options.methodNotAllowed] | `function` | throw the returned value in place of the default MethodNotAllowed error |

**Example**

```ts
import Koa from '@eggjs/koa';
import Router from '@eggjs/router';

const app = new Koa();
const router = new Router();

app.use(router.routes());
app.use(router.allowedMethods());
```

**Example with [Boom](https://github.com/hapijs/boom)**

```ts
import Koa from '@eggjs/koa';
import Router from '@eggjs/router';
import Boom from 'boom';

const app = new Koa();
const router = new Router();

app.use(router.routes());
app.use(
  router.allowedMethods({
    throw: true,
    notImplemented: () => new Boom.notImplemented(),
    methodNotAllowed: () => new Boom.methodNotAllowed(),
  }),
);
```

<a name="module_egg-router--Router+redirect"></a>

#### router.redirect(source, destination, [code]) ⇒ `Router`

Redirect `source` to `destination` URL with optional 30x status `code`.

Both `source` and `destination` can be route names.

```javascript
router.redirect('/login', 'sign-in');
```

This is equivalent to:

```ts
router.all('/login', (ctx) => {
  ctx.redirect('/sign-in');
  ctx.status = 301;
});
```

**Kind**: instance method of `[Router](#exp_module_egg-router--Router)`

| Param       | Type                | Description                      |
| ----------- | ------------------- | -------------------------------- |
| source      | `String` | URL or route name.               |
| destination | `String` | URL or route name.               |
| [code]      | `Number` | HTTP status code (default: 301). |

<a name="module_egg-router--Router+route"></a>

#### router.route(name) ⇒ `Layer` &#124; `false`

Lookup route with given `name`.

**Kind**: instance method of `[Router](#exp_module_egg-router--Router)`

| Param | Type                |
| ----- | ------------------- |
| name  | `String` |

<a name="module_egg-router--Router+url"></a>

#### router.url(name, params, [options]) ⇒ `String` &#124; `Error`

Generate URL for route. Takes a route name and map of named `params`.

**Kind**: instance method of `[Router](#exp_module_egg-router--Router)`

| Param           | Type                                           | Description       |
| --------------- | ---------------------------------------------- | ----------------- |
| name            | `String`                            | route name        |
| params          | `Object`                            | url parameters    |
| [options]       | `Object`                            | options parameter |
| [options.query] | `Object` &#124; `String` | query options     |

**Example**

```ts
router.get('user', '/users/:id', (ctx, next) => {
  // ...
});

router.url('user', 3);
// => "/users/3"

router.url('user', { id: 3 });
// => "/users/3"

router.use((ctx, next) => {
  // redirect to named route
  ctx.redirect(ctx.router.url('sign-in'));
});

router.url('user', { id: 3 }, { query: { limit: 1 } });
// => "/users/3?limit=1"

router.url('user', { id: 3 }, { query: 'limit=1' });
// => "/users/3?limit=1"
```

<a name="module_egg-router--Router+param"></a>

#### router.param(param, middleware) ⇒ `Router`

Run middleware for named route parameters. Useful for auto-loading or
validation.

**Kind**: instance method of `[Router](#exp_module_egg-router--Router)`

| Param      | Type                  |
| ---------- | --------------------- |
| param      | `String`   |
| middleware | `function` |

**Example**

```ts
router
  .param('user', (id, ctx, next) => {
    ctx.user = users[id];
    if (!ctx.user) return (ctx.status = 404);
    return next();
  })
  .get('/users/:user', (ctx) => {
    ctx.body = ctx.user;
  })
  .get('/users/:user/friends', (ctx) => {
    return ctx.user.getFriends().then(function (friends) {
      ctx.body = friends;
    });
  });
// /users/3 => {"id": 3, "name": "Alex"}
// /users/3/friends => [{"id": 4, "name": "TJ"}]
```

<a name="module_egg-router--Router.url"></a>

#### Router.url(path, params [, options]) ⇒ `String`

Generate URL from url pattern and given `params`.

**Kind**: static method of `[Router](#exp_module_egg-router--Router)`

| Param           | Type                                           | Description       |
| --------------- | ---------------------------------------------- | ----------------- |
| path            | `String`                            | url pattern       |
| params          | `Object`                            | url parameters    |
| [options]       | `Object`                            | options parameter |
| [options.query] | `Object` &#124; `String` | query options     |

**Example**

```ts
const url = Router.url('/users/:id', { id: 1 });
// => "/users/1"

const url = Router.url('/users/:id', { id: 1 }, { query: { active: true } });
// => "/users/1?active=true"
```

## Tests

Run tests using `npm test`.

## Breaking changes on v3

- Drop generator function support
- Drop Node.js < 18.19.0 support

## License

[MIT](LICENSE)

## Contributors

[](https://github.com/eggjs/egg/graphs/contributors)

Made with [contributors-img](https://contrib.rocks).

---

## File: packages/path-matching/README.md

# @eggjs/path-matching

[![NPM version][npm-image]][npm-url]
[![Known Vulnerabilities][snyk-image]][snyk-url]
[![npm download][download-image]][download-url]

[npm-image]: https://img.shields.io/npm/v/@eggjs/path-matching.svg?style=flat-square
[npm-url]: https://npmjs.org/package/@eggjs/path-matching
[snyk-image]: https://snyk.io/test/npm/@eggjs/path-matching/badge.svg?style=flat-square
[snyk-url]: https://snyk.io/test/npm/@eggjs/path-matching/badge.svg?style=flat-square
[download-image]: https://img.shields.io/npm/dm/@eggjs/path-matching.svg?style=flat-square
[download-url]: https://npmjs.org/package/@eggjs/path-matching

## Installation

```bash
npm install @eggjs/path-matching
```

## Usage

```ts
import { pathMatching } from '@eggjs/path-matching';

const options = {
  ignore: '/api', // string will use parsed by path-to-regexp
  // support regexp
  ignore: /^\/api/,
  // support function
  ignore: (ctx) => ctx.path.startsWith('/api'),
  // support Array
  ignore: [(ctx) => ctx.path.startsWith('/api'), /^\/foo$/, '/bar'],
  // support match or ignore
  match: '/api',
  // custom path-to-regexp module, default is `path-to-regexp@6`
  // pathToRegexpModule: customPathToRegexp,
};

const match = pathMatching(options);
assert.equal(match({ path: '/api' }), true);
assert.equal(match({ path: '/api/hello' }), true);
assert.equal(match({ path: '/api' }), true);
```

### options

- `match` {String | RegExp | Function | Array} - if request path hit `options.match`, will return `true`, otherwise will return `false`.
- `ignore` {String | RegExp | Function | Array} - if request path hit `options.ignore`, will return `false`, otherwise will return `true`.
- `pathToRegexpModule` {Object | Function} - custom path-to-regexp module. Default is `path-to-regexp@6`. Supports both `path-to-regexp@6` and `path-to-regexp@8+` formats.

`ignore` and `match` can not both be presented.
and if neither `ignore` nor `match` presented, the new function will always return `true`.

### License

[MIT](LICENSE)

## Contributors

[](https://github.com/eggjs/egg/graphs/contributors)

Made with [contributors-img](https://contrib.rocks).

---

## File: packages/loader-fs/README.md

# @eggjs/loader-fs

Loader-facing filesystem abstraction for Egg loaders and bundled runtimes.

## Usage

```ts
import { RealLoaderFS, type LoaderFS } from '@eggjs/loader-fs';

const loaderFS: LoaderFS = new RealLoaderFS();
```

`LoaderFS` intentionally covers only the operations required by loader code:
`exists`, `stat`, `realpath`, `readJSON`, `glob`, and `loadFile`. A source with
an authoritative, precomputed directory view may also implement
`getKnownFiles`; `undefined` means discovery should fall back to `glob`, while
an empty array means the directory is authoritatively empty.

---

## File: packages/koa-static-cache/README.md

# eggjs/koa-static-cache

[![NPM version][npm-image]][npm-url]
[![Known Vulnerabilities][snyk-image]][snyk-url]
[![npm download][download-image]][download-url]
[](https://nodejs.org/en/download/)
[](https://makeapullrequest.com)

[npm-image]: https://img.shields.io/npm/v/@eggjs/koa-static-cache.svg?style=flat-square
[npm-url]: https://npmjs.org/package/@eggjs/koa-static-cache
[snyk-image]: https://snyk.io/test/npm/@eggjs/koa-static-cache/badge.svg?style=flat-square
[snyk-url]: https://snyk.io/test/npm/@eggjs/koa-static-cache
[download-image]: https://img.shields.io/npm/dm/@eggjs/koa-static-cache.svg?style=flat-square
[download-url]: https://npmjs.org/package/@eggjs/koa-static-cache

Static cache middleware for koa.

Differences between this library and other libraries such as [static](https://github.com/koajs/static):

- There is no directory or `index.html` support.
- You may optionally store the data in memory - it streams by default.
- Caches the assets on initialization - you need to restart the process to update the assets.(can turn off with options.preload = false)
- Uses MD5 hash sum as an ETag.
- Uses `.gz` files if present on disk, like nginx gzip_static module

> Forked from https://github.com/koajs/static-cache, refactor with TypeScript to support CommonJS and ESM both.

## Installation

```bash
npm install @eggjs/koa-static-cache
```

## API

### staticCache([options])

```js
const path = require('path');
const { staticCache } = require('@eggjs/koa-static-cache');

app.use(
  staticCache(path.join(__dirname, 'public'), {
    maxAge: 365 * 24 * 60 * 60,
  }),
);
```

- `options.dir` (str) - the directory you wish to serve, default to `process.cwd`.
- `options.maxAge` (int) - cache control max age for the files, `0` by default.
- `options.cacheControl` (str) - optional cache control header. Overrides `options.maxAge`.
- `options.buffer` (bool) - store the files in memory instead of streaming from the filesystem on each request.
- `options.gzip` (bool) - when request's accept-encoding include gzip, files will compressed by gzip.
- `options.usePrecompiledGzip` (bool) - try use gzip files, loaded from disk, like nginx gzip_static
- `options.alias` (obj) - object map of aliases. See below.
- `options.prefix` (str) - the url prefix you wish to add, default to `''`.
- `options.dynamic` (bool) - dynamic load file which not cached on initialization.
- `options.filter` (function | array) - filter files at init dir, for example - skip non build (source) files. If array set - allow only listed files
- `options.preload` (bool) - caches the assets on initialization or not, default to `true`. always work together with `options.dynamic`.
- `options.files` (obj) - optional files object. See below.

### Aliases

For example, if you have this `alias` object:

```js
const options = {
  alias: {
    '/favicon.png': '/favicon-32.png',
  },
};
```

Then requests to `/favicon.png` will actually return `/favicon-32.png` without redirects or anything.
This is particularly important when serving [favicons](https://github.com/audreyr/favicon-cheat-sheet) as you don't want to store duplicate images.

### Files

You can pass in an optional files object.
This allows you to do two things:

#### Combining directories into a single middleware

Instead of doing:

```js
app.use(staticCache('/public/js'));
app.use(staticCache('/public/css'));
```

You can do this:

```js
const files = {};

// Mount the middleware
app.use(staticCache('/public/js', {}, files));

// Add additional files
staticCache('/public/css', {}, files);
```

The benefit is that you'll have one less function added to the stack as well as doing one hash lookup instead of two.

#### Editing the files object

For example, if you want to change the max age of `/package.json`, you can do the following:

```js
const files = {};

app.use(
  staticCache(
    '/public',
    {
      maxAge: 60 * 60 * 24 * 365,
    },
    files,
  ),
);

files['/package.json'].maxAge = 60 * 60 * 24 * 30;
```

#### Using a LRU cache to avoid OOM when dynamic mode enabled

You can pass in a lru cache instance which has tow methods: `get(key)` and `set(key, value)`.

```js
const LRU = require('lru-cache');
const files = new LRU({ max: 1000 });

app.use(
  staticCache({
    dir: '/public',
    dynamic: true,
    files,
  }),
);
```

## License

[MIT](LICENSE)

## Contributors

[](https://github.com/eggjs/egg/graphs/contributors)

Made with [contributors-img](https://contrib.rocks).