Skip to content

Commit

Permalink
fix: remove delegates from Context (#15)
Browse files Browse the repository at this point in the history
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

## Release Notes

- **Documentation**
- Updated documentation for Koa `Response` object, adding method to
append headers

- **Dependencies**
  - Removed `delegates` dependency
  - Added `@eggjs/bin`
  - Updated `@types/node` and `mm` dependency versions

- **Type Improvements**
- Enhanced type safety across `Application`, `Context`, `Request`, and
`Response` classes
  - Introduced more flexible generic type handling

- **Code Refactoring**
  - Removed delegation mechanism in `Context` class
- Added direct method and property definitions for request and response
handling

- **Testing**
- Added new test cases for context subclassing and application behavior

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
  • Loading branch information
fengmk2 authored Jan 2, 2025
1 parent 54e8826 commit a3bdefe
Show file tree
Hide file tree
Showing 10 changed files with 340 additions and 117 deletions.
2 changes: 1 addition & 1 deletion docs/api/response.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@

Response header object. Alias as `response.header`.


### response.socket

Response socket. Points to net.Socket instance as `request.socket`.
Expand Down Expand Up @@ -214,6 +213,7 @@ ctx.set('Cache-Control', 'no-cache');
```

### response.append(field, value)

Append additional header `field` with value `val`.

```js
Expand Down
6 changes: 2 additions & 4 deletions example/extend/middleware.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { MiddlewareFunc, Context, type ContextDelegation } from '../../src/index.js';
import { MiddlewareFunc, Context } from '../../src/index.js';

class CustomContext extends Context {
// Add your custom properties and methods here
Expand All @@ -7,9 +7,7 @@ class CustomContext extends Context {
}
}

type ICustomContext = CustomContext & ContextDelegation;

export const middleware: MiddlewareFunc<ICustomContext> = async (ctx, next) => {
export const middleware: MiddlewareFunc<CustomContext> = async (ctx, next) => {
console.log('middleware start, %s', ctx.hello, ctx.writable);
await next();
console.log('middleware end');
Expand Down
15 changes: 8 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@
},
"description": "Koa web app framework for https://eggjs.org",
"scripts": {
"test": "npm run lint -- --fix && egg-bin test",
"ci": "npm run lint && egg-bin cov && npm run prepublishOnly && attw --pack",
"pretest": "npm run lint -- --fix",
"test": "egg-bin test",
"preci": "npm run lint",
"ci": "egg-bin cov",
"postci": "npm run prepublishOnly && attw --pack",
"lint": "eslint src test --cache",
"authors": "git log --format='%aN <%aE>' | sort -u > AUTHORS",
"prepublishOnly": "tshy && tshy-after"
Expand All @@ -35,7 +38,6 @@
"content-disposition": "~0.5.4",
"content-type": "^1.0.5",
"cookies": "^0.9.1",
"delegates": "^1.0.0",
"destroy": "^1.0.4",
"encodeurl": "^1.0.2",
"escape-html": "^1.0.3",
Expand All @@ -53,12 +55,12 @@
},
"devDependencies": {
"@arethetypeswrong/cli": "^0.17.1",
"@eggjs/bin": "^7.0.1",
"@eggjs/tsconfig": "^1.3.3",
"@types/accepts": "^1.3.7",
"@types/content-disposition": "^0.5.8",
"@types/content-type": "^1.1.8",
"@types/cookies": "^0.9.0",
"@types/delegates": "^1.0.3",
"@types/destroy": "^1.0.3",
"@types/encodeurl": "^1.0.2",
"@types/escape-html": "^1.0.4",
Expand All @@ -67,17 +69,16 @@
"@types/http-errors": "^2.0.4",
"@types/koa-compose": "^3.2.8",
"@types/mocha": "^10.0.1",
"@types/node": "^20.2.5",
"@types/node": "22",
"@types/on-finished": "^2.3.4",
"@types/parseurl": "^1.3.3",
"@types/statuses": "^2.0.5",
"@types/supertest": "^6.0.2",
"@types/type-is": "^1.6.6",
"@types/vary": "^1.1.3",
"egg-bin": "^6.4.0",
"eslint": "^8.41.0",
"eslint-config-egg": "14",
"mm": "^3.3.0",
"mm": "^4.0.1",
"supertest": "^3.1.0",
"tsd": "^0.31.0",
"tshy": "3",
Expand Down
32 changes: 15 additions & 17 deletions src/application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import onFinished from 'on-finished';
import statuses from 'statuses';
import compose from 'koa-compose';
import { HttpError } from 'http-errors';
import { Context, type ContextDelegation } from './context.js';
import { Context } from './context.js';
import { Request } from './request.js';
import { Response } from './response.js';
import type { CustomError, AnyProto } from './types.js';
Expand All @@ -21,13 +21,13 @@ const debug = debuglog('@eggjs/koa/application');
export type ProtoImplClass<T = object> = new(...args: any[]) => T;
export type Next = () => Promise<void>;
type _MiddlewareFunc<T> = (ctx: T, next: Next) => Promise<void> | void;
export type MiddlewareFunc<T = ContextDelegation> = _MiddlewareFunc<T> & { _name?: string };
export type MiddlewareFunc<T extends Context = Context> = _MiddlewareFunc<T> & { _name?: string };

/**
* Expose `Application` class.
* Inherits from `Emitter.prototype`.
*/
export class Application extends Emitter {
export class Application<T extends Context = Context> extends Emitter {
[key: symbol]: unknown;
/**
* Make HttpError available to consumers of the library so that consumers don't
Expand All @@ -41,14 +41,14 @@ export class Application extends Emitter {
proxyIpHeader: string;
maxIpsCount: number;
protected _keys?: string[];
middleware: MiddlewareFunc[];
ctxStorage: AsyncLocalStorage<ContextDelegation>;
middleware: MiddlewareFunc<T>[];
ctxStorage: AsyncLocalStorage<T>;
silent: boolean;
ContextClass: ProtoImplClass<ContextDelegation>;
ContextClass: ProtoImplClass<T>;
context: AnyProto;
RequestClass: ProtoImplClass<Request>;
RequestClass: ProtoImplClass<Request<T>>;
request: AnyProto;
ResponseClass: ProtoImplClass<Response>;
ResponseClass: ProtoImplClass<Response<T>>;
response: AnyProto;

/**
Expand Down Expand Up @@ -84,11 +84,11 @@ export class Application extends Emitter {
this.middleware = [];
this.ctxStorage = getAsyncLocalStorage();
this.silent = false;
this.ContextClass = class ApplicationContext extends Context {} as any;
this.ContextClass = class ApplicationContext extends Context {} as ProtoImplClass<T>;
this.context = this.ContextClass.prototype;
this.RequestClass = class ApplicationRequest extends Request {};
this.RequestClass = class ApplicationRequest extends Request {} as ProtoImplClass<Request<T>>;
this.request = this.RequestClass.prototype;
this.ResponseClass = class ApplicationResponse extends Response {};
this.ResponseClass = class ApplicationResponse extends Response {} as ProtoImplClass<Response<T>>;
this.response = this.ResponseClass.prototype;
}

Expand Down Expand Up @@ -151,7 +151,7 @@ export class Application extends Emitter {
/**
* Use the given middleware `fn`.
*/
use(fn: MiddlewareFunc) {
use(fn: MiddlewareFunc<T>) {
if (typeof fn !== 'function') throw new TypeError('middleware must be a function!');
const name = fn._name || fn.name || '-';
if (isGeneratorFunction(fn)) {
Expand Down Expand Up @@ -196,7 +196,7 @@ export class Application extends Emitter {
* Handle request in callback.
* @private
*/
protected async handleRequest(ctx: ContextDelegation, fnMiddleware: (ctx: ContextDelegation) => Promise<void>) {
protected async handleRequest(ctx: T, fnMiddleware: (ctx: T) => Promise<void>) {
this.emit('request', ctx);
const res = ctx.res;
res.statusCode = 404;
Expand All @@ -219,7 +219,7 @@ export class Application extends Emitter {
* Initialize a new context.
* @private
*/
protected createContext(req: IncomingMessage, res: ServerResponse) {
createContext(req: IncomingMessage, res: ServerResponse) {
const context = new this.ContextClass(this, req, res);
return context;
}
Expand All @@ -246,7 +246,7 @@ export class Application extends Emitter {
/**
* Response helper.
*/
protected _respond(ctx: ContextDelegation) {
protected _respond(ctx: T) {
// allow bypassing koa
if (ctx.respond === false) return;

Expand Down Expand Up @@ -303,5 +303,3 @@ export class Application extends Emitter {
res.end(body);
}
}

export default Application;
Loading

0 comments on commit a3bdefe

Please sign in to comment.