@sdk-it/spec 0.46.0 → 0.46.2

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.
Files changed (41) hide show
  1. package/README.md +65 -0
  2. package/dist/index.d.ts +0 -2
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +0 -6
  5. package/dist/index.js.map +2 -2
  6. package/dist/lib/find-polymorphic-varients.js +1 -1
  7. package/dist/lib/find-polymorphic-varients.js.map +1 -1
  8. package/dist/lib/is-primitive-schema.js +1 -1
  9. package/dist/lib/is-primitive-schema.js.map +1 -1
  10. package/dist/lib/is.d.ts +2 -0
  11. package/dist/lib/is.d.ts.map +1 -1
  12. package/dist/lib/is.js +5 -0
  13. package/dist/lib/is.js.map +2 -2
  14. package/dist/lib/pagination/guess-pagination.js +1 -1
  15. package/dist/lib/pagination/guess-pagination.js.map +1 -1
  16. package/dist/lib/processing-plugins/extract-inline-schemas.d.ts.map +1 -1
  17. package/dist/lib/processing-plugins/extract-inline-schemas.js +151 -3
  18. package/dist/lib/processing-plugins/extract-inline-schemas.js.map +2 -2
  19. package/dist/lib/processing-plugins/normalize-request-bodies.d.ts +3 -0
  20. package/dist/lib/processing-plugins/normalize-request-bodies.d.ts.map +1 -1
  21. package/dist/lib/processing-plugins/normalize-request-bodies.js +99 -4
  22. package/dist/lib/processing-plugins/normalize-request-bodies.js.map +2 -2
  23. package/dist/lib/processing-plugins/normalize-responses.d.ts.map +1 -1
  24. package/dist/lib/processing-plugins/normalize-responses.js +126 -3
  25. package/dist/lib/processing-plugins/normalize-responses.js.map +2 -2
  26. package/dist/lib/processing-plugins/normalize-schemas.d.ts.map +1 -1
  27. package/dist/lib/processing-plugins/normalize-schemas.js +159 -2
  28. package/dist/lib/processing-plugins/normalize-schemas.js.map +3 -3
  29. package/package.json +2 -2
  30. package/dist/lib/tune-request-body.d.ts +0 -5
  31. package/dist/lib/tune-request-body.d.ts.map +0 -1
  32. package/dist/lib/tune-request-body.js +0 -101
  33. package/dist/lib/tune-request-body.js.map +0 -7
  34. package/dist/lib/tune-response.d.ts +0 -5
  35. package/dist/lib/tune-response.d.ts.map +0 -1
  36. package/dist/lib/tune-response.js +0 -135
  37. package/dist/lib/tune-response.js.map +0 -7
  38. package/dist/lib/tune.d.ts +0 -11
  39. package/dist/lib/tune.d.ts.map +0 -1
  40. package/dist/lib/tune.js +0 -315
  41. package/dist/lib/tune.js.map +0 -7
@@ -1,6 +1,100 @@
1
- import { resolveRef } from "@sdk-it/core/ref.js";
1
+ import { followRef, isRef, resolveRef } from "@sdk-it/core/ref.js";
2
+ import { isEmpty } from "@sdk-it/core/utils.js";
3
+ import { findUniqueSchemaName } from "../find-unique-schema-name.js";
2
4
  import { iterateOperations } from "../for-each-operation.js";
