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
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type { IrDeclaration } from "../../../domain/ir/nodes.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The TypeScript global types that generated code can refer to: in references (`Record<string,
|
|
5
|
+
* never>`, `Omit<…>`, the `Blob` of `--binary-as-blob`) and in the text of footer helpers
|
|
6
|
+
* (`Exclude<…>`, `Readonly<…>`). A module declaration of the same name shadows the global: every
|
|
7
|
+
* use of it then fails to compile, or silently names the module's type (`Blob`).
|
|
8
|
+
*/
|
|
9
|
+
const GLOBAL_TYPE_NAMES: ReadonlySet<string> = new Set([
|
|
10
|
+
"Array",
|
|
11
|
+
"Blob",
|
|
12
|
+
"Exclude",
|
|
13
|
+
"Extract",
|
|
14
|
+
"NonNullable",
|
|
15
|
+
"Omit",
|
|
16
|
+
"Partial",
|
|
17
|
+
"Pick",
|
|
18
|
+
"Readonly",
|
|
19
|
+
"ReadonlyArray",
|
|
20
|
+
"Record",
|
|
21
|
+
"Required",
|
|
22
|
+
]);
|
|
23
|
+
|
|
24
|
+
const GENERIC_USE = /\b([A-Z][A-Za-z]*)\s*</g;
|
|
25
|
+
|
|
26
|
+
/** `origin` holds the outside node a raw type was printed from; it is not part of the output. */
|
|
27
|
+
const SKIPPED_KEYS: ReadonlySet<string> = new Set(["origin", "comments"]);
|
|
28
|
+
|
|
29
|
+
function isObject(value: unknown): value is Readonly<Record<string, unknown>> {
|
|
30
|
+
return typeof value === "object" && value !== null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function addTextUses(text: string, used: Set<string>): void {
|
|
34
|
+
for (const match of text.matchAll(GENERIC_USE)) {
|
|
35
|
+
const name = match[1] ?? "";
|
|
36
|
+
if (GLOBAL_TYPE_NAMES.has(name)) {
|
|
37
|
+
used.add(name);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function addNodeUses(node: Readonly<Record<string, unknown>>, used: Set<string>): void {
|
|
43
|
+
if (node.kind === "reference" && typeof node.name === "string") {
|
|
44
|
+
if (GLOBAL_TYPE_NAMES.has(node.name)) {
|
|
45
|
+
used.add(node.name);
|
|
46
|
+
}
|
|
47
|
+
} else if (
|
|
48
|
+
(node.kind === "raw" || node.kind === "rawDeclaration") &&
|
|
49
|
+
typeof node.text === "string"
|
|
50
|
+
) {
|
|
51
|
+
addTextUses(node.text, used);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function visit(value: unknown, used: Set<string>): void {
|
|
56
|
+
if (Array.isArray(value)) {
|
|
57
|
+
for (const item of value) {
|
|
58
|
+
visit(item, used);
|
|
59
|
+
}
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
if (!isObject(value)) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
addNodeUses(value, used);
|
|
66
|
+
for (const [key, child] of Object.entries(value)) {
|
|
67
|
+
if (!SKIPPED_KEYS.has(key)) {
|
|
68
|
+
visit(child, used);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** The TypeScript global type names that the declarations refer to. */
|
|
74
|
+
export function globalTypeReferences(declarations: readonly IrDeclaration[]): Set<string> {
|
|
75
|
+
const used = new Set<string>();
|
|
76
|
+
visit(declarations, used);
|
|
77
|
+
return used;
|
|
78
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
IrArrayType,
|
|
3
|
+
IrReadonlyType,
|
|
4
|
+
IrTupleElement,
|
|
5
|
+
IrTupleType,
|
|
6
|
+
IrType,
|
|
7
|
+
} from "../../../domain/ir/nodes.js";
|
|
8
|
+
import { type Assignability, everyOf } from "./assignability.js";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* How the `--path-params-as-types` overlap check relates two lists: arrays, tuples and their
|
|
12
|
+
* `readonly` forms. `relate` compares two item types with the whole check.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
type Relate = (source: IrType, target: IrType) => Assignability;
|
|
16
|
+
|
|
17
|
+
/** The types of the `list` category of `assignability.ts`. */
|
|
18
|
+
export type IrListType = IrArrayType | IrTupleType | IrReadonlyType;
|
|
19
|
+
|
|
20
|
+
type IrMutableListType = IrArrayType | IrTupleType;
|
|
21
|
+
|
|
22
|
+
const UNDEFINED: IrType = { kind: "keyword", keyword: "undefined" };
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The type of a tuple element's items: a rest element `...T[]` stands for its items, and an
|
|
26
|
+
* optional element `T?` may be `undefined` (`[string?]` is no `string[]` under `strict`).
|
|
27
|
+
*/
|
|
28
|
+
function elementType(element: IrTupleElement): IrType {
|
|
29
|
+
if (element.isRest && element.type.kind === "array") {
|
|
30
|
+
return element.type.element;
|
|
31
|
+
}
|
|
32
|
+
return element.isOptional ? { kind: "union", types: [element.type, UNDEFINED] } : element.type;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function isRequired(element: IrTupleElement): boolean {
|
|
36
|
+
return !element.isRest && !element.isOptional;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** A tuple with a required element rejects an array, which may be empty. */
|
|
40
|
+
function arrayToTuple(target: IrTupleType): Assignability {
|
|
41
|
+
return target.elements.some(isRequired) ? "rejected" : "undecided";
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Tuples of the same shape compare element by element; fixed lengths must be the same. */
|
|
45
|
+
function relateTuples(source: IrTupleType, target: IrTupleType, relate: Relate): Assignability {
|
|
46
|
+
if (source.elements.length !== target.elements.length) {
|
|
47
|
+
const isFixed = source.elements.every(isRequired) && target.elements.every(isRequired);
|
|
48
|
+
return isFixed ? "rejected" : "undecided";
|
|
49
|
+
}
|
|
50
|
+
return everyOf(source.elements, (element, index) => {
|
|
51
|
+
const other = target.elements[index] as IrTupleElement;
|
|
52
|
+
if (element.isRest !== other.isRest) {
|
|
53
|
+
return "undecided";
|
|
54
|
+
}
|
|
55
|
+
// An optional element may be missing where the target requires one.
|
|
56
|
+
if (element.isOptional && !other.isOptional) {
|
|
57
|
+
return "rejected";
|
|
58
|
+
}
|
|
59
|
+
return relate(element.type, other.type);
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function relateMutableLists(
|
|
64
|
+
source: IrMutableListType,
|
|
65
|
+
target: IrMutableListType,
|
|
66
|
+
relate: Relate,
|
|
67
|
+
): Assignability {
|
|
68
|
+
if (source.kind === "array") {
|
|
69
|
+
return target.kind === "array" ? relate(source.element, target.element) : arrayToTuple(target);
|
|
70
|
+
}
|
|
71
|
+
if (target.kind === "array") {
|
|
72
|
+
return everyOf(source.elements, (element) => relate(elementType(element), target.element));
|
|
73
|
+
}
|
|
74
|
+
return relateTuples(source, target, relate);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Whether one list type fits another. */
|
|
78
|
+
export function relateLists(source: IrListType, target: IrListType, relate: Relate): Assignability {
|
|
79
|
+
if (source.kind === "readonly") {
|
|
80
|
+
// A readonly array is no mutable array (TS4104); to a readonly target it is the same type.
|
|
81
|
+
return target.kind === "readonly" ? relate(source.type, target.type) : "rejected";
|
|
82
|
+
}
|
|
83
|
+
if (target.kind === "readonly") {
|
|
84
|
+
return relate(source, target.type);
|
|
85
|
+
}
|
|
86
|
+
return relateMutableLists(source, target, relate);
|
|
87
|
+
}
|
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
irIntersection,
|
|
3
|
+
irKeyword,
|
|
4
|
+
irObject,
|
|
5
|
+
irProperty,
|
|
6
|
+
irReference,
|
|
7
|
+
} from "../../../domain/ir/constructors.js";
|
|
2
8
|
import type { IrPropertyName, IrPropertySignature, IrType } from "../../../domain/ir/nodes.js";
|
|
3
9
|
import { toPropertyName } from "../../../domain/ir/property-name.js";
|
|
4
10
|
import { createRef } from "../../../domain/openapi/json-pointer.js";
|
|
@@ -10,10 +16,12 @@ import {
|
|
|
10
16
|
QUERYSTRING_LOCATION,
|
|
11
17
|
} from "../../../domain/openapi/parameter-object.js";
|
|
12
18
|
import type { SchemaObject } from "../../../domain/openapi/schema-object.js";
|
|
19
|
+
import { OutputFix } from "../../../domain/output-fixes.js";
|
|
13
20
|
import { refType } from "../ref-type.js";
|
|
14
21
|
import type { SchemaLocation } from "../schema/schema-context.js";
|
|
15
22
|
import { withSchemaComment } from "../schema-comment.js";
|
|
16
23
|
import type { TransformContext } from "../transform-context.js";
|
|
24
|
+
import { isExplodedQueryObject } from "./exploded-query.js";
|
|
17
25
|
import { parameterType } from "./parameter.js";
|
|
18
26
|
|
|
19
27
|
const PATH_TEMPLATE_RE = /\{([^}]+)\}/g;
|
|
@@ -154,6 +162,66 @@ function querystringMember(
|
|
|
154
162
|
return withSchemaComment(property, querystring.resolved);
|
|
155
163
|
}
|
|
156
164
|
|
|
165
|
+
/** With `exploded-query-objects`, whether the parameter's object keys are keys of `query`. */
|
|
166
|
+
function isExplodedEntry(entry: ResolvedEntry, context: TransformContext): boolean {
|
|
167
|
+
return (
|
|
168
|
+
context.options.fixes.has(OutputFix.EXPLODED_QUERY_OBJECTS) &&
|
|
169
|
+
isExplodedQueryObject(entry.resolved, context.refs)
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* An exploded object parameter's part of `query`: its object type. A parameter that may be left
|
|
175
|
+
* out sends none of its keys then, so they are optional: `Partial<…>`.
|
|
176
|
+
*/
|
|
177
|
+
function explodedPart(
|
|
178
|
+
entry: ResolvedEntry,
|
|
179
|
+
location: SchemaLocation,
|
|
180
|
+
context: TransformContext,
|
|
181
|
+
): IrType {
|
|
182
|
+
const type = entryType(entry, location, context);
|
|
183
|
+
return entry.resolved.required ? type : irReference("Partial", [type]);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/** A location's parameters as members, and the object types of its exploded parameters. */
|
|
187
|
+
interface LocationParts {
|
|
188
|
+
readonly members: readonly IrPropertySignature[];
|
|
189
|
+
readonly exploded: readonly IrType[];
|
|
190
|
+
readonly isOptional: boolean;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/** Each parameter in document order, so the declarations they add keep their order. */
|
|
194
|
+
function locationParts(
|
|
195
|
+
entries: readonly ResolvedEntry[],
|
|
196
|
+
location: SchemaLocation,
|
|
197
|
+
context: TransformContext,
|
|
198
|
+
): LocationParts {
|
|
199
|
+
const members: IrPropertySignature[] = [];
|
|
200
|
+
const exploded: IrType[] = [];
|
|
201
|
+
let isOptional = true;
|
|
202
|
+
for (const entry of entries) {
|
|
203
|
+
if (isExplodedEntry(entry, context)) {
|
|
204
|
+
exploded.push(explodedPart(entry, location, context));
|
|
205
|
+
isOptional &&= !entry.resolved.required;
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
const member = parameterMember(entry, location, context);
|
|
209
|
+
members.push(member);
|
|
210
|
+
isOptional &&= member.isOptional;
|
|
211
|
+
}
|
|
212
|
+
return { members, exploded, isOptional };
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/** The members' object, intersected with the exploded parameters' types; `never` for none. */
|
|
216
|
+
function locationType({ members, exploded }: LocationParts): IrType {
|
|
217
|
+
const types = [...(members.length > 0 ? [irObject(members)] : []), ...exploded];
|
|
218
|
+
const [first] = types;
|
|
219
|
+
if (first === undefined) {
|
|
220
|
+
return irKeyword("never");
|
|
221
|
+
}
|
|
222
|
+
return types.length === 1 ? first : irIntersection(types);
|
|
223
|
+
}
|
|
224
|
+
|
|
157
225
|
/** One location's parameters; optional when all of them are, and `never` when there are none. */
|
|
158
226
|
function locationMember(
|
|
159
227
|
parameterIn: ParameterLocation,
|
|
@@ -161,17 +229,14 @@ function locationMember(
|
|
|
161
229
|
location: SchemaLocation,
|
|
162
230
|
context: TransformContext,
|
|
163
231
|
): IrPropertySignature {
|
|
164
|
-
const
|
|
165
|
-
|
|
166
|
-
.map((entry) => parameterMember(entry, location, context));
|
|
167
|
-
return irProperty(
|
|
168
|
-
toPropertyName(parameterIn),
|
|
169
|
-
members.length > 0 ? irObject(members) : irKeyword("never"),
|
|
170
|
-
{
|
|
171
|
-
isOptional: members.every((member) => member.isOptional),
|
|
172
|
-
isReadonly: context.options.immutable,
|
|
173
|
-
},
|
|
232
|
+
const located = entries.filter(
|
|
233
|
+
(entry): entry is ResolvedEntry => entry.resolved?.in === parameterIn,
|
|
174
234
|
);
|
|
235
|
+
const parts = locationParts(located, location, context);
|
|
236
|
+
return irProperty(toPropertyName(parameterIn), locationType(parts), {
|
|
237
|
+
isOptional: parts.isOptional,
|
|
238
|
+
isReadonly: context.options.immutable,
|
|
239
|
+
});
|
|
175
240
|
}
|
|
176
241
|
|
|
177
242
|
/**
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
* TS2413). These functions find the signatures that cover another key the checker rejects.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
import { isAssignableToTemplate, type TemplateShape } from "./template-key-match.js";
|
|
10
|
+
|
|
9
11
|
/** A placeholder of a template literal type, by the type it stands for. */
|
|
10
12
|
export type PathPlaceholder = "string" | "number" | "boolean";
|
|
11
13
|
|
|
@@ -21,89 +23,176 @@ export type PathKey =
|
|
|
21
23
|
|
|
22
24
|
const BOOLEAN_TEXTS: readonly string[] = ["true", "false"];
|
|
23
25
|
|
|
24
|
-
/** The
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
26
|
+
/** The shapes after one more part; a boolean placeholder doubles them, as TypeScript does. */
|
|
27
|
+
function withPart(shapes: readonly TemplateShape[], part: PathKeyPart): TemplateShape[] {
|
|
28
|
+
return shapes.flatMap((shape): TemplateShape[] => {
|
|
29
|
+
const lead = shape.texts.slice(0, -1);
|
|
30
|
+
// A shape has one text more than it has placeholders, so it always has a last text.
|
|
31
|
+
const last = shape.texts[shape.texts.length - 1] as string;
|
|
32
|
+
if (part.kind === "text") {
|
|
33
|
+
return [{ texts: [...lead, last + part.text], types: shape.types }];
|
|
34
|
+
}
|
|
35
|
+
if (part.type === "boolean") {
|
|
36
|
+
return BOOLEAN_TEXTS.map((text) => ({ texts: [...lead, last + text], types: shape.types }));
|
|
37
|
+
}
|
|
38
|
+
return [{ texts: [...shape.texts, ""], types: [...shape.types, part.type] }];
|
|
39
|
+
});
|
|
34
40
|
}
|
|
35
41
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
42
|
+
/**
|
|
43
|
+
* The key types TypeScript makes of a key: a literal is one text; a template is one shape, or a
|
|
44
|
+
* union of shapes when `${boolean}` is in it (`` `/a/${boolean}` `` is `"/a/true" | "/a/false"`).
|
|
45
|
+
*/
|
|
46
|
+
function shapesOf(key: PathKey): readonly TemplateShape[] {
|
|
47
|
+
if (key.kind === "literal") {
|
|
48
|
+
return [{ texts: [key.url], types: [] }];
|
|
39
49
|
}
|
|
40
|
-
return
|
|
50
|
+
return key.parts.reduce<TemplateShape[]>(withPart, [{ texts: [""], types: [] }]);
|
|
41
51
|
}
|
|
42
52
|
|
|
43
|
-
/**
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
53
|
+
/**
|
|
54
|
+
* Whether a signature checks the covered key: every type of the covered key is assignable to one
|
|
55
|
+
* of the signature's types. A shape without placeholders (a boolean-only template) is a string
|
|
56
|
+
* literal, which is no signature, so the targets leave it out.
|
|
57
|
+
*/
|
|
58
|
+
function isCoveredBy(
|
|
59
|
+
sources: readonly TemplateShape[],
|
|
60
|
+
targets: readonly TemplateShape[],
|
|
49
61
|
): boolean {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
62
|
+
return (
|
|
63
|
+
sources.length > 0 &&
|
|
64
|
+
sources.every((source) => targets.some((target) => isAssignableToTemplate(source, target)))
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Whether TypeScript rejects the key at `covered` for the signature at `covering`. */
|
|
69
|
+
export type CoverRejection = (covering: number, covered: number) => boolean;
|
|
70
|
+
|
|
71
|
+
/** Without type information every covered key counts. */
|
|
72
|
+
const EVERY_COVER_REJECTED: CoverRejection = () => true;
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* The key types TypeScript checks against a signature: a literal's, or a template's signature
|
|
76
|
+
* types. A boolean-only template is no signature (TS1337), so the checker neither checks it nor
|
|
77
|
+
* checks anything against it; a repeated one is still reported (TS2374, see `repeatedTemplates`).
|
|
78
|
+
*/
|
|
79
|
+
function checkedShapes(key: PathKey): readonly TemplateShape[] {
|
|
80
|
+
const shapes = shapesOf(key);
|
|
81
|
+
return key.kind === "literal" ? shapes : shapes.filter((shape) => shape.types.length > 0);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** A checked key: the key, the shapes TypeScript checks, and its template identity. */
|
|
85
|
+
interface CheckedKey {
|
|
86
|
+
readonly key: PathKey;
|
|
87
|
+
readonly shapes: readonly TemplateShape[];
|
|
88
|
+
/** The key types written as one string, for templates; null for a literal. */
|
|
89
|
+
readonly template: string | null;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function checkedKeyOf(key: PathKey): CheckedKey {
|
|
93
|
+
const template = key.kind === "template" ? JSON.stringify(shapesOf(key)) : null;
|
|
94
|
+
return { key, shapes: checkedShapes(key), template };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* The templates written more than once, as paths that differ only in parameter names give
|
|
99
|
+
* (`/a/{id}`, `/a/{name}`). OpenAPI forbids such paths, and one object with both signatures fails
|
|
100
|
+
* to compile whatever their types (TS2374), so each always counts as covering the other.
|
|
101
|
+
*/
|
|
102
|
+
function repeatedTemplates(checked: readonly CheckedKey[]): ReadonlySet<string> {
|
|
103
|
+
const seen = new Set<string>();
|
|
104
|
+
const repeated = new Set<string>();
|
|
105
|
+
for (const { template } of checked) {
|
|
106
|
+
if (template !== null) {
|
|
107
|
+
(seen.has(template) ? repeated : seen).add(template);
|
|
66
108
|
}
|
|
67
109
|
}
|
|
68
|
-
return
|
|
110
|
+
return repeated;
|
|
69
111
|
}
|
|
70
112
|
|
|
71
113
|
/**
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
114
|
+
* The checked keys by the first text of each of their shapes, sorted. A key type is assignable to
|
|
115
|
+
* a template only when its first text starts with the template's first text (both branches of
|
|
116
|
+
* `isAssignableToTemplate`), so the keys a template can cover are one run of this list. Without
|
|
117
|
+
* it the check compares every pair of keys, which takes minutes for 10 000 paths.
|
|
75
118
|
*/
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
119
|
+
interface StartTextIndex {
|
|
120
|
+
readonly starts: readonly string[];
|
|
121
|
+
readonly keyIndexes: readonly number[];
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** Code unit order: in it the strings that start with one prefix are adjacent. */
|
|
125
|
+
function compareCodeUnits(left: string, right: string): number {
|
|
126
|
+
if (left === right) {
|
|
127
|
+
return 0;
|
|
79
128
|
}
|
|
80
|
-
return
|
|
81
|
-
.map((part) => (part.kind === "text" ? part.text : SAMPLE_TEXTS[part.type]))
|
|
82
|
-
.join("");
|
|
129
|
+
return left < right ? -1 : 1;
|
|
83
130
|
}
|
|
84
131
|
|
|
85
|
-
|
|
86
|
-
|
|
132
|
+
function startTextIndexOf(checked: readonly CheckedKey[]): StartTextIndex {
|
|
133
|
+
const entries = checked.flatMap(({ shapes }, keyIndex) =>
|
|
134
|
+
shapes.map((shape) => ({ start: shape.texts[0] as string, keyIndex })),
|
|
135
|
+
);
|
|
136
|
+
entries.sort((left, right) => compareCodeUnits(left.start, right.start));
|
|
137
|
+
return {
|
|
138
|
+
starts: entries.map((entry) => entry.start),
|
|
139
|
+
keyIndexes: entries.map((entry) => entry.keyIndex),
|
|
140
|
+
};
|
|
141
|
+
}
|
|
87
142
|
|
|
88
|
-
/**
|
|
89
|
-
|
|
143
|
+
/** The indexes of the keys with a shape whose first text starts with one of the prefixes. */
|
|
144
|
+
function keysStartingWith(index: StartTextIndex, prefixes: readonly string[]): Set<number> {
|
|
145
|
+
const found = new Set<number>();
|
|
146
|
+
for (const prefix of prefixes) {
|
|
147
|
+
let low = 0;
|
|
148
|
+
let high = index.starts.length;
|
|
149
|
+
while (low < high) {
|
|
150
|
+
const middle = (low + high) >> 1;
|
|
151
|
+
if ((index.starts[middle] as string) < prefix) {
|
|
152
|
+
low = middle + 1;
|
|
153
|
+
} else {
|
|
154
|
+
high = middle;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
for (let at = low; at < index.starts.length; at += 1) {
|
|
158
|
+
if (!(index.starts[at] as string).startsWith(prefix)) {
|
|
159
|
+
break;
|
|
160
|
+
}
|
|
161
|
+
found.add(index.keyIndexes[at] as number);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return found;
|
|
165
|
+
}
|
|
90
166
|
|
|
91
|
-
/**
|
|
167
|
+
/**
|
|
168
|
+
* The indexes of the template keys that cover another key of the list, where it is rejected, or
|
|
169
|
+
* that repeat another template key. A template covers the keys TypeScript checks against it, the
|
|
170
|
+
* ones its own matching accepts (see `template-key-match.ts`), not every string it stands for:
|
|
171
|
+
* `` `/a/b/${number}` `` is no `` `/${string}/${number}` `` to the checker, which does not
|
|
172
|
+
* backtrack, so that pair is never checked.
|
|
173
|
+
*/
|
|
92
174
|
export function coveringTemplateKeys(
|
|
93
175
|
keys: readonly PathKey[],
|
|
94
176
|
isRejected: CoverRejection = EVERY_COVER_REJECTED,
|
|
95
177
|
): ReadonlySet<number> {
|
|
178
|
+
const checked = keys.map(checkedKeyOf);
|
|
179
|
+
const repeated = repeatedTemplates(checked);
|
|
180
|
+
const startTexts = startTextIndexOf(checked);
|
|
96
181
|
const covering = new Set<number>();
|
|
97
|
-
|
|
98
|
-
if (
|
|
182
|
+
checked.forEach(({ template, shapes: targets }, index) => {
|
|
183
|
+
if (template === null) {
|
|
99
184
|
return;
|
|
100
185
|
}
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
186
|
+
const prefixes = targets.map((target) => target.texts[0] as string);
|
|
187
|
+
const candidates = [...keysStartingWith(startTexts, prefixes)].sort((a, b) => a - b);
|
|
188
|
+
const isCovering =
|
|
189
|
+
repeated.has(template) ||
|
|
190
|
+
candidates.some(
|
|
191
|
+
(otherIndex) =>
|
|
192
|
+
otherIndex !== index &&
|
|
193
|
+
isCoveredBy((checked[otherIndex] as CheckedKey).shapes, targets) &&
|
|
194
|
+
isRejected(index, otherIndex),
|
|
195
|
+
);
|
|
107
196
|
if (isCovering) {
|
|
108
197
|
covering.add(index);
|
|
109
198
|
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import type { PathKeyPart, PathPlaceholder } from "./path-key-overlaps.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The `--path-params-as-types` key as the original builds it: each `{name}` match, in URL order,
|
|
5
|
+
* replaces the first occurrence of its text in the key so far. A parameter named `number`,
|
|
6
|
+
* `string` or `boolean` can so hit the `${number}` an earlier parameter got: of
|
|
7
|
+
* `/a/{id}/b/{number}` the original makes `/a/$${number}/b/{number}`. That output compiles, so it
|
|
8
|
+
* stays the default (ADR-021); the `path-param-type-names` fix gives every match its own
|
|
9
|
+
* placeholder.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/** One `{name}` of the URL and the placeholder of its parameter's type. */
|
|
13
|
+
export interface PathParameterMatch {
|
|
14
|
+
readonly match: string;
|
|
15
|
+
readonly type: PathPlaceholder;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* One UTF-16 code unit of the key so far (`indexOf` counts those): URL text, or a unit of the
|
|
20
|
+
* placeholder that the match at `owner` inserted.
|
|
21
|
+
*/
|
|
22
|
+
interface KeyUnit {
|
|
23
|
+
readonly unit: string;
|
|
24
|
+
readonly owner: number | null;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const URL_TEXT = null;
|
|
28
|
+
|
|
29
|
+
/** `${type}`: the text of a template literal type placeholder. */
|
|
30
|
+
export function placeholderText(type: PathPlaceholder): string {
|
|
31
|
+
return `\${${type}}`;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** The key's units after each match replaced the first occurrence of its text. */
|
|
35
|
+
function replacedUnits(url: string, matches: readonly PathParameterMatch[]): readonly KeyUnit[] {
|
|
36
|
+
const units: KeyUnit[] = url.split("").map((unit) => ({ unit, owner: URL_TEXT }));
|
|
37
|
+
matches.forEach(({ match, type }, owner) => {
|
|
38
|
+
const at = units
|
|
39
|
+
.map(({ unit }) => unit)
|
|
40
|
+
.join("")
|
|
41
|
+
.indexOf(match);
|
|
42
|
+
if (at >= 0) {
|
|
43
|
+
const inserted = placeholderText(type)
|
|
44
|
+
.split("")
|
|
45
|
+
.map((unit) => ({ unit, owner }));
|
|
46
|
+
units.splice(at, match.length, ...inserted);
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
return units;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** The placeholders, by match, that no later match cut into. */
|
|
53
|
+
function intactPlaceholders(
|
|
54
|
+
units: readonly KeyUnit[],
|
|
55
|
+
matches: readonly PathParameterMatch[],
|
|
56
|
+
): ReadonlyMap<number, PathPlaceholder> {
|
|
57
|
+
const intact = new Map<number, PathPlaceholder>();
|
|
58
|
+
matches.forEach(({ type }, owner) => {
|
|
59
|
+
const left = units.filter((unit) => unit.owner === owner).length;
|
|
60
|
+
if (left === placeholderText(type).length) {
|
|
61
|
+
intact.set(owner, type);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
return intact;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* The key as parts. What is left of a placeholder that a later match cut into (the `$` of
|
|
69
|
+
* `$${number}`) is text, as TypeScript reads it.
|
|
70
|
+
*/
|
|
71
|
+
export function replacedTemplateParts(
|
|
72
|
+
url: string,
|
|
73
|
+
matches: readonly PathParameterMatch[],
|
|
74
|
+
): PathKeyPart[] {
|
|
75
|
+
const units = replacedUnits(url, matches);
|
|
76
|
+
const intact = intactPlaceholders(units, matches);
|
|
77
|
+
const parts: PathKeyPart[] = [];
|
|
78
|
+
let text = "";
|
|
79
|
+
units.forEach(({ unit, owner }, index) => {
|
|
80
|
+
const type = owner === URL_TEXT ? undefined : intact.get(owner);
|
|
81
|
+
if (type === undefined) {
|
|
82
|
+
text += unit;
|
|
83
|
+
} else if (units[index - 1]?.owner !== owner) {
|
|
84
|
+
parts.push({ kind: "text", text }, { kind: "placeholder", type });
|
|
85
|
+
text = "";
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
parts.push({ kind: "text", text });
|
|
89
|
+
return parts;
|
|
90
|
+
}
|