@moostjs/swagger 0.6.25 → 0.6.26

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
@@ -5,9 +5,11 @@ Swagger/OpenAPI integration for [Moost](https://moost.org). Automatically genera
5
5
  ## Installation
6
6
 
7
7
  ```bash
8
- npm install @moostjs/swagger swagger-ui-dist
8
+ npm install @moostjs/swagger
9
9
  ```
10
10
 
11
+ The Swagger UI assets ship with the package (`swagger-ui-dist` is a regular dependency). `moost`, `@moostjs/event-http`, and `@wooksjs/http-static` are peer dependencies.
12
+
11
13
  ## Quick Start
12
14
 
13
15
  ```ts
@@ -18,9 +20,10 @@ import { SwaggerController } from '@moostjs/swagger'
18
20
  const app = new Moost()
19
21
  const http = new MoostHttp()
20
22
 
21
- app.adapter(http).controllers(SwaggerController).init()
22
-
23
- http.listen(3000)
23
+ app.adapter(http)
24
+ app.registerControllers(SwaggerController)
25
+ await app.init()
26
+ await http.listen(3000)
24
27
  // Swagger UI available at http://localhost:3000/api-docs/
25
28
  // JSON spec at http://localhost:3000/api-docs/spec.json
26
29
  // YAML spec at http://localhost:3000/api-docs/spec.yaml
@@ -33,7 +36,7 @@ http.listen(3000)
33
36
  - `@SwaggerResponse(opts)` / `@SwaggerResponse(code, opts)` — Define response schemas. Supports optional `description` to document the status code; when omitted, a standard HTTP reason phrase is used automatically.
34
37
  - `@SwaggerRequestBody(opts)` — Define request body schema.
35
38
  - `@SwaggerParam(opts)` — Define a parameter.
36
- - `@SwaggerExample(example)` — Attach an example value.
39
+ - `@SwaggerExample(example)` — Attach an example value to a DTO class (its component schema).
37
40
  - `@SwaggerOperationId(id)` — Override the auto-generated operationId. Falls back to `@Id()` from moost core, then to the auto-generated value.
38
41
  - `@SwaggerDeprecated()` — Mark a controller or handler as deprecated.
39
42
  - `@SwaggerExclude()` — Exclude a controller or handler from the spec.
@@ -52,7 +55,8 @@ When no `@SwaggerResponse` is declared for the success status code, the generato
52
55
  Security schemes are auto-discovered from `@Authenticate()` guards (provided by `@moostjs/event-http`). All four OpenAPI transport types are supported: bearer, basic, apiKey, and cookie.
53
56
 
54
57
  ```ts
55
- import { Authenticate, defineAuthGuard } from '@moostjs/event-http'
58
+ import { Controller, Param } from 'moost'
59
+ import { Authenticate, defineAuthGuard, Get } from '@moostjs/event-http'
56
60
  import { SwaggerPublic } from '@moostjs/swagger'
57
61
 
58
62
  const jwtGuard = defineAuthGuard({ bearer: { format: 'JWT' } }, (transports) => {
package/dist/index.cjs CHANGED
@@ -160,55 +160,6 @@ function SwaggerLink(codeOrName, nameOrOptions, maybeOptions) {
160
160
  return getSwaggerMate().decorate("swaggerCallbacks", config, true);
161
161
  }
162
162
 
163
- //#endregion
164
- //#region packages/swagger/src/json-to-yaml.ts
165
- const YAML_SPECIAL = /^[\s#!&*|>'{}[\],?:@`-]|[:#]\s|[\n\r]|\s$/;
166
- function quoteString(str) {
167
- if (str === "") return "''";
168
- if (YAML_SPECIAL.test(str) || str === "true" || str === "false" || str === "null") return JSON.stringify(str);
169
- const num = Number(str);
170
- if (str.length > 0 && !Number.isNaN(num) && String(num) === str) return JSON.stringify(str);
171
- return str;
172
- }
173
- function serializeValue(value, indent) {
174
- if (value === null || value === void 0) return "null";
175
- if (typeof value === "boolean") return value ? "true" : "false";
176
- if (typeof value === "number") return Number.isFinite(value) ? String(value) : "null";
177
- if (typeof value === "string") return quoteString(value);
178
- const pad = " ".repeat(indent);
179
- const childPad = " ".repeat(indent + 1);
180
- if (Array.isArray(value)) {
181
- if (value.length === 0) return "[]";
182
- const lines = [];
183
- for (const item of value) if (isObject(item) || Array.isArray(item)) {
184
- const nested = serializeValue(item, indent + 1);
185
- lines.push(`${pad}- ${nested.slice(childPad.length)}`);
186
- } else lines.push(`${pad}- ${serializeValue(item, 0)}`);
187
- return lines.join("\n");
188
- }
189
- if (isObject(value)) {
190
- const entries = Object.entries(value);
191
- if (entries.length === 0) return "{}";
192
- const lines = [];
193
- for (const [key, val] of entries) {
194
- const yamlKey = quoteString(key);
195
- if (isObject(val) || Array.isArray(val)) {
196
- const nested = serializeValue(val, indent + 1);
197
- if (nested === "[]" || nested === "{}") lines.push(`${pad}${yamlKey}: ${nested}`);
198
- else lines.push(`${pad}${yamlKey}:\n${nested}`);
199
- } else lines.push(`${pad}${yamlKey}: ${serializeValue(val, 0)}`);
200
- }
201
- return lines.join("\n");
202
- }
203
- return String(value);
204
- }
205
- function isObject(value) {
206
- return typeof value === "object" && value !== null && !Array.isArray(value);
207
- }
208
- function jsonToYaml(value) {
209
- return `${serializeValue(value, 0)}\n`;
210
- }
211
-
212
163
  //#endregion
213
164
  //#region packages/swagger/src/mapping.ts
214
165
  const globalSchemas = {};
@@ -663,12 +614,13 @@ function ensureComponentName(typeRef, schema, suggestedName) {
663
614
  * throughout the schema tree to `#/components/schemas/X`.
664
615
  * Removes the `$defs` property from the schema after hoisting.
665
616
  */ function hoistDefs(schema) {
666
- if (!schema.$defs) return;
667
- for (const [name, def] of Object.entries(schema.$defs)) if (!globalSchemas[name]) {
668
- globalSchemas[name] = cloneSchema(def);
669
- hoistDefs(globalSchemas[name]);
617
+ if (schema.$defs) {
618
+ for (const [name, def] of Object.entries(schema.$defs)) if (!globalSchemas[name]) {
619
+ globalSchemas[name] = cloneSchema(def);
620
+ hoistDefs(globalSchemas[name]);
621
+ }
622
+ delete schema.$defs;
670
623
  }
671
- delete schema.$defs;
672
624
  rewriteDefsRefs(schema);
673
625
  }
674
626
  /** Recursively rewrite `$ref: '#/$defs/X'` → `$ref: '#/components/schemas/X'` */ function rewriteDefsRefs(schema) {
@@ -871,19 +823,83 @@ function transportsToSecurityRequirement(transports) {
871
823
  if (transports.cookie) requirements.push({ cookieAuth: [] });
872
824
  return requirements;
873
825
  }
826
+ /**
827
+ * Collects schemes and builds the security requirement for one or more
828
+ * `@Authenticate` guards. Multiple stacked guards are AND-combined
829
+ * (merged into a single requirement object via cross-product), while
830
+ * multiple transports within a single guard remain OR-alternatives.
831
+ */ function applyAuthTransports(transports, schemes) {
832
+ const list = Array.isArray(transports) ? transports : [transports];
833
+ let combined = [{}];
834
+ for (const entry of list) {
835
+ collectSchemesFromTransports(entry, schemes);
836
+ const requirements = transportsToSecurityRequirement(entry);
837
+ if (requirements.length === 0) continue;
838
+ const next = [];
839
+ for (const base of combined) for (const requirement of requirements) next.push({
840
+ ...base,
841
+ ...requirement
842
+ });
843
+ combined = next;
844
+ }
845
+ return combined.filter((requirement) => Object.keys(requirement).length > 0);
846
+ }
874
847
  function resolveOperationSecurity(cmeta, hmeta, schemes) {
875
848
  if (hmeta?.swaggerPublic) return [];
876
849
  if (hmeta?.swaggerSecurity?.length) return hmeta.swaggerSecurity;
877
- if (hmeta?.authTransports) {
878
- collectSchemesFromTransports(hmeta.authTransports, schemes);
879
- return transportsToSecurityRequirement(hmeta.authTransports);
880
- }
850
+ if (hmeta?.authTransports) return applyAuthTransports(hmeta.authTransports, schemes);
881
851
  if (cmeta?.swaggerPublic) return [];
882
852
  if (cmeta?.swaggerSecurity?.length) return cmeta.swaggerSecurity;
883
- if (cmeta?.authTransports) {
884
- collectSchemesFromTransports(cmeta.authTransports, schemes);
885
- return transportsToSecurityRequirement(cmeta.authTransports);
853
+ if (cmeta?.authTransports) return applyAuthTransports(cmeta.authTransports, schemes);
854
+ }
855
+
856
+ //#endregion
857
+ //#region packages/swagger/src/json-to-yaml.ts
858
+ const YAML_SPECIAL = /^[\s#!&*|>'{}[\],?:@`-]|[:#]\s|[\n\r]|\s$/;
859
+ function quoteString(str) {
860
+ if (str === "") return "''";
861
+ if (YAML_SPECIAL.test(str) || str === "true" || str === "false" || str === "null") return JSON.stringify(str);
862
+ const num = Number(str);
863
+ if (str.length > 0 && !Number.isNaN(num) && String(num) === str) return JSON.stringify(str);
864
+ return str;
865
+ }
866
+ function serializeValue(value, indent) {
867
+ if (value === null || value === void 0) return "null";
868
+ if (typeof value === "boolean") return value ? "true" : "false";
869
+ if (typeof value === "number") return Number.isFinite(value) ? String(value) : "null";
870
+ if (typeof value === "string") return quoteString(value);
871
+ const pad = " ".repeat(indent);
872
+ const childPad = " ".repeat(indent + 1);
873
+ if (Array.isArray(value)) {
874
+ if (value.length === 0) return "[]";
875
+ const lines = [];
876
+ for (const item of value) if (isObject(item) || Array.isArray(item)) {
877
+ const nested = serializeValue(item, indent + 1);
878
+ lines.push(`${pad}- ${nested.slice(childPad.length)}`);
879
+ } else lines.push(`${pad}- ${serializeValue(item, 0)}`);
880
+ return lines.join("\n");
886
881
  }
882
+ if (isObject(value)) {
883
+ const entries = Object.entries(value);
884
+ if (entries.length === 0) return "{}";
885
+ const lines = [];
886
+ for (const [key, val] of entries) {
887
+ const yamlKey = quoteString(key);
888
+ if (isObject(val) || Array.isArray(val)) {
889
+ const nested = serializeValue(val, indent + 1);
890
+ if (nested === "[]" || nested === "{}") lines.push(`${pad}${yamlKey}: ${nested}`);
891
+ else lines.push(`${pad}${yamlKey}:\n${nested}`);
892
+ } else lines.push(`${pad}${yamlKey}: ${serializeValue(val, 0)}`);
893
+ }
894
+ return lines.join("\n");
895
+ }
896
+ return String(value);
897
+ }
898
+ function isObject(value) {
899
+ return typeof value === "object" && value !== null && !Array.isArray(value);
900
+ }
901
+ function jsonToYaml(value) {
902
+ return `${serializeValue(value, 0)}\n`;
887
903
  }
888
904
 
889
905
  //#endregion
@@ -1072,4 +1088,5 @@ exports.SwaggerResponse = SwaggerResponse;
1072
1088
  exports.SwaggerSecurity = SwaggerSecurity;
1073
1089
  exports.SwaggerSecurityAll = SwaggerSecurityAll;
1074
1090
  exports.SwaggerTag = SwaggerTag;
1075
- exports.getSwaggerMate = getSwaggerMate;
1091
+ exports.getSwaggerMate = getSwaggerMate;
1092
+ exports.mapToSwaggerSpec = mapToSwaggerSpec;
package/dist/index.d.ts CHANGED
@@ -1,8 +1,55 @@
1
- import { Mate, TMoostMetadata, TMateParamMeta } from 'moost';
1
+ import { TControllerOverview, Mate, TMoostMetadata, TMateParamMeta } from 'moost';
2
2
  import { THeaderRef, TStatusRef } from '@moostjs/event-http';
3
3
 
4
4
  type TFunction = Function;
5
5
 
6
+ interface TOpenApiLink {
7
+ operationId?: string;
8
+ operationRef?: string;
9
+ parameters?: Record<string, string>;
10
+ requestBody?: string;
11
+ description?: string;
12
+ server?: {
13
+ url: string;
14
+ description?: string;
15
+ };
16
+ }
17
+ interface TEndpointSpec {
18
+ summary?: string;
19
+ tags: string[];
20
+ operationId?: string;
21
+ description?: string;
22
+ deprecated?: boolean;
23
+ responses?: Record<string, {
24
+ description?: string;
25
+ headers?: Record<string, {
26
+ description?: string;
27
+ required?: boolean;
28
+ schema: TSwaggerSchema;
29
+ example?: unknown;
30
+ }>;
31
+ content: Record<string, {
32
+ schema: TSwaggerSchema;
33
+ }>;
34
+ links?: Record<string, TOpenApiLink>;
35
+ }>;
36
+ parameters: {
37
+ name: string;
38
+ in: string;
39
+ description?: string;
40
+ required: boolean;
41
+ schema: TSwaggerSchema;
42
+ }[];
43
+ requestBody?: {
44
+ required?: boolean;
45
+ content: Record<string, {
46
+ schema: TSwaggerSchema;
47
+ }>;
48
+ };
49
+ externalDocs?: TSwaggerExternalDocs;
50
+ security?: TSwaggerSecurityRequirement[];
51
+ callbacks?: Record<string, Record<string, Record<string, unknown>>>;
52
+ }
6
53
  interface TSwaggerOptions {
7
54
  title?: string;
8
55
  description?: string;
@@ -70,6 +117,41 @@ interface TSwaggerSchema {
70
117
  };
71
118
  $defs?: Record<string, TSwaggerSchema>;
72
119
  }
120
+ declare const globalSchemas: Record<string, TSwaggerSchema>;
121
+ declare function mapToSwaggerSpec(metadata: TControllerOverview[], options?: TSwaggerOptions): {
122
+ components: {
123
+ schemas: typeof globalSchemas;
124
+ securitySchemes?: Record<string, TSwaggerSecurityScheme>;
125
+ };
126
+ security?: TSwaggerSecurityRequirement[] | undefined;
127
+ externalDocs?: TSwaggerExternalDocs | undefined;
128
+ servers?: {
129
+ url: string;
130
+ description?: string;
131
+ }[] | undefined;
132
+ openapi: string;
133
+ info: {
134
+ termsOfService?: string | undefined;
135
+ license?: {
136
+ name: string;
137
+ url?: string;
138
+ } | undefined;
139
+ contact?: {
140
+ name?: string;
141
+ url?: string;
142
+ email?: string;
143
+ } | undefined;
144
+ version: string;
145
+ description?: string | undefined;
146
+ title: string;
147
+ };
148
+ paths: Record<string, Record<string, TEndpointSpec>>;
149
+ tags: {
150
+ name: string;
151
+ description?: string;
152
+ externalDocs?: TSwaggerExternalDocs;
153
+ }[];
154
+ };
73
155
 
74
156
  /** Returns the shared `Mate` instance extended with Swagger/OpenAPI metadata fields. */
75
157
  declare function getSwaggerMate(): Mate<TMoostMetadata & TSwaggerMate & {
@@ -365,5 +447,5 @@ declare class SwaggerController {
365
447
  serve(path: string): Promise<unknown>;
366
448
  }
367
449
 
368
- export { SwaggerCallback, SwaggerController, SwaggerDeprecated, SwaggerDescription, SwaggerExample, SwaggerExclude, SwaggerExternalDocs, SwaggerLink, SwaggerOperationId, SwaggerParam, SwaggerPublic, SwaggerRequestBody, SwaggerResponse, SwaggerSecurity, SwaggerSecurityAll, SwaggerTag, getSwaggerMate };
369
- export type { TSwaggerCallbackConfig, TSwaggerCallbackOptions, TSwaggerConfigType, TSwaggerExternalDocs, TSwaggerLinkConfig, TSwaggerLinkOptions, TSwaggerMate, TSwaggerResponseHeader, TSwaggerResponseOpts, TSwaggerSecurityRequirement, TSwaggerSecurityScheme, TSwaggerSecuritySchemeApiKey, TSwaggerSecuritySchemeHttp, TSwaggerSecuritySchemeOAuth2, TSwaggerSecuritySchemeOAuth2Flow, TSwaggerSecuritySchemeOpenIdConnect };
450
+ export { SwaggerCallback, SwaggerController, SwaggerDeprecated, SwaggerDescription, SwaggerExample, SwaggerExclude, SwaggerExternalDocs, SwaggerLink, SwaggerOperationId, SwaggerParam, SwaggerPublic, SwaggerRequestBody, SwaggerResponse, SwaggerSecurity, SwaggerSecurityAll, SwaggerTag, getSwaggerMate, mapToSwaggerSpec };
451
+ export type { TSwaggerCallbackConfig, TSwaggerCallbackOptions, TSwaggerConfigType, TSwaggerExternalDocs, TSwaggerLinkConfig, TSwaggerLinkOptions, TSwaggerMate, TSwaggerOptions, TSwaggerResponseHeader, TSwaggerResponseOpts, TSwaggerSecurityRequirement, TSwaggerSecurityScheme, TSwaggerSecuritySchemeApiKey, TSwaggerSecuritySchemeHttp, TSwaggerSecuritySchemeOAuth2, TSwaggerSecuritySchemeOAuth2Flow, TSwaggerSecuritySchemeOpenIdConnect };
package/dist/index.mjs CHANGED
@@ -131,55 +131,6 @@ function SwaggerLink(codeOrName, nameOrOptions, maybeOptions) {
131
131
  return getSwaggerMate().decorate("swaggerCallbacks", config, true);
132
132
  }
133
133
 
134
- //#endregion
135
- //#region packages/swagger/src/json-to-yaml.ts
136
- const YAML_SPECIAL = /^[\s#!&*|>'{}[\],?:@`-]|[:#]\s|[\n\r]|\s$/;
137
- function quoteString(str) {
138
- if (str === "") return "''";
139
- if (YAML_SPECIAL.test(str) || str === "true" || str === "false" || str === "null") return JSON.stringify(str);
140
- const num = Number(str);
141
- if (str.length > 0 && !Number.isNaN(num) && String(num) === str) return JSON.stringify(str);
142
- return str;
143
- }
144
- function serializeValue(value, indent) {
145
- if (value === null || value === void 0) return "null";
146
- if (typeof value === "boolean") return value ? "true" : "false";
147
- if (typeof value === "number") return Number.isFinite(value) ? String(value) : "null";
148
- if (typeof value === "string") return quoteString(value);
149
- const pad = " ".repeat(indent);
150
- const childPad = " ".repeat(indent + 1);
151
- if (Array.isArray(value)) {
152
- if (value.length === 0) return "[]";
153
- const lines = [];
154
- for (const item of value) if (isObject(item) || Array.isArray(item)) {
155
- const nested = serializeValue(item, indent + 1);
156
- lines.push(`${pad}- ${nested.slice(childPad.length)}`);
157
- } else lines.push(`${pad}- ${serializeValue(item, 0)}`);
158
- return lines.join("\n");
159
- }
160
- if (isObject(value)) {
161
- const entries = Object.entries(value);
162
- if (entries.length === 0) return "{}";
163
- const lines = [];
164
- for (const [key, val] of entries) {
165
- const yamlKey = quoteString(key);
166
- if (isObject(val) || Array.isArray(val)) {
167
- const nested = serializeValue(val, indent + 1);
168
- if (nested === "[]" || nested === "{}") lines.push(`${pad}${yamlKey}: ${nested}`);
169
- else lines.push(`${pad}${yamlKey}:\n${nested}`);
170
- } else lines.push(`${pad}${yamlKey}: ${serializeValue(val, 0)}`);
171
- }
172
- return lines.join("\n");
173
- }
174
- return String(value);
175
- }
176
- function isObject(value) {
177
- return typeof value === "object" && value !== null && !Array.isArray(value);
178
- }
179
- function jsonToYaml(value) {
180
- return `${serializeValue(value, 0)}\n`;
181
- }
182
-
183
134
  //#endregion
184
135
  //#region packages/swagger/src/mapping.ts
185
136
  const globalSchemas = {};
@@ -634,12 +585,13 @@ function ensureComponentName(typeRef, schema, suggestedName) {
634
585
  * throughout the schema tree to `#/components/schemas/X`.
635
586
  * Removes the `$defs` property from the schema after hoisting.
636
587
  */ function hoistDefs(schema) {
637
- if (!schema.$defs) return;
638
- for (const [name, def] of Object.entries(schema.$defs)) if (!globalSchemas[name]) {
639
- globalSchemas[name] = cloneSchema(def);
640
- hoistDefs(globalSchemas[name]);
588
+ if (schema.$defs) {
589
+ for (const [name, def] of Object.entries(schema.$defs)) if (!globalSchemas[name]) {
590
+ globalSchemas[name] = cloneSchema(def);
591
+ hoistDefs(globalSchemas[name]);
592
+ }
593
+ delete schema.$defs;
641
594
  }
642
- delete schema.$defs;
643
595
  rewriteDefsRefs(schema);
644
596
  }
645
597
  /** Recursively rewrite `$ref: '#/$defs/X'` → `$ref: '#/components/schemas/X'` */ function rewriteDefsRefs(schema) {
@@ -842,19 +794,83 @@ function transportsToSecurityRequirement(transports) {
842
794
  if (transports.cookie) requirements.push({ cookieAuth: [] });
843
795
  return requirements;
844
796
  }
797
+ /**
798
+ * Collects schemes and builds the security requirement for one or more
799
+ * `@Authenticate` guards. Multiple stacked guards are AND-combined
800
+ * (merged into a single requirement object via cross-product), while
801
+ * multiple transports within a single guard remain OR-alternatives.
802
+ */ function applyAuthTransports(transports, schemes) {
803
+ const list = Array.isArray(transports) ? transports : [transports];
804
+ let combined = [{}];
805
+ for (const entry of list) {
806
+ collectSchemesFromTransports(entry, schemes);
807
+ const requirements = transportsToSecurityRequirement(entry);
808
+ if (requirements.length === 0) continue;
809
+ const next = [];
810
+ for (const base of combined) for (const requirement of requirements) next.push({
811
+ ...base,
812
+ ...requirement
813
+ });
814
+ combined = next;
815
+ }
816
+ return combined.filter((requirement) => Object.keys(requirement).length > 0);
817
+ }
845
818
  function resolveOperationSecurity(cmeta, hmeta, schemes) {
846
819
  if (hmeta?.swaggerPublic) return [];
847
820
  if (hmeta?.swaggerSecurity?.length) return hmeta.swaggerSecurity;
848
- if (hmeta?.authTransports) {
849
- collectSchemesFromTransports(hmeta.authTransports, schemes);
850
- return transportsToSecurityRequirement(hmeta.authTransports);
851
- }
821
+ if (hmeta?.authTransports) return applyAuthTransports(hmeta.authTransports, schemes);
852
822
  if (cmeta?.swaggerPublic) return [];
853
823
  if (cmeta?.swaggerSecurity?.length) return cmeta.swaggerSecurity;
854
- if (cmeta?.authTransports) {
855
- collectSchemesFromTransports(cmeta.authTransports, schemes);
856
- return transportsToSecurityRequirement(cmeta.authTransports);
824
+ if (cmeta?.authTransports) return applyAuthTransports(cmeta.authTransports, schemes);
825
+ }
826
+
827
+ //#endregion
828
+ //#region packages/swagger/src/json-to-yaml.ts
829
+ const YAML_SPECIAL = /^[\s#!&*|>'{}[\],?:@`-]|[:#]\s|[\n\r]|\s$/;
830
+ function quoteString(str) {
831
+ if (str === "") return "''";
832
+ if (YAML_SPECIAL.test(str) || str === "true" || str === "false" || str === "null") return JSON.stringify(str);
833
+ const num = Number(str);
834
+ if (str.length > 0 && !Number.isNaN(num) && String(num) === str) return JSON.stringify(str);
835
+ return str;
836
+ }
837
+ function serializeValue(value, indent) {
838
+ if (value === null || value === void 0) return "null";
839
+ if (typeof value === "boolean") return value ? "true" : "false";
840
+ if (typeof value === "number") return Number.isFinite(value) ? String(value) : "null";
841
+ if (typeof value === "string") return quoteString(value);
842
+ const pad = " ".repeat(indent);
843
+ const childPad = " ".repeat(indent + 1);
844
+ if (Array.isArray(value)) {
845
+ if (value.length === 0) return "[]";
846
+ const lines = [];
847
+ for (const item of value) if (isObject(item) || Array.isArray(item)) {
848
+ const nested = serializeValue(item, indent + 1);
849
+ lines.push(`${pad}- ${nested.slice(childPad.length)}`);
850
+ } else lines.push(`${pad}- ${serializeValue(item, 0)}`);
851
+ return lines.join("\n");
857
852
  }
853
+ if (isObject(value)) {
854
+ const entries = Object.entries(value);
855
+ if (entries.length === 0) return "{}";
856
+ const lines = [];
857
+ for (const [key, val] of entries) {
858
+ const yamlKey = quoteString(key);
859
+ if (isObject(val) || Array.isArray(val)) {
860
+ const nested = serializeValue(val, indent + 1);
861
+ if (nested === "[]" || nested === "{}") lines.push(`${pad}${yamlKey}: ${nested}`);
862
+ else lines.push(`${pad}${yamlKey}:\n${nested}`);
863
+ } else lines.push(`${pad}${yamlKey}: ${serializeValue(val, 0)}`);
864
+ }
865
+ return lines.join("\n");
866
+ }
867
+ return String(value);
868
+ }
869
+ function isObject(value) {
870
+ return typeof value === "object" && value !== null && !Array.isArray(value);
871
+ }
872
+ function jsonToYaml(value) {
873
+ return `${serializeValue(value, 0)}\n`;
858
874
  }
859
875
 
860
876
  //#endregion
@@ -1022,4 +1038,4 @@ SwaggerController = _ts_decorate([
1022
1038
  ], SwaggerController);
1023
1039
 
1024
1040
  //#endregion
1025
- export { SwaggerCallback, SwaggerController, SwaggerDeprecated, SwaggerDescription, SwaggerExample, SwaggerExclude, SwaggerExternalDocs, SwaggerLink, SwaggerOperationId, SwaggerParam, SwaggerPublic, SwaggerRequestBody, SwaggerResponse, SwaggerSecurity, SwaggerSecurityAll, SwaggerTag, getSwaggerMate };
1041
+ export { SwaggerCallback, SwaggerController, SwaggerDeprecated, SwaggerDescription, SwaggerExample, SwaggerExclude, SwaggerExternalDocs, SwaggerLink, SwaggerOperationId, SwaggerParam, SwaggerPublic, SwaggerRequestBody, SwaggerResponse, SwaggerSecurity, SwaggerSecurityAll, SwaggerTag, getSwaggerMate, mapToSwaggerSpec };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moostjs/swagger",
3
- "version": "0.6.25",
3
+ "version": "0.6.26",
4
4
  "description": "@moostjs/swagger",
5
5
  "keywords": [
6
6
  "composables",
@@ -45,10 +45,10 @@
45
45
  "vitest": "3.2.4"
46
46
  },
47
47
  "peerDependencies": {
48
- "@wooksjs/event-http": "^0.7.17",
49
- "@wooksjs/http-static": "^0.7.17",
50
- "@moostjs/event-http": "^0.6.25",
51
- "moost": "^0.6.25"
48
+ "@wooksjs/event-http": "^0.7.19",
49
+ "@wooksjs/http-static": "^0.7.19",
50
+ "moost": "^0.6.26",
51
+ "@moostjs/event-http": "^0.6.26"
52
52
  },
53
53
  "scripts": {
54
54
  "pub": "pnpm publish --access public",