schema-components 1.21.0 → 1.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/README.md +1 -1
  2. package/dist/core/adapter.d.mts +20 -3
  3. package/dist/core/adapter.mjs +209 -28
  4. package/dist/core/constraints.d.mts +2 -2
  5. package/dist/core/diagnostics.d.mts +1 -1
  6. package/dist/core/errors.d.mts +1 -1
  7. package/dist/core/fieldOrder.d.mts +1 -1
  8. package/dist/core/formats.d.mts +22 -1
  9. package/dist/core/formats.mjs +21 -0
  10. package/dist/core/limits.d.mts +2 -0
  11. package/dist/core/limits.mjs +23 -0
  12. package/dist/core/merge.d.mts +1 -1
  13. package/dist/core/normalise.d.mts +29 -3
  14. package/dist/core/normalise.mjs +2 -2
  15. package/dist/core/openapi30.mjs +1 -1
  16. package/dist/core/ref.d.mts +1 -1
  17. package/dist/core/ref.mjs +1 -0
  18. package/dist/core/renderer.d.mts +1 -1
  19. package/dist/core/renderer.mjs +0 -2
  20. package/dist/core/swagger2.d.mts +1 -1
  21. package/dist/core/swagger2.mjs +1 -1
  22. package/dist/core/typeInference.d.mts +2 -2
  23. package/dist/core/types.d.mts +2 -2
  24. package/dist/core/types.mjs +1 -4
  25. package/dist/core/version.d.mts +1 -1
  26. package/dist/core/walkBuilders.d.mts +3 -3
  27. package/dist/core/walker.d.mts +1 -1
  28. package/dist/core/walker.mjs +79 -2
  29. package/dist/{diagnostics-CbBPsxSt.d.mts → diagnostics-D0QCYGv0.d.mts} +1 -1
  30. package/dist/{errors-QEwOtQAA.d.mts → errors-DpFwqs5C.d.mts} +1 -1
  31. package/dist/html/a11y.d.mts +2 -2
  32. package/dist/html/a11y.mjs +10 -3
  33. package/dist/html/renderToHtml.d.mts +10 -3
  34. package/dist/html/renderToHtml.mjs +13 -3
  35. package/dist/html/renderToHtmlStream.d.mts +2 -2
  36. package/dist/html/renderers.d.mts +2 -2
  37. package/dist/html/renderers.mjs +0 -5
  38. package/dist/html/streamRenderers.d.mts +5 -4
  39. package/dist/html/streamRenderers.mjs +91 -30
  40. package/dist/limits-Cw5QZND8.d.mts +29 -0
  41. package/dist/{normalise-DaSrnr8g.mjs → normalise-DVEJQmF7.mjs} +468 -115
  42. package/dist/openapi/ApiCallbacks.d.mts +1 -1
  43. package/dist/openapi/ApiLinks.d.mts +1 -1
  44. package/dist/openapi/ApiResponseHeaders.d.mts +1 -1
  45. package/dist/openapi/ApiSecurity.d.mts +1 -1
  46. package/dist/openapi/ApiSecurity.mjs +16 -2
  47. package/dist/openapi/components.d.mts +150 -18
  48. package/dist/openapi/components.mjs +129 -15
  49. package/dist/openapi/parser.d.mts +1 -1
  50. package/dist/openapi/parser.mjs +35 -3
  51. package/dist/openapi/resolve.d.mts +12 -5
  52. package/dist/openapi/resolve.mjs +183 -23
  53. package/dist/react/SchemaComponent.d.mts +100 -35
  54. package/dist/react/SchemaComponent.mjs +59 -45
  55. package/dist/react/SchemaView.d.mts +3 -3
  56. package/dist/react/SchemaView.mjs +2 -2
  57. package/dist/react/fieldPath.d.mts +1 -1
  58. package/dist/react/headless.d.mts +1 -1
  59. package/dist/react/headless.mjs +1 -2
  60. package/dist/react/headlessRenderers.d.mts +3 -4
  61. package/dist/react/headlessRenderers.mjs +10 -30
  62. package/dist/{ref-si8ViYun.d.mts → ref-D-_JBZkF.d.mts} +1 -1
  63. package/dist/{renderer-DI6ZYf7a.d.mts → renderer-BaRlQIuN.d.mts} +2 -2
  64. package/dist/themes/mantine.d.mts +1 -1
  65. package/dist/themes/mui.d.mts +1 -1
  66. package/dist/themes/radix.d.mts +1 -1
  67. package/dist/themes/shadcn.d.mts +1 -1
  68. package/dist/typeInference-DkcUHfaM.d.mts +982 -0
  69. package/dist/{types-BnxPEElk.d.mts → types-BrRMV0en.d.mts} +3 -10
  70. package/package.json +1 -3
  71. package/dist/typeInference-Bxw3NOG1.d.mts +0 -647
  72. /package/dist/{version-D-u7aMfy.d.mts → version-D2jfdX6E.d.mts} +0 -0
@@ -29,6 +29,8 @@ function normaliseOpenApi30Node(node) {
29
29
  return node;
30
30
  }
31
31
  const nullOption = { type: "null" };
32
+ if (typeof node.$ref === "string") return { anyOf: [{ $ref: node.$ref }, nullOption] };
33
+ if (Array.isArray(node.enum) && !node.enum.includes(null)) node.enum = [...node.enum, null];
32
34
  if (Array.isArray(node.anyOf)) {
33
35
  node.anyOf = [...node.anyOf, nullOption];
34
36
  delete node.nullable;
@@ -113,7 +115,13 @@ function normaliseOpenApi30Discriminator(node) {
113
115
  }
114
116
  if ("oneOf" in node) node.oneOf = normalisedComposite;
115
117
  else if ("anyOf" in node) node.anyOf = normalisedComposite;
116
- delete node.discriminator;
118
+ const extensions = {};
119
+ for (const [key, value] of Object.entries(discriminator)) if (key.startsWith("x-")) extensions[key] = value;
120
+ if (Object.keys(extensions).length > 0) node.discriminator = {
121
+ propertyName,
122
+ ...extensions
123
+ };
124
+ else delete node.discriminator;
117
125
  return node;
118
126
  }
