@moostjs/swagger 0.3.23 → 0.3.25

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
@@ -1 +1 @@
1
- # @moostjs/swagger
1
+ # @moostjs/swagger
package/dist/index.cjs CHANGED
@@ -1,13 +1,50 @@
1
1
  'use strict';
2
2
 
3
- var eventHttp = require('@moostjs/event-http');
4
3
  var moost = require('moost');
5
- var swaggerUiDist = require('swagger-ui-dist');
4
+ var eventHttp = require('@moostjs/event-http');
5
+ var zod = require('@moostjs/zod');
6
6
  var httpStatic = require('@wooksjs/http-static');
7
7
  var path = require('path');
8
- var zod = require('@moostjs/zod');
8
+ var swaggerUiDist = require('swagger-ui-dist');
9
9
  var zodParser = require('zod-parser');
10
10
 
11
+ function getSwaggerMate() {
12
+ return moost.getMoostMate();
13
+ }
14
+
15
+ const SwaggerTag = (tag) => getSwaggerMate().decorate('swaggerTags', tag, true);
16
+ const SwaggerExclude = () => getSwaggerMate().decorate('swaggerExclude', true);
17
+ const SwaggerDescription = (descr) => getSwaggerMate().decorate('swaggerDescription', descr);
18
+ function SwaggerResponse(code, opts) {
19
+ return getSwaggerMate().decorate(meta => {
20
+ meta.swaggerResponses = meta.swaggerResponses || {};
21
+ const keyCode = typeof code === 'number' ? code : 0;
22
+ const opt = typeof code === 'number' ? opts : code;
23
+ const contentType = typeof opt.contentType === 'string'
24
+ ? opt.contentType
25
+ : '*/*';
26
+ const response = (['object', 'function'].includes(typeof opt.response)
27
+ ? opt.response
28
+ : opt);
29
+ meta.swaggerResponses[keyCode] = meta.swaggerResponses[keyCode] || {};
30
+ meta.swaggerResponses[keyCode][contentType] = response;
31
+ return meta;
32
+ });
33
+ }
34
+ function SwaggerRequestBody(opt) {
35
+ return getSwaggerMate().decorate(meta => {
36
+ meta.swaggerRequestBody = meta.swaggerRequestBody || {};
37
+ const contentType = typeof opt.contentType === 'string'
38
+ ? opt.contentType
39
+ : 'application/json';
40
+ const response = (typeof opt.contentType === 'string'
41
+ ? opt.response
42
+ : opt);
43
+ meta.swaggerRequestBody[contentType] = response;
44
+ return meta;
45
+ });
46
+ }
47
+
11
48
  /******************************************************************************
12
49
  Copyright (c) Microsoft Corporation.
13
50
 
@@ -45,35 +82,6 @@ typeof SuppressedError === "function" ? SuppressedError : function (error, suppr
45
82
  return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
46
83
  };
47
84
 
48
- function getSwaggerMate() {
49
- return moost.getMoostMate();
50
- }
51
-
52
- const SwaggerTag = (tag) => getSwaggerMate().decorate('swaggerTags', tag, true);
53
- const SwaggerExclude = () => getSwaggerMate().decorate('swaggerExclude', true);
54
- const SwaggerDescription = (descr) => getSwaggerMate().decorate('swaggerDescription', descr);
55
- function SwaggerResponse(code, opts) {
56
- return getSwaggerMate().decorate((meta) => {
57
- meta.swaggerResponses = meta.swaggerResponses || {};
58
- const keyCode = typeof code === 'number' ? code : 0;
59
- const opt = typeof code === 'number' ? opts : code;
60
- const contentType = typeof opt.contentType === 'string' ? opt.contentType : '*/*';
61
- const response = (['object', 'function'].includes(typeof opt.response) ? opt.response : opt);
62
- meta.swaggerResponses[keyCode] = meta.swaggerResponses[keyCode] || {};
63
- meta.swaggerResponses[keyCode][contentType] = response;
64
- return meta;
65
- });
66
- }
67
- function SwaggerRequestBody(opt) {
68
- return getSwaggerMate().decorate((meta) => {
69
- meta.swaggerRequestBody = meta.swaggerRequestBody || {};
70
- const contentType = typeof opt.contentType === 'string' ? opt.contentType : 'application/json';
71
- const response = (typeof opt.contentType === 'string' ? opt.response : opt);
72
- meta.swaggerRequestBody[contentType] = response;
73
- return meta;
74
- });
75
- }
76
-
77
85
  function mapToSwaggerSpec(metadata, options, logger) {
78
86
  const swaggerSpec = {
79
87
  openapi: '3.0.0',
@@ -89,14 +97,16 @@ function mapToSwaggerSpec(metadata, options, logger) {
89
97
  };
90
98
  for (const controller of metadata) {
91
99
  const cmeta = controller.meta;
92
- if (cmeta?.swaggerExclude)
100
+ if (cmeta?.swaggerExclude) {
93
101
  continue;
102
+ }
94
103
  const controllerTags = cmeta?.swaggerTags || [];
95
104
  for (const handler of controller.handlers) {
96
105
  const hmeta = handler.meta;
97
106
  const hh = handler.handler;
98
- if (hh.type !== 'HTTP' || hmeta?.swaggerExclude || !handler.registeredAs.length)
107
+ if (hh.type !== 'HTTP' || hmeta?.swaggerExclude || handler.registeredAs.length === 0) {
99
108
  continue;
109
+ }
100
110
  const handlerPath = handler.registeredAs[0].path;
101
111
  const handlerMethod = hh.method?.toLowerCase() || 'get';
102
112
  const handlerDescription = hmeta?.description;
@@ -104,13 +114,13 @@ function mapToSwaggerSpec(metadata, options, logger) {
104
114
  if (!swaggerSpec.paths[handlerPath]) {
105
115
  swaggerSpec.paths[handlerPath] = {};
106
116
  }
107
- let responses = undefined;
117
+ let responses;
108
118
  if (hmeta?.swaggerResponses) {
109
119
  for (const [code, responseConfigs] of Object.entries(hmeta.swaggerResponses)) {
110
120
  const newCode = code === '0' ? getDefaultStatusCode(handlerMethod) : code;
111
121
  for (const [contentType, type] of Object.entries(responseConfigs)) {
112
- let schema = undefined;
113
- let zt = undefined;
122
+ let schema;
123
+ let zt;
114
124
  if (type instanceof zod.z.ZodType) {
115
125
  zt = type;
116
126
  }
@@ -153,7 +163,7 @@ function mapToSwaggerSpec(metadata, options, logger) {
153
163
  let reqBodyRequired = true;
154
164
  const bodyConfig = {};
155
165
  if (hmeta?.swaggerRequestBody) {
156
- for (const [contentType, type] of Object.entries(hmeta?.swaggerRequestBody)) {
166
+ for (const [contentType, type] of Object.entries(hmeta.swaggerRequestBody)) {
157
167
  let zt;
158
168
  let schema;
159
169
  if (type instanceof zod.z.ZodType) {
@@ -175,7 +185,10 @@ function mapToSwaggerSpec(metadata, options, logger) {
175
185
  }
176
186
  swaggerSpec.paths[handlerPath][handlerMethod] = {
177
187
  summary: handlerDescription,
178
- operationId: handlerMethod.toUpperCase() + '_' + handlerPath.replace(/\//g, '_').replace(/[\{\}]/g, '__').replace(/[^A-Za-z0-9]/g, '_'),
188
+ operationId: `${handlerMethod.toUpperCase()}_${handlerPath
189
+ .replace(/\//g, '_')
190
+ .replace(/[{}]/g, '__')
191
+ .replace(/[^\dA-Za-z]/g, '_')}`,
179
192
  tags: handlerTags,
180
193
  parameters: [],
181
194
  responses,
@@ -202,7 +215,7 @@ function mapToSwaggerSpec(metadata, options, logger) {
202
215
  name: paramName,
203
216
  in: 'path',
204
217
  description: paramMeta ? paramMeta.description : undefined,
205
- required: !paramMeta?.optional && !parsed?.$optional,
218
+ required: !paramMeta.optional && !parsed?.$optional,
206
219
  schema: schema || { type: 'string' },
207
220
  });
208
221
  }
@@ -224,29 +237,27 @@ function mapToSwaggerSpec(metadata, options, logger) {
224
237
  name: paramMeta.paramName || '',
225
238
  in: 'query',
226
239
  description: paramMeta.description,
227
- required: !paramMeta?.optional && !parsed?.$optional,
240
+ required: !paramMeta.optional && !parsed.$optional,
228
241
  schema: schema || { type: 'string' },
229
242
  });
230
243
  }
231
- else if (paramMeta.paramSource == 'QUERY') {
232
- if (parsed.$type === 'ZodObject') {
233
- for (const [key, value] of Object.entries(parsed.$inner)) {
234
- const schema = getSwaggerSchema(value, true);
235
- if (schema) {
236
- const swaggerSchema = {
237
- name: key,
238
- in: 'query',
239
- description: value.description,
240
- required: !parsed.$optional && !value.$optional,
241
- schema,
242
- };
243
- endpointSpec.parameters.push(swaggerSchema);
244
- }
244
+ else if (paramMeta.paramSource == 'QUERY' && parsed.$type === 'ZodObject') {
245
+ for (const [key, value] of Object.entries(parsed.$inner)) {
246
+ const schema = getSwaggerSchema(value, true);
247
+ if (schema) {
248
+ const swaggerSchema = {
249
+ name: key,
250
+ in: 'query',
251
+ description: value.description,
252
+ required: !parsed.$optional && !value.$optional,
253
+ schema,
254
+ };
255
+ endpointSpec.parameters.push(swaggerSchema);
245
256
  }
246
257
  }
247
258
  }
248
259
  }
249
- if (paramMeta?.paramSource === 'BODY') {
260
+ if (paramMeta.paramSource === 'BODY') {
250
261
  const zodType = zod.getZodTypeForProp({
251
262
  type: controller.type,
252
263
  key: handler.method,
@@ -265,10 +276,13 @@ function mapToSwaggerSpec(metadata, options, logger) {
265
276
  case 'ZodDate':
266
277
  case 'ZodEnum':
267
278
  case 'ZodNativeEnum':
268
- case 'ZodLiteral':
279
+ case 'ZodLiteral': {
269
280
  contentType = 'text/plan';
270
281
  break;
271
- default: contentType = 'application/json';
282
+ }
283
+ default: {
284
+ contentType = 'application/json';
285
+ }
272
286
  }
273
287
  if (!bodyConfig[contentType]) {
274
288
  bodyConfig[contentType] = { schema: getSwaggerSchema(parsed) };
@@ -276,7 +290,7 @@ function mapToSwaggerSpec(metadata, options, logger) {
276
290
  reqBodyRequired = !zodType.isOptional() && !paramMeta.optional;
277
291
  }
278
292
  }
279
- if (bodyConfig && Object.entries(bodyConfig).filter(e => !!e[1]).length > 0) {
293
+ if (bodyConfig && Object.entries(bodyConfig).some(e => !!e[1])) {
280
294
  swaggerSpec.paths[handlerPath][handlerMethod].requestBody = {
281
295
  content: bodyConfig,
282
296
  required: reqBodyRequired,
@@ -289,10 +303,12 @@ function mapToSwaggerSpec(metadata, options, logger) {
289
303
  const globalSchemas = {};
290
304
  function getSwaggerSchema(parsed, forParam) {
291
305
  const zodType = parsed.$ref;
292
- if (!forParam && zodType.__type_ref && globalSchemas[zodType.__type_ref.name])
306
+ if (!forParam && zodType.__type_ref && globalSchemas[zodType.__type_ref.name]) {
293
307
  return { $ref: `#/components/schemas/${zodType.__type_ref.name}` };
294
- if (forParam && zodType.__type_ref && globalSchemas[zodType.__type_ref.name])
308
+ }
309
+ if (forParam && zodType.__type_ref && globalSchemas[zodType.__type_ref.name]) {
295
310
  return globalSchemas[zodType.__type_ref.name];
311
+ }
296
312
  const schema = {};
297
313
  if (!forParam && zodType.__type_ref) {
298
314
  globalSchemas[zodType.__type_ref.name] = schema;
@@ -328,115 +344,151 @@ function getSwaggerSchema(parsed, forParam) {
328
344
  switch (parsed.$type) {
329
345
  case 'ZodAny':
330
346
  case 'ZodUnknown':
331
- case 'ZodString':
347
+ case 'ZodString': {
332
348
  asString();
333
349
  break;
334
- case 'ZodNumber':
350
+ }
351
+ case 'ZodNumber': {
335
352
  schema.type = 'number';
336
353
  break;
337
- case 'ZodBigInt':
354
+ }
355
+ case 'ZodBigInt': {
338
356
  schema.type = 'integer';
339
357
  break;
340
- case 'ZodBoolean':
358
+ }
359
+ case 'ZodBoolean': {
341
360
  schema.type = 'boolean';
342
361
  break;
343
- case 'ZodLiteral':
362
+ }
363
+ case 'ZodLiteral': {
344
364
  asLiteral();
345
365
  break;
346
- case 'ZodEnum':
366
+ }
367
+ case 'ZodEnum': {
347
368
  asEnum();
348
369
  break;
349
- case 'ZodNativeEnum':
370
+ }
371
+ case 'ZodNativeEnum': {
350
372
  asNativeEnum();
351
373
  break;
352
- case 'ZodDate':
374
+ }
375
+ case 'ZodDate': {
353
376
  schema.type = 'string';
354
377
  break;
355
- case 'ZodNull':
378
+ }
379
+ case 'ZodNull': {
356
380
  schema.type = 'null';
357
381
  break;
358
- default: return undefined;
382
+ }
383
+ default: {
384
+ return undefined;
385
+ }
359
386
  }
360
387
  }
361
388
  else {
362
389
  switch (parsed.$type) {
363
- case 'ZodString':
390
+ case 'ZodString': {
364
391
  asString();
365
392
  break;
366
- case 'ZodNumber':
393
+ }
394
+ case 'ZodNumber': {
367
395
  schema.type = 'number';
368
396
  break;
369
- case 'ZodBigInt':
397
+ }
398
+ case 'ZodBigInt': {
370
399
  schema.type = 'integer';
371
400
  break;
372
- case 'ZodBoolean':
401
+ }
402
+ case 'ZodBoolean': {
373
403
  schema.type = 'boolean';
374
404
  break;
375
- case 'ZodLiteral':
405
+ }
406
+ case 'ZodLiteral': {
376
407
  asLiteral();
377
408
  break;
378
- case 'ZodEnum':
409
+ }
410
+ case 'ZodEnum': {
379
411
  asEnum();
380
412
  break;
381
- case 'ZodNativeEnum':
413
+ }
414
+ case 'ZodNativeEnum': {
382
415
  asNativeEnum();
383
416
  break;
384
- case 'ZodDate':
417
+ }
418
+ case 'ZodDate': {
385
419
  schema.type = 'string';
386
420
  break;
387
- case 'ZodNull':
421
+ }
422
+ case 'ZodNull': {
388
423
  schema.type = 'null';
389
424
  break;
425
+ }
390
426
  case 'ZodFunction':
391
427
  case 'ZodSymbol':
392
428
  case 'ZodUndefined':
393
429
  case 'ZodUnknown':
394
430
  case 'ZodNever':
395
431
  case 'ZodVoid':
396
- case 'ZodNaN':
432
+ case 'ZodNaN': {
397
433
  return undefined;
398
- case 'ZodArray':
434
+ }
435
+ case 'ZodArray': {
399
436
  schema.type = 'array';
400
437
  schema.minItems = parsed.$checks?.minLength || undefined;
401
438
  schema.maxItems = parsed.$checks?.maxLength || undefined;
402
439
  schema.items = getSwaggerSchema(parsed.$inner);
403
440
  break;
404
- case 'ZodTuple':
441
+ }
442
+ case 'ZodTuple': {
405
443
  schema.type = 'array';
406
- schema.items = parsed.$inner.map(t => getSwaggerSchema(t)).filter(t => !!t);
444
+ schema.items = parsed.$inner
445
+ .map(t => getSwaggerSchema(t))
446
+ .filter(t => !!t);
407
447
  break;
408
- case 'ZodObject':
448
+ }
449
+ case 'ZodObject': {
409
450
  schema.type = 'object';
410
451
  schema.properties = {};
411
452
  schema.required = [];
412
- if (zodType._def.unknownKeys === 'passthrough')
453
+ if (zodType._def.unknownKeys === 'passthrough') {
413
454
  schema.additionalProperties = {};
455
+ }
414
456
  for (const [key, val] of Object.entries(parsed.$inner)) {
415
457
  const prop = getSwaggerSchema(val);
416
458
  if (prop) {
417
459
  schema.properties[key] = prop;
418
- if (!val.$optional)
460
+ if (!val.$optional) {
419
461
  schema.required.push(key);
462
+ }
420
463
  }
421
464
  }
422
465
  break;
466
+ }
423
467
  case 'ZodPromise':
424
468
  case 'ZodRecord':
425
469
  case 'ZodMap':
426
- case 'ZodSet':
470
+ case 'ZodSet': {
427
471
  schema.type = 'object';
428
472
  schema.properties = {};
429
473
  schema.additionalProperties = parsed.$type === 'ZodRecord' ? {} : undefined;
430
474
  break;
475
+ }
431
476
  case 'ZodUnion':
432
- case 'ZodDiscriminatedUnion':
433
- schema.oneOf = parsed.$inner.map(t => getSwaggerSchema(t)).filter(t => !!t);
477
+ case 'ZodDiscriminatedUnion': {
478
+ schema.oneOf = parsed.$inner
479
+ .map(t => getSwaggerSchema(t))
480
+ .filter(t => !!t);
434
481
  break;
435
- case 'ZodIntersection':
436
- schema.allOf = parsed.$inner.map(t => getSwaggerSchema(t)).filter(t => !!t);
482
+ }
483
+ case 'ZodIntersection': {
484
+ schema.allOf = parsed.$inner
485
+ .map(t => getSwaggerSchema(t))
486
+ .filter(t => !!t);
437
487
  break;
438
- case 'ZodLazy':
488
+ }
489
+ case 'ZodLazy': {
439
490
  return getSwaggerSchema(parsed.$get());
491
+ }
440
492
  }
441
493
  }
442
494
  if (parsed.$nullable) {
@@ -448,16 +500,20 @@ function getSwaggerSchema(parsed, forParam) {
448
500
  if (parsed.$checks) {
449
501
  const checks = parsed.$checks;
450
502
  if (parsed.$type === 'ZodString') {
451
- if (typeof checks.min === 'number')
503
+ if (typeof checks.min === 'number') {
452
504
  schema.minLength = checks.min;
453
- if (typeof checks.max === 'number')
505
+ }
506
+ if (typeof checks.max === 'number') {
454
507
  schema.maxLength = checks.max;
508
+ }
455
509
  }
456
510
  else {
457
- if (typeof checks.min === 'number')
511
+ if (typeof checks.min === 'number') {
458
512
  schema.minimum = checks.min;
459
- if (typeof checks.max === 'number')
513
+ }
514
+ if (typeof checks.max === 'number') {
460
515
  schema.maximum = checks.max;
516
+ }
461
517
  }
462
518
  }
463
519
  if (!forParam && zodType.__type_ref) {
@@ -481,9 +537,9 @@ function getDefaultStatusCode(httpMethod) {
481
537
  exports.SwaggerController = class SwaggerController {
482
538
  constructor(title = 'Moost API') {
483
539
  this.title = title;
484
- this.assetPath = swaggerUiDist.getAbsoluteFSPath();
540
+ this['assetPath'] = swaggerUiDist.getAbsoluteFSPath();
485
541
  }
486
- serveIndex(url, location, status) {
542
+ 'serveIndex'(url, location, status) {
487
543
  if (!url.endsWith('index.html') && !url.endsWith('/')) {
488
544
  status.value = 302;
489
545
  location.value = path.join(url, '/');
@@ -534,10 +590,10 @@ exports.SwaggerController = class SwaggerController {
534
590
  }
535
591
  return this.spec;
536
592
  }
537
- files(url) {
593
+ 'files'(url) {
538
594
  return this.serve(url.split('/').pop());
539
595
  }
540
- serve(path) {
596
+ 'serve'(path) {
541
597
  return httpStatic.serveFile(path, {
542
598
  baseDir: this.assetPath,
543
599
  cacheControl: {
package/dist/index.d.ts CHANGED
@@ -1,19 +1,7 @@
1
- import { THeaderHook, TStatusHook } from '@wooksjs/event-http';
2
1
  import * as _prostojs_mate from '@prostojs/mate';
3
2
  import * as moost from 'moost';
4
3
  import { z } from '@moostjs/zod';
5
-
6
- declare class SwaggerController {
7
- protected title: string;
8
- constructor(title?: string);
9
- assetPath: string;
10
- serveIndex(url: string, location: THeaderHook, status: TStatusHook): string;
11
- 'swagger-initializer.js'(): string;
12
- spec?: Record<string, unknown>;
13
- 'spec.json'(): Promise<Record<string, unknown>>;
14
- files(url: string): Promise<unknown>;
15
- serve(path: string): Promise<unknown>;
16
- }
4
+ import { THeaderHook, TStatusHook } from '@wooksjs/event-http';
17
5
 
18
6
  type TFunction = Function;
19
7
  interface TEmpty {
@@ -79,4 +67,16 @@ declare function SwaggerResponse(opts: TSwaggerResponseOpts): MethodDecorator;
79
67
  declare function SwaggerResponse(code: number, opts: TSwaggerResponseOpts): MethodDecorator;
80
68
  declare function SwaggerRequestBody(opt: TSwaggerResponseOpts): MethodDecorator & ClassDecorator & ParameterDecorator & PropertyDecorator;
81
69
 
70
+ declare class SwaggerController {
71
+ protected title: string;
72
+ constructor(title?: string);
73
+ 'assetPath': string;
74
+ 'serveIndex'(url: string, location: THeaderHook, status: TStatusHook): string;
75
+ 'swagger-initializer.js'(): string;
76
+ 'spec'?: Record<string, unknown>;
77
+ 'spec.json'(): Promise<Record<string, unknown>>;
78
+ 'files'(url: string): Promise<unknown>;
79
+ 'serve'(path: string): Promise<unknown>;
80
+ }
81
+
82
82
  export { SwaggerController, SwaggerDescription, SwaggerExclude, SwaggerRequestBody, SwaggerResponse, SwaggerTag, type TSwaggerMate, type TSwaggerResponseConfigValue, type TSwaggerResponseOpts, getSwaggerMate };
package/dist/index.mjs CHANGED
@@ -1,11 +1,48 @@
1
- import { Get, SetHeader, Url, HeaderHook, StatusHook } from '@moostjs/event-http';
2
1
  import { getMoostMate, Controller, Const, useEventLogger, useControllerContext, Moost } from 'moost';
3
- import { getAbsoluteFSPath } from 'swagger-ui-dist';
2
+ import { Get, SetHeader, Url, HeaderHook, StatusHook } from '@moostjs/event-http';
3
+ import { z, getZodType, getZodTypeForProp, ZodSkip } from '@moostjs/zod';
4
4
  import { serveFile } from '@wooksjs/http-static';
5
5
  import { join } from 'path';
6
- import { z, getZodType, getZodTypeForProp, ZodSkip } from '@moostjs/zod';
6
+ import { getAbsoluteFSPath } from 'swagger-ui-dist';
7
7
  import { parseZodType } from 'zod-parser';
8
8
 
9
+ function getSwaggerMate() {
10
+ return getMoostMate();
11
+ }
12
+
13
+ const SwaggerTag = (tag) => getSwaggerMate().decorate('swaggerTags', tag, true);
14
+ const SwaggerExclude = () => getSwaggerMate().decorate('swaggerExclude', true);
15
+ const SwaggerDescription = (descr) => getSwaggerMate().decorate('swaggerDescription', descr);
16
+ function SwaggerResponse(code, opts) {
17
+ return getSwaggerMate().decorate(meta => {
18
+ meta.swaggerResponses = meta.swaggerResponses || {};
19
+ const keyCode = typeof code === 'number' ? code : 0;
20
+ const opt = typeof code === 'number' ? opts : code;
21
+ const contentType = typeof opt.contentType === 'string'
22
+ ? opt.contentType
23
+ : '*/*';
24
+ const response = (['object', 'function'].includes(typeof opt.response)
25
+ ? opt.response
26
+ : opt);
27
+ meta.swaggerResponses[keyCode] = meta.swaggerResponses[keyCode] || {};
28
+ meta.swaggerResponses[keyCode][contentType] = response;
29
+ return meta;
30
+ });
31
+ }
32
+ function SwaggerRequestBody(opt) {
33
+ return getSwaggerMate().decorate(meta => {
34
+ meta.swaggerRequestBody = meta.swaggerRequestBody || {};
35
+ const contentType = typeof opt.contentType === 'string'
36
+ ? opt.contentType
37
+ : 'application/json';
38
+ const response = (typeof opt.contentType === 'string'
39
+ ? opt.response
40
+ : opt);
41
+ meta.swaggerRequestBody[contentType] = response;
42
+ return meta;
43
+ });
44
+ }
45
+
9
46
  /******************************************************************************
10
47
  Copyright (c) Microsoft Corporation.
11
48
 
@@ -43,35 +80,6 @@ typeof SuppressedError === "function" ? SuppressedError : function (error, suppr
43
80
  return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
44
81
  };
45
82
 
46
- function getSwaggerMate() {
47
- return getMoostMate();
48
- }
49
-
50
- const SwaggerTag = (tag) => getSwaggerMate().decorate('swaggerTags', tag, true);
51
- const SwaggerExclude = () => getSwaggerMate().decorate('swaggerExclude', true);
52
- const SwaggerDescription = (descr) => getSwaggerMate().decorate('swaggerDescription', descr);
53
- function SwaggerResponse(code, opts) {
54
- return getSwaggerMate().decorate((meta) => {
55
- meta.swaggerResponses = meta.swaggerResponses || {};
56
- const keyCode = typeof code === 'number' ? code : 0;
57
- const opt = typeof code === 'number' ? opts : code;
58
- const contentType = typeof opt.contentType === 'string' ? opt.contentType : '*/*';
59
- const response = (['object', 'function'].includes(typeof opt.response) ? opt.response : opt);
60
- meta.swaggerResponses[keyCode] = meta.swaggerResponses[keyCode] || {};
61
- meta.swaggerResponses[keyCode][contentType] = response;
62
- return meta;
63
- });
64
- }
65
- function SwaggerRequestBody(opt) {
66
- return getSwaggerMate().decorate((meta) => {
67
- meta.swaggerRequestBody = meta.swaggerRequestBody || {};
68
- const contentType = typeof opt.contentType === 'string' ? opt.contentType : 'application/json';
69
- const response = (typeof opt.contentType === 'string' ? opt.response : opt);
70
- meta.swaggerRequestBody[contentType] = response;
71
- return meta;
72
- });
73
- }
74
-
75
83
  function mapToSwaggerSpec(metadata, options, logger) {
76
84
  const swaggerSpec = {
77
85
  openapi: '3.0.0',
@@ -87,14 +95,16 @@ function mapToSwaggerSpec(metadata, options, logger) {
87
95
  };
88
96
  for (const controller of metadata) {
89
97
  const cmeta = controller.meta;
90
- if (cmeta?.swaggerExclude)
98
+ if (cmeta?.swaggerExclude) {
91
99
  continue;
100
+ }
92
101
  const controllerTags = cmeta?.swaggerTags || [];
93
102
  for (const handler of controller.handlers) {
94
103
  const hmeta = handler.meta;
95
104
  const hh = handler.handler;
96
- if (hh.type !== 'HTTP' || hmeta?.swaggerExclude || !handler.registeredAs.length)
105
+ if (hh.type !== 'HTTP' || hmeta?.swaggerExclude || handler.registeredAs.length === 0) {
97
106
  continue;
107
+ }
98
108
  const handlerPath = handler.registeredAs[0].path;
99
109
  const handlerMethod = hh.method?.toLowerCase() || 'get';
100
110
  const handlerDescription = hmeta?.description;
@@ -102,13 +112,13 @@ function mapToSwaggerSpec(metadata, options, logger) {
102
112
  if (!swaggerSpec.paths[handlerPath]) {
103
113
  swaggerSpec.paths[handlerPath] = {};
104
114
  }
105
- let responses = undefined;
115
+ let responses;
106
116
  if (hmeta?.swaggerResponses) {
107
117
  for (const [code, responseConfigs] of Object.entries(hmeta.swaggerResponses)) {
108
118
  const newCode = code === '0' ? getDefaultStatusCode(handlerMethod) : code;
109
119
  for (const [contentType, type] of Object.entries(responseConfigs)) {
110
- let schema = undefined;
111
- let zt = undefined;
120
+ let schema;
121
+ let zt;
112
122
  if (type instanceof z.ZodType) {
113
123
  zt = type;
114
124
  }
@@ -151,7 +161,7 @@ function mapToSwaggerSpec(metadata, options, logger) {
151
161
  let reqBodyRequired = true;
152
162
  const bodyConfig = {};
153
163
  if (hmeta?.swaggerRequestBody) {
154
- for (const [contentType, type] of Object.entries(hmeta?.swaggerRequestBody)) {
164
+ for (const [contentType, type] of Object.entries(hmeta.swaggerRequestBody)) {
155
165
  let zt;
156
166
  let schema;
157
167
  if (type instanceof z.ZodType) {
@@ -173,7 +183,10 @@ function mapToSwaggerSpec(metadata, options, logger) {
173
183
  }
174
184
  swaggerSpec.paths[handlerPath][handlerMethod] = {
175
185
  summary: handlerDescription,
176
- operationId: handlerMethod.toUpperCase() + '_' + handlerPath.replace(/\//g, '_').replace(/[\{\}]/g, '__').replace(/[^A-Za-z0-9]/g, '_'),
186
+ operationId: `${handlerMethod.toUpperCase()}_${handlerPath
187
+ .replace(/\//g, '_')
188
+ .replace(/[{}]/g, '__')
189
+ .replace(/[^\dA-Za-z]/g, '_')}`,
177
190
  tags: handlerTags,
178
191
  parameters: [],
179
192
  responses,
@@ -200,7 +213,7 @@ function mapToSwaggerSpec(metadata, options, logger) {
200
213
  name: paramName,
201
214
  in: 'path',
202
215
  description: paramMeta ? paramMeta.description : undefined,
203
- required: !paramMeta?.optional && !parsed?.$optional,
216
+ required: !paramMeta.optional && !parsed?.$optional,
204
217
  schema: schema || { type: 'string' },
205
218
  });
206
219
  }
@@ -222,29 +235,27 @@ function mapToSwaggerSpec(metadata, options, logger) {
222
235
  name: paramMeta.paramName || '',
223
236
  in: 'query',
224
237
  description: paramMeta.description,
225
- required: !paramMeta?.optional && !parsed?.$optional,
238
+ required: !paramMeta.optional && !parsed.$optional,
226
239
  schema: schema || { type: 'string' },
227
240
  });
228
241
  }
229
- else if (paramMeta.paramSource == 'QUERY') {
230
- if (parsed.$type === 'ZodObject') {
231
- for (const [key, value] of Object.entries(parsed.$inner)) {
232
- const schema = getSwaggerSchema(value, true);
233
- if (schema) {
234
- const swaggerSchema = {
235
- name: key,
236
- in: 'query',
237
- description: value.description,
238
- required: !parsed.$optional && !value.$optional,
239
- schema,
240
- };
241
- endpointSpec.parameters.push(swaggerSchema);
242
- }
242
+ else if (paramMeta.paramSource == 'QUERY' && parsed.$type === 'ZodObject') {
243
+ for (const [key, value] of Object.entries(parsed.$inner)) {
244
+ const schema = getSwaggerSchema(value, true);
245
+ if (schema) {
246
+ const swaggerSchema = {
247
+ name: key,
248
+ in: 'query',
249
+ description: value.description,
250
+ required: !parsed.$optional && !value.$optional,
251
+ schema,
252
+ };
253
+ endpointSpec.parameters.push(swaggerSchema);
243
254
  }
244
255
  }
245
256
  }
246
257
  }
247
- if (paramMeta?.paramSource === 'BODY') {
258
+ if (paramMeta.paramSource === 'BODY') {
248
259
  const zodType = getZodTypeForProp({
249
260
  type: controller.type,
250
261
  key: handler.method,
@@ -263,10 +274,13 @@ function mapToSwaggerSpec(metadata, options, logger) {
263
274
  case 'ZodDate':
264
275
  case 'ZodEnum':
265
276
  case 'ZodNativeEnum':
266
- case 'ZodLiteral':
277
+ case 'ZodLiteral': {
267
278
  contentType = 'text/plan';
268
279
  break;
269
- default: contentType = 'application/json';
280
+ }
281
+ default: {
282
+ contentType = 'application/json';
283
+ }
270
284
  }
271
285
  if (!bodyConfig[contentType]) {
272
286
  bodyConfig[contentType] = { schema: getSwaggerSchema(parsed) };
@@ -274,7 +288,7 @@ function mapToSwaggerSpec(metadata, options, logger) {
274
288
  reqBodyRequired = !zodType.isOptional() && !paramMeta.optional;
275
289
  }
276
290
  }
277
- if (bodyConfig && Object.entries(bodyConfig).filter(e => !!e[1]).length > 0) {
291
+ if (bodyConfig && Object.entries(bodyConfig).some(e => !!e[1])) {
278
292
  swaggerSpec.paths[handlerPath][handlerMethod].requestBody = {
279
293
  content: bodyConfig,
280
294
  required: reqBodyRequired,
@@ -287,10 +301,12 @@ function mapToSwaggerSpec(metadata, options, logger) {
287
301
  const globalSchemas = {};
288
302
  function getSwaggerSchema(parsed, forParam) {
289
303
  const zodType = parsed.$ref;
290
- if (!forParam && zodType.__type_ref && globalSchemas[zodType.__type_ref.name])
304
+ if (!forParam && zodType.__type_ref && globalSchemas[zodType.__type_ref.name]) {
291
305
  return { $ref: `#/components/schemas/${zodType.__type_ref.name}` };
292
- if (forParam && zodType.__type_ref && globalSchemas[zodType.__type_ref.name])
306
+ }
307
+ if (forParam && zodType.__type_ref && globalSchemas[zodType.__type_ref.name]) {
293
308
  return globalSchemas[zodType.__type_ref.name];
309
+ }
294
310
  const schema = {};
295
311
  if (!forParam && zodType.__type_ref) {
296
312
  globalSchemas[zodType.__type_ref.name] = schema;
@@ -326,115 +342,151 @@ function getSwaggerSchema(parsed, forParam) {
326
342
  switch (parsed.$type) {
327
343
  case 'ZodAny':
328
344
  case 'ZodUnknown':
329
- case 'ZodString':
345
+ case 'ZodString': {
330
346
  asString();
331
347
  break;
332
- case 'ZodNumber':
348
+ }
349
+ case 'ZodNumber': {
333
350
  schema.type = 'number';
334
351
  break;
335
- case 'ZodBigInt':
352
+ }
353
+ case 'ZodBigInt': {
336
354
  schema.type = 'integer';
337
355
  break;
338
- case 'ZodBoolean':
356
+ }
357
+ case 'ZodBoolean': {
339
358
  schema.type = 'boolean';
340
359
  break;
341
- case 'ZodLiteral':
360
+ }
361
+ case 'ZodLiteral': {
342
362
  asLiteral();
343
363
  break;
344
- case 'ZodEnum':
364
+ }
365
+ case 'ZodEnum': {
345
366
  asEnum();
346
367
  break;
347
- case 'ZodNativeEnum':
368
+ }
369
+ case 'ZodNativeEnum': {
348
370
  asNativeEnum();
349
371
  break;
350
- case 'ZodDate':
372
+ }
373
+ case 'ZodDate': {
351
374
  schema.type = 'string';
352
375
  break;
353
- case 'ZodNull':
376
+ }
377
+ case 'ZodNull': {
354
378
  schema.type = 'null';
355
379
  break;
356
- default: return undefined;
380
+ }
381
+ default: {
382
+ return undefined;
383
+ }
357
384
  }
358
385
  }
359
386
  else {
360
387
  switch (parsed.$type) {
361
- case 'ZodString':
388
+ case 'ZodString': {
362
389
  asString();
363
390
  break;
364
- case 'ZodNumber':
391
+ }
392
+ case 'ZodNumber': {
365
393
  schema.type = 'number';
366
394
  break;
367
- case 'ZodBigInt':
395
+ }
396
+ case 'ZodBigInt': {
368
397
  schema.type = 'integer';
369
398
  break;
370
- case 'ZodBoolean':
399
+ }
400
+ case 'ZodBoolean': {
371
401
  schema.type = 'boolean';
372
402
  break;
373
- case 'ZodLiteral':
403
+ }
404
+ case 'ZodLiteral': {
374
405
  asLiteral();
375
406
  break;
376
- case 'ZodEnum':
407
+ }
408
+ case 'ZodEnum': {
377
409
  asEnum();
378
410
  break;
379
- case 'ZodNativeEnum':
411
+ }
412
+ case 'ZodNativeEnum': {
380
413
  asNativeEnum();
381
414
  break;
382
- case 'ZodDate':
415
+ }
416
+ case 'ZodDate': {
383
417
  schema.type = 'string';
384
418
  break;
385
- case 'ZodNull':
419
+ }
420
+ case 'ZodNull': {
386
421
  schema.type = 'null';
387
422
  break;
423
+ }
388
424
  case 'ZodFunction':
389
425
  case 'ZodSymbol':
390
426
  case 'ZodUndefined':
391
427
  case 'ZodUnknown':
392
428
  case 'ZodNever':
393
429
  case 'ZodVoid':
394
- case 'ZodNaN':
430
+ case 'ZodNaN': {
395
431
  return undefined;
396
- case 'ZodArray':
432
+ }
433
+ case 'ZodArray': {
397
434
  schema.type = 'array';
398
435
  schema.minItems = parsed.$checks?.minLength || undefined;
399
436
  schema.maxItems = parsed.$checks?.maxLength || undefined;
400
437
  schema.items = getSwaggerSchema(parsed.$inner);
401
438
  break;
402
- case 'ZodTuple':
439
+ }
440
+ case 'ZodTuple': {
403
441
  schema.type = 'array';
404
- schema.items = parsed.$inner.map(t => getSwaggerSchema(t)).filter(t => !!t);
442
+ schema.items = parsed.$inner
443
+ .map(t => getSwaggerSchema(t))
444
+ .filter(t => !!t);
405
445
  break;
406
- case 'ZodObject':
446
+ }
447
+ case 'ZodObject': {
407
448
  schema.type = 'object';
408
449
  schema.properties = {};
409
450
  schema.required = [];
410
- if (zodType._def.unknownKeys === 'passthrough')
451
+ if (zodType._def.unknownKeys === 'passthrough') {
411
452
  schema.additionalProperties = {};
453
+ }
412
454
  for (const [key, val] of Object.entries(parsed.$inner)) {
413
455
  const prop = getSwaggerSchema(val);
414
456
  if (prop) {
415
457
  schema.properties[key] = prop;
416
- if (!val.$optional)
458
+ if (!val.$optional) {
417
459
  schema.required.push(key);
460
+ }
418
461
  }
419
462
  }
420
463
  break;
464
+ }
421
465
  case 'ZodPromise':
422
466
  case 'ZodRecord':
423
467
  case 'ZodMap':
424
- case 'ZodSet':
468
+ case 'ZodSet': {
425
469
  schema.type = 'object';
426
470
  schema.properties = {};
427
471
  schema.additionalProperties = parsed.$type === 'ZodRecord' ? {} : undefined;
428
472
  break;
473
+ }
429
474
  case 'ZodUnion':
430
- case 'ZodDiscriminatedUnion':
431
- schema.oneOf = parsed.$inner.map(t => getSwaggerSchema(t)).filter(t => !!t);
475
+ case 'ZodDiscriminatedUnion': {
476
+ schema.oneOf = parsed.$inner
477
+ .map(t => getSwaggerSchema(t))
478
+ .filter(t => !!t);
432
479
  break;
433
- case 'ZodIntersection':
434
- schema.allOf = parsed.$inner.map(t => getSwaggerSchema(t)).filter(t => !!t);
480
+ }
481
+ case 'ZodIntersection': {
482
+ schema.allOf = parsed.$inner
483
+ .map(t => getSwaggerSchema(t))
484
+ .filter(t => !!t);
435
485
  break;
436
- case 'ZodLazy':
486
+ }
487
+ case 'ZodLazy': {
437
488
  return getSwaggerSchema(parsed.$get());
489
+ }
438
490
  }
439
491
  }
440
492
  if (parsed.$nullable) {
@@ -446,16 +498,20 @@ function getSwaggerSchema(parsed, forParam) {
446
498
  if (parsed.$checks) {
447
499
  const checks = parsed.$checks;
448
500
  if (parsed.$type === 'ZodString') {
449
- if (typeof checks.min === 'number')
501
+ if (typeof checks.min === 'number') {
450
502
  schema.minLength = checks.min;
451
- if (typeof checks.max === 'number')
503
+ }
504
+ if (typeof checks.max === 'number') {
452
505
  schema.maxLength = checks.max;
506
+ }
453
507
  }
454
508
  else {
455
- if (typeof checks.min === 'number')
509
+ if (typeof checks.min === 'number') {
456
510
  schema.minimum = checks.min;
457
- if (typeof checks.max === 'number')
511
+ }
512
+ if (typeof checks.max === 'number') {
458
513
  schema.maximum = checks.max;
514
+ }
459
515
  }
460
516
  }
461
517
  if (!forParam && zodType.__type_ref) {
@@ -479,9 +535,9 @@ function getDefaultStatusCode(httpMethod) {
479
535
  let SwaggerController = class SwaggerController {
480
536
  constructor(title = 'Moost API') {
481
537
  this.title = title;
482
- this.assetPath = getAbsoluteFSPath();
538
+ this['assetPath'] = getAbsoluteFSPath();
483
539
  }
484
- serveIndex(url, location, status) {
540
+ 'serveIndex'(url, location, status) {
485
541
  if (!url.endsWith('index.html') && !url.endsWith('/')) {
486
542
  status.value = 302;
487
543
  location.value = join(url, '/');
@@ -532,10 +588,10 @@ let SwaggerController = class SwaggerController {
532
588
  }
533
589
  return this.spec;
534
590
  }
535
- files(url) {
591
+ 'files'(url) {
536
592
  return this.serve(url.split('/').pop());
537
593
  }
538
- serve(path) {
594
+ 'serve'(path) {
539
595
  return serveFile(path, {
540
596
  baseDir: this.assetPath,
541
597
  cacheControl: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moostjs/swagger",
3
- "version": "0.3.23",
3
+ "version": "0.3.25",
4
4
  "description": "@moostjs/swagger",
5
5
  "sideEffects": false,
6
6
  "main": "dist/index.cjs",
@@ -37,14 +37,14 @@
37
37
  },
38
38
  "homepage": "https://github.com/moostjs/moostjs/tree/main/packages/swagger#readme",
39
39
  "peerDependencies": {
40
- "moost": "0.3.23",
41
- "@moostjs/event-http": "0.3.23"
40
+ "moost": "0.3.25",
41
+ "@moostjs/event-http": "0.3.25"
42
42
  },
43
43
  "dependencies": {
44
44
  "swagger-ui-dist": "^5.10.5",
45
45
  "zod-parser": "^0.0.3",
46
- "@moostjs/zod": "0.3.23",
47
- "@wooksjs/http-static": "^0.4.26"
46
+ "@moostjs/zod": "0.3.25",
47
+ "@wooksjs/http-static": "^0.4.30"
48
48
  },
49
49
  "devDependencies": {
50
50
  "@types/swagger-ui-dist": ""