@midwayjs/swagger 4.0.0-beta.8 → 4.0.0

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.
@@ -14,5 +14,6 @@ exports.swagger = {
14
14
  : render_1.renderSwaggerUIRemote,
15
15
  swaggerUIRenderOptions: {},
16
16
  isGenerateTagForController: true,
17
+ useValidationSchema: true,
17
18
  };
18
19
  //# sourceMappingURL=config.default.js.map
@@ -381,6 +381,12 @@ export interface SwaggerOptions {
381
381
  * Weather to generate the Tag for controller
382
382
  */
383
383
  isGenerateTagForController?: boolean;
384
+ /**
385
+ * Enable inferring schema metadata from @midwayjs/validation DTO rules.
386
+ * When enabled, inferred metadata only fills fields that are not explicitly
387
+ * defined by @ApiProperty.
388
+ */
389
+ useValidationSchema?: boolean;
384
390
  }
385
391
  export interface MixDecoratorMetadata {
386
392
  key: string;
@@ -15,6 +15,13 @@ export declare class SwaggerExplorer {
15
15
  * 构造 router 提取方法
16
16
  */
17
17
  private generateRouteMethod;
18
+ private upsertOperationParameter;
19
+ private cloneOpenAPIValue;
20
+ private normalizeContentSchemas;
21
+ private normalizeOperationParameters;
22
+ private normalizeOperationRequestBody;
23
+ private normalizeOperationResponse;
24
+ private normalizeOperationResponses;
18
25
  getOperationId(controllerKey: string, webRouter: RouterOption): string;
19
26
  /**
20
27
  * 解析 ApiExtraModel
@@ -52,6 +59,11 @@ export declare class SwaggerExplorer {
52
59
  * @param clzz
53
60
  */
54
61
  protected parseClzz(clzz: Type): any;
62
+ private mergePropertyMetadata;
63
+ protected getValidationSchemaHelper(): any;
64
+ private inferValidationProperties;
65
+ private inferValidateProperties;
66
+ protected hasClassValidatorMetadata(clzz: Type): boolean;
55
67
  /**
56
68
  * 授权验证
57
69
  * @param opts
@@ -15,6 +15,8 @@ const constants_1 = require("./constants");
15
15
  const documentBuilder_1 = require("./documentBuilder");
16
16
  const _1 = require(".");
17
17
  const enum_utils_1 = require("./common/enum.utils");
18
+ const VALIDATION_RULES_KEY = 'validation:rules';
19
+ const VALIDATE_RULES_KEY = 'common:rules';
18
20
  let SwaggerExplorer = class SwaggerExplorer {
19
21
  swaggerConfig = {};
20
22
  documentBuilder = new documentBuilder_1.DocumentBuilder();
@@ -38,14 +40,15 @@ let SwaggerExplorer = class SwaggerExplorer {
38
40
  typeof this.swaggerConfig?.externalDocs === 'object') {
39
41
  this.documentBuilder.setExternalDoc(this.swaggerConfig?.externalDocs?.description, this.swaggerConfig?.externalDocs?.url);
40
42
  }
41
- if (this.swaggerConfig?.servers &&
42
- Array.isArray(this.swaggerConfig?.servers)) {
43
- for (const serv of this.swaggerConfig?.servers) {
43
+ const servers = this.swaggerConfig?.servers;
44
+ if (Array.isArray(servers)) {
45
+ for (const serv of servers) {
44
46
  this.documentBuilder.addServer(serv?.url, serv?.description, serv?.variables);
45
47
  }
46
48
  }
47
- if (this.swaggerConfig?.tags && Array.isArray(this.swaggerConfig?.tags)) {
48
- for (const t of this.swaggerConfig?.tags) {
49
+ const tags = this.swaggerConfig?.tags;
50
+ if (Array.isArray(tags)) {
51
+ for (const t of tags) {
49
52
  this.documentBuilder.addTag(t.name, t.description, t.externalDocs);
50
53
  }
51
54
  }
@@ -54,13 +57,14 @@ let SwaggerExplorer = class SwaggerExplorer {
54
57
  this.swaggerConfig.documentOptions.operationIdFactory;
55
58
  }
56
59
  // 设置 auth 类型
57
- if (Array.isArray(this.swaggerConfig?.auth)) {
58
- for (const a of this.swaggerConfig?.auth) {
60
+ const authConfig = this.swaggerConfig?.auth;
61
+ if (Array.isArray(authConfig)) {
62
+ for (const a of authConfig) {
59
63
  this.setAuth(a);
60
64
  }
61
65
  }
62
- else {
63
- this.setAuth(this.swaggerConfig?.auth);
66
+ else if (authConfig) {
67
+ this.setAuth(authConfig);
64
68
  }
65
69
  }
66
70
  addGlobalPrefix(globalPrefix) {
@@ -264,13 +268,19 @@ let SwaggerExplorer = class SwaggerExplorer {
264
268
  if (!opts) {
265
269
  opts = {};
266
270
  }
267
- const parameters = [];
271
+ const operationParameters = this.normalizeOperationParameters(operMeta?.metadata?.parameters);
272
+ const operationRequestBody = this.normalizeOperationRequestBody(operMeta?.metadata?.requestBody);
273
+ const operationResponses = this.normalizeOperationResponses(operMeta?.metadata?.responses);
274
+ const parameters = [...operationParameters];
275
+ let requestBodyFromOperation = !!operationRequestBody;
268
276
  opts[webRouter.requestMethod] = {
269
277
  summary: getNotEmptyValue(operMeta?.metadata?.summary, webRouter.summary),
270
278
  description: getNotEmptyValue(operMeta?.metadata?.description, webRouter.description),
271
279
  operationId: operMeta?.metadata?.operationId ||
272
280
  this.getOperationId(target.name, webRouter),
273
- tags: routerTags.length ? routerTags : operMeta?.metadata?.tags ?? [],
281
+ tags: routerTags.length ? routerTags : (operMeta?.metadata?.tags ?? []),
282
+ ...(operationRequestBody ? { requestBody: operationRequestBody } : {}),
283
+ ...(operationResponses ? { responses: operationResponses } : {}),
274
284
  };
275
285
  if (operMeta?.metadata?.deprecated != null) {
276
286
  opts[webRouter.requestMethod].deprecated =
@@ -316,7 +326,7 @@ let SwaggerExplorer = class SwaggerExplorer {
316
326
  const p = param.metadata;
317
327
  p.schema = this.formatType(param.metadata.schema);
318
328
  if (p.in === 'query' || p.in === 'path' || p.in === 'header') {
319
- parameters.push(p);
329
+ this.upsertOperationParameter(parameters, p);
320
330
  }
321
331
  else if (p.in === 'body') {
322
332
  p.content = p.content ?? {};
@@ -332,13 +342,28 @@ let SwaggerExplorer = class SwaggerExplorer {
332
342
  // if requestBody is already set, skip
333
343
  opts[webRouter.requestMethod].requestBody =
334
344
  opts[webRouter.requestMethod].requestBody ?? {};
335
- opts[webRouter.requestMethod].requestBody.description =
336
- opts[webRouter.requestMethod].requestBody.description ??
337
- p.description;
338
- opts[webRouter.requestMethod].requestBody.content =
339
- opts[webRouter.requestMethod].requestBody.content ?? p.content;
340
- opts[webRouter.requestMethod].requestBody.required =
341
- opts[webRouter.requestMethod].requestBody.required ?? p.required;
345
+ if (requestBodyFromOperation) {
346
+ if (p.description !== undefined) {
347
+ opts[webRouter.requestMethod].requestBody.description =
348
+ p.description;
349
+ }
350
+ if (p.content !== undefined) {
351
+ opts[webRouter.requestMethod].requestBody.content = p.content;
352
+ }
353
+ if (p.required !== undefined) {
354
+ opts[webRouter.requestMethod].requestBody.required = p.required;
355
+ }
356
+ requestBodyFromOperation = false;
357
+ }
358
+ else {
359
+ opts[webRouter.requestMethod].requestBody.description =
360
+ opts[webRouter.requestMethod].requestBody.description ??
361
+ p.description;
362
+ opts[webRouter.requestMethod].requestBody.content =
363
+ opts[webRouter.requestMethod].requestBody.content ?? p.content;
364
+ opts[webRouter.requestMethod].requestBody.required =
365
+ opts[webRouter.requestMethod].requestBody.required ?? p.required;
366
+ }
342
367
  }
343
368
  }
344
369
  for (const arg of args) {
@@ -377,7 +402,7 @@ let SwaggerExplorer = class SwaggerExplorer {
377
402
  schema: schema.properties[pName],
378
403
  required: schema.required?.includes(pName) || false,
379
404
  };
380
- parameters.push(pp);
405
+ this.upsertOperationParameter(parameters, pp);
381
406
  });
382
407
  continue;
383
408
  }
@@ -461,76 +486,23 @@ let SwaggerExplorer = class SwaggerExplorer {
461
486
  // in body 不需要处理
462
487
  continue;
463
488
  }
464
- parameters.push(p);
489
+ this.upsertOperationParameter(parameters, p);
465
490
  }
466
491
  // class header 需要使用 ApiHeader 装饰器
467
492
  if (headers && headers.length) {
468
- headers.forEach(header => parameters.unshift(header));
493
+ headers.forEach(header => this.upsertOperationParameter(parameters, header, true));
469
494
  }
470
495
  // 获取方法上的 @ApiHeader
471
496
  const methodHeaders = metaForMethods.filter(item => item.key === constants_1.DECORATORS.API_HEADERS);
472
497
  if (methodHeaders.length > 0) {
473
- methodHeaders.forEach(item => parameters.unshift(item.metadata));
498
+ methodHeaders.forEach(item => this.upsertOperationParameter(parameters, item.metadata, true));
474
499
  }
475
500
  opts[webRouter.requestMethod].parameters = parameters;
476
501
  const responses = metaForMethods.filter(item => item.key === constants_1.DECORATORS.API_RESPONSE &&
477
502
  item.propertyName === webRouter.method);
478
- const returnResponses = {};
503
+ const returnResponses = operationResponses ?? {};
479
504
  for (const r of responses) {
480
- const resp = r.metadata;
481
- const keys = Object.keys(resp);
482
- for (const k of keys) {
483
- // 这里是引用,赋值可以直接更改
484
- const tt = resp[k];
485
- if (tt.schema) {
486
- // response 的 schema 需要包含在 content 内
487
- tt.content = {
488
- 'application/json': {
489
- schema: this.formatType(tt.schema),
490
- },
491
- };
492
- delete tt.schema;
493
- }
494
- else if (tt.type) {
495
- if (core_1.Types.isClass(tt.type)) {
496
- this.parseClzz(tt.type);
497
- if (tt.isArray) {
498
- tt.content = {
499
- 'application/json': {
500
- schema: {
501
- type: 'array',
502
- items: {
503
- $ref: '#/components/schemas/' + tt.type.name,
504
- },
505
- },
506
- },
507
- };
508
- }
509
- else {
510
- tt.content = {
511
- 'application/json': {
512
- schema: {
513
- $ref: '#/components/schemas/' + tt.type.name,
514
- },
515
- },
516
- };
517
- }
518
- }
519
- else {
520
- tt.content = {
521
- 'text/plain': {
522
- schema: {
523
- type: convertSchemaType(tt.type),
524
- },
525
- },
526
- };
527
- }
528
- }
529
- delete tt.status;
530
- delete tt.type;
531
- delete tt.isArray;
532
- delete tt.format;
533
- }
505
+ const resp = this.normalizeOperationResponses(r.metadata);
534
506
  Object.assign(returnResponses, resp);
535
507
  }
536
508
  if (Object.keys(returnResponses).length > 0) {
@@ -545,6 +517,133 @@ let SwaggerExplorer = class SwaggerExplorer {
545
517
  }
546
518
  paths[url] = opts;
547
519
  }
520
+ upsertOperationParameter(parameters, parameter, prepend = false) {
521
+ const index = parameters.findIndex(item => {
522
+ return item?.name === parameter?.name && item?.in === parameter?.in;
523
+ });
524
+ if (index >= 0) {
525
+ parameters.splice(index, 1);
526
+ }
527
+ if (prepend) {
528
+ parameters.unshift(parameter);
529
+ }
530
+ else {
531
+ parameters.push(parameter);
532
+ }
533
+ }
534
+ cloneOpenAPIValue(value) {
535
+ if (Array.isArray(value)) {
536
+ return value.map(item => this.cloneOpenAPIValue(item));
537
+ }
538
+ if (value && typeof value === 'object') {
539
+ const cloned = {};
540
+ for (const key in value) {
541
+ cloned[key] = this.cloneOpenAPIValue(value[key]);
542
+ }
543
+ return cloned;
544
+ }
545
+ return value;
546
+ }
547
+ normalizeContentSchemas(content) {
548
+ if (!content) {
549
+ return content;
550
+ }
551
+ for (const key in content) {
552
+ if (content[key]?.schema) {
553
+ content[key].schema = this.formatType(content[key].schema);
554
+ }
555
+ }
556
+ return content;
557
+ }
558
+ normalizeOperationParameters(parameters) {
559
+ if (!Array.isArray(parameters)) {
560
+ return [];
561
+ }
562
+ return parameters.map(parameter => {
563
+ const normalized = this.cloneOpenAPIValue(parameter);
564
+ if (normalized?.schema) {
565
+ normalized.schema = this.formatType(normalized.schema);
566
+ }
567
+ if (normalized.content) {
568
+ normalized.content = this.normalizeContentSchemas(normalized.content);
569
+ }
570
+ return normalized;
571
+ });
572
+ }
573
+ normalizeOperationRequestBody(requestBody) {
574
+ if (!requestBody) {
575
+ return undefined;
576
+ }
577
+ const normalized = this.cloneOpenAPIValue(requestBody);
578
+ if (normalized.content) {
579
+ normalized.content = this.normalizeContentSchemas(normalized.content);
580
+ }
581
+ return normalized;
582
+ }
583
+ normalizeOperationResponse(response) {
584
+ const normalized = this.cloneOpenAPIValue(response);
585
+ if (normalized.schema) {
586
+ normalized.content = {
587
+ 'application/json': {
588
+ schema: this.formatType(normalized.schema),
589
+ },
590
+ };
591
+ delete normalized.schema;
592
+ }
593
+ else if (normalized.type) {
594
+ if (core_1.Types.isClass(normalized.type)) {
595
+ this.parseClzz(normalized.type);
596
+ if (normalized.isArray) {
597
+ normalized.content = {
598
+ 'application/json': {
599
+ schema: {
600
+ type: 'array',
601
+ items: {
602
+ $ref: '#/components/schemas/' + normalized.type.name,
603
+ },
604
+ },
605
+ },
606
+ };
607
+ }
608
+ else {
609
+ normalized.content = {
610
+ 'application/json': {
611
+ schema: {
612
+ $ref: '#/components/schemas/' + normalized.type.name,
613
+ },
614
+ },
615
+ };
616
+ }
617
+ }
618
+ else {
619
+ normalized.content = {
620
+ 'text/plain': {
621
+ schema: {
622
+ type: convertSchemaType(normalized.type),
623
+ },
624
+ },
625
+ };
626
+ }
627
+ }
628
+ else if (normalized.content) {
629
+ normalized.content = this.normalizeContentSchemas(normalized.content);
630
+ }
631
+ delete normalized.status;
632
+ delete normalized.type;
633
+ delete normalized.isArray;
634
+ delete normalized.format;
635
+ return normalized;
636
+ }
637
+ normalizeOperationResponses(responses) {
638
+ if (!responses) {
639
+ return undefined;
640
+ }
641
+ const normalizedResponses = this.cloneOpenAPIValue(responses);
642
+ for (const status in normalizedResponses) {
643
+ normalizedResponses[status] = this.normalizeOperationResponse(normalizedResponses[status]);
644
+ }
645
+ return normalizedResponses;
646
+ }
548
647
  getOperationId(controllerKey, webRouter) {
549
648
  return this.operationIdFactory(controllerKey, webRouter);
550
649
  }
@@ -772,6 +871,18 @@ let SwaggerExplorer = class SwaggerExplorer {
772
871
  for (const key in props) {
773
872
  props[key] = props[key][props[key].length - 1];
774
873
  }
874
+ if (this.swaggerConfig?.useValidationSchema) {
875
+ const inferredProps = this.inferValidationProperties(clzz);
876
+ for (const key of Object.keys(inferredProps)) {
877
+ if (!props[key]) {
878
+ props[key] = inferredProps[key];
879
+ continue;
880
+ }
881
+ const existingMeta = props[key].metadata || {};
882
+ const inferredMeta = inferredProps[key].metadata || {};
883
+ props[key].metadata = this.mergePropertyMetadata(existingMeta, inferredMeta);
884
+ }
885
+ }
775
886
  const tt = {
776
887
  type: 'object',
777
888
  properties: {},
@@ -843,6 +954,120 @@ let SwaggerExplorer = class SwaggerExplorer {
843
954
  // just for test
844
955
  return tt;
845
956
  }
957
+ mergePropertyMetadata(swaggerMetadata, inferredMetadata) {
958
+ const mergedMetadata = { ...swaggerMetadata };
959
+ const fillableKeys = [
960
+ 'type',
961
+ 'items',
962
+ 'format',
963
+ 'enum',
964
+ '$ref',
965
+ 'pattern',
966
+ 'default',
967
+ ];
968
+ for (const key of fillableKeys) {
969
+ if (mergedMetadata[key] === undefined &&
970
+ inferredMetadata[key] !== undefined) {
971
+ mergedMetadata[key] = inferredMetadata[key];
972
+ }
973
+ }
974
+ if (mergedMetadata.required === undefined &&
975
+ inferredMetadata.required !== undefined) {
976
+ mergedMetadata.required = inferredMetadata.required;
977
+ }
978
+ return mergedMetadata;
979
+ }
980
+ getValidationSchemaHelper() {
981
+ try {
982
+ const validationPkg = (0, core_1.safeRequire)('@midwayjs/validation');
983
+ const registry = validationPkg?.registry;
984
+ if (!registry?.getDefaultValidator) {
985
+ return;
986
+ }
987
+ return registry.getDefaultValidator()?.schemaHelper;
988
+ }
989
+ catch {
990
+ // When @midwayjs/validate and @midwayjs/validation are both installed in
991
+ // the same process, loading validation package may throw duplicated error
992
+ // group exceptions. Swagger should degrade gracefully in this case.
993
+ return;
994
+ }
995
+ }
996
+ inferValidationProperties(clzz) {
997
+ const ruleProps = core_1.MetadataManager.getPropertiesWithMetadata(VALIDATION_RULES_KEY, clzz) ||
998
+ {};
999
+ const hasRuleMetadata = Object.keys(ruleProps).length > 0;
1000
+ const validateRuleProps = core_1.MetadataManager.getPropertiesWithMetadata(VALIDATE_RULES_KEY, clzz) || {};
1001
+ const hasValidateRuleMetadata = Object.keys(validateRuleProps).length > 0;
1002
+ const hasClassValidatorMetadata = this.hasClassValidatorMetadata(clzz);
1003
+ if (!hasRuleMetadata &&
1004
+ !hasClassValidatorMetadata &&
1005
+ !hasValidateRuleMetadata) {
1006
+ return {};
1007
+ }
1008
+ const inferredProps = {};
1009
+ if (hasRuleMetadata || hasClassValidatorMetadata) {
1010
+ const schemaHelper = this.getValidationSchemaHelper();
1011
+ if (schemaHelper &&
1012
+ typeof schemaHelper.getSwaggerPropertyKeys === 'function' &&
1013
+ typeof schemaHelper.getSwaggerPropertyMetadata === 'function') {
1014
+ const propertyKeys = schemaHelper.getSwaggerPropertyKeys(clzz) || [];
1015
+ for (const key of propertyKeys) {
1016
+ const metadata = schemaHelper.getSwaggerPropertyMetadata(clzz, key);
1017
+ if (metadata) {
1018
+ inferredProps[key] = {
1019
+ metadata,
1020
+ };
1021
+ }
1022
+ }
1023
+ }
1024
+ }
1025
+ if (hasValidateRuleMetadata) {
1026
+ const validateInferredProps = this.inferValidateProperties(clzz);
1027
+ for (const [key, value] of Object.entries(validateInferredProps)) {
1028
+ if (!inferredProps[key]) {
1029
+ inferredProps[key] = value;
1030
+ continue;
1031
+ }
1032
+ const mergedMetadata = this.mergePropertyMetadata(inferredProps[key].metadata || {}, value.metadata || {});
1033
+ inferredProps[key] = {
1034
+ metadata: mergedMetadata,
1035
+ };
1036
+ }
1037
+ }
1038
+ return inferredProps;
1039
+ }
1040
+ inferValidateProperties(clzz) {
1041
+ const inferredProps = {};
1042
+ const ruleProps = core_1.MetadataManager.getPropertiesWithMetadata(VALIDATE_RULES_KEY, clzz) || {};
1043
+ for (const key of Object.keys(ruleProps)) {
1044
+ let schema = ruleProps[key];
1045
+ if (typeof schema === 'function') {
1046
+ schema = schema();
1047
+ }
1048
+ const metadata = inferJoiPropertyMetadata(schema);
1049
+ if (metadata) {
1050
+ inferredProps[key] = {
1051
+ metadata,
1052
+ };
1053
+ }
1054
+ }
1055
+ return inferredProps;
1056
+ }
1057
+ hasClassValidatorMetadata(clzz) {
1058
+ try {
1059
+ const classValidator = (0, core_1.safeRequire)('class-validator-multi-lang-lite');
1060
+ const storage = classValidator?.getMetadataStorage?.();
1061
+ if (!storage?.getTargetValidationMetadatas) {
1062
+ return false;
1063
+ }
1064
+ const metadatas = storage.getTargetValidationMetadatas(clzz, '', false, false) || [];
1065
+ return metadatas.length > 0;
1066
+ }
1067
+ catch {
1068
+ return false;
1069
+ }
1070
+ }
846
1071
  /**
847
1072
  * 授权验证
848
1073
  * @param opts
@@ -960,6 +1185,47 @@ function convertSchemaType(value) {
960
1185
  return 'object';
961
1186
  }
962
1187
  }
1188
+ function inferJoiPropertyMetadata(schema) {
1189
+ if (!schema || typeof schema.describe !== 'function') {
1190
+ return null;
1191
+ }
1192
+ const desc = schema.describe();
1193
+ if (!desc || typeof desc !== 'object') {
1194
+ return null;
1195
+ }
1196
+ const typeMap = {
1197
+ string: 'string',
1198
+ number: 'number',
1199
+ boolean: 'boolean',
1200
+ array: 'array',
1201
+ date: 'string',
1202
+ object: 'object',
1203
+ };
1204
+ const metadata = {
1205
+ type: typeMap[desc.type] || 'object',
1206
+ };
1207
+ if (desc?.flags?.presence === 'required') {
1208
+ metadata.required = true;
1209
+ }
1210
+ else if (desc?.flags?.presence === 'optional' || !desc?.flags?.presence) {
1211
+ metadata.required = false;
1212
+ }
1213
+ if (desc.type === 'array' && Array.isArray(desc.items)) {
1214
+ metadata.items = {
1215
+ type: typeMap[desc.items[0]?.type] || 'object',
1216
+ };
1217
+ }
1218
+ if (desc.type === 'date') {
1219
+ metadata.format = 'date-time';
1220
+ }
1221
+ if (Array.isArray(desc.allow)) {
1222
+ const enumValues = desc.allow.filter(item => item !== '' && item !== null && item !== undefined);
1223
+ if (enumValues.length > 0) {
1224
+ metadata.enum = enumValues;
1225
+ }
1226
+ }
1227
+ return metadata;
1228
+ }
963
1229
  function getNotEmptyValue(...args) {
964
1230
  for (const arg of args) {
965
1231
  if (arg) {
package/dist/ui/render.js CHANGED
@@ -23,8 +23,9 @@ function renderSwaggerUIDist(swaggerConfig, swaggerExplorer) {
23
23
  function replaceInfo(content) {
24
24
  let str = `location.href.replace('${swaggerConfig.swaggerPath}/index.html', '${swaggerConfig.swaggerPath}/index.json'),\n validatorUrl: null,`;
25
25
  if (swaggerConfig.displayOptions) {
26
- Object.keys(swaggerConfig.displayOptions).forEach(key => {
27
- str += `\n${key}: ${swaggerConfig.displayOptions[key]},`;
26
+ Object.entries(swaggerConfig.displayOptions).forEach(([key, value]) => {
27
+ const variable = typeof value === 'string' ? `'${value}'` : value;
28
+ str += `\n${key}: ${variable},`;
28
29
  });
29
30
  }
30
31
  return content.replace('"https://petstore.swagger.io/v2/swagger.json",', str);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@midwayjs/swagger",
3
- "version": "4.0.0-beta.8",
3
+ "version": "4.0.0",
4
4
  "description": "Midway Component for Swagger API Documentation",
5
5
  "main": "dist/index.js",
6
6
  "typings": "index.d.ts",
@@ -12,10 +12,10 @@
12
12
  ],
13
13
  "devDependencies": {
14
14
  "@apidevtools/swagger-parser": "11.0.1",
15
- "@midwayjs/core": "^4.0.0-beta.8",
16
- "@midwayjs/koa": "^4.0.0-beta.8",
17
- "@midwayjs/mock": "^4.0.0-beta.8",
18
- "@midwayjs/validate": "^4.0.0-beta.8",
15
+ "@midwayjs/core": "^4.0.0",
16
+ "@midwayjs/koa": "^4.0.0",
17
+ "@midwayjs/mock": "^4.0.0",
18
+ "@midwayjs/validate": "^4.0.0",
19
19
  "swagger-ui-dist": "5.18.3"
20
20
  },
21
21
  "author": "Kurten Chan <chinkurten@gmail.com>",
@@ -30,5 +30,5 @@
30
30
  "type": "git",
31
31
  "url": "https://github.com/midwayjs/midway.git"
32
32
  },
33
- "gitHead": "355e55949fdd132b0bdcb4830222a0a027e92ded"
33
+ "gitHead": "014f32c23ebc1d5ac21777c76be2fd373ce992d8"
34
34
  }