119
127
  /**
@@ -480,7 +488,7 @@ function normaliseParameter(param, normaliseSchema) {
480
488
  const content = param.content;
481
489
  if (isObject(content)) result.content = normaliseContentMap(content, normaliseSchema);
482
490
  if ("example" in result && !("examples" in result)) {
483
- result.examples = [result.example];
491
+ result.examples = { default: { value: result.example } };
484
492
  delete result.example;
485
493
  } else if ("example" in result) delete result.example;
486
494
  return result;
@@ -506,7 +514,7 @@ function normaliseHeader(header, normaliseSchema) {
506
514
  const content = header.content;
507
515
  if (isObject(content)) result.content = normaliseContentMap(content, normaliseSchema);
508
516
  if ("example" in result && !("examples" in result)) {
509
- result.examples = [result.example];
517
+ result.examples = { default: { value: result.example } };
510
518
  delete result.example;
511
519
  } else if ("example" in result) delete result.example;
512
520
  return result;
@@ -579,9 +587,19 @@ function normaliseSwagger2Document(doc, deepNormalise, normaliseDraft04Node, dia
579
587
  version: "0.0.0"
580
588
  }
581
589
  };
582
- if (typeof doc.host === "string" || typeof doc.basePath === "string" || Array.isArray(doc.schemes)) {
583
- const host = typeof doc.host === "string" ? doc.host : "localhost";
584
- const basePath = typeof doc.basePath === "string" ? doc.basePath : "/";
590
+ if (typeof doc.host !== "string") {
591
+ if (Array.isArray(doc.schemes) || typeof doc.basePath === "string") emitDiagnostic(diagnostics, {
592
+ code: "swagger-missing-host",
593
+ message: "Swagger 2.0 document declares schemes or basePath without host; skipping server URL synthesis",
594
+ pointer: "",
595
+ detail: {
596
+ hasSchemes: Array.isArray(doc.schemes),
597
+ hasBasePath: typeof doc.basePath === "string"
598
+ }
599
+ });
600
+ } else {
601
+ const host = doc.host;
602
+ const basePath = typeof doc.basePath === "string" ? doc.basePath : "";
585
603
  const schemes = Array.isArray(doc.schemes) ? doc.schemes : ["https"];
586
604
  result.servers = [{ url: `${typeof schemes[0] === "string" ? schemes[0] : "https"}://${host}${basePath}` }];
587
605
  }
@@ -597,31 +615,62 @@ function normaliseSwagger2Document(doc, deepNormalise, normaliseDraft04Node, dia
597
615
  const parameters = doc.parameters;
598
616
  const requestBodies = {};
599
617
  if (isObject(parameters)) {
600
- const globalConsumes = Array.isArray(doc.consumes) ? doc.consumes : ["application/json"];
618
+ const consumesResolution = resolveSwaggerContentTypes(void 0, doc.consumes);
619
+ const globalConsumes = consumesResolution.types;
601
620
  const convertedParameters = {};
602
621
  for (const [name, param] of Object.entries(parameters)) {
603
622
  if (!isObject(param)) {
604
623
  convertedParameters[name] = param;
605
624
  continue;
606
625
  }
607
- const resolved = resolveSwaggerParameter(param, doc);
626
+ const resolution = resolveSwaggerParameter(param, doc);
627
+ if (resolution.kind === "cycle") {
628
+ emitDiagnostic(diagnostics, {
629
+ code: "swagger-cyclic-parameter-ref",
630
+ message: `Cyclic Swagger 2.0 parameter $ref "${resolution.ref}"; skipping entry`,
631
+ pointer: appendPointer(appendPointer("", "parameters"), name),
632
+ detail: {
633
+ ref: resolution.ref,
634
+ name
635
+ }
636
+ });
637
+ continue;
638
+ }
639
+ const resolved = resolution.param;
608
640
  const location = resolved.in;
609
- if (location === "body") requestBodies[name] = buildRequestBody(resolved, globalConsumes);
610
- else if (location === "formData") requestBodies[name] = buildRequestBody(buildFormDataBody(resolved, [resolved]), formDataContentTypes(globalConsumes));
611
- else convertedParameters[name] = normaliseSwaggerParameter(resolved, doc);
641
+ if (location === "body") {
642
+ if (consumesResolution.source === "synthesised") emitDiagnostic(diagnostics, {
643
+ code: "swagger-missing-consumes",
644
+ message: "Global body parameter declared but document-level `consumes` is absent; defaulting to application/json",
645
+ pointer: appendPointer(appendPointer("", "parameters"), name),
646
+ detail: {
647
+ level: "document",
648
+ name
649
+ }
650
+ });
651
+ requestBodies[name] = buildRequestBody(resolved, globalConsumes);
652
+ } else if (location === "formData") requestBodies[name] = buildRequestBody(buildFormDataBody(resolved, [resolved]), formDataContentTypes(globalConsumes));
653
+ else {
654
+ const normalised = normaliseSwaggerParameter(resolved, doc, diagnostics, appendPointer(appendPointer("", "parameters"), name));
655
+ if (normalised !== void 0) convertedParameters[name] = normalised;
656
+ }
612
657
  }
613
658
  if (Object.keys(convertedParameters).length > 0) components.parameters = convertedParameters;
614
659
  }
615
660
  const responses = doc.responses;
616
661
  if (isObject(responses)) {
617
- const globalProduces = Array.isArray(doc.produces) ? doc.produces : ["application/json"];
662
+ const producesResolution = resolveSwaggerContentTypes(void 0, doc.produces);
618
663
  const convertedResponses = {};
619
- for (const [name, response] of Object.entries(responses)) convertedResponses[name] = isObject(response) ? normaliseSwaggerSingleResponse(response, doc, globalProduces) : response;
664
+ for (const [name, response] of Object.entries(responses)) convertedResponses[name] = isObject(response) ? normaliseSwaggerSingleResponse(response, doc, producesResolution.types, producesResolution.source, diagnostics, void 0, void 0, name) : response;
620
665
  components.responses = convertedResponses;
621
666
  }
622
667
  if (Object.keys(requestBodies).length > 0) components.requestBodies = requestBodies;
623
668
  const securityDefinitions = doc.securityDefinitions;
624
- if (isObject(securityDefinitions)) components.securitySchemes = { ...securityDefinitions };
669
+ if (isObject(securityDefinitions)) {
670
+ const translated = {};
671
+ for (const [name, scheme] of Object.entries(securityDefinitions)) translated[name] = isObject(scheme) ? translateSwaggerSecurityScheme(scheme) : scheme;
672
+ components.securitySchemes = translated;
673
+ }
625
674
  if (Object.keys(components).length > 0) result.components = components;
626
675
  if (Array.isArray(doc.tags)) result.tags = doc.tags;
627
676
  if (isObject(doc.externalDocs)) result.externalDocs = doc.externalDocs;
@@ -658,17 +707,29 @@ function normaliseSwaggerPaths(paths, doc, diagnostics) {
658
707
  normalisedPath[method] = normaliseSwaggerOperation(operation, doc, path, method, diagnostics);
659
708
  }
660
709
  const pathParams = pathItem.parameters;
661
- if (Array.isArray(pathParams)) normalisedPath.parameters = pathParams.map((p) => isObject(p) ? normaliseSwaggerParameter(p, doc) : p);
710
+ if (Array.isArray(pathParams)) {
711
+ const paramsPointer = appendPointer(appendPointer(appendPointer("", "paths"), path), "parameters");
712
+ const out = [];
713
+ for (const [index, p] of pathParams.entries()) {
714
+ if (!isObject(p)) {
715
+ out.push(p);
716
+ continue;
717
+ }
718
+ const normalised = normaliseSwaggerParameter(p, doc, diagnostics, appendPointer(paramsPointer, String(index)));
719
+ if (normalised !== void 0) out.push(normalised);
720
+ }
721
+ normalisedPath.parameters = out;
722
+ }
662
723
  result[path] = normalisedPath;
663
724
  }
664
725
  return result;
665
726
  }
666
727
  function normaliseSwaggerOperation(operation, doc, path, method, diagnostics) {
667
728
  const result = {};
668
- const globalProduces = Array.isArray(doc.produces) ? doc.produces : ["application/json"];
669
- const globalConsumes = Array.isArray(doc.consumes) ? doc.consumes : ["application/json"];
670
- const produces = Array.isArray(operation.produces) ? operation.produces : globalProduces;
671
- const consumes = Array.isArray(operation.consumes) ? operation.consumes : globalConsumes;
729
+ const consumesResolution = resolveSwaggerContentTypes(operation.consumes, doc.consumes);
730
+ const producesResolution = resolveSwaggerContentTypes(operation.produces, doc.produces);
731
+ const produces = producesResolution.types;
732
+ const consumes = consumesResolution.types;
672
733
  for (const [key, value] of Object.entries(operation)) if (key !== "parameters" && key !== "responses" && key !== "produces" && key !== "consumes") result[key] = value;
673
734
  const params = operation.parameters;
674
735
  if (Array.isArray(params)) {
@@ -681,7 +742,17 @@ function normaliseSwaggerOperation(operation, doc, path, method, diagnostics) {
681
742
  nonBodyParams.push(param);
682
743
  continue;
683
744
  }
684
- const resolvedParam = resolveSwaggerParameter(param, doc);
745
+ const paramResolution = resolveSwaggerParameter(param, doc);
746
+ if (paramResolution.kind === "cycle") {
747
+ emitDiagnostic(diagnostics, {
748
+ code: "swagger-cyclic-parameter-ref",
749
+ message: `Cyclic Swagger 2.0 parameter $ref "${paramResolution.ref}"; skipping entry`,
750
+ pointer: appendPointer(appendPointer(appendPointer(appendPointer(appendPointer("", "paths"), path), method), "parameters"), String(index)),
751
+ detail: { ref: paramResolution.ref }
752
+ });
753
+ continue;
754
+ }
755
+ const resolvedParam = paramResolution.param;
685
756
  const location = resolvedParam.in;
686
757
  if (location === "body") {
687
758
  if (bodyParam !== void 0) {
@@ -705,16 +776,51 @@ function normaliseSwaggerOperation(operation, doc, path, method, diagnostics) {
705
776
  bodyParam = buildFormDataBody(resolvedParam, params);
706
777
  usesFormData = true;
707
778
  }
708
- } else nonBodyParams.push(normaliseSwaggerParameter(resolvedParam, doc));
779
+ } else {
780
+ const normalised = normaliseSwaggerParameter(resolvedParam, doc, diagnostics, appendPointer(appendPointer(appendPointer(appendPointer(appendPointer("", "paths"), path), method), "parameters"), String(index)));
781
+ if (normalised !== void 0) nonBodyParams.push(normalised);
782
+ }
709
783
  }
710
784
  if (nonBodyParams.length > 0) result.parameters = nonBodyParams;
711
- if (bodyParam !== void 0) result.requestBody = buildRequestBody(bodyParam, usesFormData ? formDataContentTypes(consumes) : consumes);
785
+ if (bodyParam !== void 0) {
786
+ const bodyContentTypes = usesFormData ? formDataContentTypes(consumes) : consumes;
787
+ if (!usesFormData && consumesResolution.source === "synthesised") emitDiagnostic(diagnostics, {
788
+ code: "swagger-missing-consumes",
789
+ message: "Operation declares a body parameter but neither operation-level nor document-level `consumes` is set; defaulting to application/json",
790
+ pointer: appendPointer(appendPointer(appendPointer("", "paths"), path), method),
791
+ detail: {
792
+ level: "operation",
793
+ method
794
+ }
795
+ });
796
+ result.requestBody = buildRequestBody(bodyParam, bodyContentTypes);
797
+ }
712
798
  }
713
799
  const responses = operation.responses;
714
- if (isObject(responses)) result.responses = normaliseSwaggerResponses(responses, doc, produces);
800
+ if (isObject(responses)) result.responses = normaliseSwaggerResponses(responses, doc, produces, producesResolution.source, diagnostics, path, method);
715
801
  return result;
716
802
  }
717
803
  /**
804
+ * Resolve a Swagger 2.0 `consumes` or `produces` array, recording
805
+ * where the value came from so callers can decide whether to emit a
806
+ * "missing content type" diagnostic. Per the Swagger 2.0 spec, absence
807
+ * at BOTH levels means no body — not an implicit `application/json`.
808
+ */
809
+ function resolveSwaggerContentTypes(operationLevel, documentLevel) {
810
+ if (Array.isArray(operationLevel)) return {
811
+ types: operationLevel,
812
+ source: "operation"
813
+ };
814
+ if (Array.isArray(documentLevel)) return {
815
+ types: documentLevel,
816
+ source: "document"
817
+ };
818
+ return {
819
+ types: ["application/json"],
820
+ source: "synthesised"
821
+ };
822
+ }
823
+ /**
718
824
  * Determine the request body media type for a Swagger 2.0 formData operation.
719
825
  *
720
826
  * Per the OAS 3 conversion rules, `application/x-www-form-urlencoded` is
@@ -729,12 +835,77 @@ function formDataContentTypes(consumes) {
729
835
  return ["multipart/form-data"];
730
836
  }
731
837
  /**
732
- * Resolve a Swagger parameter that may be a `$ref`.
838
+ * Every JSON-Schema-compatible constraint keyword Swagger 2.0 allows on
839
+ * a Parameter Object or Header Object alongside `type`/`format`. These
840
+ * lift into the synthesised `schema` so consumers see the original
841
+ * validation semantics under OAS 3.x's parameter shape.
842
+ *
843
+ * `allowEmptyValue` is included even though it is a Swagger 2.0
844
+ * parameter-level keyword in the source (not a schema keyword) — OAS
845
+ * 3.x defines it at the Parameter Object root, so the calling function
846
+ * keeps it at the parameter root rather than copying it into `schema`.
847
+ */
848
+ const SWAGGER_PARAM_SCHEMA_KEYWORDS = [
849
+ "enum",
850
+ "default",
851
+ "minimum",
852
+ "maximum",
853
+ "exclusiveMinimum",
854
+ "exclusiveMaximum",
855
+ "multipleOf",
856
+ "minLength",
857
+ "maxLength",
858
+ "pattern",
859
+ "minItems",
860
+ "maxItems",
861
+ "uniqueItems"
862
+ ];
863
+ /**
864
+ * Set of every Swagger 2.0 parameter-root keyword that must be lifted
865
+ * into the synthesised `schema` rather than copied onto the OAS 3.x
866
+ * parameter root. Includes `type`, `format`, `items` (Swagger 2.0
867
+ * parameter-shaped array element descriptor), `collectionFormat`
868
+ * (handled separately by the caller as `style`/`explode`), and every
869
+ * entry from {@link SWAGGER_PARAM_SCHEMA_KEYWORDS}.
870
+ */
871
+ const PARAM_KEYWORDS_LIFTED_INTO_SCHEMA = new Set([
872
+ "type",
873
+ "format",
874
+ "items",
875
+ "collectionFormat",
876
+ ...SWAGGER_PARAM_SCHEMA_KEYWORDS
877
+ ]);
878
+ /**
879
+ * Synthesise an OpenAPI 3.x `schema` object from a Swagger 2.0
880
+ * parameter-shaped node (parameter or header). Copies `type`,
881
+ * `format`, and every JSON-Schema-compatible constraint that Swagger
882
+ * 2.0 places at the parameter root. Nested `items` is recursively
883
+ * synthesised the same way so array element constraints survive.
884
+ */
885
+ function buildSchemaFromSwaggerParameterShape(node) {
886
+ const schema = { type: node.type };
887
+ if (typeof node.format === "string") schema.format = node.format;
888
+ for (const keyword of SWAGGER_PARAM_SCHEMA_KEYWORDS) if (node[keyword] !== void 0) schema[keyword] = node[keyword];
889
+ if (isObject(node.items)) schema.items = buildSchemaFromSwaggerParameterShape(node.items);
890
+ return schema;
891
+ }
892
+ /**
893
+ * Resolve a Swagger parameter that may be a `$ref`. Returns the
894
+ * resolved parameter object, or a cycle marker so the caller can
895
+ * decide how to surface the failure. Non-ref parameters resolve to
896
+ * themselves; ref targets that don't exist also resolve to the input
897
+ * (the caller treats unknown refs the same as bare parameters).
733
898
  */
