openapi-typesafe 1.0.0-rc.0 → 1.0.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/redocly-loader/redocly-config-source.cjs +21 -2
- package/dist/adapters/redocly-loader/redocly-config-source.cjs.map +1 -1
- package/dist/adapters/redocly-loader/redocly-config-source.mjs +22 -3
- package/dist/adapters/redocly-loader/redocly-config-source.mjs.map +1 -1
- package/dist/adapters/redocly-loader/redocly-schema-loader.cjs +15 -5
- package/dist/adapters/redocly-loader/redocly-schema-loader.cjs.map +1 -1
- package/dist/adapters/redocly-loader/redocly-schema-loader.mjs +15 -5
- package/dist/adapters/redocly-loader/redocly-schema-loader.mjs.map +1 -1
- package/dist/adapters/type-ast-bridge/ir-to-typescript.cjs +3 -0
- package/dist/adapters/type-ast-bridge/ir-to-typescript.cjs.map +1 -1
- package/dist/adapters/type-ast-bridge/ir-to-typescript.mjs +3 -0
- package/dist/adapters/type-ast-bridge/ir-to-typescript.mjs.map +1 -1
- package/dist/adapters/type-ast-bridge/typescript-to-ir.cjs +15 -9
- package/dist/adapters/type-ast-bridge/typescript-to-ir.cjs.map +1 -1
- package/dist/adapters/type-ast-bridge/typescript-to-ir.mjs +15 -9
- package/dist/adapters/type-ast-bridge/typescript-to-ir.mjs.map +1 -1
- package/dist/api/discriminator-conflict-warnings.cjs +25 -0
- package/dist/api/discriminator-conflict-warnings.cjs.map +1 -0
- package/dist/api/discriminator-conflict-warnings.mjs +23 -0
- package/dist/api/discriminator-conflict-warnings.mjs.map +1 -0
- package/dist/api/discriminator-enum-names.cjs +19 -0
- package/dist/api/discriminator-enum-names.cjs.map +1 -0
- package/dist/api/discriminator-enum-names.mjs +17 -0
- package/dist/api/discriminator-enum-names.mjs.map +1 -0
- package/dist/api/discriminator-enum-parent.cjs +59 -0
- package/dist/api/discriminator-enum-parent.cjs.map +1 -0
- package/dist/api/discriminator-enum-parent.mjs +57 -0
- package/dist/api/discriminator-enum-parent.mjs.map +1 -0
- package/dist/api/discriminator-enum.cjs +25 -0
- package/dist/api/discriminator-enum.cjs.map +1 -1
- package/dist/api/discriminator-enum.mjs +25 -1
- package/dist/api/discriminator-enum.mjs.map +1 -1
- package/dist/api/discriminator-other-owners.cjs +67 -0
- package/dist/api/discriminator-other-owners.cjs.map +1 -0
- package/dist/api/discriminator-other-owners.mjs +64 -0
- package/dist/api/discriminator-other-owners.mjs.map +1 -0
- package/dist/api/discriminator-patch.cjs +88 -0
- package/dist/api/discriminator-patch.cjs.map +1 -0
- package/dist/api/discriminator-patch.mjs +82 -0
- package/dist/api/discriminator-patch.mjs.map +1 -0
- package/dist/api/discriminator-scan.cjs +50 -65
- package/dist/api/discriminator-scan.cjs.map +1 -1
- package/dist/api/discriminator-scan.mjs +44 -59
- package/dist/api/discriminator-scan.mjs.map +1 -1
- package/dist/api/document-run.cjs +18 -3
- package/dist/api/document-run.cjs.map +1 -1
- package/dist/api/document-run.mjs +19 -4
- package/dist/api/document-run.mjs.map +1 -1
- package/dist/api/enum-values-warnings.cjs +16 -0
- package/dist/api/enum-values-warnings.cjs.map +1 -0
- package/dist/api/enum-values-warnings.mjs +14 -0
- package/dist/api/enum-values-warnings.mjs.map +1 -0
- package/dist/api/path-template-warnings.cjs +21 -0
- package/dist/api/path-template-warnings.cjs.map +1 -0
- package/dist/api/path-template-warnings.mjs +19 -0
- package/dist/api/path-template-warnings.mjs.map +1 -0
- package/dist/api/transform-context.cjs +3 -1
- package/dist/api/transform-context.cjs.map +1 -1
- package/dist/api/transform-context.mjs +3 -1
- package/dist/api/transform-context.mjs.map +1 -1
- package/dist/api/transform-schema.cjs +52 -3
- package/dist/api/transform-schema.cjs.map +1 -1
- package/dist/api/transform-schema.mjs +53 -4
- package/dist/api/transform-schema.mjs.map +1 -1
- package/dist/api/unsupported-feature-warnings.cjs +6 -1
- package/dist/api/unsupported-feature-warnings.cjs.map +1 -1
- package/dist/api/unsupported-feature-warnings.mjs +6 -1
- package/dist/api/unsupported-feature-warnings.mjs.map +1 -1
- package/dist/application/print/type-printer.cjs +5 -0
- package/dist/application/print/type-printer.cjs.map +1 -1
- package/dist/application/print/type-printer.mjs +5 -0
- package/dist/application/print/type-printer.mjs.map +1 -1
- package/dist/application/transform/document/assignability.cjs +144 -0
- package/dist/application/transform/document/assignability.cjs.map +1 -0
- package/dist/application/transform/document/assignability.mjs +138 -0
- package/dist/application/transform/document/assignability.mjs.map +1 -0
- package/dist/application/transform/document/components.cjs +2 -2
- package/dist/application/transform/document/components.cjs.map +1 -1
- package/dist/application/transform/document/components.mjs +4 -4
- package/dist/application/transform/document/components.mjs.map +1 -1
- package/dist/application/transform/document/declared-types.cjs +66 -0
- package/dist/application/transform/document/declared-types.cjs.map +1 -0
- package/dist/application/transform/document/declared-types.mjs +63 -0
- package/dist/application/transform/document/declared-types.mjs.map +1 -0
- package/dist/application/transform/document/document.cjs +32 -9
- package/dist/application/transform/document/document.cjs.map +1 -1
- package/dist/application/transform/document/document.mjs +34 -11
- package/dist/application/transform/document/document.mjs.map +1 -1
- package/dist/application/transform/document/exploded-query.cjs +46 -0
- package/dist/application/transform/document/exploded-query.cjs.map +1 -0
- package/dist/application/transform/document/exploded-query.mjs +43 -0
- package/dist/application/transform/document/exploded-query.mjs.map +1 -0
- package/dist/application/transform/document/global-type-references.cjs +63 -0
- package/dist/application/transform/document/global-type-references.cjs.map +1 -0
- package/dist/application/transform/document/global-type-references.mjs +61 -0
- package/dist/application/transform/document/global-type-references.mjs.map +1 -0
- package/dist/application/transform/document/list-assignability.cjs +54 -0
- package/dist/application/transform/document/list-assignability.cjs.map +1 -0
- package/dist/application/transform/document/list-assignability.mjs +52 -0
- package/dist/application/transform/document/list-assignability.mjs.map +1 -0
- package/dist/application/transform/document/parameters.cjs +40 -8
- package/dist/application/transform/document/parameters.cjs.map +1 -1
- package/dist/application/transform/document/parameters.mjs +41 -9
- package/dist/application/transform/document/parameters.mjs.map +1 -1
- package/dist/application/transform/document/path-key-overlaps.cjs +83 -33
- package/dist/application/transform/document/path-key-overlaps.cjs.map +1 -1
- package/dist/application/transform/document/path-key-overlaps.mjs +83 -33
- package/dist/application/transform/document/path-key-overlaps.mjs.map +1 -1
- package/dist/application/transform/document/path-template-replace.cjs +48 -0
- package/dist/application/transform/document/path-template-replace.cjs.map +1 -0
- package/dist/application/transform/document/path-template-replace.mjs +45 -0
- package/dist/application/transform/document/path-template-replace.mjs.map +1 -0
- package/dist/application/transform/document/paths.cjs +54 -28
- package/dist/application/transform/document/paths.cjs.map +1 -1
- package/dist/application/transform/document/paths.mjs +53 -29
- package/dist/application/transform/document/paths.mjs.map +1 -1
- package/dist/application/transform/document/root-type-aliases.cjs +15 -14
- package/dist/application/transform/document/root-type-aliases.cjs.map +1 -1
- package/dist/application/transform/document/root-type-aliases.mjs +15 -13
- package/dist/application/transform/document/root-type-aliases.mjs.map +1 -1
- package/dist/application/transform/document/template-key-match.cjs +128 -0
- package/dist/application/transform/document/template-key-match.cjs.map +1 -0
- package/dist/application/transform/document/template-key-match.mjs +125 -0
- package/dist/application/transform/document/template-key-match.mjs.map +1 -0
- package/dist/application/transform/document/type-assignability.cjs +192 -0
- package/dist/application/transform/document/type-assignability.cjs.map +1 -0
- package/dist/application/transform/document/type-assignability.mjs +190 -0
- package/dist/application/transform/document/type-assignability.mjs.map +1 -0
- package/dist/application/transform/document/type-key.cjs +70 -0
- package/dist/application/transform/document/type-key.cjs.map +1 -0
- package/dist/application/transform/document/type-key.mjs +68 -0
- package/dist/application/transform/document/type-key.mjs.map +1 -0
- package/dist/application/transform/document/type-resolution.cjs +131 -0
- package/dist/application/transform/document/type-resolution.cjs.map +1 -0
- package/dist/application/transform/document/type-resolution.mjs +125 -0
- package/dist/application/transform/document/type-resolution.mjs.map +1 -0
- package/dist/application/transform/footer/document-footer.cjs +66 -4
- package/dist/application/transform/footer/document-footer.cjs.map +1 -1
- package/dist/application/transform/footer/document-footer.mjs +67 -5
- package/dist/application/transform/footer/document-footer.mjs.map +1 -1
- package/dist/application/transform/footer/enum-naming.cjs +17 -0
- package/dist/application/transform/footer/enum-naming.cjs.map +1 -1
- package/dist/application/transform/footer/enum-naming.mjs +17 -1
- package/dist/application/transform/footer/enum-naming.mjs.map +1 -1
- package/dist/application/transform/footer/enum-values-check.cjs +129 -0
- package/dist/application/transform/footer/enum-values-check.cjs.map +1 -0
- package/dist/application/transform/footer/enum-values-check.mjs +127 -0
- package/dist/application/transform/footer/enum-values-check.mjs.map +1 -0
- package/dist/application/transform/footer/evaluation.cjs +33 -0
- package/dist/application/transform/footer/evaluation.cjs.map +1 -0
- package/dist/application/transform/footer/evaluation.mjs +26 -0
- package/dist/application/transform/footer/evaluation.mjs.map +1 -0
- package/dist/application/transform/footer/shared-enum-names.cjs +60 -0
- package/dist/application/transform/footer/shared-enum-names.cjs.map +1 -0
- package/dist/application/transform/footer/shared-enum-names.mjs +57 -0
- package/dist/application/transform/footer/shared-enum-names.mjs.map +1 -0
- package/dist/application/transform/footer/type-evaluation.cjs +246 -0
- package/dist/application/transform/footer/type-evaluation.cjs.map +1 -0
- package/dist/application/transform/footer/type-evaluation.mjs +244 -0
- package/dist/application/transform/footer/type-evaluation.mjs.map +1 -0
- package/dist/application/transform/footer/type-rules.cjs +172 -0
- package/dist/application/transform/footer/type-rules.cjs.map +1 -0
- package/dist/application/transform/footer/type-rules.mjs +166 -0
- package/dist/application/transform/footer/type-rules.mjs.map +1 -0
- package/dist/application/transform/ref-pointer/generated-location.cjs +138 -0
- package/dist/application/transform/ref-pointer/generated-location.cjs.map +1 -0
- package/dist/application/transform/ref-pointer/generated-location.mjs +136 -0
- package/dist/application/transform/ref-pointer/generated-location.mjs.map +1 -0
- package/dist/application/transform/ref-pointer/generated-shape.cjs +171 -0
- package/dist/application/transform/ref-pointer/generated-shape.cjs.map +1 -0
- package/dist/application/transform/ref-pointer/generated-shape.mjs +162 -0
- package/dist/application/transform/ref-pointer/generated-shape.mjs.map +1 -0
- package/dist/application/transform/ref-pointer/inline-cycles.cjs +44 -0
- package/dist/application/transform/ref-pointer/inline-cycles.cjs.map +1 -0
- package/dist/application/transform/ref-pointer/inline-cycles.mjs +42 -0
- package/dist/application/transform/ref-pointer/inline-cycles.mjs.map +1 -0
- package/dist/application/transform/ref-pointer/original-index.cjs +62 -0
- package/dist/application/transform/ref-pointer/original-index.cjs.map +1 -0
- package/dist/application/transform/ref-pointer/original-index.mjs +60 -0
- package/dist/application/transform/ref-pointer/original-index.mjs.map +1 -0
- package/dist/application/transform/ref-pointer/pointer-type.cjs +60 -0
- package/dist/application/transform/ref-pointer/pointer-type.cjs.map +1 -0
- package/dist/application/transform/ref-pointer/pointer-type.mjs +57 -0
- package/dist/application/transform/ref-pointer/pointer-type.mjs.map +1 -0
- package/dist/application/transform/ref-pointer/pointer-walk.cjs +66 -0
- package/dist/application/transform/ref-pointer/pointer-walk.cjs.map +1 -0
- package/dist/application/transform/ref-pointer/pointer-walk.mjs +56 -0
- package/dist/application/transform/ref-pointer/pointer-walk.mjs.map +1 -0
- package/dist/application/transform/ref-pointer/typed-walk.cjs +124 -0
- package/dist/application/transform/ref-pointer/typed-walk.cjs.map +1 -0
- package/dist/application/transform/ref-pointer/typed-walk.mjs +122 -0
- package/dist/application/transform/ref-pointer/typed-walk.mjs.map +1 -0
- package/dist/application/transform/ref-type.cjs +7 -2
- package/dist/application/transform/ref-type.cjs.map +1 -1
- package/dist/application/transform/ref-type.mjs +3 -2
- package/dist/application/transform/ref-type.mjs.map +1 -1
- package/dist/application/transform/schema/access-flags.cjs +38 -0
- package/dist/application/transform/schema/access-flags.cjs.map +1 -0
- package/dist/application/transform/schema/access-flags.mjs +36 -0
- package/dist/application/transform/schema/access-flags.mjs.map +1 -0
- package/dist/application/transform/schema/array-type.cjs +32 -1
- package/dist/application/transform/schema/array-type.cjs.map +1 -1
- package/dist/application/transform/schema/array-type.mjs +33 -2
- package/dist/application/transform/schema/array-type.mjs.map +1 -1
- package/dist/application/transform/schema/composition-type.cjs +50 -15
- package/dist/application/transform/schema/composition-type.cjs.map +1 -1
- package/dist/application/transform/schema/composition-type.mjs +50 -15
- package/dist/application/transform/schema/composition-type.mjs.map +1 -1
- package/dist/application/transform/schema/core-type.cjs +3 -1
- package/dist/application/transform/schema/core-type.cjs.map +1 -1
- package/dist/application/transform/schema/core-type.mjs +3 -1
- package/dist/application/transform/schema/core-type.mjs.map +1 -1
- package/dist/application/transform/schema/discriminated-union.cjs +30 -0
- package/dist/application/transform/schema/discriminated-union.cjs.map +1 -0
- package/dist/application/transform/schema/discriminated-union.mjs +27 -0
- package/dist/application/transform/schema/discriminated-union.mjs.map +1 -0
- package/dist/application/transform/schema/discriminator-property.cjs +3 -2
- package/dist/application/transform/schema/discriminator-property.cjs.map +1 -1
- package/dist/application/transform/schema/discriminator-property.mjs +3 -2
- package/dist/application/transform/schema/discriminator-property.mjs.map +1 -1
- package/dist/application/transform/schema/enum-rule.cjs +66 -10
- package/dist/application/transform/schema/enum-rule.cjs.map +1 -1
- package/dist/application/transform/schema/enum-rule.mjs +67 -12
- package/dist/application/transform/schema/enum-rule.mjs.map +1 -1
- package/dist/application/transform/schema/inherited-discriminator.cjs +29 -0
- package/dist/application/transform/schema/inherited-discriminator.cjs.map +1 -0
- package/dist/application/transform/schema/inherited-discriminator.mjs +26 -0
- package/dist/application/transform/schema/inherited-discriminator.mjs.map +1 -0
- package/dist/application/transform/schema/object-type.cjs +38 -25
- package/dist/application/transform/schema/object-type.cjs.map +1 -1
- package/dist/application/transform/schema/object-type.mjs +39 -27
- package/dist/application/transform/schema/object-type.mjs.map +1 -1
- package/dist/application/transform/schema/required-variants.cjs +45 -0
- package/dist/application/transform/schema/required-variants.cjs.map +1 -0
- package/dist/application/transform/schema/required-variants.mjs +42 -0
- package/dist/application/transform/schema/required-variants.mjs.map +1 -0
- package/dist/application/transform/schema/schema-transform.cjs +2 -2
- package/dist/application/transform/schema/schema-transform.cjs.map +1 -1
- package/dist/application/transform/schema/schema-transform.mjs +2 -2
- package/dist/application/transform/schema/schema-transform.mjs.map +1 -1
- package/dist/application/transform/schema-comment.cjs +51 -5
- package/dist/application/transform/schema-comment.cjs.map +1 -1
- package/dist/application/transform/schema-comment.mjs +50 -6
- package/dist/application/transform/schema-comment.mjs.map +1 -1
- package/dist/application/transform/type-combinators.mjs +1 -1
- package/dist/cli/cli-arguments.cjs +11 -3
- package/dist/cli/cli-arguments.cjs.map +1 -1
- package/dist/cli/cli-arguments.mjs +11 -3
- package/dist/cli/cli-arguments.mjs.map +1 -1
- package/dist/cli/cli-errors.cjs +3 -2
- package/dist/cli/cli-errors.cjs.map +1 -1
- package/dist/cli/cli-errors.mjs +3 -2
- package/dist/cli/cli-errors.mjs.map +1 -1
- package/dist/cli/cli-output.cjs +8 -3
- package/dist/cli/cli-output.cjs.map +1 -1
- package/dist/cli/cli-output.mjs +9 -4
- package/dist/cli/cli-output.mjs.map +1 -1
- package/dist/cli/main.cjs +5 -0
- package/dist/cli/main.cjs.map +1 -1
- package/dist/cli/main.mjs +5 -0
- package/dist/cli/main.mjs.map +1 -1
- package/dist/cli/redocly-apis.cjs +7 -3
- package/dist/cli/redocly-apis.cjs.map +1 -1
- package/dist/cli/redocly-apis.mjs +8 -4
- package/dist/cli/redocly-apis.mjs.map +1 -1
- package/dist/cli/ref-limits.cjs +1 -1
- package/dist/cli/ref-limits.cjs.map +1 -1
- package/dist/cli/ref-limits.mjs +2 -2
- package/dist/cli/ref-limits.mjs.map +1 -1
- package/dist/cli/run-cli.cjs +15 -2
- package/dist/cli/run-cli.cjs.map +1 -1
- package/dist/cli/run-cli.mjs +16 -3
- package/dist/cli/run-cli.mjs.map +1 -1
- package/dist/domain/errors.cjs +2 -0
- package/dist/domain/errors.cjs.map +1 -1
- package/dist/domain/errors.mjs +2 -0
- package/dist/domain/errors.mjs.map +1 -1
- package/dist/domain/ir/constructors.cjs +5 -1
- package/dist/domain/ir/constructors.cjs.map +1 -1
- package/dist/domain/ir/constructors.mjs +5 -2
- package/dist/domain/ir/constructors.mjs.map +1 -1
- package/dist/domain/openapi/admitted-values.cjs +75 -0
- package/dist/domain/openapi/admitted-values.cjs.map +1 -0
- package/dist/domain/openapi/admitted-values.mjs +72 -0
- package/dist/domain/openapi/admitted-values.mjs.map +1 -0
- package/dist/domain/openapi/discriminated-union.cjs +68 -0
- package/dist/domain/openapi/discriminated-union.cjs.map +1 -0
- package/dist/domain/openapi/discriminated-union.mjs +62 -0
- package/dist/domain/openapi/discriminated-union.mjs.map +1 -0
- package/dist/domain/openapi/discriminator-conflicts.cjs +143 -0
- package/dist/domain/openapi/discriminator-conflicts.cjs.map +1 -0
- package/dist/domain/openapi/discriminator-conflicts.mjs +141 -0
- package/dist/domain/openapi/discriminator-conflicts.mjs.map +1 -0
- package/dist/domain/openapi/parameter-object.cjs.map +1 -1
- package/dist/domain/openapi/parameter-object.mjs.map +1 -1
- package/dist/domain/openapi/path-templates.cjs +23 -0
- package/dist/domain/openapi/path-templates.cjs.map +1 -0
- package/dist/domain/openapi/path-templates.mjs +21 -0
- package/dist/domain/openapi/path-templates.mjs.map +1 -0
- package/dist/domain/output-fixes.cjs +36 -1
- package/dist/domain/output-fixes.cjs.map +1 -1
- package/dist/domain/output-fixes.mjs +36 -1
- package/dist/domain/output-fixes.mjs.map +1 -1
- package/dist/domain/warnings.cjs +15 -0
- package/dist/domain/warnings.cjs.map +1 -0
- package/dist/domain/warnings.mjs +12 -0
- package/dist/domain/warnings.mjs.map +1 -0
- package/dist/index.d.cts +29 -0
- package/dist/index.d.mts +29 -0
- package/dist/index.d.ts +29 -0
- package/dist/transform/components-object.cjs +2 -1
- package/dist/transform/components-object.cjs.map +1 -1
- package/dist/transform/components-object.mjs +2 -1
- package/dist/transform/components-object.mjs.map +1 -1
- package/package.json +1 -1
- package/src/adapters/redocly-loader/redocly-config-source.ts +41 -6
- package/src/adapters/redocly-loader/redocly-schema-loader.ts +22 -6
- package/src/adapters/type-ast-bridge/ir-to-typescript.ts +3 -0
- package/src/adapters/type-ast-bridge/typescript-to-ir.ts +26 -12
- package/src/api/discriminator-conflict-warnings.ts +43 -0
- package/src/api/discriminator-enum-names.ts +26 -0
- package/src/api/discriminator-enum-parent.ts +129 -0
- package/src/api/discriminator-enum.ts +39 -0
- package/src/api/discriminator-other-owners.ts +97 -0
- package/src/api/discriminator-patch.ts +147 -0
- package/src/api/discriminator-scan.ts +78 -90
- package/src/api/document-run.ts +24 -6
- package/src/api/enum-values-warnings.ts +21 -0
- package/src/api/path-template-warnings.ts +27 -0
- package/src/api/transform-context.ts +7 -1
- package/src/api/transform-schema.ts +88 -4
- package/src/api/unsupported-feature-warnings.ts +8 -1
- package/src/application/ports/redocly-config-source.ts +5 -1
- package/src/application/print/type-printer.ts +6 -0
- package/src/application/transform/document/assignability.ts +203 -0
- package/src/application/transform/document/components.ts +6 -6
- package/src/application/transform/document/declared-types.ts +93 -0
- package/src/application/transform/document/document.ts +71 -13
- package/src/application/transform/document/exploded-query.ts +91 -0
- package/src/application/transform/document/global-type-references.ts +78 -0
- package/src/application/transform/document/list-assignability.ts +87 -0
- package/src/application/transform/document/parameters.ts +76 -11
- package/src/application/transform/document/path-key-overlaps.ts +148 -59
- package/src/application/transform/document/path-template-replace.ts +90 -0
- package/src/application/transform/document/paths.ts +99 -37
- package/src/application/transform/document/root-type-aliases.ts +29 -28
- package/src/application/transform/document/template-key-match.ts +183 -0
- package/src/application/transform/document/type-assignability.ts +268 -0
- package/src/application/transform/document/type-key.ts +80 -0
- package/src/application/transform/document/type-resolution.ts +163 -0
- package/src/application/transform/footer/document-footer.ts +91 -6
- package/src/application/transform/footer/enum-naming.ts +29 -0
- package/src/application/transform/footer/enum-values-check.ts +171 -0
- package/src/application/transform/footer/evaluation.ts +47 -0
- package/src/application/transform/footer/shared-enum-names.ts +86 -0
- package/src/application/transform/footer/type-evaluation.ts +294 -0
- package/src/application/transform/footer/type-rules.ts +269 -0
- package/src/application/transform/ref-pointer/generated-location.ts +211 -0
- package/src/application/transform/ref-pointer/generated-shape.ts +275 -0
- package/src/application/transform/ref-pointer/inline-cycles.ts +56 -0
- package/src/application/transform/ref-pointer/original-index.ts +95 -0
- package/src/application/transform/ref-pointer/pointer-type.ts +92 -0
- package/src/application/transform/ref-pointer/pointer-walk.ts +96 -0
- package/src/application/transform/ref-pointer/typed-walk.ts +192 -0
- package/src/application/transform/ref-type.ts +11 -6
- package/src/application/transform/schema/access-flags.ts +63 -0
- package/src/application/transform/schema/array-type.ts +66 -5
- package/src/application/transform/schema/composition-type.ts +96 -24
- package/src/application/transform/schema/core-type.ts +8 -4
- package/src/application/transform/schema/discriminated-union.ts +58 -0
- package/src/application/transform/schema/discriminator-property.ts +8 -3
- package/src/application/transform/schema/enum-rule.ts +136 -13
- package/src/application/transform/schema/inherited-discriminator.ts +54 -0
- package/src/application/transform/schema/object-type.ts +51 -33
- package/src/application/transform/schema/required-variants.ts +88 -0
- package/src/application/transform/schema/schema-context.ts +26 -0
- package/src/application/transform/schema/schema-transform.ts +2 -2
- package/src/application/transform/schema-comment.ts +84 -4
- package/src/cli/cli-arguments.ts +13 -3
- package/src/cli/cli-dependencies.ts +5 -0
- package/src/cli/cli-errors.ts +4 -2
- package/src/cli/cli-output.ts +18 -4
- package/src/cli/main.ts +6 -0
- package/src/cli/redocly-apis.ts +8 -4
- package/src/cli/ref-limits.ts +2 -2
- package/src/cli/run-cli.ts +20 -3
- package/src/domain/errors.ts +2 -0
- package/src/domain/ir/constructors.ts +6 -1
- package/src/domain/ir/nodes.ts +2 -1
- package/src/domain/openapi/admitted-values.ts +113 -0
- package/src/domain/openapi/discriminated-union.ts +109 -0
- package/src/domain/openapi/discriminator-conflicts.ts +251 -0
- package/src/domain/openapi/parameter-object.ts +4 -0
- package/src/domain/openapi/path-templates.ts +31 -0
- package/src/domain/openapi/schema-object.ts +2 -0
- package/src/domain/output-fixes.ts +43 -2
- package/src/domain/warnings.ts +20 -0
- package/src/transform/components-object.ts +3 -1
- package/src/types/generator-options.ts +11 -0
- package/dist/application/transform/document/path-item-conflicts.cjs +0 -51
- package/dist/application/transform/document/path-item-conflicts.cjs.map +0 -1
- package/dist/application/transform/document/path-item-conflicts.mjs +0 -49
- package/dist/application/transform/document/path-item-conflicts.mjs.map +0 -1
- package/src/application/transform/document/path-item-conflicts.ts +0 -79
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { IrComment, IrPropertySignature } from "../../domain/ir/nodes.js";
|
|
2
|
+
import { OutputFix } from "../../domain/output-fixes.js";
|
|
2
3
|
|
|
3
4
|
/** Schema fields that produce JSDoc lines (the original `AnnotatedSchemaObject`). */
|
|
4
5
|
export interface AnnotatedSchema {
|
|
@@ -24,6 +25,27 @@ const COMMENT_END_RE = /\*\//g;
|
|
|
24
25
|
const VALUE_TAGS = ["description", "default", "example"] as const;
|
|
25
26
|
const TAGS_KEEPING_EMPTY_STRING: ReadonlySet<string> = new Set(["default", "example"]);
|
|
26
27
|
const JSON_INDENT = 2;
|
|
28
|
+
/** The member lists whose descriptions `anyof-oneof-descriptions` adds, with their headings. */
|
|
29
|
+
const MEMBER_LISTS = [
|
|
30
|
+
["anyOf", "Any of:"],
|
|
31
|
+
["oneOf", "One of:"],
|
|
32
|
+
] as const;
|
|
33
|
+
/** The later lines of a member's description are indented under its bullet. */
|
|
34
|
+
const BULLET_CONTINUATION = "\n ";
|
|
35
|
+
|
|
36
|
+
/** How a schema's JSDoc is written. */
|
|
37
|
+
export interface SchemaCommentOptions {
|
|
38
|
+
/** `anyof-oneof-descriptions`: the descriptions of `anyOf` and `oneOf` members are listed. */
|
|
39
|
+
readonly hasMemberDescriptions: boolean;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** The JSDoc the original writes. */
|
|
43
|
+
export const ORIGINAL_SCHEMA_COMMENT: SchemaCommentOptions = { hasMemberDescriptions: false };
|
|
44
|
+
|
|
45
|
+
/** How the fixes turned on write a schema's JSDoc. */
|
|
46
|
+
export function schemaCommentOptionsOf(fixes: ReadonlySet<OutputFix>): SchemaCommentOptions {
|
|
47
|
+
return { hasMemberDescriptions: fixes.has(OutputFix.ANYOF_ONEOF_DESCRIPTIONS) };
|
|
48
|
+
}
|
|
27
49
|
|
|
28
50
|
function formatValue(value: unknown): string {
|
|
29
51
|
const text = typeof value === "object" ? JSON.stringify(value, null, JSON_INDENT) : value;
|
|
@@ -81,17 +103,74 @@ function constantAndEnumLines(schema: AnnotatedSchema): string[] {
|
|
|
81
103
|
return lines;
|
|
82
104
|
}
|
|
83
105
|
|
|
106
|
+
/** The own, non-blank `description` of each member; a member without one is left out. */
|
|
107
|
+
function memberDescriptions(members: unknown): string[] {
|
|
108
|
+
if (!Array.isArray(members)) {
|
|
109
|
+
return [];
|
|
110
|
+
}
|
|
111
|
+
return members.flatMap((member: unknown) => {
|
|
112
|
+
const description = isSchemaObject(member) ? member.description : undefined;
|
|
113
|
+
return typeof description === "string" && description.trim() !== "" ? [description.trim()] : [];
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** Each member list with a described member, as a heading and one bullet per description. */
|
|
118
|
+
function memberDescriptionLists(schema: SchemaRecord): string[] {
|
|
119
|
+
return MEMBER_LISTS.flatMap(([keyword, heading]) => {
|
|
120
|
+
const descriptions = memberDescriptions(schema[keyword]);
|
|
121
|
+
if (descriptions.length === 0) {
|
|
122
|
+
return [];
|
|
123
|
+
}
|
|
124
|
+
const bullets = descriptions.map(
|
|
125
|
+
(description) => `- ${description.replace(LINE_BREAK_RE, BULLET_CONTINUATION)}`,
|
|
126
|
+
);
|
|
127
|
+
return [[heading, ...bullets].join("\n")];
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* The schema with the descriptions of its `anyOf` and `oneOf` members after its own description
|
|
133
|
+
* (upstream #2142). A member's description holds only where that member applies, so each one is
|
|
134
|
+
* listed under its keyword rather than merged into one sentence. A `description` that is not a
|
|
135
|
+
* string is kept as it is.
|
|
136
|
+
*/
|
|
137
|
+
function withMemberDescriptions(schema: SchemaRecord): AnnotatedSchema {
|
|
138
|
+
const lists = memberDescriptionLists(schema);
|
|
139
|
+
const own = schema.description;
|
|
140
|
+
if (lists.length === 0 || (own !== undefined && typeof own !== "string")) {
|
|
141
|
+
// As the original, the keywords are read as they are, without validating them.
|
|
142
|
+
return schema as AnnotatedSchema;
|
|
143
|
+
}
|
|
144
|
+
const ownLines = typeof own === "string" && own.trim() !== "" ? [own.trim()] : [];
|
|
145
|
+
return { ...(schema as AnnotatedSchema), description: [...ownLines, ...lists].join("\n") };
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/** A schema object read without knowing its keywords. */
|
|
149
|
+
interface SchemaRecord {
|
|
150
|
+
readonly description?: unknown;
|
|
151
|
+
readonly [keyword: string]: unknown;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function isSchemaObject(value: unknown): value is SchemaRecord {
|
|
155
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
156
|
+
}
|
|
157
|
+
|
|
84
158
|
/**
|
|
85
159
|
* The JSDoc comment the original writes for a schema (`addJSDocComment`): title, summary and
|
|
86
160
|
* format, then `@deprecated`, `@description`, `@default`, `@example`, `@constant` and `@enum`
|
|
87
161
|
* tags. One short line stays on the `/**` line; anything else is a block. Returns null when there
|
|
88
162
|
* is nothing to say, or when the value is not a schema object.
|
|
89
163
|
*/
|
|
90
|
-
export function schemaComment(
|
|
91
|
-
|
|
164
|
+
export function schemaComment(
|
|
165
|
+
schema: unknown,
|
|
166
|
+
options: SchemaCommentOptions = ORIGINAL_SCHEMA_COMMENT,
|
|
167
|
+
): IrComment | null {
|
|
168
|
+
if (!isSchemaObject(schema)) {
|
|
92
169
|
return null;
|
|
93
170
|
}
|
|
94
|
-
const annotated =
|
|
171
|
+
const annotated = options.hasMemberDescriptions
|
|
172
|
+
? withMemberDescriptions(schema)
|
|
173
|
+
: (schema as AnnotatedSchema);
|
|
95
174
|
const lines = [
|
|
96
175
|
...plainLines(annotated),
|
|
97
176
|
...valueTagLines(annotated),
|
|
@@ -110,7 +189,8 @@ export function schemaComment(schema: unknown): IrComment | null {
|
|
|
110
189
|
export function withSchemaComment(
|
|
111
190
|
property: IrPropertySignature,
|
|
112
191
|
schema: unknown,
|
|
192
|
+
options: SchemaCommentOptions = ORIGINAL_SCHEMA_COMMENT,
|
|
113
193
|
): IrPropertySignature {
|
|
114
|
-
const comment = schemaComment(schema);
|
|
194
|
+
const comment = schemaComment(schema, options);
|
|
115
195
|
return comment === null ? property : { ...property, comments: [...property.comments, comment] };
|
|
116
196
|
}
|
package/src/cli/cli-arguments.ts
CHANGED
|
@@ -165,13 +165,23 @@ const KNOWN_FLAGS = new Set<string>([
|
|
|
165
165
|
* Flags that no code reads. The original CLI ignored them silently, so a typo such as
|
|
166
166
|
* `--alphabetise` gave other output with no hint; they still do not fail the run.
|
|
167
167
|
*/
|
|
168
|
-
function unknownFlagsOf(parsed: parser.Arguments): string[] {
|
|
168
|
+
function unknownFlagsOf(parsed: parser.Arguments, argv: readonly string[]): string[] {
|
|
169
169
|
const names = Object.keys(parsed)
|
|
170
170
|
.filter((key) => key !== "_" && key !== "--" && !KNOWN_FLAGS.has(key))
|
|
171
|
-
.map((key) => (key
|
|
171
|
+
.map((key) => writtenFlagName(key, argv));
|
|
172
172
|
return [...new Set(names)];
|
|
173
173
|
}
|
|
174
174
|
|
|
175
|
+
/** The flag as the user wrote it: yargs-parser reads `--no-x` as `x: false`. */
|
|
176
|
+
function writtenFlagName(key: string, argv: readonly string[]): string {
|
|
177
|
+
if (key.length === 1) {
|
|
178
|
+
return `-${key}`;
|
|
179
|
+
}
|
|
180
|
+
const negated = `--no-${kebabCase(key)}`;
|
|
181
|
+
const isNegated = argv.some((arg) => arg === negated || arg.startsWith(`${negated}=`));
|
|
182
|
+
return isNegated ? negated : `--${kebabCase(key)}`;
|
|
183
|
+
}
|
|
184
|
+
|
|
175
185
|
/**
|
|
176
186
|
* Parse the arguments after `node cli.js`. Fails with `CLI_FLAG_REMOVED` for an old flag and with
|
|
177
187
|
* `OPTION_FIX_UNKNOWN` for an unknown `--fix` name.
|
|
@@ -193,6 +203,6 @@ export function parseCliArguments(argv: readonly string[]): CliArguments {
|
|
|
193
203
|
generatorFlags: generatorFlagsOf(parsed),
|
|
194
204
|
refLimits: refLimitsOf(parsed),
|
|
195
205
|
fix: fixNamesOf(parsed.fix),
|
|
196
|
-
unknownFlags: unknownFlagsOf(parsed),
|
|
206
|
+
unknownFlags: unknownFlagsOf(parsed, argv),
|
|
197
207
|
};
|
|
198
208
|
}
|
|
@@ -33,6 +33,11 @@ export interface CliDependencies {
|
|
|
33
33
|
readonly writeStdout: (text: string) => void;
|
|
34
34
|
/** Read only when the schema comes from stdin: opening it early can keep the process alive. */
|
|
35
35
|
readonly stdin: () => Readable;
|
|
36
|
+
/**
|
|
37
|
+
* Whether stdin is a terminal. Then nothing is piped in, and reading it would wait for typed
|
|
38
|
+
* input with no prompt, which looks like a hang (upstream #1905).
|
|
39
|
+
*/
|
|
40
|
+
readonly isStdinTerminal: () => boolean;
|
|
36
41
|
readonly files: FileReader & FileWriter;
|
|
37
42
|
readonly redoclyConfigs: RedoclyConfigSource;
|
|
38
43
|
readonly generate: GenerateTypes;
|
package/src/cli/cli-errors.ts
CHANGED
|
@@ -49,8 +49,8 @@ function loadFailedMessage(error: OpenAPITypesafeError): string {
|
|
|
49
49
|
if (!(reason instanceof Error)) {
|
|
50
50
|
return error.message;
|
|
51
51
|
}
|
|
52
|
-
|
|
53
|
-
return `${error.message}: ${reason.message}${source}`;
|
|
52
|
+
// `loadFailedError`, the only source of this code, always sets the source.
|
|
53
|
+
return `${error.message}: ${reason.message} (${String(error.details.source)})`;
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
/** The message js-yaml throws at its nesting limit (docs/security/threat-model.md T2). */
|
|
@@ -88,8 +88,10 @@ const CLI_ERROR_REPORTS: Readonly<Record<ErrorCode, CliErrorReport>> = {
|
|
|
88
88
|
CLI_FLAG_REMOVED: { exitCode: EXIT_CODES.USAGE },
|
|
89
89
|
CLI_GLOB_UNSUPPORTED: { exitCode: EXIT_CODES.USAGE },
|
|
90
90
|
CLI_CONFIG_NOT_FOUND: { exitCode: EXIT_CODES.USAGE },
|
|
91
|
+
CLI_CONFIG_INVALID: { exitCode: EXIT_CODES.USAGE },
|
|
91
92
|
CLI_CONFIG_OUTPUT_MISSING: { exitCode: EXIT_CODES.USAGE },
|
|
92
93
|
CLI_CHECK_OUTPUT_MISSING: { exitCode: EXIT_CODES.USAGE },
|
|
94
|
+
CLI_INPUT_MISSING: { exitCode: EXIT_CODES.USAGE },
|
|
93
95
|
OUTPUT_STALE: { exitCode: EXIT_CODES.OUTPUT_STALE },
|
|
94
96
|
OUTPUT_READ_FAILED: { exitCode: EXIT_CODES.OUTPUT },
|
|
95
97
|
OUTPUT_WRITE_FAILED: { exitCode: EXIT_CODES.OUTPUT },
|
package/src/cli/cli-output.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
-
import { pathToFileURL } from "node:url";
|
|
2
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
3
3
|
import { formatDuration } from "../adapters/terminal-logger/terminal-logger.js";
|
|
4
4
|
import type { CliArguments } from "./cli-arguments.js";
|
|
5
5
|
import type { CliDependencies } from "./cli-dependencies.js";
|
|
@@ -21,9 +21,23 @@ export interface OutputFile {
|
|
|
21
21
|
readonly file: URL;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
/**
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
/**
|
|
25
|
+
* A scheme of two or more characters (`https:`, `file:`). One letter is a Windows drive (`C:`), so
|
|
26
|
+
* `C:\\specs\\api.yaml` stays a path.
|
|
27
|
+
*/
|
|
28
|
+
const URL_SCHEME = /^[a-z][a-z\d+.-]+:/i;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* The URL of a location the user wrote: a URL as it is, else a file path, absolute or relative to
|
|
32
|
+
* the directory of `base` (a directory or file URL). A path is not parsed as a URL, so `#`, `?` and
|
|
33
|
+
* `%` are part of the file name.
|
|
34
|
+
*/
|
|
35
|
+
export function resolveLocationUrl(location: string, base: URL): URL {
|
|
36
|
+
if (URL_SCHEME.test(location)) {
|
|
37
|
+
return new URL(location);
|
|
38
|
+
}
|
|
39
|
+
const baseDirectory = fileURLToPath(new URL(".", base));
|
|
40
|
+
return pathToFileURL(path.resolve(baseDirectory, location));
|
|
27
41
|
}
|
|
28
42
|
|
|
29
43
|
function printDone(run: CliRun, output: OutputFile): void {
|
package/src/cli/main.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
|
+
import { isatty } from "node:tty";
|
|
2
3
|
import { pathToFileURL } from "node:url";
|
|
3
4
|
import colors from "ansi-colors";
|
|
4
5
|
import { createNodeFileSystem } from "../adapters/node-file-system/node-file-system.js";
|
|
@@ -33,6 +34,8 @@ function terminalStyle(): TextStyle {
|
|
|
33
34
|
};
|
|
34
35
|
}
|
|
35
36
|
|
|
37
|
+
const STDIN_DESCRIPTOR = 0;
|
|
38
|
+
|
|
36
39
|
const generateTypes: GenerateTypes = async (source, options) =>
|
|
37
40
|
`${COMMENT_HEADER}${astToString(await openapiTS(source, options))}`;
|
|
38
41
|
|
|
@@ -54,6 +57,9 @@ function processDependencies(): CliDependencies {
|
|
|
54
57
|
console: terminalConsole(),
|
|
55
58
|
writeStdout: (text) => process.stdout.write(text),
|
|
56
59
|
stdin: () => process.stdin,
|
|
60
|
+
// `isatty` reads the descriptor without opening `process.stdin`, which would keep the
|
|
61
|
+
// process alive.
|
|
62
|
+
isStdinTerminal: () => isatty(STDIN_DESCRIPTOR),
|
|
57
63
|
files: createNodeFileSystem(),
|
|
58
64
|
redoclyConfigs: createRedoclyConfigSource(cwd),
|
|
59
65
|
generate: generateTypes,
|
package/src/cli/redocly-apis.ts
CHANGED
|
@@ -4,7 +4,7 @@ import type { OutputFixName } from "../domain/output-fixes.js";
|
|
|
4
4
|
import type { GeneratorOptions } from "../types.js";
|
|
5
5
|
import { GENERATOR_FLAGS, type GeneratorFlags } from "./cli-arguments.js";
|
|
6
6
|
import { cliError } from "./cli-errors.js";
|
|
7
|
-
import { type CliRun, emitOutput,
|
|
7
|
+
import { type CliRun, emitOutput, resolveLocationUrl } from "./cli-output.js";
|
|
8
8
|
import { MULTIPLE_SCHEMAS_DOCS_URL } from "./docs-links.js";
|
|
9
9
|
import { fixNamesOf, fixOption } from "./fix-option.js";
|
|
10
10
|
import { refLimitOptions } from "./ref-limits.js";
|
|
@@ -55,7 +55,7 @@ function apiFixNames(settings: ApiSettings): readonly OutputFixName[] | undefine
|
|
|
55
55
|
|
|
56
56
|
/** Paths in the config are relative to the config file; without a file, to the working dir. */
|
|
57
57
|
function configRootOf(redocly: RedoclyConfig, cwd: URL): URL {
|
|
58
|
-
return redocly.configFile ?
|
|
58
|
+
return redocly.configFile ? resolveLocationUrl(redocly.configFile, cwd) : cwd;
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
async function generateApi(run: CliRun, redocly: RedoclyConfig, api: RedoclyApi): Promise<void> {
|
|
@@ -75,8 +75,12 @@ async function generateApi(run: CliRun, redocly: RedoclyConfig, api: RedoclyApi)
|
|
|
75
75
|
...fixOption(apiFixNames(api.settings) ?? run.args.fix),
|
|
76
76
|
redocly,
|
|
77
77
|
};
|
|
78
|
-
const contents = await run.deps.generate(
|
|
79
|
-
emitOutput(
|
|
78
|
+
const contents = await run.deps.generate(resolveLocationUrl(api.root, configRoot), options);
|
|
79
|
+
emitOutput(
|
|
80
|
+
run,
|
|
81
|
+
{ source: api.name, output, file: resolveLocationUrl(output, configRoot) },
|
|
82
|
+
contents,
|
|
83
|
+
);
|
|
80
84
|
}
|
|
81
85
|
|
|
82
86
|
/**
|
package/src/cli/ref-limits.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { GeneratorOptions } from "../types.js";
|
|
2
2
|
import type { CliArguments } from "./cli-arguments.js";
|
|
3
|
-
import {
|
|
3
|
+
import { resolveLocationUrl } from "./cli-output.js";
|
|
4
4
|
|
|
5
5
|
/** The `$ref` limits of the CLI flags as generator options (threat model R1, R2, ADR-017). */
|
|
6
6
|
|
|
@@ -10,7 +10,7 @@ export type RefLimitOptions = Pick<GeneratorOptions, "localRefRoot" | "remoteRef
|
|
|
10
10
|
export function refLimitOptions(args: CliArguments, cwd: URL): RefLimitOptions {
|
|
11
11
|
const { localRefRoot, remoteRefHosts } = args.refLimits;
|
|
12
12
|
return {
|
|
13
|
-
...(localRefRoot === undefined ? {} : { localRefRoot:
|
|
13
|
+
...(localRefRoot === undefined ? {} : { localRefRoot: resolveLocationUrl(localRefRoot, cwd) }),
|
|
14
14
|
...(remoteRefHosts === undefined ? {} : { remoteRefHosts }),
|
|
15
15
|
};
|
|
16
16
|
}
|
package/src/cli/run-cli.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import type { Logger } from "../application/ports/logger.js";
|
|
2
2
|
import type { RedoclyConfig } from "../application/ports/redocly-config-source.js";
|
|
3
|
+
import type { SchemaSource } from "../types.js";
|
|
3
4
|
import { ineffectiveFlagNotices, parseCliArguments } from "./cli-arguments.js";
|
|
4
5
|
import type { CliDependencies } from "./cli-dependencies.js";
|
|
5
6
|
import { cliError, describeCliFailure, EXIT_CODES, type ExitCode } from "./cli-errors.js";
|
|
6
|
-
import { type CliRun, emitOutput,
|
|
7
|
+
import { type CliRun, emitOutput, resolveLocationUrl } from "./cli-output.js";
|
|
7
8
|
import { MULTIPLE_SCHEMAS_DOCS_URL } from "./docs-links.js";
|
|
8
9
|
import { fixOption } from "./fix-option.js";
|
|
9
10
|
import { HELP_TEXT } from "./help-text.js";
|
|
@@ -25,6 +26,22 @@ async function loadRedoclyConfig(run: CliRun): Promise<RedoclyConfig> {
|
|
|
25
26
|
return configPath ? redoclyConfigs.load(configPath) : redoclyConfigs.createDefault();
|
|
26
27
|
}
|
|
27
28
|
|
|
29
|
+
/** The schema's URL, or stdin when no path or URL is given and something is piped in. */
|
|
30
|
+
function schemaSource(run: CliRun): SchemaSource {
|
|
31
|
+
const { input } = run.args;
|
|
32
|
+
if (input) {
|
|
33
|
+
return resolveLocationUrl(input, run.deps.cwd);
|
|
34
|
+
}
|
|
35
|
+
if (run.deps.isStdinTerminal()) {
|
|
36
|
+
throw cliError(
|
|
37
|
+
"CLI_INPUT_MISSING",
|
|
38
|
+
"No schema given: pass a schema path or URL, pipe a schema into stdin, or define `apis` " +
|
|
39
|
+
"in redocly.yaml. See --help.",
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
return run.deps.stdin();
|
|
43
|
+
}
|
|
44
|
+
|
|
28
45
|
/** One schema from a path, a URL or stdin; without `--output`, the types go to stdout only. */
|
|
29
46
|
async function generateSingleSchema(run: CliRun, redocly: RedoclyConfig): Promise<void> {
|
|
30
47
|
const { input, output } = run.args;
|
|
@@ -42,7 +59,7 @@ async function generateSingleSchema(run: CliRun, redocly: RedoclyConfig): Promis
|
|
|
42
59
|
"--check needs the generated file to compare with: add --output <file>.",
|
|
43
60
|
);
|
|
44
61
|
}
|
|
45
|
-
const source =
|
|
62
|
+
const source = schemaSource(run);
|
|
46
63
|
const options = {
|
|
47
64
|
...run.args.generatorFlags,
|
|
48
65
|
...refLimitOptions(run.args, run.deps.cwd),
|
|
@@ -55,7 +72,7 @@ async function generateSingleSchema(run: CliRun, redocly: RedoclyConfig): Promis
|
|
|
55
72
|
run.deps.writeStdout(contents);
|
|
56
73
|
return;
|
|
57
74
|
}
|
|
58
|
-
const file =
|
|
75
|
+
const file = resolveLocationUrl(output, run.deps.cwd);
|
|
59
76
|
emitOutput(run, { source: input ?? "stdin", output, file }, contents);
|
|
60
77
|
}
|
|
61
78
|
|
package/src/domain/errors.ts
CHANGED
|
@@ -26,8 +26,10 @@ export const ERROR_CODES = {
|
|
|
26
26
|
CLI_FLAG_REMOVED: "CLI_FLAG_REMOVED",
|
|
27
27
|
CLI_GLOB_UNSUPPORTED: "CLI_GLOB_UNSUPPORTED",
|
|
28
28
|
CLI_CONFIG_NOT_FOUND: "CLI_CONFIG_NOT_FOUND",
|
|
29
|
+
CLI_CONFIG_INVALID: "CLI_CONFIG_INVALID",
|
|
29
30
|
CLI_CONFIG_OUTPUT_MISSING: "CLI_CONFIG_OUTPUT_MISSING",
|
|
30
31
|
CLI_CHECK_OUTPUT_MISSING: "CLI_CHECK_OUTPUT_MISSING",
|
|
32
|
+
CLI_INPUT_MISSING: "CLI_INPUT_MISSING",
|
|
31
33
|
OUTPUT_STALE: "OUTPUT_STALE",
|
|
32
34
|
OUTPUT_READ_FAILED: "OUTPUT_READ_FAILED",
|
|
33
35
|
OUTPUT_WRITE_FAILED: "OUTPUT_WRITE_FAILED",
|
|
@@ -62,7 +62,12 @@ export function irTuple(elements: readonly IrTupleElement[]): IrTupleType {
|
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
export function irTupleElement(type: IrType, isRest = false): IrTupleElement {
|
|
65
|
-
return { type, isRest };
|
|
65
|
+
return { type, isRest, isOptional: false };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** `T?`: an element the tuple may end before. */
|
|
69
|
+
export function irOptionalTupleElement(type: IrType): IrTupleElement {
|
|
70
|
+
return { type, isRest: false, isOptional: true };
|
|
66
71
|
}
|
|
67
72
|
|
|
68
73
|
export function irUnion(types: readonly IrType[]): IrUnionType {
|
package/src/domain/ir/nodes.ts
CHANGED
|
@@ -61,10 +61,11 @@ export interface IrArrayType {
|
|
|
61
61
|
readonly element: IrType;
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
/** A tuple element; `isRest` prints `...T
|
|
64
|
+
/** A tuple element; `isRest` prints `...T`, `isOptional` prints `T?` (never both). */
|
|
65
65
|
export interface IrTupleElement {
|
|
66
66
|
readonly type: IrType;
|
|
67
67
|
readonly isRest: boolean;
|
|
68
|
+
readonly isOptional: boolean;
|
|
68
69
|
}
|
|
69
70
|
|
|
70
71
|
export interface IrTupleType {
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { parseRefPointer } from "./json-pointer.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The values a schema admits when it admits only finitely many (`enum`, `const`, and `allOf` /
|
|
5
|
+
* `anyOf` / `oneOf` of those), read from the document. Null means "any value" as far as this
|
|
6
|
+
* reading goes: a schema of any other shape, or a `$ref` it cannot follow, is never assumed to
|
|
7
|
+
* exclude a value.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export type AdmittedValue = string | number | boolean | null;
|
|
11
|
+
/** A finite set of values, or null for every value. */
|
|
12
|
+
export type AdmittedValues = ReadonlySet<AdmittedValue> | null;
|
|
13
|
+
|
|
14
|
+
type JsonObject = Readonly<Record<string, unknown>>;
|
|
15
|
+
|
|
16
|
+
/** How deep `$ref`s and compositions are followed; deeper schemas admit every value. */
|
|
17
|
+
const MAX_DEPTH = 16;
|
|
18
|
+
const LOCAL_REF_PREFIX = "#/";
|
|
19
|
+
|
|
20
|
+
function isObject(value: unknown): value is JsonObject {
|
|
21
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** The node at a local `$ref` (`#/...`) of the document, or undefined. */
|
|
25
|
+
export function resolveLocalRef(document: unknown, ref: string): unknown {
|
|
26
|
+
if (!ref.startsWith(LOCAL_REF_PREFIX)) {
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
let node: unknown = document;
|
|
30
|
+
for (const segment of parseRefPointer(ref)) {
|
|
31
|
+
node = isObject(node) || Array.isArray(node) ? (node as JsonObject)[segment] : undefined;
|
|
32
|
+
}
|
|
33
|
+
return node;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function intersect(left: AdmittedValues, right: AdmittedValues): AdmittedValues {
|
|
37
|
+
if (left === null || right === null) {
|
|
38
|
+
return left ?? right;
|
|
39
|
+
}
|
|
40
|
+
return new Set([...left].filter((value) => right.has(value)));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function unite(sets: readonly AdmittedValues[]): AdmittedValues {
|
|
44
|
+
return sets.some((set) => set === null) ? null : new Set(sets.flatMap((set) => [...(set ?? [])]));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function isAdmittedValue(value: unknown): value is AdmittedValue {
|
|
48
|
+
return value === null || ["string", "number", "boolean"].includes(typeof value);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
interface Walk {
|
|
52
|
+
readonly document: unknown;
|
|
53
|
+
readonly depth: number;
|
|
54
|
+
/** `$ref`s whose targets are left out: they add nothing to the generated type here. */
|
|
55
|
+
readonly skipped: ReadonlySet<string>;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function follow(node: unknown, walk: Walk): unknown {
|
|
59
|
+
if (!isObject(node) || typeof node.$ref !== "string") {
|
|
60
|
+
return node;
|
|
61
|
+
}
|
|
62
|
+
return walk.skipped.has(node.$ref) ? undefined : resolveLocalRef(walk.document, node.$ref);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function members(node: JsonObject, keyword: string): readonly unknown[] {
|
|
66
|
+
const list = node[keyword];
|
|
67
|
+
return Array.isArray(list) ? list : [];
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** `enum` and `const` of the schema itself, before its compositions. */
|
|
71
|
+
function ownValues(node: JsonObject): AdmittedValues {
|
|
72
|
+
const listed = Array.isArray(node.enum) ? node.enum.filter(isAdmittedValue) : null;
|
|
73
|
+
const constant = "const" in node && isAdmittedValue(node.const) ? new Set([node.const]) : null;
|
|
74
|
+
return intersect(listed === null ? null : new Set(listed), constant);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function schemaValues(schema: unknown, walk: Walk): AdmittedValues {
|
|
78
|
+
const node = follow(schema, walk);
|
|
79
|
+
if (!isObject(node) || walk.depth > MAX_DEPTH) {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
const next = { ...walk, depth: walk.depth + 1 };
|
|
83
|
+
const parts = members(node, "allOf").map((part) => schemaValues(part, next));
|
|
84
|
+
const variants = [...members(node, "anyOf"), ...members(node, "oneOf")];
|
|
85
|
+
const union = variants.length === 0 ? null : unite(variants.map((v) => schemaValues(v, next)));
|
|
86
|
+
return [...parts, union].reduce<AdmittedValues>(intersect, ownValues(node));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function propertyValues(schema: unknown, key: string, walk: Walk): AdmittedValues {
|
|
90
|
+
const node = follow(schema, walk);
|
|
91
|
+
if (!isObject(node) || walk.depth > MAX_DEPTH) {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
const next = { ...walk, depth: walk.depth + 1 };
|
|
95
|
+
const properties = isObject(node.properties) ? node.properties : {};
|
|
96
|
+
const own = Object.hasOwn(properties, key) ? schemaValues(properties[key], next) : null;
|
|
97
|
+
const parts = members(node, "allOf").map((part) => propertyValues(part, key, next));
|
|
98
|
+
return parts.reduce<AdmittedValues>(intersect, own);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* The values property `key` of an object schema admits: the intersection over the schema's own
|
|
103
|
+
* `properties` and its `allOf` parts (followed through `$ref`s, except `skipped` ones); null for
|
|
104
|
+
* every value.
|
|
105
|
+
*/
|
|
106
|
+
export function admittedPropertyValues(
|
|
107
|
+
schema: unknown,
|
|
108
|
+
key: string,
|
|
109
|
+
document: unknown,
|
|
110
|
+
skipped: ReadonlySet<string> = new Set(),
|
|
111
|
+
): AdmittedValues {
|
|
112
|
+
return propertyValues(schema, key, { document, depth: 0, skipped });
|
|
113
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { escapePointerSegment } from "./json-pointer.js";
|
|
2
|
+
import type { Schema, SchemaObject } from "./schema-object.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* A discriminator owner is either a parent that its members `allOf` (an object with the
|
|
6
|
+
* discriminator property) or a union (`oneOf` / `anyOf` of the members). The generated type of a
|
|
7
|
+
* union is a TypeScript union, so it cannot be used like a parent: `Omit<Union, "kind">` keeps
|
|
8
|
+
* only the keys that every member has, and a member that intersects the union it belongs to
|
|
9
|
+
* refers to itself (TS2502, upstream #1665).
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/** Resolves a `$ref` of the document; `undefined` when it points nowhere. */
|
|
13
|
+
export type ResolveSchemaRef = (ref: string) => unknown;
|
|
14
|
+
|
|
15
|
+
const UNION_KEYWORDS = ["oneOf", "anyOf"] as const;
|
|
16
|
+
const COMPOSITION_KEYWORDS = ["allOf", ...UNION_KEYWORDS] as const;
|
|
17
|
+
|
|
18
|
+
function isSchemaObject(value: unknown): value is SchemaObject {
|
|
19
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function keywordItems(schema: SchemaObject, keywords: readonly string[]): readonly Schema[] {
|
|
23
|
+
const record = schema as Readonly<Record<string, unknown>>;
|
|
24
|
+
return keywords.flatMap((keyword) => {
|
|
25
|
+
const items = record[keyword];
|
|
26
|
+
return Array.isArray(items) ? (items as readonly Schema[]) : [];
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** The `oneOf` and `anyOf` members of a schema; empty when it is not a union. */
|
|
31
|
+
export function unionMembers(schema: unknown): readonly Schema[] {
|
|
32
|
+
return isSchemaObject(schema) ? keywordItems(schema, UNION_KEYWORDS) : [];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** The schema at `ref` when it is a union (has `oneOf` or `anyOf` members), else undefined. */
|
|
36
|
+
export function unionAt(ref: string, resolve: ResolveSchemaRef): SchemaObject | undefined {
|
|
37
|
+
const schema = resolve(ref);
|
|
38
|
+
return isSchemaObject(schema) && unionMembers(schema).length > 0 ? schema : undefined;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** What one search for a `$ref` has seen, so that a `$ref` cycle ends. */
|
|
42
|
+
interface Search {
|
|
43
|
+
readonly target: string;
|
|
44
|
+
readonly resolve: ResolveSchemaRef;
|
|
45
|
+
readonly visited: Set<string>;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function reaches(schema: Schema, search: Search): boolean {
|
|
49
|
+
if (!isSchemaObject(schema)) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
const ref = (schema as { readonly $ref?: unknown }).$ref;
|
|
53
|
+
if (typeof ref === "string") {
|
|
54
|
+
if (ref === search.target) {
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
if (search.visited.has(ref)) {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
search.visited.add(ref);
|
|
61
|
+
return reaches(search.resolve(ref) as Schema, search);
|
|
62
|
+
}
|
|
63
|
+
return keywordItems(schema, COMPOSITION_KEYWORDS).some((item) => reaches(item, search));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Whether the type of `union` contains the schema at `target` without an object in between: one
|
|
68
|
+
* of its members is a `$ref` to it, or `allOf` / `oneOf` / `anyOf` such a `$ref`, at any depth.
|
|
69
|
+
* Such a reference is resolved at once by TypeScript, unlike one in a property or an array.
|
|
70
|
+
*/
|
|
71
|
+
export function unionReaches(
|
|
72
|
+
union: SchemaObject,
|
|
73
|
+
target: string,
|
|
74
|
+
resolve: ResolveSchemaRef,
|
|
75
|
+
): boolean {
|
|
76
|
+
const search = { target, resolve, visited: new Set<string>() };
|
|
77
|
+
return unionMembers(union).some((member) => reaches(member, search));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const COMPONENT_SCHEMAS = "#/components/schemas/";
|
|
81
|
+
|
|
82
|
+
/** A mapping value as a `$ref`: a bare name is a component schema's name. */
|
|
83
|
+
function mappingRef(target: unknown): string | undefined {
|
|
84
|
+
if (typeof target !== "string") {
|
|
85
|
+
return undefined;
|
|
86
|
+
}
|
|
87
|
+
return target.startsWith("#") ? target : `${COMPONENT_SCHEMAS}${escapePointerSegment(target)}`;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Whether the discriminator `mapping` of `union` names the schema at `target`. */
|
|
91
|
+
export function unionMaps(union: SchemaObject, target: string): boolean {
|
|
92
|
+
const mapping = (union.discriminator as { readonly mapping?: unknown } | undefined)?.mapping;
|
|
93
|
+
if (!isSchemaObject(mapping)) {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
return Object.values(mapping).some((value) => mappingRef(value) === target);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Whether the schema at `target` is a member of `union`: the union's type contains it, or the
|
|
101
|
+
* discriminator `mapping` names it, which gives it a value of the union as a member does.
|
|
102
|
+
*/
|
|
103
|
+
export function isUnionMember(
|
|
104
|
+
union: SchemaObject,
|
|
105
|
+
target: string,
|
|
106
|
+
resolve: ResolveSchemaRef,
|
|
107
|
+
): boolean {
|
|
108
|
+
return unionMaps(union, target) || unionReaches(union, target, resolve);
|
|
109
|
+
}
|