openapi-typesafe 1.0.0-rc.0 → 1.0.0-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -5
- package/dist/adapters/redocly-loader/configured-headers.cjs +32 -0
- package/dist/adapters/redocly-loader/configured-headers.cjs.map +1 -0
- package/dist/adapters/redocly-loader/configured-headers.mjs +30 -0
- package/dist/adapters/redocly-loader/configured-headers.mjs.map +1 -0
- 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 +34 -11
- package/dist/adapters/redocly-loader/redocly-schema-loader.cjs.map +1 -1
- package/dist/adapters/redocly-loader/redocly-schema-loader.mjs +33 -10
- package/dist/adapters/redocly-loader/redocly-schema-loader.mjs.map +1 -1
- package/dist/adapters/redocly-loader/ref-access.cjs +37 -4
- package/dist/adapters/redocly-loader/ref-access.cjs.map +1 -1
- package/dist/adapters/redocly-loader/ref-access.mjs +37 -4
- package/dist/adapters/redocly-loader/ref-access.mjs.map +1 -1
- package/dist/adapters/redocly-loader/ref-resolution-check.cjs +191 -0
- package/dist/adapters/redocly-loader/ref-resolution-check.cjs.map +1 -0
- package/dist/adapters/redocly-loader/ref-resolution-check.mjs +189 -0
- package/dist/adapters/redocly-loader/ref-resolution-check.mjs.map +1 -0
- package/dist/adapters/redocly-loader/ref-resolution-steps.cjs +73 -0
- package/dist/adapters/redocly-loader/ref-resolution-steps.cjs.map +1 -0
- package/dist/adapters/redocly-loader/ref-resolution-steps.mjs +60 -0
- package/dist/adapters/redocly-loader/ref-resolution-steps.mjs.map +1 -0
- package/dist/adapters/redocly-loader/remote-fetch.cjs +41 -14
- package/dist/adapters/redocly-loader/remote-fetch.cjs.map +1 -1
- package/dist/adapters/redocly-loader/remote-fetch.mjs +40 -14
- package/dist/adapters/redocly-loader/remote-fetch.mjs.map +1 -1
- package/dist/adapters/redocly-loader/schema-load-errors.cjs +23 -5
- package/dist/adapters/redocly-loader/schema-load-errors.cjs.map +1 -1
- package/dist/adapters/redocly-loader/schema-load-errors.mjs +23 -5
- package/dist/adapters/redocly-loader/schema-load-errors.mjs.map +1 -1
- package/dist/adapters/redocly-loader/unsupported-keys.cjs +9 -1
- package/dist/adapters/redocly-loader/unsupported-keys.cjs.map +1 -1
- package/dist/adapters/redocly-loader/unsupported-keys.mjs +9 -1
- package/dist/adapters/redocly-loader/unsupported-keys.mjs.map +1 -1
- package/dist/adapters/terminal-logger/terminal-logger.cjs +6 -4
- package/dist/adapters/terminal-logger/terminal-logger.cjs.map +1 -1
- package/dist/adapters/terminal-logger/terminal-logger.mjs +6 -4
- package/dist/adapters/terminal-logger/terminal-logger.mjs.map +1 -1
- package/dist/adapters/type-ast-bridge/ir-to-typescript.cjs +5 -1
- package/dist/adapters/type-ast-bridge/ir-to-typescript.cjs.map +1 -1
- package/dist/adapters/type-ast-bridge/ir-to-typescript.mjs +5 -1
- package/dist/adapters/type-ast-bridge/ir-to-typescript.mjs.map +1 -1
- package/dist/adapters/type-ast-bridge/print-typescript.cjs.map +1 -1
- package/dist/adapters/type-ast-bridge/print-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/default-ports.cjs +9 -0
- package/dist/api/default-ports.cjs.map +1 -1
- package/dist/api/default-ports.mjs +8 -1
- package/dist/api/default-ports.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 +92 -0
- package/dist/api/discriminator-patch.cjs.map +1 -0
- package/dist/api/discriminator-patch.mjs +86 -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-refs.cjs +15 -12
- package/dist/api/document-refs.cjs.map +1 -1
- package/dist/api/document-refs.mjs +15 -12
- package/dist/api/document-refs.mjs.map +1 -1
- package/dist/api/document-run.cjs +21 -4
- package/dist/api/document-run.cjs.map +1 -1
- package/dist/api/document-run.mjs +22 -5
- 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/footer-sink.cjs +25 -2
- package/dist/api/footer-sink.cjs.map +1 -1
- package/dist/api/footer-sink.mjs +25 -2
- package/dist/api/footer-sink.mjs.map +1 -1
- package/dist/api/legacy-callback-warning.cjs +4 -1
- package/dist/api/legacy-callback-warning.cjs.map +1 -1
- package/dist/api/legacy-callback-warning.mjs +4 -1
- package/dist/api/legacy-callback-warning.mjs.map +1 -1
- package/dist/api/legacy-ts-helpers.cjs.map +1 -1
- package/dist/api/legacy-ts-helpers.mjs.map +1 -1
- package/dist/api/legacy-utils.cjs.map +1 -1
- package/dist/api/legacy-utils.mjs.map +1 -1
- package/dist/api/openapi-ts.cjs +2 -1
- package/dist/api/openapi-ts.cjs.map +1 -1
- package/dist/api/openapi-ts.mjs +3 -2
- package/dist/api/openapi-ts.mjs.map +1 -1
- 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 +4 -2
- package/dist/api/transform-context.cjs.map +1 -1
- package/dist/api/transform-context.mjs +4 -2
- 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 +7 -1
- package/dist/application/print/type-printer.cjs.map +1 -1
- package/dist/application/print/type-printer.mjs +7 -1
- 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 +41 -9
- package/dist/application/transform/document/parameters.cjs.map +1 -1
- package/dist/application/transform/document/parameters.mjs +42 -10
- 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 +55 -29
- package/dist/application/transform/document/paths.cjs.map +1 -1
- package/dist/application/transform/document/paths.mjs +54 -30
- 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 +42 -0
- package/dist/application/transform/footer/evaluation.cjs.map +1 -0
- package/dist/application/transform/footer/evaluation.mjs +33 -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 +138 -0
- package/dist/application/transform/footer/type-evaluation.cjs.map +1 -0
- package/dist/application/transform/footer/type-evaluation.mjs +136 -0
- package/dist/application/transform/footer/type-evaluation.mjs.map +1 -0
- package/dist/application/transform/footer/type-indexing.cjs +107 -0
- package/dist/application/transform/footer/type-indexing.cjs.map +1 -0
- package/dist/application/transform/footer/type-indexing.mjs +105 -0
- package/dist/application/transform/footer/type-indexing.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/api-settings.cjs +78 -0
- package/dist/cli/api-settings.cjs.map +1 -0
- package/dist/cli/api-settings.mjs +73 -0
- package/dist/cli/api-settings.mjs.map +1 -0
- package/dist/cli/cli-arguments.cjs +12 -3
- package/dist/cli/cli-arguments.cjs.map +1 -1
- package/dist/cli/cli-arguments.mjs +12 -4
- 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/help-text.cjs +3 -3
- package/dist/cli/help-text.cjs.map +1 -1
- package/dist/cli/help-text.mjs +3 -3
- package/dist/cli/help-text.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 +15 -7
- package/dist/cli/redocly-apis.cjs.map +1 -1
- package/dist/cli/redocly-apis.mjs +15 -7
- 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 +20 -3
- package/dist/cli/run-cli.cjs.map +1 -1
- package/dist/cli/run-cli.mjs +21 -4
- package/dist/cli/run-cli.mjs.map +1 -1
- package/dist/domain/errors.cjs +6 -1
- package/dist/domain/errors.cjs.map +1 -1
- package/dist/domain/errors.mjs +6 -1
- package/dist/domain/errors.mjs.map +1 -1
- package/dist/domain/ir/constructors.cjs +7 -1
- package/dist/domain/ir/constructors.cjs.map +1 -1
- package/dist/domain/ir/constructors.mjs +6 -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/url-credentials.cjs +10 -0
- package/dist/domain/url-credentials.cjs.map +1 -0
- package/dist/domain/url-credentials.mjs +8 -0
- package/dist/domain/url-credentials.mjs.map +1 -0
- package/dist/domain/warnings.cjs +23 -0
- package/dist/domain/warnings.cjs.map +1 -0
- package/dist/domain/warnings.mjs +20 -0
- package/dist/domain/warnings.mjs.map +1 -0
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1182 -46
- package/dist/index.d.mts +1182 -46
- package/dist/index.d.ts +1182 -46
- package/dist/index.mjs +1 -0
- package/dist/index.mjs.map +1 -1
- 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/dist/transform/path-item-object.cjs.map +1 -1
- package/dist/transform/path-item-object.mjs.map +1 -1
- package/package.json +6 -5
- package/src/adapters/redocly-loader/configured-headers.ts +65 -0
- package/src/adapters/redocly-loader/redocly-config-source.ts +41 -6
- package/src/adapters/redocly-loader/redocly-schema-loader.ts +44 -13
- package/src/adapters/redocly-loader/ref-access.ts +60 -5
- package/src/adapters/redocly-loader/ref-resolution-check.ts +257 -0
- package/src/adapters/redocly-loader/ref-resolution-steps.ts +113 -0
- package/src/adapters/redocly-loader/remote-fetch.ts +77 -21
- package/src/adapters/redocly-loader/schema-load-errors.ts +33 -11
- package/src/adapters/redocly-loader/unsupported-keys.ts +17 -2
- package/src/adapters/terminal-logger/terminal-logger.ts +9 -5
- package/src/adapters/type-ast-bridge/ir-to-typescript.ts +5 -1
- package/src/adapters/type-ast-bridge/print-typescript.ts +3 -1
- package/src/adapters/type-ast-bridge/typescript-to-ir.ts +26 -12
- package/src/adapters/type-ast-bridge/typescript-types.ts +57 -5
- package/src/api/default-ports.ts +16 -0
- 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 +152 -0
- package/src/api/discriminator-scan.ts +78 -90
- package/src/api/document-refs.ts +19 -15
- package/src/api/document-run.ts +32 -11
- package/src/api/enum-values-warnings.ts +21 -0
- package/src/api/footer-sink.ts +34 -2
- package/src/api/legacy-callback-warning.ts +7 -3
- package/src/api/legacy-ts-helpers.ts +7 -5
- package/src/api/legacy-utils.ts +1 -1
- package/src/api/openapi-ts.ts +2 -0
- package/src/api/path-template-warnings.ts +27 -0
- package/src/api/run-context.ts +34 -0
- package/src/api/transform-context.ts +17 -9
- package/src/api/transform-schema.ts +90 -5
- 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 +8 -1
- 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 +78 -12
- 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 +101 -38
- 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 +57 -0
- package/src/application/transform/footer/shared-enum-names.ts +86 -0
- package/src/application/transform/footer/type-evaluation.ts +167 -0
- package/src/application/transform/footer/type-indexing.ts +152 -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/api-settings.ts +109 -0
- package/src/cli/cli-arguments.ts +18 -5
- 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/help-text.ts +4 -4
- package/src/cli/main.ts +6 -0
- package/src/cli/redocly-apis.ts +21 -11
- package/src/cli/ref-limits.ts +2 -2
- package/src/cli/run-cli.ts +25 -4
- package/src/domain/errors.ts +10 -2
- package/src/domain/ir/constructors.ts +9 -1
- package/src/domain/ir/nodes.ts +11 -7
- 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 +32 -0
- package/src/domain/openapi/schema-object.ts +2 -0
- package/src/domain/output-fixes.ts +43 -2
- package/src/domain/url-credentials.ts +18 -0
- package/src/domain/warnings.ts +28 -0
- package/src/index.ts +1 -0
- package/src/transform/components-object.ts +3 -1
- package/src/transform/path-item-object.ts +6 -2
- package/src/types/generator-options.ts +12 -21
- package/src/types/openapi-common.ts +1 -0
- package/src/types/openapi-request.ts +3 -0
- package/src/types/openapi-schema.ts +2 -0
- package/src/types/openapi-security.ts +1 -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,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether a key type is assignable to a template literal key type, decided the way TypeScript's
|
|
3
|
+
* checker decides it (`inferFromLiteralPartsToTemplateLiteral` and
|
|
4
|
+
* `isValidTypeForTemplateLiteralPlaceholder` in TypeScript 5 and 6). An index signature checks
|
|
5
|
+
* exactly the keys that are assignable to its key type, so the overlap check must match the
|
|
6
|
+
* checker, not the strings a template stands for: the checker matches each text of the target at
|
|
7
|
+
* its first occurrence, so it misses some keys a regular expression would match, and a key it
|
|
8
|
+
* misses is never checked against the signature.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/** A placeholder type of a template, after `${boolean}` became the texts `true` and `false`. */
|
|
12
|
+
export type TemplatePlaceholder = "string" | "number";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* A key type as TypeScript stores a template literal: texts around placeholders, one more text
|
|
16
|
+
* than placeholders. A string literal is one text and no placeholder.
|
|
17
|
+
*/
|
|
18
|
+
export interface TemplateShape {
|
|
19
|
+
readonly texts: readonly string[];
|
|
20
|
+
readonly types: readonly TemplatePlaceholder[];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** The text at an index of the shape; every caller passes one of `0 .. types.length`. */
|
|
24
|
+
function textAt(shape: TemplateShape, index: number): string {
|
|
25
|
+
return shape.texts[index] as string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** The type the checker infers for one target placeholder: a text, or a slice of the source. */
|
|
29
|
+
type InferredMatch =
|
|
30
|
+
| { readonly kind: "text"; readonly text: string }
|
|
31
|
+
| { readonly kind: "template"; readonly shape: TemplateShape };
|
|
32
|
+
|
|
33
|
+
/** TypeScript's `isValidNumberString(text, false)`: a text that converts to a finite number. */
|
|
34
|
+
export function isNumberText(text: string): boolean {
|
|
35
|
+
return text !== "" && Number.isFinite(Number(text));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Whether the inferred type fits a placeholder: any string for `string`, a number for `number`. */
|
|
39
|
+
function isValidMatch(match: InferredMatch, placeholder: TemplatePlaceholder): boolean {
|
|
40
|
+
if (placeholder === "string") {
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
if (match.kind === "text") {
|
|
44
|
+
return isNumberText(match.text);
|
|
45
|
+
}
|
|
46
|
+
const { texts, types } = match.shape;
|
|
47
|
+
return texts.length === 2 && texts[0] === "" && texts[1] === "" && types[0] === "number";
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** The state of the scan through the source: the current text and the position in it. */
|
|
51
|
+
interface Cursor {
|
|
52
|
+
readonly segment: number;
|
|
53
|
+
readonly position: number;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** The scan of `inferFromLiteralPartsToTemplateLiteral`, over one source and one target. */
|
|
57
|
+
class LiteralPartsScan {
|
|
58
|
+
private readonly matches: InferredMatch[] = [];
|
|
59
|
+
private cursor: Cursor;
|
|
60
|
+
private readonly source: TemplateShape;
|
|
61
|
+
private readonly lastSourceIndex: number;
|
|
62
|
+
private readonly remainingEndText: string;
|
|
63
|
+
|
|
64
|
+
constructor(source: TemplateShape, startPosition: number, endTextLength: number) {
|
|
65
|
+
this.source = source;
|
|
66
|
+
this.lastSourceIndex = source.texts.length - 1;
|
|
67
|
+
const endText = textAt(source, this.lastSourceIndex);
|
|
68
|
+
this.remainingEndText = endText.slice(0, endText.length - endTextLength);
|
|
69
|
+
this.cursor = { segment: 0, position: startPosition };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** The matches for the target's texts between its first and last; null when one is missing. */
|
|
73
|
+
run(delimiters: readonly string[]): readonly InferredMatch[] | null {
|
|
74
|
+
for (const delimiter of delimiters) {
|
|
75
|
+
if (!this.matchDelimiter(delimiter)) {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
this.addMatch(this.lastSourceIndex, this.sourceText(this.lastSourceIndex).length);
|
|
80
|
+
return this.matches;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
private matchDelimiter(delimiter: string): boolean {
|
|
84
|
+
const { segment, position } = this.cursor;
|
|
85
|
+
if (delimiter.length > 0) {
|
|
86
|
+
const found = this.find(delimiter);
|
|
87
|
+
if (found === null) {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
this.addMatch(found.segment, found.position);
|
|
91
|
+
this.cursor = { ...this.cursor, position: this.cursor.position + delimiter.length };
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
if (position < this.sourceText(segment).length) {
|
|
95
|
+
this.addMatch(segment, position + 1);
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
if (segment < this.lastSourceIndex) {
|
|
99
|
+
this.addMatch(segment + 1, 0);
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** The first occurrence of the delimiter at or after the cursor, in this or a later text. */
|
|
106
|
+
private find(delimiter: string): Cursor | null {
|
|
107
|
+
let { segment, position } = this.cursor;
|
|
108
|
+
while (segment < this.source.texts.length) {
|
|
109
|
+
const index = this.sourceText(segment).indexOf(delimiter, position);
|
|
110
|
+
if (index >= 0) {
|
|
111
|
+
return { segment, position: index };
|
|
112
|
+
}
|
|
113
|
+
segment += 1;
|
|
114
|
+
position = 0;
|
|
115
|
+
}
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
private sourceText(index: number): string {
|
|
120
|
+
return index < this.lastSourceIndex ? textAt(this.source, index) : this.remainingEndText;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
private addMatch(segment: number, position: number): void {
|
|
124
|
+
const start = this.cursor;
|
|
125
|
+
if (segment === start.segment) {
|
|
126
|
+
const text = this.sourceText(segment).slice(start.position, position);
|
|
127
|
+
this.matches.push({ kind: "text", text });
|
|
128
|
+
} else {
|
|
129
|
+
const texts = [
|
|
130
|
+
textAt(this.source, start.segment).slice(start.position),
|
|
131
|
+
...this.source.texts.slice(start.segment + 1, segment),
|
|
132
|
+
this.sourceText(segment).slice(0, position),
|
|
133
|
+
];
|
|
134
|
+
const types = this.source.types.slice(start.segment, segment);
|
|
135
|
+
this.matches.push({ kind: "template", shape: { texts, types } });
|
|
136
|
+
}
|
|
137
|
+
this.cursor = { segment, position };
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** The checker's inferences for the target's placeholders; null when the texts do not fit. */
|
|
142
|
+
function inferMatches(
|
|
143
|
+
source: TemplateShape,
|
|
144
|
+
target: TemplateShape,
|
|
145
|
+
): readonly InferredMatch[] | null {
|
|
146
|
+
const sourceStart = textAt(source, 0);
|
|
147
|
+
const sourceEnd = textAt(source, source.texts.length - 1);
|
|
148
|
+
const targetStart = textAt(target, 0);
|
|
149
|
+
const targetEnd = textAt(target, target.texts.length - 1);
|
|
150
|
+
const isTooShort =
|
|
151
|
+
source.texts.length === 1 && sourceStart.length < targetStart.length + targetEnd.length;
|
|
152
|
+
if (isTooShort || !sourceStart.startsWith(targetStart) || !sourceEnd.endsWith(targetEnd)) {
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
const scan = new LiteralPartsScan(source, targetStart.length, targetEnd.length);
|
|
156
|
+
return scan.run(target.texts.slice(1, -1));
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function haveSameTexts(source: TemplateShape, target: TemplateShape): boolean {
|
|
160
|
+
return (
|
|
161
|
+
source.texts.length === target.texts.length &&
|
|
162
|
+
source.texts.every((text, index) => text === target.texts[index])
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Whether the source key type is assignable to the target template. Templates with the same texts
|
|
168
|
+
* compare placeholder by placeholder (`inferTypesFromTemplateLiteralType`).
|
|
169
|
+
*/
|
|
170
|
+
export function isAssignableToTemplate(source: TemplateShape, target: TemplateShape): boolean {
|
|
171
|
+
if (source.types.length > 0 && haveSameTexts(source, target)) {
|
|
172
|
+
return source.types.every((type, index) => {
|
|
173
|
+
const placeholder = target.types[index];
|
|
174
|
+
return placeholder === "string" || type === placeholder;
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
const matches = inferMatches(source, target);
|
|
178
|
+
const isEveryMatchValid = matches?.every((match, index) => {
|
|
179
|
+
const placeholder = target.types[index];
|
|
180
|
+
return placeholder !== undefined && isValidMatch(match, placeholder);
|
|
181
|
+
});
|
|
182
|
+
return isEveryMatchValid === true;
|
|
183
|
+
}
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
import { irBoolean } from "../../../domain/ir/constructors.js";
|
|
2
|
+
import type {
|
|
3
|
+
IrIndexSignature,
|
|
4
|
+
IrObjectType,
|
|
5
|
+
IrPropertySignature,
|
|
6
|
+
IrType,
|
|
7
|
+
IrUnionType,
|
|
8
|
+
} from "../../../domain/ir/nodes.js";
|
|
9
|
+
import {
|
|
10
|
+
type Assignability,
|
|
11
|
+
crossCategoryRelation,
|
|
12
|
+
everyOf,
|
|
13
|
+
primitiveRelation,
|
|
14
|
+
someOf,
|
|
15
|
+
} from "./assignability.js";
|
|
16
|
+
import type { DeclaredTypeLookup } from "./declared-types.js";
|
|
17
|
+
import { type IrListType, relateLists } from "./list-assignability.js";
|
|
18
|
+
import { createTypeKey } from "./type-key.js";
|
|
19
|
+
import {
|
|
20
|
+
indexSignaturesOf,
|
|
21
|
+
nameOf,
|
|
22
|
+
propertiesOf,
|
|
23
|
+
readType,
|
|
24
|
+
TypeResolution,
|
|
25
|
+
} from "./type-resolution.js";
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Whether TypeScript accepts one type where another is expected (`strict`, without
|
|
29
|
+
* `exactOptionalPropertyTypes`), for the `--path-params-as-types` overlap check. It follows the
|
|
30
|
+
* checker's rules for the types the generator writes: unions, intersections, objects with optional
|
|
31
|
+
* properties and index signatures, weak types, arrays, tuples, literals and keywords, and the
|
|
32
|
+
* references to declarations of the document. A pair it does not model (a utility type, raw text)
|
|
33
|
+
* is undecided unless both are the same type, and an undecided pair keeps the original output.
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
/** Results by source type, then by target type. */
|
|
37
|
+
type PairResults = Map<IrType, Map<IrType, Assignability>>;
|
|
38
|
+
|
|
39
|
+
function resultOf(results: PairResults, source: IrType, target: IrType): Assignability | undefined {
|
|
40
|
+
return results.get(source)?.get(target);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function setResult(
|
|
44
|
+
results: PairResults,
|
|
45
|
+
pair: { readonly source: IrType; readonly target: IrType },
|
|
46
|
+
value: Assignability,
|
|
47
|
+
): void {
|
|
48
|
+
const byTarget = results.get(pair.source) ?? new Map<IrType, Assignability>();
|
|
49
|
+
byTarget.set(pair.target, value);
|
|
50
|
+
results.set(pair.source, byTarget);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** A weak type has only optional properties; TypeScript rejects a source sharing none (TS2559). */
|
|
54
|
+
function isWeakTypeRejected(source: IrObjectType, target: IrObjectType): boolean {
|
|
55
|
+
const targetProperties = [...propertiesOf(target).values()];
|
|
56
|
+
const isWeak =
|
|
57
|
+
targetProperties.length > 0 &&
|
|
58
|
+
indexSignaturesOf(target).length === 0 &&
|
|
59
|
+
targetProperties.every((property) => property.isOptional);
|
|
60
|
+
const sourceNames = [...propertiesOf(source).keys()];
|
|
61
|
+
return (
|
|
62
|
+
isWeak &&
|
|
63
|
+
sourceNames.length > 0 &&
|
|
64
|
+
!sourceNames.some((name) => targetProperties.some((property) => nameOf(property) === name))
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function isStringIndex(signature: IrIndexSignature): boolean {
|
|
69
|
+
return signature.parameterType.kind === "keyword" && signature.parameterType.keyword === "string";
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** `boolean` as the checker stores it: the union `true | false`. */
|
|
73
|
+
const BOOLEAN_MEMBERS: readonly IrType[] = [irBoolean(true), irBoolean(false)];
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* The members a source is compared by, one by one, or null for a source compared as a whole.
|
|
77
|
+
* `boolean` counts as `true | false` against a union, which it fits though it fits neither member
|
|
78
|
+
* alone; `string` and `number` are no finite unions, so no literal union holds all their values.
|
|
79
|
+
*/
|
|
80
|
+
function sourceUnionMembers(source: IrType, target: IrType): readonly IrType[] | null {
|
|
81
|
+
if (source.kind === "union") {
|
|
82
|
+
return source.types;
|
|
83
|
+
}
|
|
84
|
+
const isBoolean = source.kind === "keyword" && source.keyword === "boolean";
|
|
85
|
+
return isBoolean && target.kind === "union" ? BOOLEAN_MEMBERS : null;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** The memoized check over the declarations of one generated file. */
|
|
89
|
+
class AssignabilityCheck {
|
|
90
|
+
private readonly inProgress: PairResults = new Map();
|
|
91
|
+
private readonly results: PairResults = new Map();
|
|
92
|
+
private readonly typeKeyOf = createTypeKey();
|
|
93
|
+
private readonly resolution: TypeResolution;
|
|
94
|
+
/** How often a pair being compared was assumed related, and how deep the comparison is. */
|
|
95
|
+
private assumptions = 0;
|
|
96
|
+
private depth = 0;
|
|
97
|
+
|
|
98
|
+
constructor(lookup: DeclaredTypeLookup) {
|
|
99
|
+
this.resolution = new TypeResolution(lookup);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
relate(source: IrType, target: IrType): Assignability {
|
|
103
|
+
const pair = {
|
|
104
|
+
source: this.resolution.resolved(source),
|
|
105
|
+
target: this.resolution.resolved(target),
|
|
106
|
+
};
|
|
107
|
+
if (
|
|
108
|
+
pair.source === pair.target ||
|
|
109
|
+
this.typeKeyOf(pair.source) === this.typeKeyOf(pair.target)
|
|
110
|
+
) {
|
|
111
|
+
return "assignable";
|
|
112
|
+
}
|
|
113
|
+
const known = resultOf(this.results, pair.source, pair.target);
|
|
114
|
+
if (known !== undefined) {
|
|
115
|
+
return known;
|
|
116
|
+
}
|
|
117
|
+
// A pair being compared is assumed related, as the checker does for recursive types.
|
|
118
|
+
if (resultOf(this.inProgress, pair.source, pair.target) !== undefined) {
|
|
119
|
+
this.assumptions += 1;
|
|
120
|
+
return "assignable";
|
|
121
|
+
}
|
|
122
|
+
const assumedBefore = this.assumptions;
|
|
123
|
+
setResult(this.inProgress, pair, "assignable");
|
|
124
|
+
this.depth += 1;
|
|
125
|
+
const result = this.compare(pair.source, pair.target);
|
|
126
|
+
this.depth -= 1;
|
|
127
|
+
this.inProgress.get(pair.source)?.delete(pair.target);
|
|
128
|
+
// "Assignable" that rests on an assumption about an outer pair holds only if that pair does,
|
|
129
|
+
// so it is kept only once no outer pair is open. The assumption can only make a pair more
|
|
130
|
+
// related, so a rejection or an undecided pair reached under it is safe to keep.
|
|
131
|
+
if (result !== "assignable" || this.assumptions === assumedBefore || this.depth === 0) {
|
|
132
|
+
setResult(this.results, pair, result);
|
|
133
|
+
}
|
|
134
|
+
return result;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
private compare(source: IrType, target: IrType): Assignability {
|
|
138
|
+
if (target.kind === "keyword" && target.keyword === "unknown") {
|
|
139
|
+
return "assignable";
|
|
140
|
+
}
|
|
141
|
+
if (source.kind === "keyword" && source.keyword === "never") {
|
|
142
|
+
return "assignable";
|
|
143
|
+
}
|
|
144
|
+
const sourceMembers = sourceUnionMembers(source, target);
|
|
145
|
+
if (sourceMembers !== null) {
|
|
146
|
+
return everyOf(sourceMembers, (member) => this.relate(member, target));
|
|
147
|
+
}
|
|
148
|
+
if (target.kind === "intersection") {
|
|
149
|
+
return everyOf(target.types, (member) => this.relateToIntersectionPart(source, member));
|
|
150
|
+
}
|
|
151
|
+
if (target.kind === "union") {
|
|
152
|
+
return this.relateToUnion(source, target);
|
|
153
|
+
}
|
|
154
|
+
if (source.kind === "intersection") {
|
|
155
|
+
// A part that fits is enough; the parts as one type are not modelled (see `merged`).
|
|
156
|
+
const fits = source.types.some((part) => this.relate(part, target) === "assignable");
|
|
157
|
+
return fits ? "assignable" : "undecided";
|
|
158
|
+
}
|
|
159
|
+
return this.compareSingle(source, target);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* A part of an intersection the check cannot merge into one object. The checker skips the weak
|
|
164
|
+
* type check for such a part, so a rejection that may rest on it decides nothing.
|
|
165
|
+
*/
|
|
166
|
+
private relateToIntersectionPart(source: IrType, part: IrType): Assignability {
|
|
167
|
+
const result = this.relate(source, part);
|
|
168
|
+
const from = this.resolution.resolved(source);
|
|
169
|
+
const to = this.resolution.resolved(part);
|
|
170
|
+
const isWeakRejection =
|
|
171
|
+
from.kind === "object" && to.kind === "object" && isWeakTypeRejected(from, to);
|
|
172
|
+
return result === "rejected" && isWeakRejection ? "undecided" : result;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* An object may fit a union of objects that none of its members accepts alone, through a
|
|
177
|
+
* discriminant property (`{ k: "a" | "b" }` fits `{ k: "a" } | { k: "b" }`); not modelled.
|
|
178
|
+
*/
|
|
179
|
+
private relateToUnion(source: IrType, target: IrUnionType): Assignability {
|
|
180
|
+
const result = someOf(target.types, (member) => this.relate(source, member));
|
|
181
|
+
const objectMembers = target.types.filter(
|
|
182
|
+
(member) => this.resolution.resolved(member).kind === "object",
|
|
183
|
+
);
|
|
184
|
+
const mayBeDiscriminated = source.kind === "object" && objectMembers.length > 1;
|
|
185
|
+
return result === "rejected" && mayBeDiscriminated ? "undecided" : result;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
private compareSingle(source: IrType, target: IrType): Assignability {
|
|
189
|
+
const crossCategory = crossCategoryRelation(source, target);
|
|
190
|
+
if (crossCategory !== null) {
|
|
191
|
+
return crossCategory;
|
|
192
|
+
}
|
|
193
|
+
switch (source.kind) {
|
|
194
|
+
case "array":
|
|
195
|
+
case "tuple":
|
|
196
|
+
case "readonly":
|
|
197
|
+
return relateLists(source, target as IrListType, (from, to) => this.relate(from, to));
|
|
198
|
+
case "object":
|
|
199
|
+
return this.relateObjects(source, target as IrObjectType);
|
|
200
|
+
default:
|
|
201
|
+
return primitiveRelation(source, target);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
private relateObjects(source: IrObjectType, target: IrObjectType): Assignability {
|
|
206
|
+
if (isWeakTypeRejected(source, target)) {
|
|
207
|
+
return "rejected";
|
|
208
|
+
}
|
|
209
|
+
const sourceProperties = propertiesOf(source);
|
|
210
|
+
const indexes = everyOf(indexSignaturesOf(target), (signature) =>
|
|
211
|
+
this.relateToIndex(source, signature),
|
|
212
|
+
);
|
|
213
|
+
if (indexes === "rejected") {
|
|
214
|
+
return indexes;
|
|
215
|
+
}
|
|
216
|
+
const properties = everyOf([...propertiesOf(target).values()], (property) =>
|
|
217
|
+
this.relateProperty(sourceProperties.get(nameOf(property)), property),
|
|
218
|
+
);
|
|
219
|
+
return everyOf([indexes, properties], (result) => result);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* A string index signature of the target. A string index of the source is compared with it;
|
|
224
|
+
* without one, every property and index signature of the source is. An optional property is
|
|
225
|
+
* compared without the `undefined` its optionality adds, as the checker does.
|
|
226
|
+
*/
|
|
227
|
+
private relateToIndex(source: IrObjectType, signature: IrIndexSignature): Assignability {
|
|
228
|
+
if (!isStringIndex(signature)) {
|
|
229
|
+
return "undecided";
|
|
230
|
+
}
|
|
231
|
+
const indexes = indexSignaturesOf(source);
|
|
232
|
+
const own = indexes.find(isStringIndex);
|
|
233
|
+
if (own !== undefined) {
|
|
234
|
+
return this.relate(own.type, signature.type);
|
|
235
|
+
}
|
|
236
|
+
const types = [
|
|
237
|
+
...[...propertiesOf(source).values()].map((property) => property.type),
|
|
238
|
+
...indexes.map((index) => index.type),
|
|
239
|
+
];
|
|
240
|
+
return everyOf(types, (type) => this.relate(type, signature.type));
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* A property of the target. A missing optional one is not checked, not even against a string
|
|
245
|
+
* index of the source; a missing or optional source property fails a required one (TS2741).
|
|
246
|
+
*/
|
|
247
|
+
private relateProperty(
|
|
248
|
+
source: IrPropertySignature | undefined,
|
|
249
|
+
target: IrPropertySignature,
|
|
250
|
+
): Assignability {
|
|
251
|
+
if (source === undefined) {
|
|
252
|
+
return target.isOptional ? "assignable" : "rejected";
|
|
253
|
+
}
|
|
254
|
+
if (source.isOptional && !target.isOptional) {
|
|
255
|
+
return "rejected";
|
|
256
|
+
}
|
|
257
|
+
return this.relate(readType(source), readType(target));
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/** What the check proves about a source type where a target is expected. */
|
|
262
|
+
export type AssignabilityProof = (source: IrType, target: IrType) => Assignability;
|
|
263
|
+
|
|
264
|
+
/** One memoized check, for all the comparisons of a `paths` type. */
|
|
265
|
+
export function assignabilityProof(lookup: DeclaredTypeLookup): AssignabilityProof {
|
|
266
|
+
const check = new AssignabilityCheck(lookup);
|
|
267
|
+
return (source, target) => check.relate(source, target);
|
|
268
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { assertNever } from "../../../domain/assert-never.js";
|
|
2
|
+
import type { IrMember, IrPropertyName, IrTupleElement, IrType } from "../../../domain/ir/nodes.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* A text that is the same for two types exactly when they are written the same, comments left
|
|
6
|
+
* out: the overlap check accepts a type it does not model when it is the same as the other.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
function nameKey(name: IrPropertyName): string {
|
|
10
|
+
return JSON.stringify(name.kind === "string" ? name.value : name.text);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function memberKey(member: IrMember, typeKeyOf: TypeKey): string {
|
|
14
|
+
if (member.kind === "indexSignature") {
|
|
15
|
+
const readonly = member.isReadonly ? "readonly " : "";
|
|
16
|
+
return `${readonly}[${typeKeyOf(member.parameterType)}]:${typeKeyOf(member.type)}`;
|
|
17
|
+
}
|
|
18
|
+
const readonly = member.isReadonly ? "readonly " : "";
|
|
19
|
+
const optional = member.isOptional ? "?" : "";
|
|
20
|
+
return `${readonly}${nameKey(member.name)}${optional}:${typeKeyOf(member.type)}`;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function listKey(types: readonly IrType[], typeKeyOf: TypeKey): string {
|
|
24
|
+
return types.map(typeKeyOf).join(",");
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function elementKey(element: IrTupleElement, typeKeyOf: TypeKey): string {
|
|
28
|
+
const prefix = element.isRest ? "..." : "";
|
|
29
|
+
return `${prefix}${typeKeyOf(element.type)}${element.isOptional ? "?" : ""}`;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function computeKey(type: IrType, typeKeyOf: TypeKey): string {
|
|
33
|
+
switch (type.kind) {
|
|
34
|
+
case "keyword":
|
|
35
|
+
return type.keyword;
|
|
36
|
+
case "stringLiteral":
|
|
37
|
+
return JSON.stringify(type.value);
|
|
38
|
+
case "numberLiteral":
|
|
39
|
+
case "booleanLiteral":
|
|
40
|
+
return String(type.value);
|
|
41
|
+
case "reference":
|
|
42
|
+
return `${type.name}<${listKey(type.typeArguments, typeKeyOf)}>`;
|
|
43
|
+
case "array":
|
|
44
|
+
return `(${typeKeyOf(type.element)})[]`;
|
|
45
|
+
case "tuple":
|
|
46
|
+
return `[${type.elements.map((element) => elementKey(element, typeKeyOf)).join(",")}]`;
|
|
47
|
+
case "union":
|
|
48
|
+
return `union(${listKey(type.types, typeKeyOf)})`;
|
|
49
|
+
case "intersection":
|
|
50
|
+
return `intersection(${listKey(type.types, typeKeyOf)})`;
|
|
51
|
+
case "object":
|
|
52
|
+
return `{${type.members.map((member) => memberKey(member, typeKeyOf)).join(";")}}`;
|
|
53
|
+
case "indexedAccess":
|
|
54
|
+
return `${typeKeyOf(type.object)}[${typeKeyOf(type.index)}]`;
|
|
55
|
+
case "readonly":
|
|
56
|
+
return `readonly(${typeKeyOf(type.type)})`;
|
|
57
|
+
case "raw":
|
|
58
|
+
return `raw(${type.text})`;
|
|
59
|
+
default:
|
|
60
|
+
return assertNever(type);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** The key of a type. */
|
|
65
|
+
export type TypeKey = (type: IrType) => string;
|
|
66
|
+
|
|
67
|
+
/** A key function that computes each node's key once; one per comparison, not shared. */
|
|
68
|
+
export function createTypeKey(): TypeKey {
|
|
69
|
+
const keys = new WeakMap<IrType, string>();
|
|
70
|
+
const typeKeyOf: TypeKey = (type) => {
|
|
71
|
+
const known = keys.get(type);
|
|
72
|
+
if (known !== undefined) {
|
|
73
|
+
return known;
|
|
74
|
+
}
|
|
75
|
+
const key = computeKey(type, typeKeyOf);
|
|
76
|
+
keys.set(type, key);
|
|
77
|
+
return key;
|
|
78
|
+
};
|
|
79
|
+
return typeKeyOf;
|
|
80
|
+
}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
IrIndexSignature,
|
|
3
|
+
IrMember,
|
|
4
|
+
IrObjectType,
|
|
5
|
+
IrPropertySignature,
|
|
6
|
+
IrType,
|
|
7
|
+
} from "../../../domain/ir/nodes.js";
|
|
8
|
+
import type { DeclaredTypeLookup } from "./declared-types.js";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* The types the `--path-params-as-types` overlap check compares, as TypeScript sees them:
|
|
12
|
+
* references followed to the declarations of the document, `T["key"]` and `T[number]` read from
|
|
13
|
+
* `T`, and an intersection of object types merged into one object.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const UNDEFINED: IrType = { kind: "keyword", keyword: "undefined" };
|
|
17
|
+
|
|
18
|
+
export function nameOf(member: IrPropertySignature): string {
|
|
19
|
+
return member.name.kind === "string" ? member.name.value : member.name.text;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function propertiesOf(type: IrObjectType): Map<string, IrPropertySignature> {
|
|
23
|
+
const properties = type.members.filter(
|
|
24
|
+
(member: IrMember): member is IrPropertySignature => member.kind === "property",
|
|
25
|
+
);
|
|
26
|
+
return new Map(properties.map((property) => [nameOf(property), property]));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function indexSignaturesOf(type: IrObjectType): IrIndexSignature[] {
|
|
30
|
+
return type.members.filter(
|
|
31
|
+
(member: IrMember): member is IrIndexSignature => member.kind === "indexSignature",
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** The type read from a property: an optional one may be `undefined`. */
|
|
36
|
+
export function readType(property: IrPropertySignature): IrType {
|
|
37
|
+
return property.isOptional ? { kind: "union", types: [property.type, UNDEFINED] } : property.type;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* `T[number]` for an array `T`, as the generator writes for an item of an array response: the
|
|
42
|
+
* element type, readonly or not. A tuple's is the union of its elements, which is not modelled.
|
|
43
|
+
*/
|
|
44
|
+
function arrayElement(type: IrType): IrType | null {
|
|
45
|
+
const list = type.kind === "readonly" ? type.type : type;
|
|
46
|
+
return list.kind === "array" ? list.element : null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** One property for two of the same name in an intersection. */
|
|
50
|
+
function mergedProperty(
|
|
51
|
+
first: IrPropertySignature,
|
|
52
|
+
second: IrPropertySignature,
|
|
53
|
+
): IrPropertySignature {
|
|
54
|
+
return {
|
|
55
|
+
...first,
|
|
56
|
+
type: { kind: "intersection", types: [first.type, second.type] },
|
|
57
|
+
isOptional: first.isOptional && second.isOptional,
|
|
58
|
+
isReadonly: first.isReadonly || second.isReadonly,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Resolves types once each, for one comparison. */
|
|
63
|
+
export class TypeResolution {
|
|
64
|
+
private readonly lookup: DeclaredTypeLookup;
|
|
65
|
+
private readonly resolvedTypes = new Map<IrType, IrType>();
|
|
66
|
+
|
|
67
|
+
constructor(lookup: DeclaredTypeLookup) {
|
|
68
|
+
this.lookup = lookup;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** The type a reference names, followed through references to references; computed once. */
|
|
72
|
+
resolved(type: IrType): IrType {
|
|
73
|
+
const known = this.resolvedTypes.get(type);
|
|
74
|
+
if (known !== undefined) {
|
|
75
|
+
return known;
|
|
76
|
+
}
|
|
77
|
+
// A type that reaches itself while it is resolved stays as it is written there.
|
|
78
|
+
this.resolvedTypes.set(type, type);
|
|
79
|
+
const seen = new Set<IrType>();
|
|
80
|
+
let current = type;
|
|
81
|
+
while (!seen.has(current)) {
|
|
82
|
+
seen.add(current);
|
|
83
|
+
const named = this.lookup(current) ?? this.indexedProperty(current);
|
|
84
|
+
if (named === null) {
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
current = named;
|
|
88
|
+
}
|
|
89
|
+
const result = current.kind === "intersection" ? (this.merged(current) ?? current) : current;
|
|
90
|
+
this.resolvedTypes.set(type, result);
|
|
91
|
+
return result;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* `T["key"]` read from the object type `T` resolves to, which the lookup does not do for an
|
|
96
|
+
* object it reached through another lookup (`paths["/a/{id}"]["get"]`). An optional property
|
|
97
|
+
* reads as `T | undefined`; `T[number]` is the element of the array `T`.
|
|
98
|
+
*/
|
|
99
|
+
private indexedProperty(type: IrType): IrType | null {
|
|
100
|
+
if (type.kind !== "indexedAccess") {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
const { index } = type;
|
|
104
|
+
if (index.kind === "keyword" && index.keyword === "number") {
|
|
105
|
+
return arrayElement(this.resolved(type.object));
|
|
106
|
+
}
|
|
107
|
+
const object = this.resolved(type.object);
|
|
108
|
+
if (index.kind !== "stringLiteral" || object.kind !== "object") {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
const property = propertiesOf(object).get(index.value);
|
|
112
|
+
return property === undefined ? null : readType(property);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* An intersection of object types as the one object TypeScript makes of it: a property in
|
|
117
|
+
* several parts has the intersection of their types, and is optional only when it is in each.
|
|
118
|
+
* Null when a part is no object type.
|
|
119
|
+
*/
|
|
120
|
+
private merged(type: IrType): IrObjectType | null {
|
|
121
|
+
const parts = this.objectParts(type);
|
|
122
|
+
if (parts === null) {
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
const properties = new Map<string, IrPropertySignature>();
|
|
126
|
+
const indexes: IrIndexSignature[] = [];
|
|
127
|
+
for (const member of parts.flatMap((part) => part.members)) {
|
|
128
|
+
if (member.kind === "indexSignature") {
|
|
129
|
+
indexes.push(member);
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
const name = nameOf(member);
|
|
133
|
+
const other = properties.get(name);
|
|
134
|
+
properties.set(name, other === undefined ? member : mergedProperty(other, member));
|
|
135
|
+
}
|
|
136
|
+
return { kind: "object", members: [...properties.values(), ...indexes] };
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* The object types an intersection is made of, nested ones flattened; null for any other, and
|
|
141
|
+
* for an intersection that contains itself, which TypeScript does not merge either.
|
|
142
|
+
*/
|
|
143
|
+
private objectParts(
|
|
144
|
+
type: IrType,
|
|
145
|
+
visiting: ReadonlySet<IrType> = new Set(),
|
|
146
|
+
): IrObjectType[] | null {
|
|
147
|
+
if (type.kind === "object") {
|
|
148
|
+
return [type];
|
|
149
|
+
}
|
|
150
|
+
if (type.kind !== "intersection" || visiting.has(type)) {
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
153
|
+
const parts: IrObjectType[] = [];
|
|
154
|
+
for (const part of type.types) {
|
|
155
|
+
const objects = this.objectParts(this.resolved(part), new Set([...visiting, type]));
|
|
156
|
+
if (objects === null) {
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
parts.push(...objects);
|
|
160
|
+
}
|
|
161
|
+
return parts;
|
|
162
|
+
}
|
|
163
|
+
}
|