734
899
  function resolveSwaggerParameter(param, doc, visited = /* @__PURE__ */ new Set()) {
735
900
  const ref = param.$ref;
736
- if (typeof ref !== "string" || !ref.startsWith("#/parameters/")) return param;
737
- if (visited.has(ref)) return param;
901
+ if (typeof ref !== "string" || !ref.startsWith("#/parameters/")) return {
902
+ kind: "ok",
903
+ param
904
+ };
905
+ if (visited.has(ref)) return {
906
+ kind: "cycle",
907
+ ref
908
+ };
738
909
  const nextVisited = new Set(visited);
739
910
  nextVisited.add(ref);
740
911
  const name = ref.slice(13);
@@ -743,33 +914,55 @@ function resolveSwaggerParameter(param, doc, visited = /* @__PURE__ */ new Set()
743
914
  const resolved = globalParams[name];
744
915
  if (isObject(resolved)) {
745
916
  if (typeof resolved.$ref === "string") return resolveSwaggerParameter(resolved, doc, nextVisited);
746
- return resolved;
917
+ return {
918
+ kind: "ok",
919
+ param: resolved
920
+ };
747
921
  }
748
922
  }
749
- return param;
923
+ return {
924
+ kind: "ok",
925
+ param
926
+ };
750
927
  }