3
- import { patchParameters, tuneRequestBody } from "../tune-request-body.js";
5
+ import { securityToOptions } from "../security.js";
6
+ function patchParameters(spec, schema, parameters, security) {
7
+ const securityOptions = securityToOptions(
8
+ spec,
9
+ security,
10
+ spec.components.securitySchemes
11
+ );
12
+ const required = new Set(
13
+ Array.isArray(schema.required) ? schema.required : []
14
+ );
15
+ schema["x-properties"] ??= {};
16
+ for (const param of parameters) {
17
+ if (param.required) {
18
+ required.add(param.name);
19
+ }
20
+ schema["x-properties"][param.name] = {
21
+ "x-in": param.in,
22
+ ...isRef(param.schema) ? followRef(spec, param.schema.$ref) : param.schema ?? { type: "string" }
23
+ };
24
+ }
25
+ for (const param of securityOptions) {
26
+ required.delete(param.name);
27
+ schema["x-properties"][param.name] = {
28
+ "x-in": "header",
29
+ ...isRef(param.schema) ? followRef(spec, param.schema.$ref) : param.schema ?? { type: "string" }
30
+ };
31
+ }
32
+ schema["x-required"] = [...required];
33
+ }
34
+ function normalizeRequestBody(spec, operationId, operation, parameters, security) {
35
+ const requestBodySource = isRef(operation.requestBody) ? followRef(spec, operation.requestBody.$ref) : operation.requestBody ?? {
36
+ content: {},
37
+ required: false
38
+ };
39
+ const requestBody = structuredClone(requestBodySource);
40
+ if (isEmpty(requestBody.content)) {
41
+ const inputName = findUniqueSchemaName(spec, operationId, [
42
+ "input",
43
+ "payload",
44
+ "request"
45
+ ]);
46
+ const schema = {
47
+ "x-inputname": inputName,
48
+ "x-requestbody": true
49
+ };
50
+ patchParameters(spec, schema, parameters, security);
51
+ const normalized = {
52
+ ...requestBody,
53
+ content: {
54
+ "application/empty": {
55
+ schema: { $ref: `#/components/schemas/${inputName}` }
56
+ }
57
+ }
58
+ };
59
+ spec.components.schemas[inputName] = schema;
60
+ return normalized;
61
+ }
62
+ for (const contentType in requestBody.content) {
63
+ const mediaType = requestBody.content[contentType];
64
+ const inputName = findUniqueSchemaName(spec, operationId, [
65
+ "input",
66
+ "payload",
67
+ "request"
68
+ ]);
69
+ let schema;
70
+ switch (true) {
71
+ case isRef(mediaType.schema):
72
+ schema = structuredClone(
73
+ followRef(spec, mediaType.schema.$ref)
74
+ );
75
+ break;
76
+ case isEmpty(mediaType.schema):
77
+ schema ??= {};
78
+ console.warn(
79
+ `Request body schema for content type "${contentType}" is empty.`
80
+ );
81
+ break;
82
+ default:
83
+ schema = structuredClone(mediaType.schema);
84
+ break;
85
+ }
86
+ patchParameters(spec, schema, parameters, security);
87
+ spec.components.schemas[inputName] = {
88
+ ...schema,
89
+ "x-requestbody": true,
90
+ "x-inputname": inputName
91
+ };
92
+ requestBody.content[contentType].schema = {
93
+ $ref: `#/components/schemas/${inputName}`
94
+ };
95
+ }
96
+ return requestBody;
97
+ }
4
98
  function normalizeRequestBodies() {
5
99
  return {
6
100
  name: "normalize-request-bodies",
@@ -27,7 +121,7 @@ function normalizeRequestBodies() {
27
121
  continue;
28
122
  }
29
123
  }
30
- tunedOperation.requestBody = tuneRequestBody(
124
+ tunedOperation.requestBody = normalizeRequestBody(
31
125
  spec,
32
126
  tunedOperation.operationId,
33
127
  operation,
@@ -39,6 +133,7 @@ function normalizeRequestBodies() {
39
133
  };
40
134
  }
41
135
  export {
42
- normalizeRequestBodies
136
+ normalizeRequestBodies,
137
+ patchParameters
43
138
  };
44
139
  //# sourceMappingURL=normalize-request-bodies.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/lib/processing-plugins/normalize-request-bodies.ts"],
4
- "sourcesContent": ["import type { RequestBodyObject, SchemaObject } from 'openapi3-ts/oas31';\n\nimport { resolveRef } from '@sdk-it/core/ref.js';\n\nimport { iterateOperations } from '../for-each-operation.js';\nimport type { ProcessingPlugin } from '../processing.js';\nimport { patchParameters, tuneRequestBody } from '../tune-request-body.js';\nimport type { TunedOperationObject } from '../types.js';\n\nexport function normalizeRequestBodies(): ProcessingPlugin {\n return {\n name: 'normalize-request-bodies',\n process({ spec }) {\n for (const { operation } of iterateOperations(spec)) {\n const tunedOperation = operation as TunedOperationObject;\n if (operation.requestBody) {\n const requestBody = resolveRef<RequestBodyObject>(\n spec,\n operation.requestBody,\n );\n const schemas = Object.values(requestBody.content).map(\n ({ schema }) =>\n schema ? resolveRef<SchemaObject>(spec, schema) : undefined,\n );\n if (\n schemas.length > 0 &&\n schemas.every((schema) => schema?.['x-requestbody'])\n ) {\n for (const schema of new Set(schemas)) {\n patchParameters(\n spec,\n schema as SchemaObject,\n tunedOperation.parameters,\n operation.security ?? [],\n );\n }\n continue;\n }\n }\n tunedOperation.requestBody = tuneRequestBody(\n spec,\n tunedOperation.operationId,\n operation,\n tunedOperation.parameters,\n operation.security ?? [],\n );\n }\n },\n };\n}\n"],
5
- "mappings": "AAEA,SAAS,kBAAkB;AAE3B,SAAS,yBAAyB;AAElC,SAAS,iBAAiB,uBAAuB;AAG1C,SAAS,yBAA2C;AACzD,SAAO;AAAA,IACL,MAAM;AAAA,IACN,QAAQ,EAAE,KAAK,GAAG;AAChB,iBAAW,EAAE,UAAU,KAAK,kBAAkB,IAAI,GAAG;AACnD,cAAM,iBAAiB;AACvB,YAAI,UAAU,aAAa;AACzB,gBAAM,cAAc;AAAA,YAClB;AAAA,YACA,UAAU;AAAA,UACZ;AACA,gBAAM,UAAU,OAAO,OAAO,YAAY,OAAO,EAAE;AAAA,YACjD,CAAC,EAAE,OAAO,MACR,SAAS,WAAyB,MAAM,MAAM,IAAI;AAAA,UACtD;AACA,cACE,QAAQ,SAAS,KACjB,QAAQ,MAAM,CAAC,WAAW,SAAS,eAAe,CAAC,GACnD;AACA,uBAAW,UAAU,IAAI,IAAI,OAAO,GAAG;AACrC;AAAA,gBACE;AAAA,gBACA;AAAA,gBACA,eAAe;AAAA,gBACf,UAAU,YAAY,CAAC;AAAA,cACzB;AAAA,YACF;AACA;AAAA,UACF;AAAA,QACF;AACA,uBAAe,cAAc;AAAA,UAC3B;AAAA,UACA,eAAe;AAAA,UACf;AAAA,UACA,eAAe;AAAA,UACf,UAAU,YAAY,CAAC;AAAA,QACzB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;",
4
+ "sourcesContent": ["import type {\n OperationObject,\n ParameterObject,\n RequestBodyObject,\n SchemaObject,\n SecurityRequirementObject,\n} from 'openapi3-ts/oas31';\n\nimport { followRef, isRef, resolveRef } from '@sdk-it/core/ref.js';\nimport { isEmpty } from '@sdk-it/core/utils.js';\n\nimport { findUniqueSchemaName } from '../find-unique-schema-name.js';\nimport { iterateOperations } from '../for-each-operation.js';\nimport type { ProcessingPlugin } from '../processing.js';\nimport { securityToOptions } from '../security.js';\nimport type {\n IR,\n OurRequestBodyObject,\n TunedOperationObject,\n} from '../types.js';\n\nexport function patchParameters(\n spec: IR,\n schema: SchemaObject,\n parameters: ParameterObject[],\n security: SecurityRequirementObject[],\n) {\n const securityOptions = securityToOptions(\n spec,\n security,\n spec.components.securitySchemes,\n );\n\n const required = new Set(\n Array.isArray(schema.required) ? schema.required : [],\n );\n schema['x-properties'] ??= {};\n for (const param of parameters) {\n if (param.required) {\n required.add(param.name);\n }\n schema['x-properties'][param.name] = {\n 'x-in': param.in,\n ...(isRef(param.schema)\n ? followRef<SchemaObject>(spec, param.schema.$ref)\n : (param.schema ?? { type: 'string' })),\n };\n }\n for (const param of securityOptions) {\n required.delete(param.name);\n schema['x-properties'][param.name] = {\n 'x-in': 'header',\n ...(isRef(param.schema)\n ? followRef<SchemaObject>(spec, param.schema.$ref)\n : (param.schema ?? { type: 'string' })),\n };\n }\n schema['x-required'] = [...required];\n}\n\nfunction normalizeRequestBody(\n spec: IR,\n operationId: string,\n operation: OperationObject,\n parameters: ParameterObject[],\n security: SecurityRequirementObject[],\n): OurRequestBodyObject {\n const requestBodySource = isRef(operation.requestBody)\n ? followRef<RequestBodyObject>(spec, operation.requestBody.$ref)\n : (operation.requestBody ?? {\n content: {},\n required: false,\n });\n const requestBody = structuredClone(requestBodySource);\n if (isEmpty(requestBody.content)) {\n const inputName = findUniqueSchemaName(spec, operationId, [\n 'input',\n 'payload',\n 'request',\n ]);\n const schema: SchemaObject = {\n 'x-inputname': inputName,\n 'x-requestbody': true,\n };\n patchParameters(spec, schema, parameters, security);\n const normalized: OurRequestBodyObject = {\n ...requestBody,\n content: {\n 'application/empty': {\n schema: { $ref: `#/components/schemas/${inputName}` },\n },\n },\n };\n\n spec.components.schemas[inputName] = schema;\n return normalized;\n }\n for (const contentType in requestBody.content) {\n const mediaType = requestBody.content[contentType];\n const inputName = findUniqueSchemaName(spec, operationId, [\n 'input',\n 'payload',\n 'request',\n ]);\n let schema: SchemaObject | undefined;\n\n switch (true) {\n case isRef(mediaType.schema):\n schema = structuredClone(\n followRef<SchemaObject>(spec, mediaType.schema.$ref),\n );\n break;\n case isEmpty(mediaType.schema):\n schema ??= {};\n console.warn(\n `Request body schema for content type \"${contentType}\" is empty.`,\n );\n break;\n default:\n schema = structuredClone(mediaType.schema);\n break;\n }\n\n patchParameters(spec, schema, parameters, security);\n spec.components.schemas[inputName] = {\n ...schema,\n 'x-requestbody': true,\n 'x-inputname': inputName,\n };\n\n requestBody.content[contentType].schema = {\n $ref: `#/components/schemas/${inputName}`,\n };\n }\n return requestBody as OurRequestBodyObject;\n}\n\nexport function normalizeRequestBodies(): ProcessingPlugin {\n return {\n name: 'normalize-request-bodies',\n process({ spec }) {\n for (const { operation } of iterateOperations(spec)) {\n const tunedOperation = operation as TunedOperationObject;\n if (operation.requestBody) {\n const requestBody = resolveRef<RequestBodyObject>(\n spec,\n operation.requestBody,\n );\n const schemas = Object.values(requestBody.content).map(\n ({ schema }) =>\n schema ? resolveRef<SchemaObject>(spec, schema) : undefined,\n );\n if (\n schemas.length > 0 &&\n schemas.every((schema) => schema?.['x-requestbody'])\n ) {\n for (const schema of new Set(schemas)) {\n patchParameters(\n spec,\n schema as SchemaObject,\n tunedOperation.parameters,\n operation.security ?? [],\n );\n }\n continue;\n }\n }\n tunedOperation.requestBody = normalizeRequestBody(\n spec,\n tunedOperation.operationId,\n operation,\n tunedOperation.parameters,\n operation.security ?? [],\n );\n }\n },\n };\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1 +1 @@
1
- {"version":3,"file":"normalize-responses.d.ts","sourceRoot":"","sources":["../../../src/lib/processing-plugins/normalize-responses.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAIzD,wBAAgB,kBAAkB,IAAI,gBAAgB,CA8BrD"}
1
+ {"version":3,"file":"normalize-responses.d.ts","sourceRoot":"","sources":["../../../src/lib/processing-plugins/normalize-responses.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AA8IzD,wBAAgB,kBAAkB,IAAI,gBAAgB,CA+BrD"}
@@ -1,6 +1,129 @@
1
+ import { parse as parseContentType } from "fast-content-type-parse";
2
+ import { isRef, parseRef, resolveRef } from "@sdk-it/core/ref.js";
3
+ import { isEmpty, pascalcase } from "@sdk-it/core/utils.js";
4
+ import { findUniqueSchemaName } from "../find-unique-schema-name.js";
1
5
  import { iterateOperations } from "../for-each-operation.js";
2
- import { isSuccessStatusCode } from "../is.js";
3
- import { resolveResponses } from "../tune-response.js";
6
+ import {
7
+ isBinaryContentType,
8
+ isSseContentType,
9
+ isStreamingContentType,
10
+ isSuccessStatusCode,
11
+ isTextContentType,
12
+ parseJsonContentType
13
+ } from "../is.js";
14
+ function normalizeOperationResponses(spec, operationId, operation, responsesConfig) {
15
+ const responses = operation.responses ?? {};
16
+ operation.responses ??= {};
17
+ let foundSuccessResponse = false;
18
+ for (const status in responses) {
19
+ operation.responses[status] = structuredClone(
20
+ resolveRef(spec, responses[status])
21
+ );
22
+ if (status !== "default" && isSuccessStatusCode(status)) {
23
+ foundSuccessResponse = true;
24
+ }
25
+ }
26
+ if (!foundSuccessResponse) {
27
+ operation.responses["200"] = {
28
+ description: "OK",
29
+ content: {
30
+ "application/json": {
31
+ schema: {}
32
+ }
33
+ }
34
+ };
35
+ }
36
+ for (const status in operation.responses) {
37
+ const response = operation.responses[status];
38
+ const statusCode = +status;
39
+ if (!responsesConfig?.flattenErrorResponses && !isSuccessStatusCode(status)) {
40
+ continue;
41
+ }
42
+ if (isEmpty(response.content)) {
43
+ response.content = {
44
+ "application/octet-stream": {}
45
+ };
46
+ }
47
+ let responseName;
48
+ for (const [contentType, mediaType] of Object.entries(
49
+ response.content
50
+ )) {
51
+ if (isRef(mediaType.schema)) {
52
+ const { model } = parseRef(mediaType.schema.$ref);
53
+ Object.assign(spec.components.schemas[model], {
54
+ "x-responsebody": true
55
+ });
56
+ responseName ??= model;
57
+ continue;
58
+ }
59
+ const outputName = statusCode !== 200 ? findUniqueSchemaName(spec, `${pascalcase(operationId)}${status}`, [
60
+ "output",
61
+ "payload",
62
+ "result"
63
+ ]) : findUniqueSchemaName(spec, operationId, [
64
+ "output",
65
+ "payload",
66
+ "result"
67
+ ]);
68
+ responseName ??= outputName;
69
+ const isSse = isSseContentType(contentType);
70
+ const normalizedContentType = normalizeContentType(contentType);
71
+ const hasContentDisposition = hasHeader(
72
+ response.headers,
73
+ "Content-Disposition"
74
+ );
75
+ const isBinary = isBinaryContentType(contentType) || isStreamingContentType(normalizedContentType) && hasContentDisposition;
76
+ const isText = isTextContentType(contentType);
77
+ if (parseJsonContentType(contentType)) {
78
+ if (isEmpty(mediaType.schema)) {
79
+ spec.components.schemas[outputName] = {
80
+ type: "object",
81
+ additionalProperties: true
82
+ };
83
+ }
84
+ } else {
85
+ if (isEmpty(mediaType.schema) && (isText || isBinary)) {
86
+ mediaType.schema = {
87
+ type: "string",
88
+ ...isBinary ? { format: "binary" } : {}
89
+ };
90
+ }
91
+ spec.components.schemas[outputName] = {
92
+ ...spec.components.schemas[outputName],
93
+ "x-stream": isSse || !isText && !isBinary,
94
+ ...isSse ? { "x-sse": true } : {}
95
+ };
96
+ }
97
+ spec.components.schemas[outputName] = {
98
+ ...spec.components.schemas[outputName],
99
+ ...mediaType.schema,
100
+ "x-responsebody": true,
101
+ "x-response-group": operationId
102
+ };
103
+ operation.responses[status].content[contentType].schema = {
104
+ $ref: `#/components/schemas/${outputName}`
105
+ };
106
+ }
107
+ if (responseName) {
108
+ response["x-response-name"] = responseName;
109
+ }
110
+ }
111
+ return operation.responses;
112
+ }
113
+ function normalizeContentType(contentType) {
114
+ try {
115
+ return parseContentType(contentType)?.type?.toLowerCase();
116
+ } catch {
117
+ return contentType.split(";")[0]?.trim().toLowerCase();
118
+ }
119
+ }
120
+ function hasHeader(headers, name) {
121
+ if (!headers) {
122
+ return false;
123
+ }
124
+ const target = name.toLowerCase();
125
+ return Object.keys(headers).some((key) => key.toLowerCase() === target);
126
+ }
4
127
  function normalizeResponses() {
5
128
  return {
6
129
  name: "normalize-responses",
@@ -14,7 +137,7 @@ function normalizeResponses() {
14
137
  const hadSuccessResponse = Object.keys(operation.responses ?? {}).some(
15
138
  isSuccessStatusCode
16
139
  );
17
- operation.responses = resolveResponses(
140
+ operation.responses = normalizeOperationResponses(
18
141
  spec,
19
142
  operation.operationId,
20
143
  operation,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/lib/processing-plugins/normalize-responses.ts"],
4
- "sourcesContent": ["import { iterateOperations } from '../for-each-operation.js';\nimport { isSuccessStatusCode } from '../is.js';\nimport type { ProcessingPlugin } from '../processing.js';\nimport { resolveResponses } from '../tune-response.js';\nimport type { TunedOperationObject } from '../types.js';\n\nexport function normalizeResponses(): ProcessingPlugin {\n return {\n name: 'normalize-responses',\n process({ spec, options, report }) {\n for (const { entry, operation } of iterateOperations(spec)) {\n if (!operation.operationId) {\n throw new Error(\n `Cannot normalize responses before assigning an operation ID for ${entry.method.toUpperCase()} ${entry.path}`,\n );\n }\n const hadSuccessResponse = Object.keys(operation.responses ?? {}).some(\n isSuccessStatusCode,\n );\n (operation as TunedOperationObject).responses = resolveResponses(\n spec,\n operation.operationId,\n operation,\n options.responses,\n );\n if (!hadSuccessResponse) {\n report({\n severity: 'warning',\n code: 'success-response-added',\n message: 'Added a default 200 success response',\n path: `${entry.method.toUpperCase()} ${entry.path}`,\n });\n }\n }\n },\n };\n}\n"],
5
- "mappings": "AAAA,SAAS,yBAAyB;AAClC,SAAS,2BAA2B;AAEpC,SAAS,wBAAwB;AAG1B,SAAS,qBAAuC;AACrD,SAAO;AAAA,IACL,MAAM;AAAA,IACN,QAAQ,EAAE,MAAM,SAAS,OAAO,GAAG;AACjC,iBAAW,EAAE,OAAO,UAAU,KAAK,kBAAkB,IAAI,GAAG;AAC1D,YAAI,CAAC,UAAU,aAAa;AAC1B,gBAAM,IAAI;AAAA,YACR,mEAAmE,MAAM,OAAO,YAAY,CAAC,IAAI,MAAM,IAAI;AAAA,UAC7G;AAAA,QACF;AACA,cAAM,qBAAqB,OAAO,KAAK,UAAU,aAAa,CAAC,CAAC,EAAE;AAAA,UAChE;AAAA,QACF;AACA,QAAC,UAAmC,YAAY;AAAA,UAC9C;AAAA,UACA,UAAU;AAAA,UACV;AAAA,UACA,QAAQ;AAAA,QACV;AACA,YAAI,CAAC,oBAAoB;AACvB,iBAAO;AAAA,YACL,UAAU;AAAA,YACV,MAAM;AAAA,YACN,SAAS;AAAA,YACT,MAAM,GAAG,MAAM,OAAO,YAAY,CAAC,IAAI,MAAM,IAAI;AAAA,UACnD,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;",
4
+ "sourcesContent": ["import { parse as parseContentType } from 'fast-content-type-parse';\nimport type {\n MediaTypeObject,\n OperationObject,\n ResponseObject,\n} from 'openapi3-ts/oas31';\n\nimport { isRef, parseRef, resolveRef } from '@sdk-it/core/ref.js';\nimport { isEmpty, pascalcase } from '@sdk-it/core/utils.js';\n\nimport { findUniqueSchemaName } from '../find-unique-schema-name.js';\nimport { iterateOperations } from '../for-each-operation.js';\nimport {\n isBinaryContentType,\n isSseContentType,\n isStreamingContentType,\n isSuccessStatusCode,\n isTextContentType,\n parseJsonContentType,\n} from '../is.js';\nimport type { ResponsesConfig } from '../options.js';\nimport type { ProcessingPlugin } from '../processing.js';\nimport type { IR, TunedOperationObject } from '../types.js';\n\nfunction normalizeOperationResponses(\n spec: IR,\n operationId: string,\n operation: OperationObject,\n responsesConfig?: ResponsesConfig,\n) {\n const responses = operation.responses ?? {};\n operation.responses ??= {};\n let foundSuccessResponse = false;\n for (const status in responses) {\n operation.responses[status] = structuredClone(\n resolveRef<ResponseObject>(spec, responses[status]),\n );\n\n if (status !== 'default' && isSuccessStatusCode(status)) {\n foundSuccessResponse = true;\n }\n }\n\n if (!foundSuccessResponse) {\n operation.responses['200'] = {\n description: 'OK',\n content: {\n 'application/json': {\n schema: {},\n },\n },\n };\n }\n\n for (const status in operation.responses) {\n const response = operation.responses[status] as ResponseObject;\n const statusCode = +status;\n\n if (\n !responsesConfig?.flattenErrorResponses &&\n !isSuccessStatusCode(status)\n ) {\n continue;\n }\n\n if (isEmpty(response.content)) {\n response.content = {\n 'application/octet-stream': {},\n };\n }\n\n let responseName: string | undefined;\n for (const [contentType, mediaType] of Object.entries(\n response.content as Record<string, MediaTypeObject>,\n )) {\n if (isRef(mediaType.schema)) {\n const { model } = parseRef(mediaType.schema.$ref);\n Object.assign(spec.components.schemas[model], {\n 'x-responsebody': true,\n });\n responseName ??= model;\n continue;\n }\n const outputName =\n statusCode !== 200\n ? findUniqueSchemaName(spec, `${pascalcase(operationId)}${status}`, [\n 'output',\n 'payload',\n 'result',\n ])\n : findUniqueSchemaName(spec, operationId, [\n 'output',\n 'payload',\n 'result',\n ]);\n responseName ??= outputName;\n const isSse = isSseContentType(contentType);\n const normalizedContentType = normalizeContentType(contentType);\n const hasContentDisposition = hasHeader(\n response.headers,\n 'Content-Disposition',\n );\n const isBinary =\n isBinaryContentType(contentType) ||\n (isStreamingContentType(normalizedContentType) &&\n hasContentDisposition);\n const isText = isTextContentType(contentType);\n\n if (parseJsonContentType(contentType)) {\n if (isEmpty(mediaType.schema)) {\n spec.components.schemas[outputName] = {\n type: 'object',\n additionalProperties: true,\n };\n }\n } else {\n if (isEmpty(mediaType.schema) && (isText || isBinary)) {\n mediaType.schema = {\n type: 'string',\n ...(isBinary ? { format: 'binary' } : {}),\n };\n }\n spec.components.schemas[outputName] = {\n ...spec.components.schemas[outputName],\n 'x-stream': isSse || (!isText && !isBinary),\n ...(isSse ? { 'x-sse': true } : {}),\n };\n }\n\n spec.components.schemas[outputName] = {\n ...spec.components.schemas[outputName],\n ...mediaType.schema,\n 'x-responsebody': true,\n 'x-response-group': operationId,\n };\n operation.responses[status].content[contentType].schema = {\n $ref: `#/components/schemas/${outputName}`,\n };\n }\n if (responseName) {\n response['x-response-name'] = responseName;\n }\n }\n\n return operation.responses;\n}\n\nfunction normalizeContentType(contentType: string) {\n try {\n return parseContentType(contentType)?.type?.toLowerCase();\n } catch {\n return contentType.split(';')[0]?.trim().toLowerCase();\n }\n}\n\nfunction hasHeader(headers: ResponseObject['headers'], name: string) {\n if (!headers) {\n return false;\n }\n const target = name.toLowerCase();\n return Object.keys(headers).some((key) => key.toLowerCase() === target);\n}\n\nexport function normalizeResponses(): ProcessingPlugin {\n return {\n name: 'normalize-responses',\n process({ spec, options, report }) {\n for (const { entry, operation } of iterateOperations(spec)) {\n if (!operation.operationId) {\n throw new Error(\n `Cannot normalize responses before assigning an operation ID for ${entry.method.toUpperCase()} ${entry.path}`,\n );\n }\n const hadSuccessResponse = Object.keys(operation.responses ?? {}).some(\n isSuccessStatusCode,\n );\n (operation as TunedOperationObject).responses =\n normalizeOperationResponses(\n spec,\n operation.operationId,\n operation,\n options.responses,\n );\n if (!hadSuccessResponse) {\n report({\n severity: 'warning',\n code: 'success-response-added',\n message: 'Added a default 200 success response',\n path: `${entry.method.toUpperCase()} ${entry.path}`,\n });\n }\n }\n },\n };\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1 +1 @@
1
- {"version":3,"file":"normalize-schemas.d.ts","sourceRoot":"","sources":["../../../src/lib/processing-plugins/normalize-schemas.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAGzD,wBAAgB,gBAAgB,IAAI,gBAAgB,CAOnD"}
1
+ {"version":3,"file":"normalize-schemas.d.ts","sourceRoot":"","sources":["../../../src/lib/processing-plugins/normalize-schemas.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAmLzD,wBAAgB,gBAAgB,IAAI,gBAAgB,CAOnD"}
@@ -1,9 +1,166 @@
1
- import { fixSpec } from "../tune.js";
1
+ import { merge, uniq } from "lodash-es";
2
+ import assert from "node:assert";
3
+ import { isEmpty, isRef, notRef, resolveRef, snakecase } from "@sdk-it/core";
4
+ import { findVarients } from "../find-polymorphic-varients.js";
5
+ import { formatName } from "../format-name.js";
6
+ import { isPrimitiveSchema } from "../is-primitive-schema.js";
7
+ function normalizeSchemaObjects(spec, schemas, visited = /* @__PURE__ */ new Set()) {
8
+ for (const schema of schemas) {
9
+ if (isRef(schema)) continue;
10
+ if (!isEmpty(schema.properties)) {
11
+ schema.type = "object";
12
+ delete schema.oneOf;
13
+ delete schema.anyOf;
14
+ normalizeSchemaObjects(spec, Object.values(schema.properties), visited);
15
+ for (const [key, value] of Object.entries(schema.properties)) {
16
+ if (notRef(value) && isPrimitiveSchema(value)) {
17
+ value.default ??= schema.default?.[key];
18
+ delete schema.default?.[key];
19
+ }
20
+ }
21
+ delete schema.default;
22
+ }
23
+ if (!isEmpty(schema["x-properties"])) {
24
+ normalizeSchemaObjects(
25
+ spec,
26
+ Object.values(schema["x-properties"]),
27
+ visited
28
+ );
29
+ }
30
+ if (!isEmpty(schema.items)) {
31
+ delete schema.oneOf;
32
+ delete schema.anyOf;
33
+ schema.type = "array";
34
+ normalizeSchemaObjects(spec, [schema.items], visited);
35
+ const items = resolveRef(spec, schema.items);
36
+ if (Array.isArray(items.default)) {
37
+ schema.default ??= structuredClone(items.default);
38
+ }
39
+ delete items.default;
40
+ }
41
+ if (!isEmpty(schema.anyOf) && !isEmpty(schema.oneOf)) {
42
+ delete schema.anyOf;
43
+ }
44
+ if (isEmpty(schema.enum)) {
45
+ delete schema.enum;
46
+ }
47
+ if (!isEmpty(schema.enum)) {
48
+ if (schema.enum.length === 1) {
49
+ schema.const = schema.enum[0];
50
+ delete schema.enum;
51
+ } else {
52
+ const valuesSet = /* @__PURE__ */ new Set();
53
+ const valuesList = [];
54
+ for (const value of schema.enum) {
55
+ const formattedValue = formatName(snakecase(formatName(value)));
56
+ if (!valuesSet.has(formattedValue)) {
57
+ valuesSet.add(formattedValue);
58
+ valuesList.push(value);
59
+ }
60
+ }
61
+ schema.enum = valuesList;
62
+ }
63
+ delete schema.allOf;
64
+ }
65
+ if (schema.const !== void 0) {
66
+ schema.default = schema.const;
67
+ }
68
+ if (!isEmpty(schema.allOf)) {
69
+ const schemas2 = schema.allOf;
70
+ const resolved = schemas2.map(
71
+ (item) => resolveRef(spec, item)
72
+ );
73
+ const hasObjects = resolved.some((item) => item.type === "object");
74
+ const hasOtherTypes = resolved.some(
75
+ (item) => item.type && item.type !== "object"
76
+ );
77
+ if (hasObjects && hasOtherTypes) {
78
+ assert(false, `allOf must be an object`);
79
+ }
80
+ merge(
81
+ schema,
82
+ ...resolved.map((resolvedSchema, index) => {
83
+ const sourceSchema = schemas2[index];
84
+ if (isRef(sourceSchema)) {
85
+ if (visited.has(sourceSchema.$ref)) {
86
+ throw new Error(
87
+ `Circular allOf reference detected: ${[
88
+ ...visited,
89
+ sourceSchema.$ref
90
+ ].join(" -> ")}`
91
+ );
92
+ }
93
+ normalizeSchemaObjects(
94
+ spec,
95
+ [resolvedSchema],
96
+ new Set(visited).add(sourceSchema.$ref)
97
+ );
98
+ } else {
99
+ normalizeSchemaObjects(spec, [resolvedSchema], visited);
100
+ }
101
+ return resolvedSchema;
102
+ })
103
+ );
104
+ delete schema.allOf;
105
+ } else {
106
+ delete schema.allOf;
107
+ }
108
+ if (schema.type === "object" && isEmpty(schema.properties) && typeof schema.additionalProperties === "object" && !isEmpty(schema.additionalProperties) && notRef(schema.additionalProperties) && !isEmpty(schema.additionalProperties.properties)) {
109
+ normalizeSchemaObjects(
110
+ spec,
111
+ Object.values(schema.additionalProperties.properties),
112
+ visited
113
+ );
114
+ }
115
+ for (const kind of ["oneOf", "anyOf"]) {
116
+ if (!isEmpty(schema[kind])) {
117
+ delete schema.type;
118
+ normalizeSchemaObjects(spec, schema[kind], visited);
119
+ if (isEmpty(schema[kind])) {
120
+ continue;
121
+ }
122
+ let enumSchemaIndex = -1;
123
+ const enumValues = [];
124
+ for (let index = 0; index < schema[kind].length; index++) {
125
+ const item = schema[kind][index];
126
+ if (notRef(item) && item.type === "string") {
127
+ if (item.enum && item.enum.length > 1) {
128
+ enumValues.push(...item.enum);
129
+ if (enumSchemaIndex === -1) {
130
+ enumSchemaIndex = index;
131
+ }
132
+ }
133
+ }
134
+ }
135
+ if (enumSchemaIndex !== -1) {
136
+ const enumSchema = schema[kind][enumSchemaIndex];
137
+ if (notRef(enumSchema)) {
138
+ enumSchema.enum = uniq(enumValues);
139
+ }
140
+ schema[kind] = schema[kind].filter(
141
+ (item, index) => index === enumSchemaIndex || isRef(item)
142
+ );
143
+ }
144
+ const otherTypes = schema[kind].filter(
145
+ (item) => resolveRef(spec, item).type !== "null"
146
+ );
147
+ if (otherTypes.length === 1) {
148
+ Object.assign(schema, otherTypes[0]);
149
+ delete schema[kind];
150
+ continue;
151
+ }
152
+ schema["x-varients"] = findVarients(spec, schema[kind]);
153
+ } else {
154
+ delete schema[kind];
155
+ }
156
+ }
157
+ }
158
+ }
2
159
  function normalizeSchemas() {
3
160
  return {
4
161
  name: "normalize-schemas",
5
162
  process({ spec }) {
6
- fixSpec(spec, Object.values(spec.components.schemas));
163
+ normalizeSchemaObjects(spec, Object.values(spec.components.schemas));
7
164
  }
8
165
  };
9
166
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/lib/processing-plugins/normalize-schemas.ts"],
4
- "sourcesContent": ["import type { ProcessingPlugin } from '../processing.js';\nimport { fixSpec } from '../tune.js';\n\nexport function normalizeSchemas(): ProcessingPlugin {\n return {\n name: 'normalize-schemas',\n process({ spec }) {\n fixSpec(spec, Object.values(spec.components.schemas));\n },\n };\n}\n"],
5
- "mappings": "AACA,SAAS,eAAe;AAEjB,SAAS,mBAAqC;AACnD,SAAO;AAAA,IACL,MAAM;AAAA,IACN,QAAQ,EAAE,KAAK,GAAG;AAChB,cAAQ,MAAM,OAAO,OAAO,KAAK,WAAW,OAAO,CAAC;AAAA,IACtD;AAAA,EACF;AACF;",
6
- "names": []
4
+ "sourcesContent": ["import { merge, uniq } from 'lodash-es';\nimport assert from 'node:assert';\nimport type { ReferenceObject, SchemaObject } from 'openapi3-ts/oas31';\n\nimport { isEmpty, isRef, notRef, resolveRef, snakecase } from '@sdk-it/core';\n\nimport { findVarients } from '../find-polymorphic-varients.js';\nimport { formatName } from '../format-name.js';\nimport { isPrimitiveSchema } from '../is-primitive-schema.js';\nimport type { ProcessingPlugin } from '../processing.js';\nimport type { IR } from '../types.js';\n\nfunction normalizeSchemaObjects(\n spec: IR,\n schemas: (SchemaObject | ReferenceObject)[],\n visited = new Set<string>(),\n) {\n for (const schema of schemas) {\n if (isRef(schema)) continue;\n\n if (!isEmpty(schema.properties)) {\n schema.type = 'object';\n delete schema.oneOf;\n delete schema.anyOf;\n normalizeSchemaObjects(spec, Object.values(schema.properties), visited);\n for (const [key, value] of Object.entries(schema.properties)) {\n if (notRef(value) && isPrimitiveSchema(value)) {\n value.default ??= schema.default?.[key];\n delete schema.default?.[key];\n }\n }\n delete schema.default;\n }\n\n if (!isEmpty(schema['x-properties'])) {\n normalizeSchemaObjects(\n spec,\n Object.values(schema['x-properties']),\n visited,\n );\n }\n\n if (!isEmpty(schema.items)) {\n delete schema.oneOf;\n delete schema.anyOf;\n schema.type = 'array';\n normalizeSchemaObjects(spec, [schema.items], visited);\n const items = resolveRef<SchemaObject>(spec, schema.items);\n if (Array.isArray(items.default)) {\n schema.default ??= structuredClone(items.default);\n }\n delete items.default;\n }\n\n if (!isEmpty(schema.anyOf) && !isEmpty(schema.oneOf)) {\n delete schema.anyOf;\n }\n\n if (isEmpty(schema.enum)) {\n delete schema.enum;\n }\n\n if (!isEmpty(schema.enum)) {\n if (schema.enum.length === 1) {\n schema.const = schema.enum[0];\n delete schema.enum;\n } else {\n const valuesSet = new Set<string>();\n const valuesList = [];\n for (const value of schema.enum) {\n const formattedValue = formatName(snakecase(formatName(value)));\n if (!valuesSet.has(formattedValue)) {\n valuesSet.add(formattedValue);\n valuesList.push(value);\n }\n }\n schema.enum = valuesList;\n }\n delete schema.allOf;\n }\n\n if (schema.const !== undefined) {\n schema.default = schema.const;\n }\n\n if (!isEmpty(schema.allOf)) {\n const schemas = schema.allOf;\n const resolved = schemas.map((item) =>\n resolveRef<SchemaObject>(spec, item),\n );\n const hasObjects = resolved.some((item) => item.type === 'object');\n const hasOtherTypes = resolved.some(\n (item) => item.type && item.type !== 'object',\n );\n if (hasObjects && hasOtherTypes) {\n assert(false, `allOf must be an object`);\n }\n merge(\n schema,\n ...resolved.map((resolvedSchema, index) => {\n const sourceSchema = schemas[index];\n if (isRef(sourceSchema)) {\n if (visited.has(sourceSchema.$ref)) {\n throw new Error(\n `Circular allOf reference detected: ${[\n ...visited,\n sourceSchema.$ref,\n ].join(' -> ')}`,\n );\n }\n normalizeSchemaObjects(\n spec,\n [resolvedSchema],\n new Set(visited).add(sourceSchema.$ref),\n );\n } else {\n normalizeSchemaObjects(spec, [resolvedSchema], visited);\n }\n return resolvedSchema;\n }),\n );\n delete schema.allOf;\n } else {\n delete schema.allOf;\n }\n\n if (\n schema.type === 'object' &&\n isEmpty(schema.properties) &&\n typeof schema.additionalProperties === 'object' &&\n !isEmpty(schema.additionalProperties) &&\n notRef(schema.additionalProperties) &&\n !isEmpty(schema.additionalProperties.properties)\n ) {\n normalizeSchemaObjects(\n spec,\n Object.values(schema.additionalProperties.properties),\n visited,\n );\n }\n\n for (const kind of ['oneOf', 'anyOf'] as const) {\n if (!isEmpty(schema[kind])) {\n delete schema.type;\n normalizeSchemaObjects(spec, schema[kind], visited);\n if (isEmpty(schema[kind])) {\n continue;\n }\n\n let enumSchemaIndex = -1;\n const enumValues: string[] = [];\n for (let index = 0; index < schema[kind].length; index++) {\n const item = schema[kind][index];\n if (notRef(item) && item.type === 'string') {\n if (item.enum && item.enum.length > 1) {\n enumValues.push(...item.enum);\n if (enumSchemaIndex === -1) {\n enumSchemaIndex = index;\n }\n }\n }\n }\n if (enumSchemaIndex !== -1) {\n const enumSchema = schema[kind][enumSchemaIndex];\n if (notRef(enumSchema)) {\n enumSchema.enum = uniq(enumValues);\n }\n schema[kind] = schema[kind].filter(\n (item, index) => index === enumSchemaIndex || isRef(item),\n );\n }\n const otherTypes = schema[kind].filter(\n (item) => resolveRef<SchemaObject>(spec, item).type !== 'null',\n );\n if (otherTypes.length === 1) {\n Object.assign(schema, otherTypes[0]);\n delete schema[kind];\n continue;\n }\n\n schema['x-varients'] = findVarients(spec, schema[kind]);\n } else {\n delete schema[kind];\n }\n }\n }\n}\n\nexport function normalizeSchemas(): ProcessingPlugin {\n return {\n name: 'normalize-schemas',\n process({ spec }) {\n normalizeSchemaObjects(spec, Object.values(spec.components.schemas));\n },\n };\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["schemas"]
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sdk-it/spec",
3
- "version": "0.46.0",
3
+ "version": "0.46.2",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",
@@ -27,7 +27,7 @@
27
27
  "!**/*.test.*"
28
28
  ],
29
29
  "dependencies": {
30
- "@sdk-it/core": "0.46.0",
30
+ "@sdk-it/core": "0.46.2",
31
31
  "openapi3-ts": "4.5.0",
32
32
  "pluralize": "^8.0.0",
33
33
  "stringcase": "^4.3.1",
@@ -1,5 +0,0 @@
1
- import type { OperationObject, ParameterObject, SchemaObject, SecurityRequirementObject } from 'openapi3-ts/oas31';
2
- import type { IR, OurRequestBodyObject } from './types.js';
3
- export declare function patchParameters(spec: IR, schema: SchemaObject, parameters: ParameterObject[], security: SecurityRequirementObject[]): void;
4
- export declare function tuneRequestBody(spec: IR, operationId: string, operation: OperationObject, parameters: ParameterObject[], security: SecurityRequirementObject[]): OurRequestBodyObject;
5
- //# sourceMappingURL=tune-request-body.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"tune-request-body.d.ts","sourceRoot":"","sources":["../../src/lib/tune-request-body.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EACf,eAAe,EAEf,YAAY,EACZ,yBAAyB,EAC1B,MAAM,mBAAmB,CAAC;AAO3B,OAAO,KAAK,EAAE,EAAE,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAE3D,wBAAgB,eAAe,CAC7B,IAAI,EAAE,EAAE,EACR,MAAM,EAAE,YAAY,EACpB,UAAU,EAAE,eAAe,EAAE,EAC7B,QAAQ,EAAE,yBAAyB,EAAE,QAiCtC;AAED,wBAAgB,eAAe,CAC7B,IAAI,EAAE,EAAE,EACR,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,eAAe,EAC1B,UAAU,EAAE,eAAe,EAAE,EAC7B,QAAQ,EAAE,yBAAyB,EAAE,GACpC,oBAAoB,CAuEtB"}