toolcraft-openapi 0.0.13 → 0.0.14

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.
@@ -2,6 +2,9 @@ import { type HttpMethod } from "./naming.js";
2
2
  type OpenApiOperation = OpenApiOperationObject | OpenApiReferenceObject;
3
3
  type OpenApiOperationMap = Partial<Record<HttpMethod, OpenApiOperation>>;
4
4
  type OpenApiParameterLocation = "path" | "query" | "header" | "cookie";
5
+ type OpenApiScalarType = "string" | "number" | "integer" | "boolean";
6
+ type OpenApiSchemaType = OpenApiScalarType | "object" | "array";
7
+ type OpenApiJsonSchemaType = OpenApiSchemaType | "null";
5
8
  export type GeneratedRequestLocation = Exclude<GeneratedParam["location"], "transport">;
6
9
  export interface OpenApiDocument {
7
10
  openapi?: string;
@@ -57,7 +60,7 @@ export interface OpenApiMediaTypeObject {
57
60
  schema?: OpenApiSchemaObject | OpenApiReferenceObject;
58
61
  }
59
62
  export interface OpenApiSchemaObject {
60
- type?: "string" | "number" | "integer" | "boolean" | "object" | "array";
63
+ type?: OpenApiJsonSchemaType | readonly OpenApiJsonSchemaType[];
61
64
  additionalProperties?: boolean | OpenApiSchemaObject | OpenApiReferenceObject;
62
65
  allOf?: Array<OpenApiSchemaObject | OpenApiReferenceObject>;
63
66
  anyOf?: Array<OpenApiSchemaObject | OpenApiReferenceObject>;
package/dist/generate.js CHANGED
@@ -324,17 +324,11 @@ function assertSupportedSuccessResponseSchema(document, schema, operationId, con
324
324
  }
325
325
  }
326
326
  function expectSupportedSuccessResponseSchema(document, schema, operationId, context) {
327
- const resolvedSchema = resolveSchema(document, schema, operationId, context);
327
+ const resolvedSchema = normalizeNullableSchema(document, resolveSchema(document, schema, operationId, context), operationId, context);
328
328
  const compositionKeyword = getCompositionKeyword(resolvedSchema);
329
329
  if (compositionKeyword === undefined) {
330
330
  return resolvedSchema;
331
331
  }
332
- const nullableAnyOfSchema = compositionKeyword === "anyOf"
333
- ? resolveNullableAnyOfSchema(document, resolvedSchema, operationId, context)
334
- : undefined;
335
- if (nullableAnyOfSchema !== undefined) {
336
- return nullableAnyOfSchema;
337
- }
338
332
  throw new UserError(`Operation ${JSON.stringify(operationId)} uses unsupported ${context}. JSON Schema composition keyword ${JSON.stringify(compositionKeyword)} is not supported in v1.`);
339
333
  }
340
334
  function createGeneratedParameter(document, parameter, operationId) {
@@ -578,6 +572,9 @@ function createCollectedRequestBodyParams(assemblies, bodyOptional, requestBodyD
578
572
  };
579
573
  }
580
574
  function createParamDefinition(document, schema, operationId, context) {
575
+ if (Array.isArray(schema.type)) {
576
+ throw new UserError(`Operation ${JSON.stringify(operationId)} uses unsupported ${context}. JSON Schema type arrays with multiple non-null types are not supported in v1.`);
577
+ }
581
578
  if (schema.type === "array") {
582
579
  const itemSchema = expectArrayItemsSchema(document, schema, operationId, context);
583
580
  return {
@@ -589,9 +586,9 @@ function createParamDefinition(document, schema, operationId, context) {
589
586
  ...(schema.nullable === true ? { nullable: true } : {})
590
587
  };
591
588
  }
592
- const scalarDefinition = schema.type === undefined || !(schema.type in SCHEMA_TYPE_TO_KIND)
593
- ? undefined
594
- : SCHEMA_TYPE_TO_KIND[schema.type];
589
+ const scalarDefinition = isOpenApiScalarType(schema.type)
590
+ ? SCHEMA_TYPE_TO_KIND[schema.type]
591
+ : undefined;
595
592
  const enumValues = normalizeEnumValues(schema.enum, operationId, context, schema.nullable === true, schema.type);
596
593
  if (enumValues !== undefined) {
597
594
  return {
@@ -747,6 +744,9 @@ function expectOperation(document, operation, method, path, refChain = []) {
747
744
  function isEnumPrimitiveValue(value) {
748
745
  return typeof value === "string" || typeof value === "number" || typeof value === "boolean";
749
746
  }
747
+ function isOpenApiScalarType(type) {
748
+ return typeof type === "string" && type in SCHEMA_TYPE_TO_KIND;
749
+ }
750
750
  function expectRequestBody(document, requestBody, operationId, context, refChain = []) {
751
751
  if (!isReferenceObject(requestBody)) {
752
752
  return requestBody;
@@ -763,7 +763,7 @@ function expectResponse(document, response, operationId, statusCode, refChain =
763
763
  return expectResponse(document, resolveLocalReference(document, response.$ref, operationId, context), operationId, statusCode, [...refChain, response.$ref]);
764
764
  }
765
765
  function expectSchema(document, schema, operationId, context, refChain = []) {
766
- const resolvedSchema = resolveSchema(document, schema, operationId, context, refChain);
766
+ const resolvedSchema = normalizeNullableSchema(document, resolveSchema(document, schema, operationId, context, refChain), operationId, context);
767
767
  const compositionKeyword = getCompositionKeyword(resolvedSchema);
768
768
  if (compositionKeyword !== undefined) {
769
769
  throw new UserError(`Operation ${JSON.stringify(operationId)} uses unsupported ${context}. JSON Schema composition keyword ${JSON.stringify(compositionKeyword)} is not supported in v1.`);
@@ -780,6 +780,28 @@ function resolveSchema(document, schema, operationId, context, refChain = []) {
780
780
  }
781
781
  return schema;
782
782
  }
783
+ function normalizeNullableSchema(document, schema, operationId, context) {
784
+ const typeNormalizedSchema = normalizeNullableTypeArray(schema);
785
+ if (getCompositionKeyword(typeNormalizedSchema) !== "anyOf") {
786
+ return typeNormalizedSchema;
787
+ }
788
+ return (resolveNullableAnyOfSchema(document, typeNormalizedSchema, operationId, context) ??
789
+ typeNormalizedSchema);
790
+ }
791
+ function normalizeNullableTypeArray(schema) {
792
+ if (!Array.isArray(schema.type)) {
793
+ return schema;
794
+ }
795
+ const nonNullTypes = schema.type.filter((type) => type !== "null");
796
+ if (nonNullTypes.length === schema.type.length) {
797
+ return schema;
798
+ }
799
+ return {
800
+ ...schema,
801
+ type: nonNullTypes.length === 1 ? nonNullTypes[0] : nonNullTypes,
802
+ nullable: true
803
+ };
804
+ }
783
805
  function getCompositionKeyword(schema) {
784
806
  for (const keyword of ["allOf", "anyOf", "oneOf"]) {
785
807
  if (schema[keyword] !== undefined) {
@@ -793,7 +815,7 @@ function resolveNullableAnyOfSchema(document, schema, operationId, context) {
793
815
  if (variants === undefined || variants.length !== 2) {
794
816
  return undefined;
795
817
  }
796
- const resolvedVariants = variants.map((variant, index) => resolveSchema(document, variant, operationId, `${context} anyOf variant ${index}`));
818
+ const resolvedVariants = variants.map((variant, index) => normalizeNullableSchema(document, resolveSchema(document, variant, operationId, `${context} anyOf variant ${index}`), operationId, `${context} anyOf variant ${index}`));
797
819
  const nullVariantIndex = resolvedVariants.findIndex(isExplicitNullSchema);
798
820
  if (nullVariantIndex === -1) {
799
821
  return undefined;
@@ -802,8 +824,14 @@ function resolveNullableAnyOfSchema(document, schema, operationId, context) {
802
824
  if (nonNullVariant === undefined || getCompositionKeyword(nonNullVariant) !== undefined) {
803
825
  return undefined;
804
826
  }
827
+ const { anyOf: _anyOf, nullable: _nullable, ...wrapperSchema } = schema;
828
+ void _anyOf;
829
+ void _nullable;
805
830
  return {
831
+ ...wrapperSchema,
806
832
  ...nonNullVariant,
833
+ description: nonNullVariant.description ?? schema.description,
834
+ default: nonNullVariant.default ?? schema.default,
807
835
  nullable: true
808
836
  };
809
837
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toolcraft-openapi",
3
- "version": "0.0.13",
3
+ "version": "0.0.14",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -30,7 +30,7 @@
30
30
  "auth-store": "^0.0.1",
31
31
  "chalk": "^5.6.2",
32
32
  "console-table-printer": "^2.15.0",
33
- "toolcraft": "^0.0.13",
33
+ "toolcraft": "^0.0.14",
34
34
  "yaml": "^2.8.2"
35
35
  },
36
36
  "engines": {