751
928
  /**
752
929
  * Normalise a single Swagger parameter to OpenAPI 3.x form.
753
930
  */
754
- function normaliseSwaggerParameter(param, doc) {
931
+ function normaliseSwaggerParameter(param, doc, diagnostics, pointer = "") {
755
932
  if (typeof param.$ref === "string") {
756
- const resolved = resolveSwaggerParameter(param, doc);
757
- if (resolved !== param) return normaliseSwaggerParameter(resolved, doc);
933
+ const resolution = resolveSwaggerParameter(param, doc);
934
+ if (resolution.kind === "cycle") {
935
+ emitDiagnostic(diagnostics, {
936
+ code: "swagger-cyclic-parameter-ref",
937
+ message: `Cyclic Swagger 2.0 parameter $ref "${resolution.ref}"; skipping entry`,
938
+ pointer,
939
+ detail: { ref: resolution.ref }
940
+ });
941
+ return;
942
+ }
943
+ const resolved = resolution.param;
944
+ if (resolved !== param) return normaliseSwaggerParameter(resolved, doc, diagnostics, pointer);
758
945
  }
759
946
  const result = {};
760
947
  for (const [key, value] of Object.entries(param)) {
761
- if (key === "type" || key === "format" || key === "collectionFormat") continue;
948
+ if (PARAM_KEYWORDS_LIFTED_INTO_SCHEMA.has(key)) continue;
762
949
  result[key] = value;
763
950
  }
764
- if (typeof param.type === "string") {
765
- const schema = { type: param.type };
766
- if (typeof param.format === "string") schema.format = param.format;
767
- if (param.enum !== void 0) schema.enum = param.enum;
768
- if (param.default !== void 0) schema.default = param.default;
769
- if (param.minimum !== void 0) schema.minimum = param.minimum;
770
- if (param.maximum !== void 0) schema.maximum = param.maximum;
771
- result.schema = schema;
772
- }
951
+ if (typeof param.type === "string") if (param.type === "file" && param.in !== "formData") {
952
+ emitDiagnostic(diagnostics, {
953
+ code: "swagger-invalid-file-parameter",
954
+ message: `Swagger 2.0 type: "file" is only valid under in: formData; converting to { type: "string", format: "binary" }`,
955
+ pointer,
956
+ detail: {
957
+ name: param.name,
958
+ in: param.in
959
+ }
960
+ });
961
+ result.schema = {
962
+ type: "string",
963
+ format: "binary"
964
+ };
965
+ } else result.schema = buildSchemaFromSwaggerParameterShape(param);
773
966
  const cf = param.collectionFormat;
774
967
  if (typeof cf === "string") switch (cf) {
775
968
  case "csv":
@@ -860,14 +1053,14 @@ function resolveSwaggerResponse(response, doc, visited = /* @__PURE__ */ new Set
860
1053
  }
861
1054
  return response;
862
1055
  }
863
- function normaliseSwaggerResponses(responses, doc, produces) {
1056
+ function normaliseSwaggerResponses(responses, doc, produces, producesSource, diagnostics, path, method) {
864
1057
  const result = {};
865
1058
  for (const [code, response] of Object.entries(responses)) {
866
1059
  if (!isObject(response)) {
867
1060
  result[code] = response;
868
1061
  continue;
869
1062
  }
870
- result[code] = normaliseSwaggerSingleResponse(response, doc, produces);
1063
+ result[code] = normaliseSwaggerSingleResponse(response, doc, produces, producesSource, diagnostics, path, method, code);
871
1064
  }
872
1065
  return result;
873
1066
  }
@@ -880,7 +1073,7 @@ function normaliseSwaggerResponses(responses, doc, produces) {
880
1073
  * operation’s `responses` map or under document-level `responses`
881
1074
  * (now `components.responses`).
882
1075
  */
883
- function normaliseSwaggerSingleResponse(response, doc, produces) {
1076
+ function normaliseSwaggerSingleResponse(response, doc, produces, producesSource = "synthesised", diagnostics, path, method, statusCode) {
884
1077
  const resolved = resolveSwaggerResponse(response, doc);
885
1078
  const normalised = {};
886
1079
  for (const [key, value] of Object.entries(resolved)) if (key !== "schema" && key !== "headers") normalised[key] = value;
@@ -890,6 +1083,15 @@ function normaliseSwaggerSingleResponse(response, doc, produces) {
890
1083
  const contentTypes = produces.length > 0 ? produces : ["application/json"];
891
1084
  for (const ct of contentTypes) if (typeof ct === "string") content[ct] = { schema };
892
1085
  normalised.content = content;
1086
+ if (producesSource === "synthesised") emitDiagnostic(diagnostics, {
1087
+ code: "swagger-missing-consumes",
1088
+ message: "Response declares a schema but neither operation-level nor document-level `produces` is set; defaulting to application/json",
1089
+ pointer: path !== void 0 && method !== void 0 && statusCode !== void 0 ? appendPointer(appendPointer(appendPointer(appendPointer(appendPointer("", "paths"), path), method), "responses"), statusCode) : "",
1090
+ detail: {
1091
+ level: "response",
1092
+ statusCode
1093
+ }
1094
+ });
893
1095
  }
894
1096
  const headers = resolved.headers;
895
1097
  if (isObject(headers)) {
@@ -915,18 +1117,10 @@ function normaliseSwaggerSingleResponse(response, doc, produces) {
915
1117
  function normaliseSwaggerHeader(header) {
916
1118
  const result = {};
917
1119
  for (const [key, value] of Object.entries(header)) {
918
- if (key === "type" || key === "format" || key === "collectionFormat") continue;
1120
+ if (PARAM_KEYWORDS_LIFTED_INTO_SCHEMA.has(key)) continue;
919
1121
  result[key] = value;
920
1122
  }
921
- if (typeof header.type === "string") {
922
- const schema = { type: header.type };
923
- if (typeof header.format === "string") schema.format = header.format;
924
- if (header.enum !== void 0) schema.enum = header.enum;
925
- if (header.default !== void 0) schema.default = header.default;
926
- if (header.minimum !== void 0) schema.minimum = header.minimum;
927
- if (header.maximum !== void 0) schema.maximum = header.maximum;
928
- result.schema = schema;
929
- }
1123
+ if (typeof header.type === "string") result.schema = buildSchemaFromSwaggerParameterShape(header);
930
1124
  const cf = header.collectionFormat;
931
1125
  if (typeof cf === "string") switch (cf) {
932
1126
  case "csv":
@@ -976,6 +1170,64 @@ function rewriteSwaggerRefs(node) {
976
1170
  else if (Array.isArray(value)) for (const item of value) rewriteSwaggerRefs(item);
977
1171
  }
978
1172
  /**
1173
+ * Map from Swagger 2.0 `oauth2.flow` (singular) to the OAS 3.x flow key
1174
+ * under `flows.<key>`. `application` and `accessCode` were renamed in
1175
+ * OAS 3.x to align with RFC 6749 grant-type names.
1176
+ */
1177
+ const SWAGGER_OAUTH_FLOW_RENAME = {
1178
+ implicit: "implicit",
1179
+ password: "password",
1180
+ application: "clientCredentials",
1181
+ accessCode: "authorizationCode"
1182
+ };
1183
+ /**
1184
+ * Translate a Swagger 2.0 Security Scheme Object into an OpenAPI 3.x
1185
+ * Security Scheme Object. The Swagger 2.0 spec defines three types:
1186
+ *
1187
+ * - `basic` — has no other fields; OAS 3.x represents this as
1188
+ * `{ type: "http", scheme: "basic" }`.
1189
+ * - `apiKey` — carries `name`/`in`; OAS 3.x uses the same shape.
1190
+ * - `oauth2` — carries `flow`/`authorizationUrl`/`tokenUrl`/`scopes` at
1191
+ * the root. OAS 3.x nests these under `flows.<name>` where the flow
1192
+ * name maps via {@link SWAGGER_OAUTH_FLOW_RENAME}.
1193
+ *
1194
+ * Unknown `type` values pass through verbatim — downstream validation
1195
+ * (`unknown-security-scheme-type` diagnostic in the parser) handles
1196
+ * those cases.
1197
+ */
1198
+ function translateSwaggerSecurityScheme(scheme) {
1199
+ const type = scheme.type;
1200
+ if (type === "basic") {
1201
+ const result = {
1202
+ type: "http",
1203
+ scheme: "basic"
1204
+ };
1205
+ if (typeof scheme.description === "string") result.description = scheme.description;
1206
+ return result;
1207
+ }
1208
+ if (type === "oauth2") {
1209
+ const flowName = scheme.flow;
1210
+ if (typeof flowName !== "string") return {
1211
+ ...scheme,
1212
+ type: "oauth2"
1213
+ };
1214
+ const renamedFlow = SWAGGER_OAUTH_FLOW_RENAME[flowName] ?? flowName;
1215
+ const flowBody = {};
1216
+ if (typeof scheme.authorizationUrl === "string") flowBody.authorizationUrl = scheme.authorizationUrl;
1217
+ if (typeof scheme.tokenUrl === "string") flowBody.tokenUrl = scheme.tokenUrl;
1218
+ if (typeof scheme.refreshUrl === "string") flowBody.refreshUrl = scheme.refreshUrl;
1219
+ const scopes = scheme.scopes;
1220
+ flowBody.scopes = isObject(scopes) ? { ...scopes } : {};
1221
+ const result = {
1222
+ type: "oauth2",
1223
+ flows: { [renamedFlow]: flowBody }
1224
+ };
1225
+ if (typeof scheme.description === "string") result.description = scheme.description;
1226
+ return result;
1227
+ }
1228
+ return { ...scheme };
1229
+ }
1230
+ /**
979
1231
  * Recursively check whether any node in the supplied subtree carries an
980
1232
  * `xml` annotation. Walks both objects and arrays so the check works for
981
1233
  * schemas (definitions, parameter schemas, response schemas, request body
@@ -1075,24 +1327,34 @@ function deepNormalise(schema, transform, visited = /* @__PURE__ */ new WeakSet(
1075
1327
  } else result[key] = value;
1076
1328
  return result;
1077
1329
  }
1330
+ /**
1331
+ * Construct a child {@link NodeContext} that descends to `segment`,
1332
+ * preserving document-level flags (`documentHasDynamicAnchor`,
1333
+ * `documentHasRecursiveAnchor`, `declaredDraft`). Centralising the copy
1334
+ * keeps the recursion in `deepNormaliseWithContext` from drifting from
1335
+ * the {@link NodeContext} shape.
1336
+ */
1337
+ function childContext(ctx, segment) {
1338
+ return {
1339
+ diagnostics: ctx.diagnostics,
1340
+ pointer: appendPointer(ctx.pointer, segment),
1341
+ documentHasDynamicAnchor: ctx.documentHasDynamicAnchor,
1342
+ documentHasRecursiveAnchor: ctx.documentHasRecursiveAnchor,
1343
+ declaredDraft: ctx.declaredDraft
1344
+ };
1345
+ }
1078
1346
  function normaliseArrayWithContext(items, transform, ctx) {
1079
1347
  const result = [];
1080
1348
  for (let i = 0; i < items.length; i++) {
1081
1349
  const item = items[i];
1082
- if (isObject(item)) result.push(deepNormaliseWithContext(item, transform, {
1083
- diagnostics: ctx.diagnostics,
1084
- pointer: appendPointer(ctx.pointer, String(i))
1085
- }));
1350
+ if (isObject(item)) result.push(deepNormaliseWithContext(item, transform, childContext(ctx, String(i))));
1086
1351
  else result.push(item);
1087
1352
  }
1088
1353
  return result;
1089
1354
  }
1090
1355
  function normaliseSubSchemaMapWithContext(map, transform, ctx) {
1091
1356
  const result = {};
1092
- for (const [k, v] of Object.entries(map)) if (isObject(v)) result[k] = deepNormaliseWithContext(v, transform, {
1093
- diagnostics: ctx.diagnostics,
1094
- pointer: appendPointer(ctx.pointer, k)
1095
- });
1357
+ for (const [k, v] of Object.entries(map)) if (isObject(v)) result[k] = deepNormaliseWithContext(v, transform, childContext(ctx, k));
1096
1358
  else result[k] = v;
1097
1359
  return result;
1098
1360
  }
@@ -1108,34 +1370,16 @@ function normaliseSubSchemaMapWithContext(map, transform, ctx) {
1108
1370
  function deepNormaliseWithContext(schema, transform, ctx) {
1109
1371
  const node = transform({ ...schema }, ctx);
1110
1372
  const result = {};
1111
- for (const [key, value] of Object.entries(node)) if (isObject(value) && OBJECT_SUBSCHEMA_KEYS.has(key)) result[key] = normaliseSubSchemaMapWithContext(value, transform, {
1112
- diagnostics: ctx.diagnostics,
1113
- pointer: appendPointer(ctx.pointer, key)
1114
- });
1115
- else if (Array.isArray(value) && ARRAY_SUBSCHEMA_KEYS.has(key)) result[key] = normaliseArrayWithContext(value, transform, {
1116
- diagnostics: ctx.diagnostics,
1117
- pointer: appendPointer(ctx.pointer, key)
1118
- });
1119
- else if (isObject(value) && SINGLE_SUBSCHEMA_KEYS.has(key)) result[key] = deepNormaliseWithContext(value, transform, {
1120
- diagnostics: ctx.diagnostics,
1121
- pointer: appendPointer(ctx.pointer, key)
1122
- });
1123
- else if (key === "items") if (Array.isArray(value)) result[key] = normaliseArrayWithContext(value, transform, {
1124
- diagnostics: ctx.diagnostics,
1125
- pointer: appendPointer(ctx.pointer, key)
1126
- });
1127
- else if (isObject(value)) result[key] = deepNormaliseWithContext(value, transform, {
1128
- diagnostics: ctx.diagnostics,
1129
- pointer: appendPointer(ctx.pointer, key)
1130
- });
1373
+ for (const [key, value] of Object.entries(node)) if (isObject(value) && OBJECT_SUBSCHEMA_KEYS.has(key)) result[key] = normaliseSubSchemaMapWithContext(value, transform, childContext(ctx, key));
1374
+ else if (Array.isArray(value) && ARRAY_SUBSCHEMA_KEYS.has(key)) result[key] = normaliseArrayWithContext(value, transform, childContext(ctx, key));
1375
+ else if (isObject(value) && SINGLE_SUBSCHEMA_KEYS.has(key)) result[key] = deepNormaliseWithContext(value, transform, childContext(ctx, key));
1376
+ else if (key === "items") if (Array.isArray(value)) result[key] = normaliseArrayWithContext(value, transform, childContext(ctx, key));
1377
+ else if (isObject(value)) result[key] = deepNormaliseWithContext(value, transform, childContext(ctx, key));
1131
1378
  else result[key] = value;
1132
1379
  else if (key === "dependencies" && isObject(value)) {
1133
1380
  const normalised = {};
1134
- const depsPointer = appendPointer(ctx.pointer, key);
1135
- for (const [dk, dv] of Object.entries(value)) if (isObject(dv)) normalised[dk] = deepNormaliseWithContext(dv, transform, {
1136
- diagnostics: ctx.diagnostics,
1137
- pointer: appendPointer(depsPointer, dk)
1138
- });
1381
+ const depsContext = childContext(ctx, key);
1382
+ for (const [dk, dv] of Object.entries(value)) if (isObject(dv)) normalised[dk] = deepNormaliseWithContext(dv, transform, childContext(depsContext, dk));
1139
1383
  else normalised[dk] = dv;
1140
1384
  result[key] = normalised;
1141
1385
  } else result[key] = value;
@@ -1391,8 +1635,18 @@ function normaliseDraft06Or07NodeWithContext(node, ctx) {
1391
1635
  * `$recursiveAnchor` names are likewise preserved as `$anchor`.
1392
1636
  */
1393
1637
  function normaliseDraft201909NodeWithContext(node, ctx) {
1394
- if (typeof node.$recursiveRef === "string") {
1395
- node.$ref = node.$recursiveRef;
1638
+ const recursiveRef = node.$recursiveRef;
1639
+ if (typeof recursiveRef === "string") {
1640
+ if (!recursiveRef.startsWith("#")) emitDiagnostic(ctx.diagnostics, {
1641
+ code: "dynamic-ref-degraded",
1642
+ message: `Cross-document \`$recursiveRef\` "${recursiveRef}" rewritten to a static \`$ref\`; dynamic-scope resolution is not preserved`,
1643
+ pointer: appendPointer(ctx.pointer, "$recursiveRef"),
1644
+ detail: {
1645
+ keyword: "$recursiveRef",
1646
+ ref: recursiveRef
1647
+ }
1648
+ });
1649
+ node.$ref = recursiveRef;
1396
1650
  delete node.$recursiveRef;
1397
1651
  }
1398
1652
  if (node.$recursiveAnchor === true) {
@@ -1402,12 +1656,24 @@ function normaliseDraft201909NodeWithContext(node, ctx) {
1402
1656
  if (typeof node.$anchor !== "string") node.$anchor = node.$recursiveAnchor;
1403
1657
  delete node.$recursiveAnchor;
1404
1658
  }
1659
+ splitDependencies(node, ctx, false);
1405
1660
  validateDependentRequired(node, ctx);
1406
1661
  return node;
1407
1662
  }
1408
1663
  function normaliseDynamicRefNodeWithContext(node, ctx) {
1409
- if (typeof node.$dynamicRef === "string") {
1410
- node.$ref = node.$dynamicRef;
1664
+ const dynamicRef = node.$dynamicRef;
1665
+ if (typeof dynamicRef === "string") {
1666
+ const crossDocument = !dynamicRef.startsWith("#");
1667
+ if (crossDocument || ctx.documentHasDynamicAnchor) emitDiagnostic(ctx.diagnostics, {
1668
+ code: "dynamic-ref-degraded",
1669
+ message: crossDocument ? `Cross-document \`$dynamicRef\` "${dynamicRef}" rewritten to a static \`$ref\`; dynamic-scope resolution is not preserved` : `\`$dynamicRef\` "${dynamicRef}" rewritten to a static \`$ref\` in a document declaring \`$dynamicAnchor\`; dynamic-scope resolution is not preserved`,
1670
+ pointer: appendPointer(ctx.pointer, "$dynamicRef"),
1671
+ detail: {
1672
+ keyword: "$dynamicRef",
1673
+ ref: dynamicRef
1674
+ }
1675
+ });
1676
+ node.$ref = dynamicRef;
1411
1677
  delete node.$dynamicRef;
1412
1678
  }
1413
1679
  if (typeof node.$dynamicAnchor === "string") {
@@ -1419,6 +1685,63 @@ function normaliseDynamicRefNodeWithContext(node, ctx) {
1419
1685
  return node;
1420
1686
  }
1421
1687
  /**
1688
+ * Pick the per-node transform that normalises a single Schema Object to
1689
+ * canonical Draft 2020-12 form for the supplied draft. Exposed so the
1690
+ * OpenAPI 3.1 path can honour a non-default `jsonSchemaDialect`
1691
+ * declaration by routing each Schema Object through the matching
1692
+ * transform without re-implementing the dispatch.
1693
+ */
1694
+ function selectDraftTransform(draft) {
1695
+ switch (draft) {
1696
+ case "draft-04": return normaliseDraft04NodeWithContext;
1697
+ case "draft-06":
1698
+ case "draft-07": return normaliseDraft06Or07NodeWithContext;
1699
+ case "draft-2019-09": return normaliseDraft201909NodeWithContext;
1700
+ case "draft-2020-12": return normaliseDynamicRefNodeWithContext;
1701
+ }
1702
+ }
1703
+ /**
1704
+ * Scan a JSON document body for the presence of a named keyword
1705
+ * anywhere in the structure. Walks both arrays and objects without
1706
+ * regard to schema-vs-data position — the caller is responsible for
1707
+ * passing a keyword whose presence is meaningful at any depth.
1708
+ *
1709
+ * Cycle-safe: cyclic references introduced by the OpenAPI bundler's
1710
+ * `structuredClone` of external refs are short-circuited via the
1711
+ * `visited` set so the scan terminates.
1712
+ */
1713
+ function documentContainsKeyword(value, keyword, visited = /* @__PURE__ */ new WeakSet()) {
1714
+ if (Array.isArray(value)) {
1715
+ if (visited.has(value)) return false;
1716
+ visited.add(value);
1717
+ for (const item of value) if (documentContainsKeyword(item, keyword, visited)) return true;
1718
+ return false;
1719
+ }
1720
+ if (isObject(value)) {
1721
+ if (visited.has(value)) return false;
1722
+ visited.add(value);
1723
+ if (keyword in value) return true;
1724
+ for (const v of Object.values(value)) if (documentContainsKeyword(v, keyword, visited)) return true;
1725
+ return false;
1726
+ }
1727
+ return false;
1728
+ }
1729
+ /**
1730
+ * Build a root {@link NodeContext} for a document being normalised.
1731
+ * Pre-scans for `$dynamicAnchor` and `$recursiveAnchor` so per-node
1732
+ * transforms can decide whether a `$dynamicRef`/`$recursiveRef`
1733
+ * rewrite needs a `dynamic-ref-degraded` diagnostic.
1734
+ */
1735
+ function buildRootContext(schema, diagnostics, declaredDraft) {
1736
+ return {
1737
+ diagnostics,
1738
+ pointer: "",
1739
+ documentHasDynamicAnchor: documentContainsKeyword(schema, "$dynamicAnchor"),
1740
+ documentHasRecursiveAnchor: documentContainsKeyword(schema, "$recursiveAnchor"),
1741
+ declaredDraft
1742
+ };
1743
+ }
1744
+ /**
1422
1745
  * Normalise a JSON Schema to canonical Draft 2020-12 form.
1423
1746
  * Deep-clones the input — the original is never mutated.
1424
1747
  *
@@ -1428,27 +1751,8 @@ function normaliseDynamicRefNodeWithContext(node, ctx) {
1428
1751
  * `dependencies` reaching the 2020-12 path).
1429
1752
  */
1430
1753
  function normaliseJsonSchema(schema, draft, diagnostics) {
1431
- const ctx = {
1432
- diagnostics,
1433
- pointer: ""
1434
- };
1435
- let normalised;
1436
- switch (draft) {
1437
- case "draft-04":
1438
- normalised = deepNormaliseWithContext(schema, normaliseDraft04NodeWithContext, ctx);
1439
- break;
1440
- case "draft-2019-09":
1441
- normalised = deepNormaliseWithContext(schema, normaliseDraft201909NodeWithContext, ctx);
1442
- break;
1443
- case "draft-2020-12":
1444
- normalised = deepNormaliseWithContext(schema, normaliseDynamicRefNodeWithContext, ctx);
1445
- break;
1446
- case "draft-06":
1447
- case "draft-07":
1448
- normalised = deepNormaliseWithContext(schema, normaliseDraft06Or07NodeWithContext, ctx);
1449
- break;
1450
- }
1451
- return resolveRelativeRefs(normalised, diagnostics);
1754
+ const ctx = buildRootContext(schema, diagnostics, draft);
1755
+ return resolveRelativeRefs(deepNormaliseWithContext(schema, selectDraftTransform(draft), ctx), diagnostics);
1452
1756
  }
1453
1757
  /**
1454
1758
  * Parse a string as an absolute URI, returning `undefined` when it has
@@ -1486,6 +1790,29 @@ function stripFragment(url) {
1486
1790
  return clone.toString();
1487
1791
  }
1488
1792
  /**
1793
+ * Emit an `invalid-id-fragment` diagnostic when an `$id` value carries
1794
+ * a fragment that will be stripped during base-URI resolution. Per
1795
+ * JSON Schema 2020-12 §8.2.1, the URI in `$id` MUST NOT contain a
1796
+ * non-empty fragment (an empty `#` fragment is permitted for historical
1797
+ * reasons but conveys nothing). Stripping it silently loses authoring
1798
+ * intent — the caller almost certainly meant to declare an `$anchor`
1799
+ * or sibling identifier instead.
1800
+ */
1801
+ function reportFragmentInId(value, url, pointer, diagnostics) {
1802
+ if (diagnostics === void 0) return;
1803
+ if (typeof value !== "string") return;
1804
+ if (url.hash.length === 0) return;
1805
+ emitDiagnostic(diagnostics, {
1806
+ code: "invalid-id-fragment",
1807
+ message: `\`$id\` URI "${value}" includes the fragment "${url.hash}", which is not permitted by JSON Schema §8.2.1; the fragment is stripped before use`,
1808
+ pointer: appendPointer(pointer, "$id"),
1809
+ detail: {
1810
+ id: value,
1811
+ fragment: url.hash
1812
+ }
1813
+ });
1814
+ }
1815
+ /**
1489
1816
  * Recursively rewrite relative `$ref`s in a schema so they resolve
1490
1817
  * correctly under the JSON Schema base-URI rules (RFC 3986 + JSON
1491
1818
  * Schema §8.2). Refs that resolve to the document's own `$id` are
@@ -1519,7 +1846,10 @@ function rewriteRelativeRefsNode(node, currentBase, docBase, pointer, diagnostic
1519
1846
  const nodeId = node.$id;
1520
1847
  if (typeof nodeId === "string" && nodeId.length > 0) {
1521
1848
  const resolved = resolveAgainst(nodeId, currentBase);
1522
- if (resolved !== void 0) nextBase = stripFragment(resolved);
1849
+ if (resolved !== void 0) {
1850
+ reportFragmentInId(nodeId, resolved, pointer, diagnostics);
1851
+ nextBase = stripFragment(resolved);
1852
+ }
1523
1853
  }
1524
1854
  const result = {};
1525
1855
  for (const [key, value] of Object.entries(node)) {
@@ -1588,6 +1918,24 @@ function rewriteRef(ref, currentBase, docBase, pointer, diagnostics) {
1588
1918
  function normaliseOpenApiSchemas(doc, version, diagnostics) {
1589
1919
  if (isSwagger2(version)) return normaliseSwagger2Document(doc, deepNormalise, normaliseDraft04Node, diagnostics);
1590
1920
  if (isOpenApi30(version)) return deepNormaliseOpenApi30Doc(applyDiscriminatorAllOfPrepass(doc), deepNormalise);
1921
+ if (!isOpenApi31(version)) {
1922
+ const rawOpenApi = typeof doc.openapi === "string" ? doc.openapi : void 0;
1923
+ const rawSwagger = typeof doc.swagger === "string" ? doc.swagger : void 0;
1924
+ const versionLabel = rawOpenApi ?? rawSwagger ?? `${String(version.major)}.${String(version.minor)}.${String(version.patch)}`;
1925
+ const pointer = rawOpenApi !== void 0 ? "/openapi" : "/swagger";
1926
+ emitDiagnostic(diagnostics, {
1927
+ code: "unknown-openapi-version",
1928
+ message: `Unsupported OpenAPI/Swagger version "${versionLabel}"; falling back to the OpenAPI 3.1 pipeline`,
1929
+ pointer,
1930
+ detail: {
1931
+ version: versionLabel,
1932
+ major: version.major,
1933
+ minor: version.minor,
1934
+ patch: version.patch
1935
+ }
1936
+ });
1937
+ }
1938
+ let dialectDraft;
1591
1939
  if (isOpenApi31(version)) {
1592
1940
  const dialect = readJsonSchemaDialect(doc);
1593
1941
  if (dialect.kind === "unknown") emitDiagnostic(diagnostics, {
@@ -1596,8 +1944,13 @@ function normaliseOpenApiSchemas(doc, version, diagnostics) {
1596
1944
  pointer: "/jsonSchemaDialect",
1597
1945
  detail: { uri: dialect.uri }
1598
1946
  });
1947
+ else if (dialect.kind === "known") dialectDraft = dialect.draft;
1599
1948
  }
1600
- return deepNormaliseOpenApiDoc(applyDiscriminatorAllOfPrepass(doc), (schema) => resolveRelativeRefs(deepNormalise(schema, normaliseOpenApi30Discriminator), diagnostics));
1949
+ return deepNormaliseOpenApiDoc(applyDiscriminatorAllOfPrepass(doc), (schema) => {
1950
+ let intermediate = schema;
1951
+ if (dialectDraft !== void 0 && dialectDraft !== "draft-2020-12") intermediate = deepNormaliseWithContext(intermediate, selectDraftTransform(dialectDraft), buildRootContext(intermediate, diagnostics, dialectDraft));
1952
+ return resolveRelativeRefs(deepNormalise(intermediate, normaliseOpenApi30Discriminator), diagnostics);
1953
+ });
1601
1954
  }
1602
1955
  //#endregion
1603
- export { normaliseOpenApiSchemas as a, deepNormaliseOpenApi30Doc as c, normaliseOpenApi30Discriminator as d, normaliseOpenApi30Node as f, normaliseJsonSchema as i, deepNormaliseOpenApiDoc as l, deepNormaliseWithContext as n, normaliseSwagger2Document as o, normaliseDraft04Node as r, applyDiscriminatorAllOfPrepass as s, deepNormalise as t, normaliseOpenApi30Combined as u };
1956
+ export { normaliseOpenApiSchemas as a, applyDiscriminatorAllOfPrepass as c, normaliseOpenApi30Combined as d, normaliseOpenApi30Discriminator as f, normaliseJsonSchema as i, deepNormaliseOpenApi30Doc as l, deepNormaliseWithContext as n, selectDraftTransform as o, normaliseOpenApi30Node as p, normaliseDraft04Node as r, normaliseSwagger2Document as s, deepNormalise as t, deepNormaliseOpenApiDoc as u };