@midwayjs/koa 4.0.0-beta.1 → 4.0.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -9,4 +9,4 @@ Document: [https://midwayjs.org](https://midwayjs.org)
9
9
 
10
10
  ## License
11
11
 
12
- [MIT]((https://github.com/midwayjs/midway/blob/master/LICENSE))
12
+ [MIT](https://github.com/midwayjs/midway/blob/master/LICENSE)
@@ -26,5 +26,7 @@ export declare class MidwayKoaFramework extends BaseFramework<IMidwayKoaApplicat
26
26
  getPort(): string;
27
27
  useMiddleware(Middleware: CommonMiddlewareUnion<IMidwayKoaContext, Next, unknown>): void;
28
28
  useFilter(Filter: CommonFilterUnion<IMidwayKoaContext, Next, unknown>): void;
29
+ private createVersioningMiddleware;
30
+ private extractVersion;
29
31
  }
30
32
  //# sourceMappingURL=framework.d.ts.map
package/dist/framework.js CHANGED
@@ -14,6 +14,7 @@ const koa = require("koa");
14
14
  const onerror_1 = require("./onerror");
15
15
  const qs = require("qs");
16
16
  const querystring = require("querystring");
17
+ const utils_1 = require("./utils");
17
18
  const COOKIES = Symbol('context#cookies');
18
19
  class KoaControllerGenerator extends core_1.WebControllerGenerator {
19
20
  constructor(app, webRouterService) {
@@ -136,10 +137,16 @@ let MidwayKoaFramework = class MidwayKoaFramework extends core_1.BaseFramework {
136
137
  throw new core_1.httpError.NotFoundError(`${ctx.path} Not Found`);
137
138
  }
138
139
  };
140
+ const applyMiddlewares = [notFound];
141
+ // versioning middleware
142
+ const versioningConfig = this.configurationOptions.versioning;
143
+ if (versioningConfig?.enabled) {
144
+ applyMiddlewares.push(this.createVersioningMiddleware(versioningConfig));
145
+ }
139
146
  // root middleware
140
147
  const midwayRootMiddleware = async (ctx, next) => {
141
148
  this.app.createAnonymousContext(ctx);
142
- await (await this.applyMiddleware(notFound))(ctx, next);
149
+ await (await this.applyMiddleware(applyMiddlewares))(ctx, next);
143
150
  if (ctx.body === undefined &&
144
151
  !ctx.response._explicitStatus &&
145
152
  ctx._matchedRoute) {
@@ -216,27 +223,38 @@ let MidwayKoaFramework = class MidwayKoaFramework extends core_1.BaseFramework {
216
223
  if (core_1.Types.isNumber(this.configurationOptions.serverTimeout)) {
217
224
  this.server.setTimeout(this.configurationOptions.serverTimeout);
218
225
  }
226
+ this.configurationOptions.listenOptions = {
227
+ port: this.configurationOptions.port,
228
+ host: this.configurationOptions.hostname,
229
+ ...this.configurationOptions.listenOptions,
230
+ };
219
231
  // set port and listen server
220
- const customPort = process.env.MIDWAY_HTTP_PORT ?? this.configurationOptions.port;
221
- if (customPort) {
232
+ let customPort = process.env.MIDWAY_HTTP_PORT ||
233
+ this.configurationOptions.listenOptions.port;
234
+ if (customPort === 0 || customPort === '0') {
235
+ customPort = await (0, utils_1.getFreePort)();
236
+ this.logger.info(`[midway:koa] server has auto-assigned port ${customPort}`);
237
+ }
238
+ this.configurationOptions.listenOptions.port = Number(customPort);
239
+ if (this.configurationOptions.listenOptions.port) {
222
240
  new Promise(resolve => {
223
- const args = [customPort];
224
- if (this.configurationOptions.hostname) {
225
- args.push(this.configurationOptions.hostname);
226
- }
227
- args.push(() => {
241
+ // 使用 ListenOptions 对象启动服务器
242
+ this.server.listen(this.configurationOptions.listenOptions, () => {
228
243
  resolve();
229
244
  });
230
- this.server.listen(...args);
231
- process.env.MIDWAY_HTTP_PORT = String(customPort);
245
+ // 设置环境变量
246
+ process.env.MIDWAY_HTTP_PORT = String(this.configurationOptions.listenOptions.port);
232
247
  });
248
+ this.logger.debug(`[midway:koa] server is listening on port ${customPort}`);
233
249
  }
234
250
  }
235
251
  async beforeStop() {
236
252
  if (this.server) {
237
253
  new Promise(resolve => {
238
254
  this.server.close(resolve);
255
+ process.env.MIDWAY_HTTP_PORT = '';
239
256
  });
257
+ this.logger.debug('[midway:koa] server is stopped!');
240
258
  }
241
259
  }
242
260
  getFrameworkName() {
@@ -254,6 +272,52 @@ let MidwayKoaFramework = class MidwayKoaFramework extends core_1.BaseFramework {
254
272
  useFilter(Filter) {
255
273
  this.filterManager.useFilter(Filter);
256
274
  }
275
+ createVersioningMiddleware(config) {
276
+ return async (ctx, next) => {
277
+ // 提取版本信息
278
+ const version = this.extractVersion(ctx, config);
279
+ ctx.apiVersion = version;
280
+ // 对于 URI 版本控制,重写路径
281
+ if (config.type === 'URI' && version) {
282
+ const versionPrefix = `/${config.prefix || 'v'}${version}`;
283
+ if (ctx.path.startsWith(versionPrefix)) {
284
+ ctx.originalPath = ctx.path;
285
+ ctx.path = ctx.path.replace(versionPrefix, '') || '/';
286
+ }
287
+ }
288
+ await next();
289
+ };
290
+ }
291
+ extractVersion(ctx, config) {
292
+ // 自定义提取函数优先
293
+ if (config.extractVersionFn) {
294
+ return config.extractVersionFn(ctx);
295
+ }
296
+ const type = config.type || 'URI';
297
+ switch (type) {
298
+ case 'HEADER': {
299
+ const headerName = config.header || 'x-api-version';
300
+ const headerValue = ctx.headers[headerName];
301
+ if (typeof headerValue === 'string') {
302
+ return headerValue.replace(/^v/, '');
303
+ }
304
+ return undefined;
305
+ }
306
+ case 'MEDIA_TYPE': {
307
+ const accept = ctx.headers.accept;
308
+ const paramName = config.mediaTypeParam || 'version';
309
+ const match = accept?.match(new RegExp(`${paramName}=(\\d+)`));
310
+ return match ? match[1] : undefined;
311
+ }
312
+ case 'URI': {
313
+ const prefix = config.prefix || 'v';
314
+ const uriMatch = ctx.path.match(new RegExp(`^/${prefix}(\\d+)`));
315
+ return uriMatch ? uriMatch[1] : undefined;
316
+ }
317
+ default:
318
+ return config.defaultVersion;
319
+ }
320
+ }
257
321
  };
258
322
  exports.MidwayKoaFramework = MidwayKoaFramework;
259
323
  exports.MidwayKoaFramework = MidwayKoaFramework = __decorate([
@@ -1,10 +1,12 @@
1
1
  /// <reference types="node" />
2
2
  /// <reference types="node" />
3
+ /// <reference types="node" />
3
4
  import { IConfigurationOptions, IMidwayApplication, IMidwayContext } from '@midwayjs/core';
4
5
  import * as koa from 'koa';
5
6
  import { Context as KoaContext, DefaultState, Middleware, Next } from 'koa';
6
7
  import { RouterParamValue } from '@midwayjs/core';
7
8
  import * as qs from 'qs';
9
+ import { ListenOptions } from 'net';
8
10
  export interface State extends DefaultState {
9
11
  }
10
12
  export type IMidwayKoaContext = IMidwayContext<KoaContext>;
@@ -15,6 +17,10 @@ export type IMidwayKoaApplication = IMidwayApplication<IMidwayKoaContext, koa<St
15
17
  * @param middlewareId
16
18
  */
17
19
  generateMiddleware(middlewareId: any): Promise<Middleware<State, IMidwayKoaContext>>;
20
+ /**
21
+ * Get the port that the application is listening on
22
+ */
23
+ getPort(): string;
18
24
  }>;
19
25
  /**
20
26
  * @deprecated use NextFunction definition
@@ -86,7 +92,47 @@ export interface IMidwayKoaConfigurationOptions extends IConfigurationOptions {
86
92
  * qs options
87
93
  */
88
94
  queryParseOptions?: qs.IParseOptions;
95
+ /**
96
+ * https/https/http2 server options
97
+ */
89
98
  serverOptions?: Record<string, any>;
99
+ /**
100
+ * listen options
101
+ */
102
+ listenOptions?: ListenOptions;
103
+ /**
104
+ * 版本控制配置
105
+ */
106
+ versioning?: {
107
+ /**
108
+ * 是否启用版本控制
109
+ */
110
+ enabled?: boolean;
111
+ /**
112
+ * 默认版本控制类型
113
+ */
114
+ type?: 'URI' | 'HEADER' | 'MEDIA_TYPE' | 'CUSTOM';
115
+ /**
116
+ * 默认版本
117
+ */
118
+ defaultVersion?: string;
119
+ /**
120
+ * URI 版本前缀,默认为 'v'
121
+ */
122
+ prefix?: string;
123
+ /**
124
+ * Header 版本控制时的 header 名称
125
+ */
126
+ header?: string;
127
+ /**
128
+ * Media Type 版本控制时的参数名
129
+ */
130
+ mediaTypeParam?: string;
131
+ /**
132
+ * 自定义版本提取函数
133
+ */
134
+ extractVersionFn?: (ctx: IMidwayKoaContext) => string | undefined;
135
+ };
90
136
  }
91
137
  export type MiddlewareParamArray = Array<Middleware<DefaultState, IMidwayKoaContext>>;
92
138
  export interface IWebMiddleware {
@@ -95,6 +141,8 @@ export interface IWebMiddleware {
95
141
  export type Application = IMidwayKoaApplication;
96
142
  export interface Context extends IMidwayKoaContext {
97
143
  state: State;
144
+ apiVersion?: string;
145
+ originalPath?: string;
98
146
  }
99
147
  export interface BodyParserOptions {
100
148
  enable?: boolean;
package/dist/utils.d.ts CHANGED
@@ -11,4 +11,5 @@ export declare const tpl = "\n<!DOCTYPE html>\n<html>\n <head>\n <title>Erro
11
11
  * @public
12
12
  */
13
13
  export declare function escapeHtml(string: any): string;
14
+ export declare function getFreePort(): Promise<number>;
14
15
  //# sourceMappingURL=utils.d.ts.map
package/dist/utils.js CHANGED
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.escapeHtml = exports.tpl = exports.isProduction = exports.sendToWormhole = exports.accepts = exports.detectStatus = void 0;
3
+ exports.getFreePort = exports.escapeHtml = exports.tpl = exports.isProduction = exports.sendToWormhole = exports.accepts = exports.detectStatus = void 0;
4
+ const net_1 = require("net");
4
5
  function detectStatus(err) {
5
6
  // detect status
6
7
  let status = err.status || 500;
@@ -153,4 +154,20 @@ function escapeHtml(string) {
153
154
  return lastIndex !== index ? html + str.substring(lastIndex, index) : html;
154
155
  }
155
156
  exports.escapeHtml = escapeHtml;
157
+ async function getFreePort() {
158
+ return new Promise((resolve, reject) => {
159
+ const server = (0, net_1.createServer)();
160
+ server.listen(0, () => {
161
+ try {
162
+ const port = server.address().port;
163
+ server.close();
164
+ resolve(port);
165
+ }
166
+ catch (err) {
167
+ reject(err);
168
+ }
169
+ });
170
+ });
171
+ }
172
+ exports.getFreePort = getFreePort;
156
173
  //# sourceMappingURL=utils.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@midwayjs/koa",
3
- "version": "4.0.0-beta.1",
3
+ "version": "4.0.0-beta.3",
4
4
  "description": "Midway Web Framework for KOA",
5
5
  "main": "dist/index.js",
6
6
  "typings": "index.d.ts",
@@ -24,18 +24,19 @@
24
24
  ],
25
25
  "license": "MIT",
26
26
  "devDependencies": {
27
- "@midwayjs/core": "^4.0.0-beta.1",
28
- "@midwayjs/mock": "^4.0.0-beta.1",
27
+ "@midwayjs/core": "^4.0.0-beta.3",
28
+ "@midwayjs/mock": "^4.0.0-beta.3",
29
29
  "@types/koa-router": "7.4.8",
30
+ "axios": "1.12.0",
30
31
  "fs-extra": "11.3.0"
31
32
  },
32
33
  "dependencies": {
33
34
  "@koa/router": "^12.0.0",
34
35
  "@midwayjs/cookies": "^1.3.0",
35
- "@midwayjs/session": "^4.0.0-beta.1",
36
- "@types/koa": "2.15.0",
36
+ "@midwayjs/session": "^4.0.0-beta.3",
37
+ "@types/koa": "3.0.0",
37
38
  "@types/qs": "6.9.18",
38
- "koa": "2.16.1",
39
+ "koa": "3.0.1",
39
40
  "koa-bodyparser": "4.4.1",
40
41
  "qs": "6.14.0"
41
42
  },
@@ -45,7 +46,7 @@
45
46
  "url": "https://github.com/midwayjs/midway.git"
46
47
  },
47
48
  "engines": {
48
- "node": ">=12"
49
+ "node": ">=20"
49
50
  },
50
- "gitHead": "832961ec3aff123c033197d8c00cb2bc9bad7ff8"
51
+ "gitHead": "b5b7dfaafb81823cdba5fd10ad2709b3e495c6b4"
51
52
  }