diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 10c2fd430..cc329279f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -174,7 +174,7 @@ jobs: node-version: 'lts/*' - if: contains(matrix.generator, 'typescript-fetch') run: | - npm install -g typescript@7.0.2 vite-plus@1.5.0 + npm install -g typescript@7.0.2 vite-plus@1.5.1 tsc --version - uses: actions/setup-go@v6 if: matrix.generator == 'go-http' diff --git a/CHANGELOG.md b/CHANGELOG.md index 07d7e70a5..213072ac6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,13 +15,50 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 Enable full `vp check` and validate freshly generated clients before and after packing, without a formatter preparation step. Exclude `dist/` from formatting and provide a `fmt` script for manual edits. -- Upgrade generated TypeScript clients to Vite+ `^1.5.0`, including Vite `8.3.5`, - Vitest `5.1.0`, Rolldown `1.2.16`, Oxfmt `0.76.0`, Oxlint `1.91.0`, +- Upgrade generated TypeScript clients to Vite+ `^1.5.0`, resolving Vite `8.3.7`, + Vitest `5.1.0`, Rolldown `1.2.18`, Oxfmt `0.76.0`, Oxlint `1.91.0`, oxlint-tsgolint `7.0.2005`, and tsdown `0.23.4`. Pin CI validation and - documented setup to Vite+ `1.5.0` and TypeScript `7.0.2`, and report the resolved + documented setup to Vite+ `1.5.1` and TypeScript `7.0.2`, and report the resolved tool versions during client builds. Vite+ projects require Node.js `^22.18.0 || ^24.11.0 || >=26.0.0`. +- Upgrade `sigil-stitch` to 0.7.5 for structured TypeScript namespace-qualified + types with import-aware nested arguments. +- Retain inline scalar enum values in `IrTypeExpr::ScalarEnum` for union selection. + Rust callers exhaustively matching the public IR enum must handle this variant; + its `representation_nullable` fact retains an extracted null permission at the + original enum boundary, while `representation_is_constraint` distinguishes + authored types from synthetic untyped-enum fallbacks. Other generators retain + their existing representation and enum policies. + +### Fixed + +- Convert camelCase TypeScript untagged-union members in both JSON directions, + including mixed client/search tools and containing requests. Add configurable + `firstMatch`, `strict`, and wire-preserving `raw` selection policies instead + of treating every multi-member union converter as an identity cast. Retain + nested converter/strict-policy delegation, concrete-before-fallback priority + through nullable and `allOf` aliases, binary model values, and adjacent-tagged + unit variants. Convert inline container members and concrete values in open + nested dictionaries, and preserve array/null shapes through single-member + `allOf` aliases. Use the same value plan for models, nested HTTP bodies, + multipart JSON parts, response/error payloads, and tagged payloads/type guards. + Retain scalar enum and typed additional-value checks, productive recursive + matching, honest raw alias types, and readonly dictionary contracts. Required + nullable JSON bodies distinguish a supplied `null` from an omitted body. + Optional bodies also delegate supplied null, without an unformatted inline + conditional. Intersect nested enum constraints at their original nullability + boundary and retain authored concrete null matches beside opaque fallbacks. + Untyped numeric enums no longer inherit a synthetic string constraint; + extracted enum null permission restores a typed nullable conversion boundary. + Nullable `oneOf`/`anyOf` wrappers retain outer enum constraints; explicit null + alternatives do not bypass strict candidate counts or nested strict errors. + Emit compound conversions and predicates as static internal helpers with + direct Vite+-compatible layout, preserving readonly inputs and public exports. +- Preserve tagged TypeScript models named `Record` or `Readonly` by qualifying + built-in utility references without renaming the generated public model. + + ## [0.3.0] ### Breaking Changes diff --git a/Cargo.lock b/Cargo.lock index 07f01fbc9..88d7fa637 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1291,9 +1291,9 @@ checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" [[package]] name = "sigil-stitch" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78624fedb60989fea7b094ef573b399d0f7659d01b0101c61e9657ad7797ce29" +checksum = "6a0dc289326a598b65ad48c7e8ad8965611206c5d6fd736e269a8ab315b2a594" dependencies = [ "pretty", "serde", @@ -1308,9 +1308,9 @@ dependencies = [ [[package]] name = "sigil-stitch-macros" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "996f3ff74fe3932fcd3f8af4cba138e460157245d99234f889f4dcbc1cc645fa" +checksum = "abfa48a30eed3cb3757f47981ee883e38f68db9f646122bc8a0be5e55939cd96" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 9cc8da8ce..cf7863c1f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,7 +30,7 @@ serde = { version = "1.0.228", features = ["derive"] } serde_json = "1.0.148" serde_norway = "0.9.42" serde_plain = "1.0.2" -sigil-stitch = "0.7.4" +sigil-stitch = "0.7.5" similar = "2.7.0" snafu = "0.9.2" toml = "0.9.8" diff --git a/docs/src/golden_testing.md b/docs/src/golden_testing.md index ad86ecf7c..e39647afe 100644 --- a/docs/src/golden_testing.md +++ b/docs/src/golden_testing.md @@ -136,7 +136,7 @@ rewrites. Full `vp check` runs on the original generated output, then runs again after packing. The consumer type check uses the generated package's installed compiler rather than the global `tsc` selection. The current global Vite+ CLI also needs TypeScript installed alongside the CLI -for declaration packing (`npm install -g typescript@7.0.2 vite-plus@1.5.0`). CI +for declaration packing (`npm install -g typescript@7.0.2 vite-plus@1.5.1`). CI reuses that bootstrap and its generator cache in the existing TypeScript/Vite+ golden-build job; no separate consumer job is added. `just ci` includes these checks too. See `tests/generated-clients/typescript/README.md` for details. @@ -195,8 +195,9 @@ suite. The independent JVM Client Tests CI matrix and `just ci` run these checks - Development and ordinary CI validation select current Rust `stable` (locally validated with Rust 1.99.0). MSRV checks explicitly select Rust 1.90.0; they do not substitute for stable validation. -- Local TypeScript validation uses TypeScript 7.0.2 and Vite+ 1.5.0 on Node - 26.13.0. The native validators use the active `tsc` and `vp` on `PATH`. To select +- Local TypeScript validation uses TypeScript 7.0.2 and Vite+ 1.5.1 on Node + 26.13.0, resolving Vite 8.3.7, Rolldown 1.2.18 and Oxfmt 0.76.0. The native + validators use the active `tsc` and `vp` on `PATH`. To select that compiler explicitly, `npm exec --package=typescript@7.0.2 -- just golden-typescript::build-tsc` supplies it without changing generated packages. - Current local JVM validation uses Homebrew JDK 27.0.1 with Gradle 9.8.2, respecting diff --git a/docs/src/ir_reference.md b/docs/src/ir_reference.md index f0eb059a0..ec64e90fe 100644 --- a/docs/src/ir_reference.md +++ b/docs/src/ir_reference.md @@ -108,6 +108,12 @@ pub enum IrTypeExpr { Primitive(IrPrimitive), StringLiteral(String), StringEnum(Vec), + ScalarEnum { + representation: Box, + representation_is_constraint: bool, + representation_nullable: bool, + values: Vec, + }, Array(Box), Map(Box), Nullable(Box), @@ -132,6 +138,35 @@ For object properties, the outer nullable wrapper is recorded in container does not make its contents nullable, and nullability is independent of whether a property is required. +Inline scalar enum facts survive all three lowerers, including property, array +item, and dictionary value positions. `ScalarEnum` retains finite values together +with the pre-existing `representation`; existing string literal/enum expressions +already carry their values. TypeScript uses these facts for structural union +selection. Other generators consume the retained representation without changing +their enum policy. Rust callers exhaustively matching `IrTypeExpr` must account +for this variant. + +Finite-value constraints apply to the original node: a nullable type with +`enum: [1]` still excludes null during selection, while `enum: [1, null]` permits +it. Extracting `IrProperty.nullable` does not widen that constraint. The +`representation_nullable` fact records a removed nullable wrapper at its exact +enum boundary: an outer enum intersects the inner constraints without moving +null permission across another enum. It defaults to false when constructing a +new `ScalarEnum` whose complete nullable representation is still present. +Nested finite constraints are intersected rather than replaced by outer values. +`representation_is_constraint` distinguishes an authored type or constrained +wrapper from the legacy string fallback used for an enum with no declared type. +The latter keeps its existing emitted type, but numeric/boolean/null enum values +are matched directly rather than intersected with a synthetic string check. +The `representation()` view exposes the legacy type beneath scalar metadata for +generators that do not use finite values. OpenAPI 3.1/3.2 nullable `oneOf`/`anyOf` +wrappers retain enums attached to the wrapper, including inside arrays and maps. +TypeScript conversion restores an extracted nullable wrapper around its original +delegate; matching metadata does not imply that a nonnullable delegate accepts +null. Null already accepted by the inner union still reaches its strict policy. +Inner finite constraints remain inside the restored wrapper so null excluded by +that inner constraint can still be admitted exclusively by the outer wrapper. + OpenAPI 3.0 boolean `exclusiveMinimum` / `exclusiveMaximum` flags retain both `true` and `false` in validation metadata. The public 3.0 `ObjectSchema` fields use `ExclusiveBound` to preserve those booleans as well as the legacy numeric diff --git a/docs/src/ts_config.md b/docs/src/ts_config.md index 125ef61aa..a1d9b6fb9 100644 --- a/docs/src/ts_config.md +++ b/docs/src/ts_config.md @@ -16,6 +16,7 @@ include_build_scripts = true emit_enum_constants = true emit_type_guards = true property_naming = "camelCase" +untagged_union_strategy = "firstMatch" indent = " " ``` @@ -204,12 +205,128 @@ Works with all schema kinds: - **Tagged unions** (internal, adjacent, external): dual type aliases + switch/if-chain converters - **Intersections** (allOf): dual type aliases; compatible open objects share one conversion plan, while other intersections use spread-based converters -- **Unions** (oneOf): dual type aliases + cast-through converters - -Referenced types that are themselves convertible (objects, intersections, unions) get their converter functions called recursively. Enums and simple aliases pass through unchanged. +- **Untagged unions** (oneOf/anyOf): dual type aliases + member-selecting converters + +Models, JSON request bodies, JSON multipart parts, successful responses, and lazy +error values use one value plan per generation. It projects public and wire types +and delegates conversion recursively through aliases, properties, arrays, and +dictionaries. An unchanged type does not remove a `strict` runtime check: even an +inline `integer | number` union must still detect overlapping matches. + +Compound conversions and structural predicates use statically generated helper +functions in internal runtime modules, not a runtime schema interpreter. They +are not re-exported by the public barrel. Conversion helpers retain separate +dependency modules so importing a model does not load unrelated model graphs. +Helper inputs accept readonly arrays +without widening public API results or changing model array mutability. Source +layout is emitted directly; full Vite+ checks need no formatter preparation. Defaults to `"preserve"` (no renaming, single interface, no converters). +### `untagged_union_strategy` + +Controls untagged-union conversion when `property_naming = "camelCase"`: + +- `"firstMatch"` (default): select the first structurally matching member and + call its converter. Concrete members precede unconstrained JSON/object/map + fallbacks, with schema order preserved within each group. Unmatched values + pass through unchanged rather than making imperfect specifications unusable. +- `"strict"`: use the same structural selection, but throw `TypeError` for + multiple concrete matches or no matching member. A raw fallback is considered + only after the concrete members, so it does not itself create ambiguity. +- `"raw"`: do not select or convert union members. Both `Name` and `Name$Wire` + expose the wire representation; pass original wire keys such as `input_schema`, + not ergonomic keys such as `inputSchema`. This is an explicit escape hatch, + not an identity cast pretending to return a renamed model. This applies to the + entire union subtree, including inline container unions and aliases; ordinary + enclosing objects and sibling properties still convert normally. + +```toml +[generators.typescript-fetch] +property_naming = "camelCase" +untagged_union_strategy = "strict" +``` + +The same choice is available through a CLI override: + +```sh +openapi-nexus generate --input openapi.yaml --generators typescript-fetch \ + --generator-config typescript-fetch.property_naming=camelCase \ + --generator-config typescript-fetch.untagged_union_strategy=strict +``` + +Selection checks generated value kinds, required own fields, declared property +shapes, typed additional values, and scalar literal/enum values in the appropriate +wire or ergonomic naming direction. Named fields are excluded from the additional +value check, even though TypeScript widens the dictionary's index type to include +them. +Nested enum constraints intersect at their original nullable boundaries; an outer +enum does not replace or widen an inner enum. +Untyped scalar enums match their finite values without treating the legacy +string type fallback as an authored constraint. +Arrays and dictionaries inspect their element/value shapes; dictionary keys and +external variant tags stay verbatim. This is conversion dispatch, not full JSON +Schema validation: formats, numeric bounds, and other validation keywords are +not checked. Productive recursive references use statically generated matching +functions rather than becoming unrestricted guards. These helpers are private to +the generated runtime, not public barrel exports; selected member converters +still handle recursive payload conversion. Same-value circular type aliases are +not made representable by this mechanism. +Binary properties match their generated `Blob | File` representation rather +than strings. Adjacent-tagged unit variants do not require a content property; +payload variants retain their content checks. + +A named nested union can provide both concrete matches and a raw fallback. +Its concrete matches take priority over fallbacks, and both paths delegate to +the nested converter so its strict ambiguity policy is not bypassed. Nullable +and `allOf` wrappers do not turn an unrestricted map/JSON fallback into a +concrete match. A dictionary of open nested unions retains a concrete selection +path when at least one entry matches concretely, even when other entries are +opaque. Empty or entirely opaque dictionaries remain fallbacks. Both paths +delegate through the dictionary converter, preserving inner strict errors. + +Matching is pure: select/count the original member candidates before running a +converter, then invoke only the selected delegate. A nested strict error propagates +instead of triggering a retry with another member. Repeated nullable wrappers are +idempotent; they do not create extra candidates or promote an opaque fallback. +An explicit null alternative does not bypass ambiguity among other nullable +members, or suppress a selected nested converter's strict error. +An unrestricted fallback beside authored nullable members does not erase those +members' concrete null matches. Null permission added only by an opaque wrapper +remains a fallback rather than creating an extra concrete candidate. +Property, array and dictionary nullable wrappers delegate null when the inner +value already accepts it. Null admitted only by the outer wrapper still bypasses +a non-nullable inner converter. +This remains true when property nullability was extracted through scalar enum +metadata: conversion restores the wrapper rather than passing null directly to +the nonnullable delegate. + +Inline unions inside a selected array or dictionary member use the same +selection policy recursively, rather than returning their contents unchanged. +A single-member `allOf` alias delegates without spreading its result, +preserving primitive, array, and null values too. + +For example, a mixed `ClientTool | SearchTool` union can distinguish a required +`input_schema` / `inputSchema` field from a search member's required literal +`type`, then convert `inputSchema` and `maxUses` to `input_schema` and `max_uses`. +Conversions also work through containing arrays, dictionaries, and model aliases. +Declared additional-property behavior remains owned by the selected member's +converter. Direct tagged payloads and optional type guards use the same type and +conversion projections. Preserve mode does not add JSON converters. + +Tagged declarations and guards namespace-qualify built-in utilities when a model +name shadows them, including open external wrappers. A tagged schema named +`Record` or `Readonly` keeps its public model and converter names; its generated +utility types use `globalThis.Record` or `globalThis.Readonly` instead. Planned +dictionaries and structural matching casts use the same qualification. + +A required JSON body must be supplied, but a schema-permitted `null` is a value, +not a missing body. Such bodies serialize as JSON `null`; omitted bodies still +throw `RequiredError` before sending a request. +Optional JSON bodies skip conversion only when omitted (`undefined`). A supplied +null uses the same planned conversion and strict ambiguity checks as a required +body or direct model conversion. + ### Additional properties Objects declaring `additionalProperties` expose a readonly string dictionary in diff --git a/just/golden-typescript-clients.just b/just/golden-typescript-clients.just index de4dd655b..1de5288c8 100644 --- a/just/golden-typescript-clients.just +++ b/just/golden-typescript-clients.just @@ -3,9 +3,13 @@ set working-directory := '..' @prepare: - rm -rf tests/generated-clients/typescript/generated/camel-case tests/generated-clients/typescript/generated/preserve + rm -rf tests/generated-clients/typescript/generated/camel-case tests/generated-clients/typescript/generated/preserve tests/generated-clients/typescript/generated/union-default tests/generated-clients/typescript/generated/union-strict tests/generated-clients/typescript/generated/union-raw tests/generated-clients/typescript/generated/isolation cargo run --quiet --bin openapi-nexus -- generate --config tests/generated-clients/config.toml --input tests/fixtures/valid/typescript-additional-properties.yaml --output tests/generated-clients/typescript/generated/camel-case --generators typescript-fetch --generator-config typescript-fetch.property_naming=camelCase --generator-config typescript-fetch.toolchain=vp cargo run --quiet --bin openapi-nexus -- generate --config tests/generated-clients/config.toml --input tests/fixtures/valid/typescript-additional-properties.yaml --output tests/generated-clients/typescript/generated/preserve --generators typescript-fetch --generator-config typescript-fetch.toolchain=vp + cargo run --quiet --bin openapi-nexus -- generate --config tests/generated-clients/config.toml --input tests/fixtures/valid/typescript-untagged-union.yaml --output tests/generated-clients/typescript/generated/union-default --generators typescript-fetch --generator-config typescript-fetch.property_naming=camelCase --generator-config typescript-fetch.toolchain=vp --generator-config typescript-fetch.emit_type_guards=true + cargo run --quiet --bin openapi-nexus -- generate --config tests/generated-clients/config.toml --input tests/fixtures/valid/typescript-untagged-union.yaml --output tests/generated-clients/typescript/generated/union-strict --generators typescript-fetch --generator-config typescript-fetch.property_naming=camelCase --generator-config typescript-fetch.toolchain=vp --generator-config typescript-fetch.emit_type_guards=true --generator-config typescript-fetch.untagged_union_strategy=strict + cargo run --quiet --bin openapi-nexus -- generate --config tests/generated-clients/config.toml --input tests/fixtures/valid/typescript-untagged-union.yaml --output tests/generated-clients/typescript/generated/union-raw --generators typescript-fetch --generator-config typescript-fetch.property_naming=camelCase --generator-config typescript-fetch.toolchain=vp --generator-config typescript-fetch.emit_type_guards=true --generator-config typescript-fetch.untagged_union_strategy=raw + cargo run --quiet --bin openapi-nexus -- generate --config tests/generated-clients/config.toml --input tests/fixtures/valid/typescript-helper-isolation.yaml --output tests/generated-clients/typescript/generated/isolation --generators typescript-fetch --generator-config typescript-fetch.property_naming=camelCase @build: prepare #!/usr/bin/env bash @@ -13,7 +17,7 @@ set working-directory := '..' project="$PWD/tests/generated-clients/typescript" builds=() trap 'rm -rf "${builds[@]}"' EXIT - for mode in camel-case preserve; do + for mode in camel-case preserve union-default union-strict union-raw; do output="$project/generated/$mode" build=$(mktemp -d) builds+=("$build") @@ -25,6 +29,7 @@ set working-directory := '..' cp "$build"/dist/* "$output/dist/" done "$build/node_modules/.bin/tsc" --project "$project/tsconfig.json" + "$build/node_modules/.bin/tsc" --project "$project/tsconfig.isolation.json" @test: build node --test tests/generated-clients/typescript/tests/*.test.mjs diff --git a/src/generators/closed_sum.rs b/src/generators/closed_sum.rs index 4d8c887ca..4b57b70b7 100644 --- a/src/generators/closed_sum.rs +++ b/src/generators/closed_sum.rs @@ -1,7 +1,8 @@ use heck::ToPascalCase; use crate::ir::types::{ - IrPrimitive, IrSchemaKind, IrSpec, IrTaggedUnion, IrTypeExpr, IrUnion, TaggingStyle, + IrPrimitive, IrSchemaKind, IrSpec, IrTaggedUnion, IrTaggedVariant, IrTypeExpr, IrUnion, + TaggingStyle, }; /// Target-independent plan for a closed sum emitted from an OpenAPI union. @@ -187,6 +188,9 @@ fn tagged_case_name(expr: &IrTypeExpr, index: usize) -> String { fn union_case_name(expr: &IrTypeExpr, index: usize) -> String { let name = match expr { + IrTypeExpr::ScalarEnum { representation, .. } => { + return union_case_name(representation, index); + } IrTypeExpr::Named(name) => name.to_pascal_case(), IrTypeExpr::Primitive(primitive) => primitive_case_name(primitive), IrTypeExpr::Array(_) => "Array".to_string(), @@ -204,7 +208,22 @@ fn union_case_name(expr: &IrTypeExpr, index: usize) -> String { } } -fn is_tag_only_variant( +/// Shared unit-payload rule for tagged declarations and structural matching. +pub(crate) fn is_tagged_unit_variant( + ir: &IrSpec, + union: &IrTaggedUnion, + variant: &IrTaggedVariant, +) -> bool { + is_tag_only_variant( + ir, + &union.discriminator_field, + &variant.discriminator_value, + &variant.content_type, + ) || matches!(union.tagging, TaggingStyle::Internal) + && matches!(variant.content_type, IrTypeExpr::StringLiteral(_)) +} + +pub(crate) fn is_tag_only_variant( ir: &IrSpec, discriminator_field: &str, discriminator_value: &str, diff --git a/src/generators/go/http/sigil_emit.rs b/src/generators/go/http/sigil_emit.rs index 0947d458d..91ca04709 100644 --- a/src/generators/go/http/sigil_emit.rs +++ b/src/generators/go/http/sigil_emit.rs @@ -466,6 +466,7 @@ fn render_alias_file( /// needed within the `models/` tree. fn go_type_name(expr: &IrTypeExpr) -> TypeName { match expr { + IrTypeExpr::ScalarEnum { representation, .. } => go_type_name(representation), IrTypeExpr::Named(name) => TypeName::primitive(&name.to_pascal_case()), IrTypeExpr::Primitive(p) => TypeName::primitive(go_primitive(p)), IrTypeExpr::Array(inner) => TypeName::slice(go_type_name(inner)), @@ -479,6 +480,7 @@ fn go_type_name(expr: &IrTypeExpr) -> TypeName { /// Map an IR type expression to a Go type as a bare string. fn go_type_str(expr: &IrTypeExpr) -> String { match expr { + IrTypeExpr::ScalarEnum { representation, .. } => go_type_str(representation), IrTypeExpr::Named(name) => name.to_pascal_case(), IrTypeExpr::Primitive(p) => go_primitive(p).to_string(), IrTypeExpr::Array(inner) => format!("[]{}", go_type_str(inner)), diff --git a/src/generators/go/http/sigil_emit_api.rs b/src/generators/go/http/sigil_emit_api.rs index 1f78211ad..1bd048e95 100644 --- a/src/generators/go/http/sigil_emit_api.rs +++ b/src/generators/go/http/sigil_emit_api.rs @@ -278,7 +278,7 @@ fn collect_body_imports(plans: &[OpPlan<'_>], module_path: &str) -> Vec) { - match t { + match t.representation() { IrTypeExpr::Primitive( IrPrimitive::String | IrPrimitive::Date @@ -313,7 +313,7 @@ fn collect_stringify_imports(t: &IrTypeExpr, pkgs: &mut BTreeSet) { fn is_stringish_primitive(t: &IrTypeExpr) -> bool { matches!( - t, + t.representation(), IrTypeExpr::Primitive( IrPrimitive::String | IrPrimitive::Date @@ -1869,7 +1869,7 @@ fn go_field_name(name: &str) -> String { } fn render_value_as_string(value_expr: &str, t: &IrTypeExpr) -> String { - match t { + match t.representation() { IrTypeExpr::Primitive( IrPrimitive::String | IrPrimitive::Date @@ -1899,7 +1899,7 @@ fn render_value_as_string(value_expr: &str, t: &IrTypeExpr) -> String { format!("strings.Join({value_expr}, \",\")") } else { let (item_expr, item_type) = - if let IrTypeExpr::Nullable(real_inner) = inner.as_ref() { + if let IrTypeExpr::Nullable(real_inner) = inner.representation() { ("*v", real_inner.as_ref()) } else { ("v", inner.as_ref()) @@ -1920,6 +1920,7 @@ fn render_value_as_string(value_expr: &str, t: &IrTypeExpr) -> String { fn go_type_str(expr: &IrTypeExpr) -> String { match expr { + IrTypeExpr::ScalarEnum { representation, .. } => go_type_str(representation), IrTypeExpr::Named(name) => format!("models.{}", name.to_pascal_case()), IrTypeExpr::Primitive(p) => go_primitive(p).to_string(), IrTypeExpr::Array(inner) => format!("[]{}", go_type_str(inner)), diff --git a/src/generators/java/okhttp/sigil_emit.rs b/src/generators/java/okhttp/sigil_emit.rs index 1c55859b2..0c8772453 100644 --- a/src/generators/java/okhttp/sigil_emit.rs +++ b/src/generators/java/okhttp/sigil_emit.rs @@ -572,6 +572,7 @@ fn java_closed_sum_case(case: &ClosedSumCasePlan) -> Result TypeName { match expr { + IrTypeExpr::ScalarEnum { representation, .. } => java_type_name(representation, boxed), IrTypeExpr::Named(name) => TypeName::primitive(&name.to_pascal_case()), IrTypeExpr::Primitive(primitive) => TypeName::primitive(if boxed { java_primitive_boxed(primitive) @@ -817,6 +818,7 @@ fn java_adapter_read_value( payload: &IrTypeExpr, source: CodeBlock, ) -> Result { + let payload = payload.representation(); let payload_type = java_type_name(payload, true); let root_name = plan.name.as_str(); let case_name = case.name.as_str(); @@ -942,6 +944,9 @@ fn java_json_condition( schemas: &indexmap::IndexMap, ) -> Result { match expr { + IrTypeExpr::ScalarEnum { representation, .. } => { + java_json_condition(representation, schemas) + } IrTypeExpr::Primitive( IrPrimitive::String | IrPrimitive::StringWithFormat(_) diff --git a/src/generators/java/okhttp/util.rs b/src/generators/java/okhttp/util.rs index d919de032..2c48d0cdf 100644 --- a/src/generators/java/okhttp/util.rs +++ b/src/generators/java/okhttp/util.rs @@ -8,6 +8,7 @@ use sigil_stitch::prelude::*; pub fn java_type_str(expr: &IrTypeExpr) -> String { match expr { + IrTypeExpr::ScalarEnum { representation, .. } => java_type_str(representation), IrTypeExpr::Named(name) => name.to_pascal_case(), IrTypeExpr::Primitive(p) => java_primitive(p).to_string(), IrTypeExpr::Array(inner) => format!("List<{}>", java_boxed_type_str(inner)), @@ -20,6 +21,7 @@ pub fn java_type_str(expr: &IrTypeExpr) -> String { pub fn java_boxed_type_str(expr: &IrTypeExpr) -> String { match expr { + IrTypeExpr::ScalarEnum { representation, .. } => java_boxed_type_str(representation), IrTypeExpr::Primitive(p) => java_primitive_boxed(p).to_string(), _ => java_type_str(expr), } @@ -187,7 +189,7 @@ pub fn sanitize_operation_id(op_id: &str, method: &str, path: &str) -> String { } pub fn render_value_as_string(value_expr: &str, t: &IrTypeExpr) -> String { - match t { + match t.representation() { IrTypeExpr::Primitive( IrPrimitive::String | IrPrimitive::Date @@ -207,7 +209,7 @@ pub fn render_value_as_string(value_expr: &str, t: &IrTypeExpr) -> String { IrTypeExpr::Nullable(inner) => render_value_as_string(value_expr, inner), IrTypeExpr::Array(inner) => { if matches!( - inner.as_ref(), + inner.representation(), IrTypeExpr::Primitive( IrPrimitive::String | IrPrimitive::Date @@ -242,7 +244,7 @@ pub fn build_java_getter(getter_name: &str, type_str: &str, field_name: &str) -> } pub fn type_uses_list(expr: &IrTypeExpr) -> bool { - match expr { + match expr.representation() { IrTypeExpr::Array(_) => true, IrTypeExpr::Nullable(inner) => type_uses_list(inner), IrTypeExpr::Map(inner) => type_uses_list(inner), @@ -251,7 +253,7 @@ pub fn type_uses_list(expr: &IrTypeExpr) -> bool { } pub fn type_uses_map(expr: &IrTypeExpr) -> bool { - match expr { + match expr.representation() { IrTypeExpr::Map(_) => true, IrTypeExpr::Nullable(inner) => type_uses_map(inner), IrTypeExpr::Array(inner) => type_uses_map(inner), diff --git a/src/generators/kotlin/okhttp/sigil_emit.rs b/src/generators/kotlin/okhttp/sigil_emit.rs index 6173dcb2f..c9283b0c4 100644 --- a/src/generators/kotlin/okhttp/sigil_emit.rs +++ b/src/generators/kotlin/okhttp/sigil_emit.rs @@ -367,6 +367,7 @@ fn kotlin_closed_sum_case(case: &ClosedSumCasePlan) -> Result TypeName { match expr { + IrTypeExpr::ScalarEnum { representation, .. } => kotlin_type_name(representation), IrTypeExpr::Named(name) => TypeName::primitive(&name.to_pascal_case()), IrTypeExpr::Primitive(primitive) => TypeName::primitive(kt_primitive(primitive)), IrTypeExpr::Array(inner) => TypeName::application( @@ -607,6 +608,7 @@ fn kotlin_adapter_read_value( payload: &IrTypeExpr, source: CodeBlock, ) -> Result { + let payload = payload.representation(); let payload_type = kotlin_type_name(payload); let root_name = plan.name.as_str(); let case_name = case.name.as_str(); @@ -751,6 +753,9 @@ fn kotlin_json_condition( schemas: &indexmap::IndexMap, ) -> Result { match expr { + IrTypeExpr::ScalarEnum { representation, .. } => { + kotlin_json_condition(representation, schemas) + } IrTypeExpr::Primitive( IrPrimitive::String | IrPrimitive::StringWithFormat(_) diff --git a/src/generators/kotlin/okhttp/util.rs b/src/generators/kotlin/okhttp/util.rs index ea68644eb..0a6cf035d 100644 --- a/src/generators/kotlin/okhttp/util.rs +++ b/src/generators/kotlin/okhttp/util.rs @@ -7,6 +7,7 @@ use sigil_stitch::prelude::RendererLang; pub fn kt_type_str(expr: &IrTypeExpr) -> String { match expr { + IrTypeExpr::ScalarEnum { representation, .. } => kt_type_str(representation), IrTypeExpr::Named(name) => name.to_pascal_case(), IrTypeExpr::Primitive(p) => kt_primitive(p).to_string(), IrTypeExpr::Array(inner) => format!("List<{}>", kt_type_str(inner)), @@ -101,7 +102,7 @@ pub fn sanitize_operation_id(op_id: &str, method: &str, path: &str) -> String { } pub fn render_value_as_string(value_expr: &str, t: &IrTypeExpr) -> String { - match t { + match t.representation() { IrTypeExpr::Primitive( IrPrimitive::String | IrPrimitive::Date diff --git a/src/generators/mod.rs b/src/generators/mod.rs index dcde97339..fdeb52305 100644 --- a/src/generators/mod.rs +++ b/src/generators/mod.rs @@ -11,6 +11,7 @@ pub mod response_headers; pub mod response_names; pub mod rust; pub mod typescript; +pub(crate) mod union_match; pub use error::{ArtifactKind, GeneratorError}; diff --git a/src/generators/multipart.rs b/src/generators/multipart.rs index 1bf1428b2..312287585 100644 --- a/src/generators/multipart.rs +++ b/src/generators/multipart.rs @@ -86,7 +86,7 @@ fn multipart_part_from_property( } fn resolve_object<'a>(expr: &IrTypeExpr, ir: &'a IrSpec) -> Option<&'a IrObject> { - match expr { + match expr.representation() { IrTypeExpr::Named(name) => match ir.schemas.get(name).map(|schema| &schema.kind) { Some(IrSchemaKind::Object(obj)) => Some(obj), Some(IrSchemaKind::Alias(inner)) => resolve_object(inner, ir), @@ -98,7 +98,7 @@ fn resolve_object<'a>(expr: &IrTypeExpr, ir: &'a IrSpec) -> Option<&'a IrObject> } fn is_binary_type(expr: &IrTypeExpr, ir: &IrSpec) -> bool { - match expr { + match expr.representation() { IrTypeExpr::Primitive(IrPrimitive::Binary) => true, IrTypeExpr::Nullable(inner) => is_binary_type(inner, ir), IrTypeExpr::Named(name) => ir.schemas.get(name).is_some_and(|schema| { @@ -109,7 +109,7 @@ fn is_binary_type(expr: &IrTypeExpr, ir: &IrSpec) -> bool { } fn is_multipart_text_type(expr: &IrTypeExpr, ir: &IrSpec) -> bool { - match expr { + match expr.representation() { IrTypeExpr::Primitive(_) | IrTypeExpr::StringLiteral(_) | IrTypeExpr::StringEnum(_) => true, IrTypeExpr::Nullable(inner) => is_multipart_text_type(inner, ir), IrTypeExpr::Named(name) => ir.schemas.get(name).is_some_and(|schema| { diff --git a/src/generators/python/httpx/emit_api.rs b/src/generators/python/httpx/emit_api.rs index e26f645de..2f0b40054 100644 --- a/src/generators/python/httpx/emit_api.rs +++ b/src/generators/python/httpx/emit_api.rs @@ -552,7 +552,7 @@ fn render_multipart_json_value(access: &str, expr: &IrTypeExpr, ir: &IrSpec) -> } fn render_stringify(var: &str, type_expr: &IrTypeExpr) -> String { - match type_expr { + match type_expr.representation() { IrTypeExpr::Primitive( IrPrimitive::String | IrPrimitive::Date @@ -593,7 +593,7 @@ fn render_response_parse(response: &TypedResponse, ir: &IrSpec) -> String { } fn render_json_response_parse(type_expr: &IrTypeExpr, ir: &IrSpec) -> String { - match type_expr { + match type_expr.representation() { IrTypeExpr::Named(name) => { let py_name = name.to_pascal_case(); if is_object_schema(name, ir) { @@ -1326,5 +1326,5 @@ fn sanitize_operation_id(op_id: &str, method: &str, path: &str) -> String { /// Returns true if the type expression is already nullable (wrapped in None), /// so that the caller can avoid double-wrapping with TypeName::optional. fn is_already_optional(expr: &IrTypeExpr) -> bool { - matches!(expr, IrTypeExpr::Nullable(_)) + matches!(expr.representation(), IrTypeExpr::Nullable(_)) } diff --git a/src/generators/python/httpx/emit_models.rs b/src/generators/python/httpx/emit_models.rs index bd16406e2..9e1a70c05 100644 --- a/src/generators/python/httpx/emit_models.rs +++ b/src/generators/python/httpx/emit_models.rs @@ -748,7 +748,7 @@ fn emit_elif( /// Like `python_type_name` but Named types import from `..models.{snake}` (for API files). pub fn api_type_name(expr: &IrTypeExpr) -> TypeName { - match expr { + match expr.representation() { IrTypeExpr::Named(name) => { let py_name = name.to_pascal_case(); let module = format!("..models.{}", name.to_snake_case()); @@ -776,6 +776,7 @@ pub fn api_type_name(expr: &IrTypeExpr) -> TypeName { /// Map an IR type expression to a Python type string (for serialization helpers). pub fn python_type_str(expr: &IrTypeExpr) -> String { match expr { + IrTypeExpr::ScalarEnum { representation, .. } => python_type_str(representation), IrTypeExpr::Named(name) => name.to_pascal_case(), IrTypeExpr::Primitive(p) => python_primitive(p).to_string(), IrTypeExpr::StringLiteral(s) => { @@ -829,7 +830,7 @@ fn collect_request_input_imports( expr: &IrTypeExpr, imports: &mut std::collections::BTreeSet, ) { - match expr { + match expr.representation() { IrTypeExpr::Named(name) => { let py_name = name.to_pascal_case(); let module = name.to_snake_case(); @@ -863,7 +864,7 @@ fn collect_request_input_imports( } fn needs_typing_literal(expr: &IrTypeExpr) -> bool { - match expr { + match expr.representation() { IrTypeExpr::StringLiteral(_) | IrTypeExpr::StringEnum(_) => true, IrTypeExpr::Array(inner) | IrTypeExpr::Map(inner) | IrTypeExpr::Nullable(inner) => { needs_typing_literal(inner) diff --git a/src/generators/python/requests/emit_api.rs b/src/generators/python/requests/emit_api.rs index 6c5e546e0..48d1b38ca 100644 --- a/src/generators/python/requests/emit_api.rs +++ b/src/generators/python/requests/emit_api.rs @@ -551,7 +551,7 @@ fn render_multipart_json_value(access: &str, expr: &IrTypeExpr, ir: &IrSpec) -> } fn render_stringify(var: &str, type_expr: &IrTypeExpr) -> String { - match type_expr { + match type_expr.representation() { IrTypeExpr::Primitive( IrPrimitive::String | IrPrimitive::Date @@ -592,7 +592,7 @@ fn render_response_parse(response: &TypedResponse, ir: &IrSpec) -> String { } fn render_json_response_parse(type_expr: &IrTypeExpr, ir: &IrSpec) -> String { - match type_expr { + match type_expr.representation() { IrTypeExpr::Named(name) => { let py_name = name.to_pascal_case(); if is_object_schema(name, ir) { @@ -1325,5 +1325,5 @@ fn sanitize_operation_id(op_id: &str, method: &str, path: &str) -> String { /// Returns true if the type expression is already nullable (wrapped in None), /// so that the caller can avoid double-wrapping with TypeName::optional. fn is_already_optional(expr: &IrTypeExpr) -> bool { - matches!(expr, IrTypeExpr::Nullable(_)) + matches!(expr.representation(), IrTypeExpr::Nullable(_)) } diff --git a/src/generators/python/requests/emit_models.rs b/src/generators/python/requests/emit_models.rs index bd16406e2..9e1a70c05 100644 --- a/src/generators/python/requests/emit_models.rs +++ b/src/generators/python/requests/emit_models.rs @@ -748,7 +748,7 @@ fn emit_elif( /// Like `python_type_name` but Named types import from `..models.{snake}` (for API files). pub fn api_type_name(expr: &IrTypeExpr) -> TypeName { - match expr { + match expr.representation() { IrTypeExpr::Named(name) => { let py_name = name.to_pascal_case(); let module = format!("..models.{}", name.to_snake_case()); @@ -776,6 +776,7 @@ pub fn api_type_name(expr: &IrTypeExpr) -> TypeName { /// Map an IR type expression to a Python type string (for serialization helpers). pub fn python_type_str(expr: &IrTypeExpr) -> String { match expr { + IrTypeExpr::ScalarEnum { representation, .. } => python_type_str(representation), IrTypeExpr::Named(name) => name.to_pascal_case(), IrTypeExpr::Primitive(p) => python_primitive(p).to_string(), IrTypeExpr::StringLiteral(s) => { @@ -829,7 +830,7 @@ fn collect_request_input_imports( expr: &IrTypeExpr, imports: &mut std::collections::BTreeSet, ) { - match expr { + match expr.representation() { IrTypeExpr::Named(name) => { let py_name = name.to_pascal_case(); let module = name.to_snake_case(); @@ -863,7 +864,7 @@ fn collect_request_input_imports( } fn needs_typing_literal(expr: &IrTypeExpr) -> bool { - match expr { + match expr.representation() { IrTypeExpr::StringLiteral(_) | IrTypeExpr::StringEnum(_) => true, IrTypeExpr::Array(inner) | IrTypeExpr::Map(inner) | IrTypeExpr::Nullable(inner) => { needs_typing_literal(inner) diff --git a/src/generators/python/type_names.rs b/src/generators/python/type_names.rs index 574107b38..638db062c 100644 --- a/src/generators/python/type_names.rs +++ b/src/generators/python/type_names.rs @@ -14,6 +14,9 @@ pub(crate) fn python_type_name_for_schema(expr: &IrTypeExpr, current_schema: &st fn python_type_name_with_current(expr: &IrTypeExpr, current_schema: Option<&str>) -> TypeName { match expr { + IrTypeExpr::ScalarEnum { representation, .. } => { + python_type_name_with_current(representation, current_schema) + } IrTypeExpr::Named(name) => { let py_name = name.to_pascal_case(); if current_schema == Some(name.as_str()) { diff --git a/src/generators/response_headers.rs b/src/generators/response_headers.rs index cfded135b..670723560 100644 --- a/src/generators/response_headers.rs +++ b/src/generators/response_headers.rs @@ -101,6 +101,9 @@ fn response_header_value_kind_inner( seen: &mut HashSet, ) -> ResponseHeaderValueKind { match expr { + IrTypeExpr::ScalarEnum { representation, .. } => { + response_header_value_kind_inner(representation, ir, seen) + } IrTypeExpr::Primitive(IrPrimitive::Integer | IrPrimitive::IntegerWithFormat(_)) => { ResponseHeaderValueKind::Integer } diff --git a/src/generators/rust/common/emit_api.rs b/src/generators/rust/common/emit_api.rs index 3e69ff2f2..d11eb9b07 100644 --- a/src/generators/rust/common/emit_api.rs +++ b/src/generators/rust/common/emit_api.rs @@ -493,7 +493,7 @@ fn rust_type_name_qualified(expr: &IrTypeExpr, ir: &IrSpec) -> TypeName { } fn rust_type_name(expr: &IrTypeExpr, ir: &IrSpec, qualify_named: bool) -> TypeName { - match expr { + match expr.representation() { IrTypeExpr::Named(name) => { if qualify_named && let Some(schema) = ir.schemas.get(name) @@ -531,7 +531,7 @@ pub fn param_rust_type(p: &IrParameter, ir: &IrSpec) -> (String, bool) { let base = rust_type_str_qualified(&p.type_expr, ir); if p.required { (base, false) - } else if matches!(p.type_expr, IrTypeExpr::Nullable(_)) { + } else if matches!(p.type_expr.representation(), IrTypeExpr::Nullable(_)) { // Already wrapped in Option by rust_type_str_qualified → avoid double-wrapping (base, true) } else { diff --git a/src/generators/rust/common/emit_models.rs b/src/generators/rust/common/emit_models.rs index 44a649748..e6823b57e 100644 --- a/src/generators/rust/common/emit_models.rs +++ b/src/generators/rust/common/emit_models.rs @@ -530,7 +530,7 @@ fn emit_union( } fn union_variant_name(expr: &IrTypeExpr, index: usize) -> String { - match expr { + match expr.representation() { IrTypeExpr::Named(n) => n.to_pascal_case(), IrTypeExpr::Primitive(p) => primitive_variant_name(p), IrTypeExpr::Array(_) => format!("Array{index}"), @@ -886,6 +886,7 @@ fn resolve_object<'a>(expr: &IrTypeExpr, ir: &'a IrSpec) -> Option<&'a IrObject> pub fn rust_type_str(expr: &IrTypeExpr) -> String { match expr { + IrTypeExpr::ScalarEnum { representation, .. } => rust_type_str(representation), IrTypeExpr::Named(name) => name.to_pascal_case(), IrTypeExpr::Primitive(p) => rust_primitive(p).to_string(), IrTypeExpr::Array(inner) => format!("Vec<{}>", rust_type_str(inner)), @@ -902,7 +903,7 @@ pub fn rust_type_str(expr: &IrTypeExpr) -> String { /// Map an IR type to a Rust type string, qualified for use from API modules. /// Named references to suppressed primitive aliases are resolved to the primitive type. pub fn rust_type_str_qualified(expr: &IrTypeExpr, ir: &IrSpec) -> String { - match expr { + match expr.representation() { IrTypeExpr::Named(name) => { if let Some(schema) = ir.schemas.get(name) && let IrSchemaKind::Alias(inner) = &schema.kind @@ -924,7 +925,7 @@ pub fn rust_type_str_qualified(expr: &IrTypeExpr, ir: &IrSpec) -> String { /// Map an IR type for use within model files (sibling references use `super::`). fn rust_type_str_model(expr: &IrTypeExpr) -> String { - match expr { + match expr.representation() { IrTypeExpr::Named(name) => format!("super::{}", name.to_pascal_case()), IrTypeExpr::Array(inner) => format!("Vec<{}>", rust_type_str_model(inner)), IrTypeExpr::Map(inner) => format!( @@ -1019,7 +1020,7 @@ fn collect_named_type_refs(schema: &IrSchema) -> Vec<&str> { } fn collect_named_from_expr<'a>(expr: &'a IrTypeExpr, refs: &mut Vec<&'a str>) { - match expr { + match expr.representation() { IrTypeExpr::Named(name) => refs.push(name.as_str()), IrTypeExpr::Array(inner) | IrTypeExpr::Map(inner) | IrTypeExpr::Nullable(inner) => { collect_named_from_expr(inner, refs); diff --git a/src/generators/typescript/fetch/codegen.rs b/src/generators/typescript/fetch/codegen.rs index e02a81186..a5a09c530 100644 --- a/src/generators/typescript/fetch/codegen.rs +++ b/src/generators/typescript/fetch/codegen.rs @@ -4,20 +4,21 @@ use heck::{ToKebabCase as _, ToLowerCamelCase as _, ToPascalCase as _, ToSnakeCa use super::layout::named_list; use sigil_stitch::lang::typescript::TypeScript; +use sigil_stitch::prelude::*; use super::config::TypeScriptFetchConfig; use super::config::typescript_fetch_config::Toolchain; +use super::plan::{Scope, TypeScriptPlan}; use super::project_files::{render_index_file, render_readme_file, render_runtime_file}; use crate::codegen::NamingConvention; use crate::codegen::traits::code_generator::CodeGenerator; use crate::codegen::traits::file_writer::{FileInfo, FileWriter}; use crate::codegen::{GeneratorType, Language}; -use crate::generators::GeneratorError; use crate::generators::request_inputs::{ - RequestInputField, RequestInputFieldKind, RequestInputModel, RequestInputPlan, - plan_multipart_request_inputs, + RequestInputModel, RequestInputPlan, plan_multipart_request_inputs, }; -use crate::ir::types::{IrPrimitive, IrSpec, IrTypeExpr}; +use crate::generators::{ArtifactKind, GeneratorError}; +use crate::ir::types::IrSpec; /// TypeScript Fetch code generator #[derive(Debug, Clone)] @@ -57,14 +58,29 @@ impl TypeScriptFetchCodeGenerator { ir: &crate::ir::types::IrSpec, request_inputs: &RequestInputPlan, ) -> Result, GeneratorError> { - let ts = TypeScript::new().with_indent(&self.config.indent); - let flags = super::sigil_emit::EmitFlags { + let plan = TypeScriptPlan::new(ir, self.emit_flags()); + self.generate_models_with_plan(ir, request_inputs, &plan) + } + + fn emit_flags(&self) -> super::sigil_emit::EmitFlags { + super::sigil_emit::EmitFlags { emit_enum_constants: self.config.emit_enum_constants, emit_type_guards: self.config.emit_type_guards, property_naming_camel_case: self.config.property_naming == super::config::PropertyNaming::CamelCase, - }; - let mut files = super::sigil_emit::generate_model_files(ir, flags, &ts)?; + untagged_union_strategy: self.config.untagged_union_strategy, + } + } + + fn generate_models_with_plan( + &self, + ir: &IrSpec, + request_inputs: &RequestInputPlan, + plan: &TypeScriptPlan<'_>, + ) -> Result, GeneratorError> { + let ts = TypeScript::new().with_indent(&self.config.indent); + let flags = plan.flags(); + let mut files = super::sigil_emit::generate_model_files_with_plan(plan, &ts)?; if !ir.schemas.is_empty() || !request_inputs.models().is_empty() { let mut names: Vec = ir.schemas.keys().cloned().collect(); @@ -75,7 +91,6 @@ impl TypeScriptFetchCodeGenerator { // produce identically-named type guard functions. let mut used_value_names: std::collections::HashSet = std::collections::HashSet::new(); - let convertible = super::sigil_emit::build_convertible_set(ir, flags); for name in &names { let type_name = name.to_pascal_case(); @@ -85,16 +100,12 @@ impl TypeScriptFetchCodeGenerator { let value_names = ir .schemas .get(name) - .map(|s| { - super::sigil_emit::value_exports_for_schema(s, flags, &convertible, ir) - }) + .map(|s| super::sigil_emit::value_exports_for_schema(s, flags, plan, ir)) .unwrap_or_default(); let extra_types = ir .schemas .get(name) - .map(|s| { - super::sigil_emit::extra_type_exports_for_schema(s, flags, &convertible) - }) + .map(|s| super::sigil_emit::extra_type_exports_for_schema(s, flags, plan)) .unwrap_or_default(); if value_names.is_empty() { @@ -157,45 +168,22 @@ impl TypeScriptFetchCodeGenerator { } for model in request_inputs.models() { - files.push(self.request_input_model_file(ir, model)); + files.push(self.request_input_model_file(ir, model, plan, &ts)?); } Ok(files) } - fn request_input_model_file(&self, ir: &IrSpec, model: &RequestInputModel) -> FileInfo { + fn request_input_model_file( + &self, + ir: &IrSpec, + model: &RequestInputModel, + plan: &TypeScriptPlan<'_>, + ts: &TypeScript, + ) -> Result { let header = super::project_files::render_file_header(&ir.info); - let mut imports: std::collections::BTreeMap> = - std::collections::BTreeMap::new(); - let mut needs_upload = false; - for field in &model.fields { - if field.is_upload() { - needs_upload = true; - } else { - collect_ts_model_imports(&field.type_expr, &mut imports); - } - } - - let mut content = String::new(); - content.push_str(&header); - if needs_upload { - content.push_str("import type { UploadFileInput } from '../runtime/runtime';\n"); - } - for (schema_name, type_names) in imports { - let filename = self.generate_filename(&schema_name); - let stem = filename.trim_end_matches(".ts"); - let names = type_names.into_iter().collect::>(); - content.push_str(&named_list("import type", &names, &format!("./{stem}"))); - content.push('\n'); - } - if needs_upload || !model.fields.is_empty() { - content.push('\n'); - } - - content.push_str(&format!( - "export interface {} {{\n", - model.name.to_pascal_case() - )); + let mut declaration = TypeSpec::builder(&model.name.to_pascal_case(), TypeKind::Interface) + .visibility(Visibility::Public); for field in &model.fields { let field_name = if self.config.property_naming == super::config::PropertyNaming::CamelCase { @@ -203,27 +191,44 @@ impl TypeScriptFetchCodeGenerator { } else { field.wire_name.clone() }; - let ty = request_input_field_ts_type(field); - let optional = if field.required { "" } else { "?" }; - content.push_str(&format!( - " {}{}: {};\n", - ts_property_name(&field_name), - optional, - ty - )); + let ty = if field.is_upload() { + TypeName::importable_type("../runtime/runtime", "UploadFileInput") + } else { + plan.value(&field.type_expr, Scope::model(&model.name)) + .public_type() + }; + let mut spec = FieldSpec::builder(&ts_property_name(&field_name), ty); + if !field.required { + spec = spec.is_optional(); + } + declaration = declaration.add_field(spec.build().map_err(|source| { + GeneratorError::emission(ArtifactKind::Model, &model.name, source) + })?); } - content.push_str("}\n"); - - FileInfo::model(self.generate_filename(&model.name), content) + let filename = self.generate_filename(&model.name); + let file = declaration + .build() + .and_then(|declaration| { + FileSpec::builder_with(&filename, ts.clone()) + .add_type(declaration) + .build() + }) + .and_then(|file| { + file.with_lang(super::layout::TypeScript::from(ts.clone())) + .render(100) + }) + .map_err(|source| GeneratorError::emission(ArtifactKind::Model, &model.name, source))?; + Ok(FileInfo::model(filename, format!("{header}{file}"))) } /// Generate ALL files from the IR spec. fn generate_ir(&self, ir: &IrSpec) -> Result, GeneratorError> { let mut files = Vec::new(); let request_inputs = plan_multipart_request_inputs(ir); + let plan = TypeScriptPlan::new(ir, self.emit_flags()); - files.extend(self.generate_apis_from_ir(ir, &request_inputs)?); - files.extend(self.generate_models_from_ir(ir, &request_inputs)?); + files.extend(self.generate_apis_with_plan(ir, &request_inputs, &plan)?); + files.extend(self.generate_models_with_plan(ir, &request_inputs, &plan)?); let base_path = ir .servers @@ -253,20 +258,17 @@ impl TypeScriptFetchCodeGenerator { /// Generate API files from IR operations via the sigil-stitch path. /// - /// `sigil_emit_api::generate_api_files` produces one `{Tag}Api.ts` per tag. + /// The API emitter produces one `{Tag}Api.ts` per tag from the same value plan. /// This wrapper adds the `apis/index.ts` aggregator. - fn generate_apis_from_ir( + fn generate_apis_with_plan( &self, - ir: &crate::ir::types::IrSpec, + ir: &IrSpec, request_inputs: &RequestInputPlan, + plan: &TypeScriptPlan<'_>, ) -> Result, GeneratorError> { let ts = TypeScript::new().with_indent(&self.config.indent); - let mut files = super::sigil_emit_api::generate_api_files( - ir, - self.config.property_naming == super::config::PropertyNaming::CamelCase, - request_inputs, - &ts, - )?; + let mut files = + super::sigil_emit_api::generate_api_files_with_plan(ir, request_inputs, plan, &ts)?; if !files.is_empty() { let mut plans = super::sigil_emit_api::collect_api_file_exports(ir); @@ -490,89 +492,6 @@ export default defineConfig({ } } -fn request_input_field_ts_type(field: &RequestInputField) -> String { - match field.kind { - RequestInputFieldKind::UploadFile { .. } => "UploadFileInput".to_string(), - RequestInputFieldKind::SchemaValue => ts_type_string(&field.type_expr), - } -} - -fn ts_type_string(expr: &IrTypeExpr) -> String { - match expr { - IrTypeExpr::Named(name) => name.to_pascal_case(), - IrTypeExpr::Primitive(p) => match p { - IrPrimitive::Binary => "Blob | File".to_string(), - IrPrimitive::String - | IrPrimitive::Date - | IrPrimitive::DateTime - | IrPrimitive::Uuid - | IrPrimitive::StringWithFormat(_) => "string".to_string(), - IrPrimitive::Integer - | IrPrimitive::IntegerWithFormat(_) - | IrPrimitive::Number - | IrPrimitive::NumberWithFormat(_) => "number".to_string(), - IrPrimitive::Boolean => "boolean".to_string(), - }, - IrTypeExpr::Array(inner) => format!("readonly {}[]", ts_type_string_nested(inner)), - IrTypeExpr::Nullable(inner) => { - format!("{} | null", parenthesize_union(&ts_type_string(inner))) - } - IrTypeExpr::StringLiteral(value) => { - format!("'{}'", value.replace('\\', "\\\\").replace('\'', "\\'")) - } - IrTypeExpr::StringEnum(values) => values - .iter() - .map(|value| format!("'{}'", value.replace('\\', "\\\\").replace('\'', "\\'"))) - .collect::>() - .join(" | "), - IrTypeExpr::Map(inner) => format!("Record", ts_type_string(inner)), - IrTypeExpr::Union(members) => members - .iter() - .map(ts_type_string) - .collect::>() - .join(" | "), - IrTypeExpr::Any => "unknown".to_string(), - } -} - -fn ts_type_string_nested(expr: &IrTypeExpr) -> String { - match expr { - IrTypeExpr::Array(inner) => format!("{}[]", ts_type_string_nested(inner)), - other => parenthesize_union(&ts_type_string(other)), - } -} - -fn parenthesize_union(ty: &str) -> String { - if ty.contains(" | ") { - format!("({ty})") - } else { - ty.to_string() - } -} - -fn collect_ts_model_imports( - expr: &IrTypeExpr, - imports: &mut std::collections::BTreeMap>, -) { - match expr { - IrTypeExpr::Named(name) => { - imports - .entry(name.clone()) - .or_default() - .insert(name.to_pascal_case()); - } - IrTypeExpr::Array(inner) | IrTypeExpr::Nullable(inner) | IrTypeExpr::Map(inner) => { - collect_ts_model_imports(inner, imports); - } - IrTypeExpr::Union(members) => { - for member in members { - collect_ts_model_imports(member, imports); - } - } - _ => {} - } -} - fn ts_property_name(name: &str) -> String { if is_js_identifier(name) { name.to_string() diff --git a/src/generators/typescript/fetch/config/mod.rs b/src/generators/typescript/fetch/config/mod.rs index a15b1e59e..26709e225 100644 --- a/src/generators/typescript/fetch/config/mod.rs +++ b/src/generators/typescript/fetch/config/mod.rs @@ -2,4 +2,4 @@ pub mod module; pub mod typescript_fetch_config; pub use module::TypeScriptModule; -pub use typescript_fetch_config::{PropertyNaming, TypeScriptFetchConfig}; +pub use typescript_fetch_config::{PropertyNaming, TypeScriptFetchConfig, UntaggedUnionStrategy}; diff --git a/src/generators/typescript/fetch/config/typescript_fetch_config.rs b/src/generators/typescript/fetch/config/typescript_fetch_config.rs index 7aa3ad577..1f2c0176f 100644 --- a/src/generators/typescript/fetch/config/typescript_fetch_config.rs +++ b/src/generators/typescript/fetch/config/typescript_fetch_config.rs @@ -31,6 +31,19 @@ pub enum Toolchain { Vp, } +/// Runtime selection policy for camelCase untagged-union converters. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub enum UntaggedUnionStrategy { + /// Convert the first structurally matching member; preserve unmatched input. + #[default] + FirstMatch, + /// Reject ambiguous or unmatched structural shapes at conversion time. + Strict, + /// Keep the union in its original wire representation without conversion. + Raw, +} + /// TypeScript Fetch generator-specific configuration #[derive(Debug, Clone, Serialize, Deserialize)] pub struct TypeScriptFetchConfig { @@ -89,6 +102,10 @@ pub struct TypeScriptFetchConfig { #[serde(default)] pub property_naming: PropertyNaming, + /// Selection behavior for untagged unions in camelCase mode. + #[serde(default)] + pub untagged_union_strategy: UntaggedUnionStrategy, + /// Toolchain for type checking and building. /// /// - `tsc` (default): bare TypeScript compiler. @@ -178,6 +195,7 @@ impl Default for TypeScriptFetchConfig { emit_enum_constants: false, emit_type_guards: false, property_naming: PropertyNaming::default(), + untagged_union_strategy: UntaggedUnionStrategy::default(), toolchain: Toolchain::default(), indent: default_indent(), } @@ -199,3 +217,29 @@ impl From for TypeScriptFetchConfig { } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn untagged_union_selection_policy_is_explicit_and_defaults_to_first_match() { + let default: TypeScriptFetchConfig = toml::from_str("").unwrap(); + assert_eq!( + default.untagged_union_strategy, + UntaggedUnionStrategy::FirstMatch + ); + for (value, expected) in [ + ("firstMatch", UntaggedUnionStrategy::FirstMatch), + ("strict", UntaggedUnionStrategy::Strict), + ("raw", UntaggedUnionStrategy::Raw), + ] { + let config: TypeScriptFetchConfig = + toml::from_str(&format!("untagged_union_strategy = '{value}'")).unwrap(); + assert_eq!(config.untagged_union_strategy, expected); + } + assert!( + toml::from_str::("untagged_union_strategy = 'unknown'").is_err() + ); + } +} diff --git a/src/generators/typescript/fetch/layout.rs b/src/generators/typescript/fetch/layout.rs index b89745362..038c7b29a 100644 --- a/src/generators/typescript/fetch/layout.rs +++ b/src/generators/typescript/fetch/layout.rs @@ -8,6 +8,7 @@ use sigil_stitch::error::SigilStitchError; use sigil_stitch::import::ImportGroup; use sigil_stitch::lang::capability::{FunctionContext, FunctionForm, LanguageCapabilities}; use sigil_stitch::lang::{CodeLang, RendererLang}; +use sigil_stitch::prelude::sigil_quote; use sigil_stitch::spec::modifiers::{DeclarationContext, TypeKind, Visibility}; use sigil_stitch::type_name::TypeName; @@ -17,15 +18,14 @@ const WIDTH: usize = 100; pub struct TypeScript(sigil_stitch::lang::typescript::TypeScript); impl TypeScript { - fn width(&self, block: &CodeBlock) -> usize { - block - .render_standalone(&self.0, usize::MAX) - .expect("valid TypeScript fragment") + fn width(&self, block: &CodeBlock) -> Result { + Ok(block + .render_standalone(&self.0, usize::MAX)? .lines() .next() .unwrap_or("") .chars() - .count() + .count()) } } @@ -492,9 +492,14 @@ impl CodeLang for TypeScript { let width = (if top { 0 } else { self.indent_unit().len() }) + prefix.chars().count() + 1 - + parameters.iter().map(|p| self.width(p)).sum::() + + parameters + .iter() + .map(|p| self.width(p)) + .collect::, _>>()? + .iter() + .sum::() + parameters.len().saturating_sub(1) * 2 - + self.width(&suffix); + + self.width(&suffix)?; let multiline = !parameters.is_empty() && width > WIDTH; cb.add("%L(", prefix); if multiline { @@ -551,7 +556,7 @@ impl TypeScript { depth: usize, ) -> Result { let fragment = CodeBlock::of("%T", ty.clone())?; - if column + self.width(&fragment) + 2 <= WIDTH { + if column + self.width(&fragment)? + 2 <= WIDTH { return Ok(fragment); } if let TypeName::Application { base, arguments } = ty { @@ -584,11 +589,36 @@ impl TypeScript { depth: usize, ) -> Result { let inline = CodeBlock::of("%T", ty.clone())?; + if let TypeName::Intersection(parts) = ty + && column + self.width(&inline)? > WIDTH + && let Some((first, rest)) = parts.split_first() + { + use sigil_stitch::code_node::CodeNode; + let mut result = + sigil_quote!(TypeScript { $C(self.type_part(first, column, depth)?) })?; + result = super::layout::inline(result); + for part in rest { + // The type-name renderer has no width-aware intersection layout. + // Keep each member semantic and add only connector/layout nodes. + result.nodes_mut().extend([ + CodeNode::Literal(" &".into()), + CodeNode::Newline, + CodeNode::Indent, + CodeNode::Nested(self.type_part(part, (depth + 1) * 2, depth + 1)?), + CodeNode::Dedent, + ]); + } + let mut spaced = sigil_quote!(TypeScript {})?; + spaced + .nodes_mut() + .extend([CodeNode::Literal(" ".into()), CodeNode::Nested(result)]); + return Ok(spaced); + } if let TypeName::Callable { parameters, returns, } = ty - && column + self.width(&inline) > WIDTH + && column + self.width(&inline)? > WIDTH { use sigil_stitch::spec::where_spec::{CallableParam, CallableParamPresence}; let mut cb = CodeBlock::builder(); @@ -624,7 +654,7 @@ impl TypeScript { ); return cb.build(); } - if column + self.width(&inline) <= WIDTH || !matches!(ty, TypeName::Union(_)) { + if column + self.width(&inline)? <= WIDTH || !matches!(ty, TypeName::Union(_)) { let mut cb = CodeBlock::builder(); cb.add(" ", ()); cb.add_code(self.type_part(ty, column, depth)?); @@ -641,12 +671,24 @@ impl TypeScript { } if let TypeName::Intersection(parts) = member { cb.add("| (", ()); - let multiline = 5 + self.width(&CodeBlock::of("%T", member.clone())?) > WIDTH; + let multiline = 5 + self.width(&CodeBlock::of("%T", member.clone())?)? > WIDTH; for (i, part) in parts.iter().enumerate() { if i > 0 { cb.add(if multiline { " &\n%>%>" } else { " & " }, ()); } - cb.add("%T", part.clone()); + cb.add_code(self.type_part( + part, + if multiline && i > 0 { + (depth + 3) * 2 + } else { + 5 + }, + if multiline && i > 0 { + depth + 3 + } else { + depth + 1 + }, + )?); if multiline && i > 0 { cb.add("%<%<", ()); } @@ -678,33 +720,233 @@ pub(super) fn inline(mut block: CodeBlock) -> CodeBlock { block } -pub(super) fn fragment_width(block: &CodeBlock) -> usize { - block +pub(super) fn fragment_width(block: &CodeBlock) -> Result { + Ok(block .render_standalone( &sigil_stitch::lang::typescript::TypeScript::new(), usize::MAX, - ) - .expect("valid TypeScript fragment") + )? .lines() .next() .unwrap_or("") .chars() - .count() + .count()) } -pub(super) fn return_object(fields: &[String], cast: Option<&str>, column: usize) -> String { - let suffix = cast.map_or_else(String::new, |ty| format!(" as {ty}")); - let inline = format!("return {{ {} }}{suffix};", fields.join(", ")); - if column + inline.chars().count() <= WIDTH { - inline +fn fits(block: &CodeBlock, column: usize) -> Result { + let rendered = block.render_standalone( + &TypeScript::from(sigil_stitch::lang::typescript::TypeScript::new()), + usize::MAX, + )?; + Ok( + !rendered.trim_end().contains('\n') + && column + rendered.trim_end().chars().count() <= WIDTH, + ) +} + +fn delimited( + mut prefix: CodeBlock, + open: &'static str, + close: &'static str, + items: &[CodeBlock], + multiline: bool, +) -> CodeBlock { + use sigil_stitch::code_node::CodeNode; + // sigil-stitch has no expression-list layout API; quoted object braces and + // parenthesized argument lists discard their boundary newlines. Only their + // static delimiters/whitespace live here. Items remain semantic quote trees. + let nodes = prefix.nodes_mut(); + nodes.push(CodeNode::Literal(open.into())); + if multiline { + nodes.extend([CodeNode::Newline, CodeNode::Indent]); + } + for (index, item) in items.iter().enumerate() { + if index > 0 && !multiline { + nodes.push(CodeNode::Literal(", ".into())); + } + nodes.push(CodeNode::Nested(inline(item.clone()))); + if multiline { + nodes.extend([CodeNode::Literal(",".into()), CodeNode::Newline]); + } + } + if multiline { + nodes.push(CodeNode::Dedent); + } + nodes.push(CodeNode::Literal(close.into())); + prefix +} + +pub(super) fn call( + function: CodeBlock, + arguments: &[CodeBlock], + column: usize, +) -> Result { + let flat = delimited(inline(function.clone()), "(", ")", arguments, false); + if fits(&flat, column)? { + return Ok(flat); + } + Ok(delimited(inline(function), "(", ")", arguments, true)) +} + +pub(super) fn block_arrow(mut parameters: CodeBlock, body: CodeBlock) -> CodeBlock { + use sigil_stitch::code_node::CodeNode; + // As with expression lists above, quotes do not retain the opening newline + // of a block-bodied arrow nested inside call parentheses. + parameters = inline(parameters); + parameters.nodes_mut().extend([ + CodeNode::Literal(" => {".into()), + CodeNode::Newline, + CodeNode::Indent, + CodeNode::Nested(body), + CodeNode::Dedent, + CodeNode::Literal("}".into()), + ]); + parameters +} + +pub(super) fn array(items: &[CodeBlock], column: usize) -> Result { + let flat = delimited(sigil_quote!(TypeScript {})?, "[", "]", items, false); + if fits(&flat, column)? { + return Ok(flat); + } + Ok(delimited( + sigil_quote!(TypeScript {})?, + "[", + "]", + items, + true, + )) +} + +pub(super) fn const_value( + name: &str, + value: CodeBlock, + column: usize, +) -> Result { + use sigil_stitch::code_node::CodeNode; + let flat = sigil_quote!(TypeScript { const $N(name) = $C(inline(value.clone())); })?; + if fits(&flat, column)? { + return Ok(flat); + } + // Local bindings have no declaration spec. Preserve the quoted identifier + // and expression, adding only the unsupported continuation indentation. + let mut prefix = inline(sigil_quote!(TypeScript { const $N(name) = })?); + prefix.nodes_mut().extend([ + CodeNode::Newline, + CodeNode::Indent, + CodeNode::Nested(inline(value)), + CodeNode::Literal(";".into()), + CodeNode::Newline, + CodeNode::Dedent, + ]); + Ok(prefix) +} + +pub(super) fn indexed_assertion( + value: CodeBlock, + ty: TypeName, + key: &str, + column: usize, +) -> Result { + use sigil_stitch::code_node::CodeNode; + let flat = inline(sigil_quote!(TypeScript { ($C(value.clone()) as $T(ty.clone()))[$S(key)] })?); + if fits(&flat, column)? { + return Ok(flat); + } + let mut head = inline(sigil_quote!(TypeScript { ($C(value.clone()) as $T(ty.clone())) })?); + if fits(&head, column)? { + // A wide computed index, unlike an argument list, has no trailing comma. + // Its string key stays quoted semantically inside the layout group. + head.nodes_mut().extend([ + CodeNode::Literal("[".into()), + CodeNode::Newline, + CodeNode::Indent, + CodeNode::Nested(inline(sigil_quote!(TypeScript { $S(key) })?)), + CodeNode::Newline, + CodeNode::Dedent, + CodeNode::Literal("]".into()), + ]); + return Ok(head); + } + let lang = TypeScript::from(sigil_stitch::lang::typescript::TypeScript::new()); + let mut assertion = inline(sigil_quote!(TypeScript { $C(value) as })?); + let ty = lang.type_expression( + &ty, + column + 2 + lang.width(&assertion)? + 1, + column / 2 + 1, + )?; + assertion.nodes_mut().push(CodeNode::Nested(ty)); + // Quotes have no parenthesized-expression layout node. Keep the assertion's + // name/type and the indexed string semantic; only group punctuation is raw. + let mut group = sigil_quote!(TypeScript {})?; + group.nodes_mut().extend([ + CodeNode::Literal("(".into()), + CodeNode::Newline, + CodeNode::Indent, + CodeNode::Nested(assertion), + CodeNode::Newline, + CodeNode::Dedent, + CodeNode::Literal(")".into()), + ]); + Ok(inline(sigil_quote!(TypeScript { $C(group)[$S(key)] })?)) +} + +pub(super) fn type_guard( + name: &str, + parent: TypeName, + narrowed: TypeName, + body: CodeBlock, +) -> Result { + use sigil_stitch::code_node::CodeNode; + let lang = TypeScript::from(sigil_stitch::lang::typescript::TypeScript::new()); + let prefix = inline(sigil_quote!(TypeScript { export function $N(name) })?); + let parameter = inline(sigil_quote!(TypeScript { value: $T(parent) })?); + let suffix = inline(sigil_quote!(TypeScript { : value is $T(narrowed.clone()) })?); + let header = delimited( + prefix.clone(), + "(", + ")", + std::slice::from_ref(¶meter), + false, + ); + let multiline = lang.width(&header)? + lang.width(&suffix)? + 2 > WIDTH; + let mut header = delimited(prefix, "(", ")", &[parameter], multiline); + let column = if multiline { 1 } else { lang.width(&header)? } + ": value is".len() + 1; + // FunSpec has no semantic type-predicate return slot. The small `value is` + // delimiter is the only raw syntax; both types and the body stay structured. + header.nodes_mut().extend([ + CodeNode::Literal(": value is".into()), + CodeNode::Nested(lang.type_expression(&narrowed, column, 0)?), + CodeNode::Literal(" {".into()), + CodeNode::Newline, + CodeNode::Indent, + CodeNode::Nested(body), + CodeNode::Dedent, + CodeNode::Literal("}".into()), + CodeNode::Newline, + ]); + Ok(header) +} + +pub(super) fn return_object( + fields: &[CodeBlock], + cast: Option, + column: usize, +) -> Result { + let object = delimited(sigil_quote!(TypeScript {})?, "{ ", " }", fields, false); + let flat = if let Some(cast) = &cast { + sigil_quote!(TypeScript { return $C(object) as $T(cast.clone()); })? } else { - format!( - "return {{\n{}}}{suffix};", - fields - .iter() - .map(|field| format!(" {field},\n")) - .collect::() - ) + sigil_quote!(TypeScript { return $C(object); })? + }; + if fits(&flat, column)? { + return Ok(flat); + } + let object = delimited(sigil_quote!(TypeScript {})?, "{", "}", fields, true); + if let Some(cast) = cast { + sigil_quote!(TypeScript { return $C(object) as $T(cast); }) + } else { + sigil_quote!(TypeScript { return $C(object); }) } } @@ -713,3 +955,54 @@ impl From for TypeScript { Self(lang) } } + +#[cfg(test)] +mod tests { + use super::*; + use sigil_stitch::assert_rendered; + + #[test] + fn expression_layout_keeps_semantic_imports_and_object_punctuation() { + let converter = TypeName::importable("./Detail", "detailFromJSON"); + let argument = sigil_quote!(TypeScript { json as unknown as $T(TypeName::importable_type("./Detail", "Detail$Wire")) }).unwrap(); + let call = call( + sigil_quote!(TypeScript { $T(converter) }).unwrap(), + &[argument], + 80, + ) + .unwrap(); + let field = sigil_quote!(TypeScript { [$S("payload")]: $C(call) }).unwrap(); + let block = return_object(&[field], Some(TypeName::primitive("Event")), 4).unwrap(); + assert_rendered!( + TypeScript::from(sigil_stitch::lang::typescript::TypeScript::new()), + width = 100, + block, + "import type { Detail$Wire } from './Detail';\nimport { detailFromJSON } from './Detail';\n\nreturn {\n ['payload']: detailFromJSON(\n json as unknown as Detail$Wire,\n ),\n} as Event;\n" + ); + } + + #[test] + fn indexed_assertion_breaks_the_key_when_the_cast_head_fits() { + let payload = TypeName::importable_type( + "./Payload", + "FooAdjacentPayloadFooInlineObjectPayloadValue$Wire", + ); + let ty = crate::generators::type_application( + TypeName::primitive("Record"), + vec![TypeName::string_literal("payload_value"), payload], + ); + let block = indexed_assertion( + sigil_quote!(TypeScript { json }).unwrap(), + ty, + "payload_value", + 8, + ) + .unwrap(); + assert_rendered!( + TypeScript::from(sigil_stitch::lang::typescript::TypeScript::new()), + width = 100, + block, + "import type { FooAdjacentPayloadFooInlineObjectPayloadValue$Wire } from './Payload';\n\n(json as Record<'payload_value', FooAdjacentPayloadFooInlineObjectPayloadValue$Wire>)[\n 'payload_value'\n]\n" + ); + } +} diff --git a/src/generators/typescript/fetch/matching.rs b/src/generators/typescript/fetch/matching.rs new file mode 100644 index 000000000..18cbf3fdc --- /dev/null +++ b/src/generators/typescript/fetch/matching.rs @@ -0,0 +1,513 @@ +//! Static, shared predicates for the value plan's structural matching shapes. +//! +//! Small functions keep selection code readable and directly formatter-stable; +//! no schema objects or interpreter are shipped to the generated client. + +use heck::{ToLowerCamelCase, ToPascalCase}; +use sigil_stitch::error::SigilStitchError; +use sigil_stitch::lang::typescript::TypeScript; +use sigil_stitch::prelude::{CodeBlock, TypeName, sigil_quote}; +use sigil_stitch::spec::{ + file_spec::FileSpec, fun_spec::FunSpec, modifiers::Visibility, parameter_spec::ParameterSpec, +}; + +use crate::generators::type_application; +use crate::generators::union_match::{JsonShape, MatchScope, accepts_any, selection_shapes}; + +use super::layout; +use super::plan::Direction; + +#[derive(Default)] +pub(super) struct MatchPlan { + shapes: Vec, +} + +#[derive(Clone, Copy)] +enum MatchOutcome { + Unmatched, + Fallback, + Concrete, +} + +impl MatchOutcome { + // Numeric literals are the private generated matcher protocol, not public API. + fn literal(self) -> &'static str { + match self { + Self::Unmatched => "0", + Self::Fallback => "1", + Self::Concrete => "2", + } + } +} + +impl MatchPlan { + pub(super) fn references(&self) -> std::collections::BTreeSet { + let mut references = std::collections::BTreeSet::new(); + for shape in &self.shapes { + crate::generators::union_match::referenced_shapes(shape, &mut references); + } + references + } + fn constant(shape: &JsonShape) -> Option { + match shape { + JsonShape::Any => Some(true), + JsonShape::Never => Some(false), + JsonShape::AnyOf(_) | JsonShape::AllOf(_) | JsonShape::Nullable(_) + if accepts_any(shape) => + { + Some(true) + } + JsonShape::Literal(serde_json::Value::Array(_) | serde_json::Value::Object(_)) => { + Some(false) + } + _ => None, + } + } + + fn callback( + &self, + shape: &JsonShape, + direction: Direction, + ) -> Result { + match Self::constant(shape) { + Some(true) => sigil_quote!(TypeScript { () => true }), + Some(false) => sigil_quote!(TypeScript { () => false }), + None => sigil_quote!(TypeScript { $T(self.symbol(shape, direction, true)) }), + } + } + pub(super) fn register(&mut self, shape: &JsonShape) { + if self.shapes.contains(shape) { + return; + } + self.shapes.push(shape.clone()); + match shape { + JsonShape::Array(inner) | JsonShape::Map(inner) | JsonShape::Nullable(inner) => { + self.register(inner) + } + JsonShape::MapWithConcreteValue { values, concrete } => { + self.register(&JsonShape::Map(values.clone())); + self.register(concrete); + } + JsonShape::Object(properties) => { + for property in properties { + self.register(&property.shape); + } + } + JsonShape::ObjectWithAdditional { properties, values } => { + self.register(&JsonShape::Object(properties.clone())); + self.register(values); + } + JsonShape::AnyOf(children) | JsonShape::AllOf(children) => { + for child in children { + self.register(child); + } + } + _ => {} + } + } + + fn symbol(&self, shape: &JsonShape, direction: Direction, local: bool) -> TypeName { + let index = self + .shapes + .iter() + .position(|candidate| candidate == shape) + .expect("matching shape was registered by the value plan"); + // Schema-derived symbols cannot contain `$`, so helper names cannot + // collide with named recursive matchers or public model converters. + let name = format!("matches${index}{}", direction.suffix()); + if local { + TypeName::primitive(&name) + } else { + TypeName::importable("../runtime/unionMatch", &name) + } + } + + pub(super) fn condition( + &self, + shape: &JsonShape, + value: CodeBlock, + direction: Direction, + local: bool, + ) -> Result { + match Self::constant(shape) { + Some(true) => return sigil_quote!(TypeScript { true }), + Some(false) => return sigil_quote!(TypeScript { false }), + None => {} + } + let function = self.symbol(shape, direction, local); + layout::call(sigil_quote!(TypeScript { $T(function) })?, &[value], 6) + } + + fn body( + &self, + shape: &JsonShape, + direction: Direction, + record: &TypeName, + ) -> Result { + let candidate = || sigil_quote!(TypeScript { candidate }); + let record_type = type_application( + record.clone(), + vec![ + TypeName::primitive("string"), + TypeName::primitive("unknown"), + ], + ); + let object_guard = sigil_quote!(TypeScript { + if (candidate === null || typeof candidate !== $S("object") || Array.isArray(candidate)) { + return false; + } + })?; + match shape { + JsonShape::Any => sigil_quote!(TypeScript { return true; }), + JsonShape::Never => sigil_quote!(TypeScript { return false; }), + JsonShape::Null => sigil_quote!(TypeScript { return candidate === null; }), + JsonShape::Nullable(inner) => { + let condition = self.condition(inner, candidate()?, direction, true)?; + sigil_quote!(TypeScript { return candidate === null || $C(condition); }) + } + JsonShape::Boolean => { + sigil_quote!(TypeScript { return typeof candidate === $S("boolean"); }) + } + JsonShape::String => { + sigil_quote!(TypeScript { return typeof candidate === $S("string"); }) + } + JsonShape::Number { integer } => { + let predicate = if *integer { "isInteger" } else { "isFinite" }; + sigil_quote!(TypeScript { return typeof candidate === $S("number") && Number.$N(predicate)(candidate); }) + } + JsonShape::Binary => sigil_quote!(TypeScript { + if (typeof Blob !== $S("undefined") && candidate instanceof Blob) { return true; } + return typeof File !== $S("undefined") && candidate instanceof File; + }), + JsonShape::Literal(value) => match value { + serde_json::Value::Null => sigil_quote!(TypeScript { return candidate === null; }), + serde_json::Value::Bool(true) => { + sigil_quote!(TypeScript { return candidate === true; }) + } + serde_json::Value::Bool(false) => { + sigil_quote!(TypeScript { return candidate === false; }) + } + serde_json::Value::String(value) => { + sigil_quote!(TypeScript { return candidate === $S(value); }) + } + serde_json::Value::Number(value) => { + // Exact JSON numeric tokens are not identifiers or strings. + sigil_quote!(TypeScript { return candidate === $L(value.to_string()); }) + } + _ => sigil_quote!(TypeScript { return false; }), + }, + JsonShape::Reference { schema, scope, .. } => { + let function = TypeName::primitive(&format!( + "match{}{}", + schema.to_pascal_case(), + direction.suffix() + )); + match scope { + MatchScope::Concrete => { + sigil_quote!(TypeScript { return $T(function)(candidate) === $L(MatchOutcome::Concrete.literal()); }) + } + MatchScope::Fallback => { + sigil_quote!(TypeScript { return $T(function)(candidate) === $L(MatchOutcome::Fallback.literal()); }) + } + MatchScope::Any => { + sigil_quote!(TypeScript { return $T(function)(candidate) > $L(MatchOutcome::Unmatched.literal()); }) + } + } + } + JsonShape::Array(inner) => { + if accepts_any(inner) { + return sigil_quote!(TypeScript { return Array.isArray(candidate); }); + } + let item = self.callback(inner, direction)?; + sigil_quote!(TypeScript { + if (!Array.isArray(candidate)) { return false; } + return candidate.every($C(item)); + }) + } + JsonShape::Map(inner) => { + let item = self.callback(inner, direction)?; + sigil_quote!(TypeScript { + $C(object_guard) + $if(accepts_any(inner)) { return true; } + $else { return Object.values(candidate).every($C(item)); } + }) + } + JsonShape::MapWithConcreteValue { values, concrete } => { + let values = self.condition( + &JsonShape::Map(values.clone()), + candidate()?, + direction, + true, + )?; + let concrete = self.callback(concrete, direction)?; + sigil_quote!(TypeScript { + if (!$C(values)) { return false; } + return Object.values(candidate as $T(record_type)).some($C(concrete)); + }) + } + JsonShape::Object(properties) => { + let mut checks = Vec::new(); + for property in properties { + let key = if direction == Direction::ToWire && property.rename { + property.wire_name.to_lower_camel_case() + } else { + property.wire_name.clone() + }; + let owns = layout::call( + sigil_quote!(TypeScript { Object.prototype.hasOwnProperty.call })?, + &[candidate()?, sigil_quote!(TypeScript { $S(key.clone()) })?], + 6, + )?; + let any = accepts_any(&property.shape); + let check = self.condition( + &property.shape, + sigil_quote!(TypeScript { object[$S(key)] })?, + direction, + true, + )?; + checks.push(if property.required { + sigil_quote!(TypeScript { + if (!$C(owns)) { return false; } + $if(!any) { if (!$C(check)) { return false; } } + })? + } else if any { + continue; + } else { + let item_check = self.condition( + &property.shape, + sigil_quote!(TypeScript { item })?, + direction, + true, + )?; + sigil_quote!(TypeScript { + if ($C(owns)) { + const item = object[$S(key)]; + if (item !== undefined && !$C(item_check)) { return false; } + } + })? + }); + } + let typed = properties + .iter() + .any(|property| !accepts_any(&property.shape)); + sigil_quote!(TypeScript { + $C(object_guard) + $if(typed) { const object = candidate as $T(record_type); } + $C_each(checks) + return true; + }) + } + JsonShape::ObjectWithAdditional { properties, values } => { + let object = self.condition( + &JsonShape::Object(properties.clone()), + candidate()?, + direction, + true, + )?; + if accepts_any(values) { + return sigil_quote!(TypeScript { return $C(object); }); + } + let item = self.callback(values, direction)?; + let item_check = + self.condition(values, sigil_quote!(TypeScript { item })?, direction, true)?; + let keys: Vec<_> = properties + .iter() + .map(|property| { + if direction == Direction::ToWire && property.rename { + property.wire_name.to_lower_camel_case() + } else { + property.wire_name.clone() + } + }) + .collect(); + let retained = direction == Direction::ToWire + && properties.iter().any(|property| { + property.rename + && property.wire_name != property.wire_name.to_lower_camel_case() + }); + let retained_type = + TypeName::union(vec![record_type.clone(), TypeName::primitive("undefined")]); + sigil_quote!(TypeScript { + if (!$C(object)) { return false; } + const object = candidate as $T(record_type); + const declared = [$for(key in &keys; separator = ", ") { $S(key) }]; + for (const [key, item] of Object.entries(object)) { + if (!declared.includes(key) && !$C(item_check)) { return false; } + } + $if(retained) { + const metadata = Symbol.for($S("openapi-nexus.additionalProperties")); + const descriptor = Object.getOwnPropertyDescriptor(candidate, metadata); + const retained = descriptor?.value as $T(retained_type); + if (retained !== undefined) { return Object.values(retained).every($C(item)); } + } + return true; + }) + } + JsonShape::AnyOf(children) | JsonShape::AllOf(children) => { + if accepts_any(shape) { + return sigil_quote!(TypeScript { return true; }); + } + let disjunction = matches!(shape, JsonShape::AnyOf(_)); + let checks = children + .iter() + .filter(|child| disjunction || !accepts_any(child)) + .map(|child| { + let condition = self.condition(child, candidate()?, direction, true)?; + if disjunction { + sigil_quote!(TypeScript { if ($C(condition)) { return true; } }) + } else { + sigil_quote!(TypeScript { if (!$C(condition)) { return false; } }) + } + }) + .collect::, _>>()?; + sigil_quote!(TypeScript { + $C_each(checks) + $if(disjunction) { return false; } $else { return true; } + }) + } + } + } + + pub(super) fn file( + &self, + recursive: &std::collections::BTreeMap, + record: &TypeName, + ts: &TypeScript, + ) -> Result, SigilStitchError> { + if self + .shapes + .iter() + .all(|shape| Self::constant(shape).is_some()) + && recursive.is_empty() + { + return Ok(None); + } + let mut file = FileSpec::builder_with("unionMatch.ts", ts.clone()); + for shape in &self.shapes { + if Self::constant(shape).is_some() { + continue; + } + for direction in [Direction::FromWire, Direction::ToWire] { + let function = FunSpec::builder(&format!( + "matches${}{}", + self.shapes.iter().position(|item| item == shape).unwrap(), + direction.suffix() + )) + .visibility(Visibility::Public) + .add_param(ParameterSpec::of( + "candidate", + TypeName::primitive("unknown"), + )) + .returns(vec![TypeName::primitive("boolean")]) + .body(self.body(shape, direction, record)?) + .build()?; + file = file.add_function(function); + } + } + for (name, shape) in recursive { + let (concrete, fallback) = selection_shapes(shape.clone()); + for direction in [Direction::FromWire, Direction::ToWire] { + let candidate = sigil_quote!(TypeScript { candidate })?; + let concrete_condition = + self.condition(&concrete, candidate.clone(), direction, true)?; + let fallback_condition = self.condition(&fallback, candidate, direction, true)?; + let body = sigil_quote!(TypeScript { + $if(concrete != JsonShape::Never) { if ($C(concrete_condition)) { return $L(MatchOutcome::Concrete.literal()); } } + $if(accepts_any(&fallback)) { return $L(MatchOutcome::Fallback.literal()); } + $else { + $if(fallback != JsonShape::Never) { if ($C(fallback_condition)) { return $L(MatchOutcome::Fallback.literal()); } } + return $L(MatchOutcome::Unmatched.literal()); + } + })?; + file = file.add_function( + FunSpec::builder(&format!( + "match{}{}", + name.to_pascal_case(), + direction.suffix() + )) + .visibility(Visibility::Public) + .add_param(ParameterSpec::of( + "candidate", + TypeName::primitive("unknown"), + )) + .returns(vec![TypeName::primitive("number")]) + .body(body) + .build()?, + ); + } + } + file.build().map(Some) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use sigil_stitch::assert_rendered; + + #[test] + fn union_literal_and_numeric_guards_render_exactly() { + let plan = MatchPlan::default(); + let integer = plan + .body( + &JsonShape::Number { integer: true }, + Direction::FromWire, + &TypeName::primitive("Record"), + ) + .unwrap(); + assert_rendered!( + TypeScript::new(), + integer, + "return typeof candidate === 'number' && Number.isInteger(candidate);\n" + ); + let boolean = plan + .body( + &JsonShape::Literal(false.into()), + Direction::FromWire, + &TypeName::primitive("Record"), + ) + .unwrap(); + assert_rendered!(TypeScript::new(), boolean, "return candidate === false;\n"); + } + + #[test] + fn binary_union_guard_renders_blob_and_file_checks_exactly() { + let plan = MatchPlan::default(); + for direction in [Direction::FromWire, Direction::ToWire] { + let body = plan + .body( + &JsonShape::Binary, + direction, + &TypeName::primitive("Record"), + ) + .unwrap(); + assert_rendered!( + TypeScript::new(), + body, + "if (typeof Blob !== 'undefined' && candidate instanceof Blob) {\n return true;\n}\nreturn typeof File !== 'undefined' && candidate instanceof File;\n" + ); + } + } + + #[test] + fn open_dictionary_guard_requires_at_least_one_concrete_value() { + let shape = JsonShape::MapWithConcreteValue { + values: Box::new(JsonShape::Any), + concrete: Box::new(JsonShape::String), + }; + let mut plan = MatchPlan::default(); + plan.register(&shape); + let body = plan + .body( + &shape, + Direction::FromWire, + &TypeName::qualified("globalThis", "Record"), + ) + .unwrap(); + assert_rendered!( + layout::TypeScript::from(TypeScript::new()), + body, + "if (!matches$1FromJSON(candidate)) {\n return false;\n}\nreturn Object.values(candidate as globalThis.Record).some(matches$3FromJSON);\n" + ); + } +} diff --git a/src/generators/typescript/fetch/mod.rs b/src/generators/typescript/fetch/mod.rs index 68c82f674..ebd18de31 100644 --- a/src/generators/typescript/fetch/mod.rs +++ b/src/generators/typescript/fetch/mod.rs @@ -1,9 +1,12 @@ pub mod codegen; pub mod config; mod layout; +mod matching; +mod plan; pub mod project_files; pub mod sigil_emit; pub mod sigil_emit_api; +mod tagged; pub use codegen::TypeScriptFetchCodeGenerator; pub use config::{TypeScriptFetchConfig, TypeScriptModule}; diff --git a/src/generators/typescript/fetch/plan.rs b/src/generators/typescript/fetch/plan.rs new file mode 100644 index 000000000..8ae58ca87 --- /dev/null +++ b/src/generators/typescript/fetch/plan.rs @@ -0,0 +1,1004 @@ +//! One value-planning seam for model declarations, converters and HTTP payloads. +//! +//! Symbol requirements, public/wire representations and runtime effects are +//! deliberately separate facts. In particular an identity representation does +//! not erase a strict union check, and raw changes a union's public projection. + +use std::borrow::Cow; +use std::collections::{BTreeMap, BTreeSet, HashSet}; + +use heck::{ToLowerCamelCase, ToPascalCase}; +use sigil_stitch::error::SigilStitchError; +use sigil_stitch::prelude::{CodeBlock, TypeName, sigil_quote}; +use sigil_stitch::spec::{ + file_spec::FileSpec, fun_spec::FunSpec, modifiers::Visibility, parameter_spec::ParameterSpec, +}; + +use crate::generators::type_application; +use crate::generators::union_match::{ + JsonShape, UnionMemberMatch, plan_named_shape, plan_union_members, referenced_shapes, + selection_shapes, +}; +use crate::ir::types::{IrPrimitive, IrSchemaKind, IrSpec, IrTypeExpr, IrUnion}; + +use super::config::UntaggedUnionStrategy; +use super::layout; +use super::matching::MatchPlan; +use super::sigil_emit::{EmitFlags, build_type_alias, fn_base_name}; + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub(super) enum Direction { + FromWire, + ToWire, +} + +impl Direction { + pub(super) fn source(self) -> &'static str { + match self { + Self::FromWire => "json", + Self::ToWire => "value", + } + } + pub(super) fn suffix(self) -> &'static str { + match self { + Self::FromWire => "FromJSON", + Self::ToWire => "ToJSON", + } + } + pub(super) fn source_key(self, property: &crate::ir::types::IrProperty) -> String { + match self { + Self::FromWire => property.name.clone(), + Self::ToWire => property.name.to_lower_camel_case(), + } + } + pub(super) fn target_key(self, property: &crate::ir::types::IrProperty) -> String { + match self { + Self::FromWire => property.name.to_lower_camel_case(), + Self::ToWire => property.name.clone(), + } + } +} + +#[derive(Clone, Copy)] +pub(super) struct Scope<'a> { + current: Option<&'a str>, + models: &'static str, + readonly_arrays: bool, + current_helper: Option, + nested_arrays: bool, + readonly_input: bool, +} + +impl<'a> Scope<'a> { + pub(super) fn model(name: &'a str) -> Self { + Self { + current: Some(name), + models: "./", + readonly_arrays: true, + current_helper: None, + nested_arrays: false, + readonly_input: false, + } + } + pub(super) fn api() -> Self { + Self { + current: None, + models: "../models/", + readonly_arrays: false, + current_helper: None, + nested_arrays: false, + readonly_input: false, + } + } + + fn helpers(readonly_arrays: bool, nested_arrays: bool, index: usize) -> Self { + Self { + current: None, + models: "../models/", + readonly_arrays, + current_helper: Some(index), + nested_arrays, + readonly_input: false, + } + } +} + +#[derive(Clone, Copy)] +enum Representation { + Public, + Wire, +} + +#[derive(PartialEq)] +struct ValueHelper { + expr: IrTypeExpr, + readonly_arrays: bool, + nested_arrays: bool, +} + +pub(super) struct TypeScriptPlan<'a> { + ir: &'a IrSpec, + flags: EmitFlags, + converters: HashSet, + wire_symbols: HashSet, + unknown: HashSet, + unions: BTreeMap>, + recursive_shapes: BTreeMap, + local_type_names: HashSet, + conversions: Vec, + matching: MatchPlan, +} + +pub(super) struct ValuePlan<'a, 'ir> { + plan: &'a TypeScriptPlan<'ir>, + expr: &'a IrTypeExpr, + scope: Scope<'a>, +} + +pub(super) fn representation_is_unknown(expr: &IrTypeExpr, aliases: &HashSet) -> bool { + match expr.representation() { + IrTypeExpr::Any => true, + IrTypeExpr::Named(name) => aliases.contains(name), + IrTypeExpr::Nullable(inner) => representation_is_unknown(inner, aliases), + IrTypeExpr::Union(members) => members + .iter() + .any(|member| representation_is_unknown(member, aliases)), + _ => false, + } +} + +/// Normalize only the rendered representation. Candidate identity and runtime +/// effects remain in the value plan, even when integer | number renders number. +pub(super) fn union_type(members: Vec) -> TypeName { + fn flatten(members: Vec, out: &mut Vec) { + for member in members { + if let TypeName::Union(members) = member { + flatten(members, out); + } else if !out.contains(&member) { + out.push(member); + } + } + } + let mut out = Vec::new(); + flatten(members, &mut out); + if out.contains(&TypeName::primitive("unknown")) { + return TypeName::primitive("unknown"); + } + if out.len() == 1 { + out.pop().expect("one union type") + } else { + TypeName::union(out) + } +} + +impl<'a> TypeScriptPlan<'a> { + pub(super) fn new(ir: &'a IrSpec, flags: EmitFlags) -> Self { + let mut result = Self { + ir, + flags, + converters: HashSet::new(), + wire_symbols: HashSet::new(), + unknown: HashSet::new(), + unions: BTreeMap::new(), + recursive_shapes: BTreeMap::new(), + local_type_names: ir + .schemas + .keys() + .map(|name| name.to_pascal_case()) + .collect(), + conversions: Vec::new(), + matching: MatchPlan::default(), + }; + // Monotone worklists retain named-reference edges, including recursive + // payloads. No expression is expanded into an unbounded type tree. + loop { + let previous = ( + result.converters.len(), + result.wire_symbols.len(), + result.unknown.len(), + ); + for (name, schema) in &ir.schemas { + let unknown = match &schema.kind { + IrSchemaKind::Alias(expr) => result.is_unknown(expr), + IrSchemaKind::Union(union) => { + union.members.iter().any(|expr| result.is_unknown(expr)) + } + IrSchemaKind::Intersection(all) => { + !all.members.is_empty() + && all.members.iter().all(|expr| result.is_unknown(expr)) + } + _ => false, + }; + if unknown { + result.unknown.insert(name.clone()); + } + if !flags.property_naming_camel_case { + continue; + } + let has_symbols = match &schema.kind { + IrSchemaKind::Object(_) | IrSchemaKind::Union(_) => true, + IrSchemaKind::TaggedUnion(union) => !union.variants.is_empty(), + IrSchemaKind::Alias(expr) => { + result.references_wire(expr) || result.has_effect(expr) + } + IrSchemaKind::Intersection(all) => all + .members + .iter() + .any(|expr| result.references_wire(expr) || result.has_effect(expr)), + IrSchemaKind::Enum(_) => false, + }; + if has_symbols { + result.wire_symbols.insert(name.clone()); + } + let has_converter = has_symbols + || match &schema.kind { + IrSchemaKind::Alias(expr) => result.has_effect(expr), + IrSchemaKind::Intersection(all) => { + all.members.iter().any(|expr| result.has_effect(expr)) + } + _ => false, + }; + if has_converter { + result.converters.insert(name.clone()); + } + } + if previous + == ( + result.converters.len(), + result.wire_symbols.len(), + result.unknown.len(), + ) + { + break; + } + } + if flags.property_naming_camel_case + && flags.untagged_union_strategy != UntaggedUnionStrategy::Raw + { + let shapes: BTreeMap<_, _> = ir + .schemas + .keys() + .map(|name| (name.clone(), plan_named_shape(name, ir))) + .collect(); + let mut referenced = BTreeSet::new(); + for shape in shapes.values() { + referenced_shapes(shape, &mut referenced); + } + result.recursive_shapes = shapes + .into_iter() + .filter(|(name, _)| referenced.contains(name)) + .collect(); + for (name, schema) in &ir.schemas { + if let IrSchemaKind::Union(union) = &schema.kind { + result + .unions + .insert(name.clone(), plan_union_members(name, union, ir)); + } + } + } + // Plan compound expressions once, before any emitter runs. Runtime + // helpers retain model/API array projections and never become barrel + // exports. They only factor existing conversions into short functions. + let mut expressions = Vec::new(); + for schema in ir.schemas.values() { + match &schema.kind { + IrSchemaKind::Object(object) => { + expressions.extend( + object + .properties + .values() + .map(|property| (&property.type_expr, true)), + ); + expressions + .extend(object.additional_properties.iter().map(|expr| (expr, true))); + } + IrSchemaKind::Alias(expr) => expressions.push((expr, true)), + IrSchemaKind::Union(union) => { + expressions.extend(union.members.iter().map(|expr| (expr, true))) + } + IrSchemaKind::Intersection(all) => { + expressions.extend(all.members.iter().map(|expr| (expr, true))) + } + IrSchemaKind::TaggedUnion(union) => { + for variant in &union.variants { + expressions.push((&variant.content_type, true)); + expressions.extend( + variant + .additional_properties + .iter() + .map(|expr| (expr, true)), + ); + } + } + IrSchemaKind::Enum(_) => {} + } + } + for operation in &ir.operations { + if let Some(body) = &operation.request_body { + expressions.extend(body.content.values().map(|expr| (expr, false))); + } + for response in &operation.responses { + expressions.extend( + response + .content + .values() + .chain(response.item_content.values()) + .map(|expr| (expr, false)), + ); + } + } + for (expr, readonly) in expressions { + result.register_conversion(expr, readonly); + } + for schema in ir.schemas.values() { + if let IrSchemaKind::Intersection(all) = &schema.kind + && let Some(object) = + super::sigil_emit::open_intersection_object(all, ir, &result.unknown) + { + for property in object.properties.values() { + result.register_conversion(&property.type_expr, true); + } + if let Some(additional) = &object.additional_properties { + result.register_conversion(additional, true); + } + } + } + for operation in &ir.operations { + if let Some(body) = &operation.request_body { + for media in body.content.keys() { + if crate::generators::multipart::media_type_base(media) != "multipart/form-data" + { + continue; + } + if let Some(parts) = + crate::generators::multipart::multipart_parts_for_request_body( + body, media, ir, + ) + { + for part in parts { + if part.value_encoding + == crate::generators::multipart::MultipartValueEncoding::Json + { + result.register_conversion(&part.type_expr, false); + } + } + } + } + } + } + for members in result.unions.values() { + for member in members { + result.matching.register(&member.shape); + } + } + let mut required_matchers = result.matching.references(); + let mut pending_matchers = required_matchers.clone(); + while let Some(name) = pending_matchers.pop_first() { + let shape = result + .recursive_shapes + .get(&name) + .expect("productive reference has a named shape plan"); + let (concrete, fallback) = selection_shapes(shape.clone()); + result.matching.register(&concrete); + result.matching.register(&fallback); + let mut dependencies = BTreeSet::new(); + referenced_shapes(shape, &mut dependencies); + for dependency in dependencies { + if required_matchers.insert(dependency.clone()) { + pending_matchers.insert(dependency); + } + } + } + result + .recursive_shapes + .retain(|name, _| required_matchers.contains(name)); + result + } + + pub(super) fn ir(&self) -> &'a IrSpec { + self.ir + } + pub(super) fn flags(&self) -> EmitFlags { + self.flags + } + pub(super) fn has_converter(&self, name: &str) -> bool { + self.converters.contains(name) + } + pub(super) fn has_wire_type(&self, name: &str) -> bool { + self.wire_symbols.contains(name) + } + pub(super) fn unknown_aliases(&self) -> &HashSet { + &self.unknown + } + + pub(super) fn utility_type(&self, name: &str) -> TypeName { + if self.local_type_names.contains(name) { + TypeName::qualified("globalThis", name) + } else { + TypeName::primitive(name) + } + } + + pub(super) fn matching_file( + &self, + ts: &sigil_stitch::lang::typescript::TypeScript, + ) -> Result, SigilStitchError> { + self.matching + .file(&self.recursive_shapes, &self.utility_type("Record"), ts) + } + + fn register_conversion(&mut self, expr: &IrTypeExpr, readonly: bool) { + self.register_conversion_at(expr, readonly, false); + } + + /// Selection metadata is transparent to conversion, except that extracting + /// property nullability removed a real wrapper the delegate still needs. + /// Restore that wrapper without changing legacy public type projections. + /// Keep its inner finite facts: they decide whether null belongs to the + /// wrapper or must reach an already-nullable delegate's strict policy. + fn conversion_representation(expr: &IrTypeExpr) -> Cow<'_, IrTypeExpr> { + match expr { + IrTypeExpr::ScalarEnum { + representation, + representation_nullable, + .. + } => { + if *representation_nullable { + Cow::Owned(IrTypeExpr::Nullable(representation.clone())) + } else { + Self::conversion_representation(representation) + } + } + _ => Cow::Borrowed(expr), + } + } + + fn register_conversion_at(&mut self, expr: &IrTypeExpr, readonly: bool, nested: bool) { + if !self.has_effect(expr) { + return; + } + if matches!(expr, IrTypeExpr::ScalarEnum { .. }) { + self.register_conversion_at(&Self::conversion_representation(expr), readonly, nested); + return; + } + if !matches!( + expr, + IrTypeExpr::Array(_) + | IrTypeExpr::Map(_) + | IrTypeExpr::Nullable(_) + | IrTypeExpr::Union(_) + ) { + return; + } + let helper = ValueHelper { + expr: expr.clone(), + readonly_arrays: readonly, + nested_arrays: nested, + }; + if self.conversions.contains(&helper) { + return; + } + self.conversions.push(helper); + match expr { + IrTypeExpr::Array(inner) | IrTypeExpr::Map(inner) | IrTypeExpr::Nullable(inner) => { + let child_nested = match expr { + IrTypeExpr::Array(_) => true, + IrTypeExpr::Map(_) => false, + _ => nested, + }; + self.register_conversion_at(inner, readonly, child_nested); + } + IrTypeExpr::Union(members) => { + for member in + crate::generators::union_match::plan_inline_union_members(members, self.ir) + { + self.matching.register(&member.shape); + } + for member in members { + self.register_conversion_at(member, readonly, nested); + } + } + _ => {} + } + } + + fn conversion_index(&self, expr: &IrTypeExpr, scope: Scope<'_>) -> usize { + self.conversions + .iter() + .position(|helper| { + &helper.expr == expr + && helper.readonly_arrays == scope.readonly_arrays + && helper.nested_arrays == scope.nested_arrays + }) + .expect("compound conversion was registered by the value plan") + } + + fn helper_type(&self, expr: &IrTypeExpr, scope: Scope<'_>, wire: bool) -> TypeName { + let index = self.conversion_index(expr, scope); + let name = format!("Value${}{}", index, if wire { "Wire" } else { "" }); + if scope.current_helper == Some(index) { + TypeName::primitive(&name) + } else { + TypeName::importable_type(&Self::helper_module(index, scope), &name) + } + } + + fn helper_module(index: usize, scope: Scope<'_>) -> String { + let prefix = if scope.current_helper.is_some() { + "./" + } else { + "../runtime/" + }; + format!("{prefix}valueConversion${index}") + } + + fn converter_symbol( + &self, + expr: &IrTypeExpr, + scope: Scope<'_>, + direction: Direction, + ) -> TypeName { + let expr = Self::conversion_representation(expr); + match expr.as_ref() { + IrTypeExpr::Named(name) => { + let pascal = name.to_pascal_case(); + let symbol = format!("{}{}", fn_base_name(&pascal), direction.suffix()); + if scope.current == Some(name) { + TypeName::primitive(&symbol) + } else { + TypeName::importable(&format!("{}{pascal}", scope.models), &symbol) + } + } + _ => { + let index = self.conversion_index(&expr, scope); + let name = format!("convert${}{}", index, direction.suffix()); + if scope.current_helper == Some(index) { + TypeName::primitive(&name) + } else { + TypeName::importable(&Self::helper_module(index, scope), &name) + } + } + } + } + + pub(super) fn conversion_files( + &self, + ts: &sigil_stitch::lang::typescript::TypeScript, + ) -> Result, SigilStitchError> { + let mut files = Vec::with_capacity(self.conversions.len()); + for (index, helper) in self.conversions.iter().enumerate() { + // Separate dependency graphs: importing one model must not force + // unrelated model types or converters into its consumer's program. + let mut file = + FileSpec::builder_with(&format!("valueConversion${index}.ts"), ts.clone()); + let expr = &helper.expr; + let scope = Scope::helpers(helper.readonly_arrays, helper.nested_arrays, index); + for (wire, representation) in [ + (true, Representation::Wire), + (false, Representation::Public), + ] { + let name = format!("Value${index}{}", if wire { "Wire" } else { "" }); + file = file.add_type(build_type_alias( + &name, + self.type_name(expr, scope, representation, helper.nested_arrays), + None, + )?); + } + for direction in [Direction::FromWire, Direction::ToWire] { + let source = direction.source(); + // Converters only read inputs. Accept deeply readonly arrays + // (including multipart inputs) without making API results or + // the established readonly-outer/plain-inner model types wider. + let input = self.type_name( + expr, + Scope { + readonly_input: true, + ..scope + }, + match direction { + Direction::FromWire => Representation::Wire, + Direction::ToWire => Representation::Public, + }, + helper.nested_arrays, + ); + let output = self.helper_type(expr, scope, direction == Direction::ToWire); + let body = match expr { + IrTypeExpr::Array(inner) => { + let converter = self.converter_symbol( + inner, + Scope { + nested_arrays: true, + ..scope + }, + direction, + ); + sigil_quote!(TypeScript { return $N(source).map($T(converter)); })? + } + IrTypeExpr::Map(inner) => { + let converted = self.conversion( + inner, + sigil_quote!(TypeScript { item })?, + Scope { + nested_arrays: false, + ..scope + }, + direction, + )?; + let mapping = sigil_quote!(TypeScript { Object.entries($N(source) ?? {}).map(([key, item]) => [key, $C(converted)]) })?; + let value = layout::call( + sigil_quote!(TypeScript { Object.fromEntries })?, + &[mapping], + 9, + )?; + sigil_quote!(TypeScript { return $C(value); })? + } + IrTypeExpr::Nullable(inner) => { + // A wrapper adds null permission; it must not intercept + // null already owned by an inner union's strict policy. + let standalone_null = + !crate::generators::union_match::accepts_null(inner, self.ir); + let converted = self.conversion( + inner, + sigil_quote!(TypeScript { $N(source) })?, + scope, + direction, + )?; + sigil_quote!(TypeScript { + $if(standalone_null) { if ($N(source) == null) { return null; } } + return $C(converted); + })? + } + IrTypeExpr::Union(members) => self.union_body( + &IrUnion { + members: members.clone(), + nullable: false, + }, + scope, + output.clone(), + direction, + "inline", + false, + )?, + _ => unreachable!("only compound conversions have helpers"), + }; + file = file.add_function( + FunSpec::builder(&format!("convert${index}{}", direction.suffix())) + .visibility(Visibility::Public) + .add_param(ParameterSpec::of(source, input)) + .returns(vec![output]) + .body(body) + .build()?, + ); + } + files.push(file.build()?); + } + Ok(files) + } + + pub(super) fn value<'p>(&'p self, expr: &'p IrTypeExpr, scope: Scope<'p>) -> ValuePlan<'p, 'a> { + ValuePlan { + plan: self, + expr, + scope, + } + } + + pub(super) fn has_effect(&self, expr: &IrTypeExpr) -> bool { + if !self.flags.property_naming_camel_case { + return false; + } + match expr { + IrTypeExpr::Named(name) => self.has_converter(name), + IrTypeExpr::Array(inner) | IrTypeExpr::Map(inner) | IrTypeExpr::Nullable(inner) => { + self.has_effect(inner) + } + IrTypeExpr::ScalarEnum { representation, .. } => self.has_effect(representation), + IrTypeExpr::Union(_) => { + self.flags.untagged_union_strategy != UntaggedUnionStrategy::Raw + } + _ => false, + } + } + + fn references_wire(&self, expr: &IrTypeExpr) -> bool { + match expr { + IrTypeExpr::Named(name) => self.has_wire_type(name), + IrTypeExpr::Array(inner) | IrTypeExpr::Map(inner) | IrTypeExpr::Nullable(inner) => { + self.references_wire(inner) + } + IrTypeExpr::ScalarEnum { representation, .. } => self.references_wire(representation), + IrTypeExpr::Union(members) => members.iter().any(|expr| self.references_wire(expr)), + _ => false, + } + } + + pub(super) fn is_unknown(&self, expr: &IrTypeExpr) -> bool { + representation_is_unknown(expr, &self.unknown) + } + + fn type_name( + &self, + expr: &IrTypeExpr, + scope: Scope<'_>, + representation: Representation, + nested: bool, + ) -> TypeName { + match expr { + IrTypeExpr::Named(name) => { + let pascal = name.to_pascal_case(); + let symbol = + if matches!(representation, Representation::Wire) && self.has_wire_type(name) { + format!("{pascal}$Wire") + } else { + pascal.clone() + }; + if scope.current == Some(name) { + TypeName::primitive(&symbol) + } else { + TypeName::importable_type(&format!("{}{pascal}", scope.models), &symbol) + } + } + IrTypeExpr::Primitive(IrPrimitive::Binary) => TypeName::union(vec![ + TypeName::primitive("Blob"), + TypeName::primitive("File"), + ]), + IrTypeExpr::Primitive(primitive) => TypeName::primitive(match primitive { + IrPrimitive::Boolean => "boolean", + IrPrimitive::Integer + | IrPrimitive::IntegerWithFormat(_) + | IrPrimitive::Number + | IrPrimitive::NumberWithFormat(_) => "number", + _ => "string", + }), + IrTypeExpr::ScalarEnum { + representation: inner, + .. + } => self.type_name(inner, scope, representation, nested), + IrTypeExpr::StringLiteral(value) => TypeName::string_literal(value.clone()), + IrTypeExpr::StringEnum(values) => union_type( + values + .iter() + .map(|value| TypeName::string_literal(value.clone())) + .collect(), + ), + IrTypeExpr::Array(inner) => { + let element = self.type_name(inner, scope, representation, true); + if scope.readonly_input { + return type_application(self.utility_type("ReadonlyArray"), vec![element]); + } + // sigil-stitch cannot render adjacent readonly array modifiers. + // Preserve the existing readonly-outer/plain-inner contract. + if nested || !scope.readonly_arrays { + TypeName::array(element) + } else { + TypeName::readonly_array(element) + } + } + IrTypeExpr::Map(inner) => type_application( + self.utility_type("Record"), + vec![ + TypeName::primitive("string"), + self.type_name(inner, scope, representation, false), + ], + ), + IrTypeExpr::Nullable(inner) => { + if self.is_unknown(inner) { + TypeName::primitive("unknown") + } else { + union_type(vec![ + self.type_name(inner, scope, representation, nested), + TypeName::primitive("null"), + ]) + } + } + IrTypeExpr::Union(members) => { + if members.iter().any(|expr| self.is_unknown(expr)) { + return TypeName::primitive("unknown"); + } + let projection = if self.flags.property_naming_camel_case + && self.flags.untagged_union_strategy == UntaggedUnionStrategy::Raw + { + Representation::Wire + } else { + representation + }; + union_type( + members + .iter() + .map(|expr| self.type_name(expr, scope, projection, nested)) + .collect(), + ) + } + IrTypeExpr::Any => TypeName::primitive("unknown"), + } + } + + fn conversion( + &self, + expr: &IrTypeExpr, + access: CodeBlock, + scope: Scope<'_>, + direction: Direction, + ) -> Result { + self.conversion_at(expr, access, scope, direction, 9) + } + + fn conversion_at( + &self, + expr: &IrTypeExpr, + access: CodeBlock, + scope: Scope<'_>, + direction: Direction, + column: usize, + ) -> Result { + if !self.has_effect(expr) { + return Ok(access); + } + let function = self.converter_symbol(expr, scope, direction); + layout::call( + sigil_quote!(TypeScript { $T(function) })?, + &[access], + column, + ) + } + + pub(super) fn union_body( + &self, + union: &IrUnion, + scope: Scope<'_>, + output: TypeName, + direction: Direction, + label: &str, + named: bool, + ) -> Result { + let source = direction.source(); + if self.flags.untagged_union_strategy == UntaggedUnionStrategy::Raw { + return sigil_quote!(TypeScript { return $N(source); }); + } + let members = if named { + self.unions + .get(scope.current.unwrap_or(label)) + .cloned() + .unwrap_or_default() + } else { + crate::generators::union_match::plan_inline_union_members(&union.members, self.ir) + }; + let strict = self.flags.untagged_union_strategy == UntaggedUnionStrategy::Strict; + let mut checks = Vec::new(); + let mut concrete = Vec::new(); + let mut fallback = Vec::new(); + let mut terminal = None; + for member in members { + let check = self.matching.condition( + &member.shape, + sigil_quote!(TypeScript { candidate })?, + direction, + false, + )?; + let condition = if strict && !member.fallback { + let index = checks.len(); + checks.push(check); + // sigil-stitch literal arguments take text rather than usize. + sigil_quote!(TypeScript { matches[$L(index.to_string())] })? + } else { + check + }; + let conversion_expr = Self::conversion_representation(&member.type_expr); + let input_type = if matches!( + conversion_expr.as_ref(), + IrTypeExpr::Array(_) + | IrTypeExpr::Map(_) + | IrTypeExpr::Nullable(_) + | IrTypeExpr::Union(_) + ) && self.has_effect(&member.type_expr) + { + self.helper_type(&conversion_expr, scope, direction == Direction::FromWire) + } else { + self.type_name( + &member.type_expr, + scope, + match direction { + Direction::FromWire => Representation::Wire, + Direction::ToWire => Representation::Public, + }, + scope.nested_arrays, + ) + }; + let value = self.conversion( + &member.type_expr, + sigil_quote!(TypeScript { $N(source) as unknown as $T(input_type) })?, + scope, + direction, + )?; + if member.shape == JsonShape::Any { + terminal = Some(sigil_quote!(TypeScript { return $C(value); })?); + break; + } + let branch = sigil_quote!(TypeScript { if ($C(condition)) { return $C(value); } })?; + if member.fallback { + fallback.push(branch); + } else { + concrete.push(branch); + } + } + let ambiguous = format!("Ambiguous {label} untagged union value"); + let unmatched = format!("No matching {label} untagged union member"); + let needs_candidate = !concrete.is_empty() || !fallback.is_empty() || union.nullable; + let terminal = match terminal { + Some(terminal) => terminal, + None if strict => sigil_quote!(TypeScript { throw new TypeError($S(unmatched)); })?, + None => sigil_quote!(TypeScript { return $N(source) as unknown as $T(output); })?, + }; + // A standalone null allowance must not bypass original concrete member + // counts or their delegates, including nested strict errors. + let checked_candidates = layout::array(&checks, 18)?; + sigil_quote!(TypeScript { + $if(needs_candidate) { const candidate: $T(TypeName::primitive("unknown")) = $N(source); } + $if(strict && !checks.is_empty()) { + const matches = $C(checked_candidates); + if (matches.filter(Boolean).length > 1) { throw new TypeError($S(ambiguous)); } + } + $C_each(concrete) + $if(union.nullable) { if (candidate === null) { return null; } } + $C_each(fallback) + $C(terminal) + }) + } +} + +impl ValuePlan<'_, '_> { + pub(super) fn convert_at( + &self, + access: CodeBlock, + direction: Direction, + column: usize, + ) -> Result { + self.plan + .conversion_at(self.expr, access, self.scope, direction, column) + } + pub(super) fn accepts_null(&self) -> bool { + crate::generators::union_match::accepts_null(self.expr, self.plan.ir) + } + + pub(super) fn public_type(&self) -> TypeName { + self.plan + .type_name(self.expr, self.scope, Representation::Public, false) + } + pub(super) fn wire_type(&self) -> TypeName { + self.plan + .type_name(self.expr, self.scope, Representation::Wire, false) + } + pub(super) fn convert( + &self, + access: CodeBlock, + direction: Direction, + ) -> Result { + self.plan + .conversion(self.expr, access, self.scope, direction) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn restoring_a_nullable_conversion_wrapper_keeps_inner_finite_facts() { + for inner_values in [vec![1.into()], vec![1.into(), serde_json::Value::Null]] { + let inner = IrTypeExpr::Nullable(Box::new(IrTypeExpr::Named("InnerChoice".into()))) + .with_scalar_enum(&inner_values, true); + let outer = IrTypeExpr::Nullable(Box::new(inner)) + .with_scalar_enum(&[1.into(), serde_json::Value::Null], true); + let (property, nullable) = outer.split_nullable(); + assert!(nullable); + let IrTypeExpr::ScalarEnum { representation, .. } = &property else { + panic!("outer finite constraint"); + }; + assert_eq!( + TypeScriptPlan::conversion_representation(&property).as_ref(), + &IrTypeExpr::Nullable(representation.clone()), + "the delegate must retain the inner finite node that owns null acceptance" + ); + } + } +} diff --git a/src/generators/typescript/fetch/project_files.rs b/src/generators/typescript/fetch/project_files.rs index 06d79cf20..c48b95cd1 100644 --- a/src/generators/typescript/fetch/project_files.rs +++ b/src/generators/typescript/fetch/project_files.rs @@ -31,7 +31,10 @@ export function uploadFileFilename(value: UploadFileInput, fallback: string): st } export function multipartHeaderValue(value: string): string { - return value.replace(/[\r\n]/g, '').replace(/\\/g, '\\\\').replace(/"/g, '\\"'); + return value + .replace(/[\r\n]/g, '') + .replace(/\\/g, '\\\\') + .replace(/"/g, '\\"'); } function isUploadFile(value: UploadFileInput): value is UploadFile { diff --git a/src/generators/typescript/fetch/sigil_emit.rs b/src/generators/typescript/fetch/sigil_emit.rs index ac1a2d03e..da40f8c5e 100644 --- a/src/generators/typescript/fetch/sigil_emit.rs +++ b/src/generators/typescript/fetch/sigil_emit.rs @@ -14,6 +14,7 @@ //! tagging styles, each variant narrows on the discriminator literal. use crate::codegen::traits::file_writer::FileInfo; +use crate::generators::closed_sum::is_tag_only_variant; use crate::generators::type_application; use crate::generators::{ArtifactKind, GeneratorError}; use crate::ir::types::{ @@ -29,19 +30,25 @@ use sigil_stitch::prelude::sigil_quote; use sigil_stitch::spec::field_spec::FieldSpec; use sigil_stitch::spec::file_spec::FileSpec; use sigil_stitch::spec::fun_spec::FunSpec; -use sigil_stitch::spec::import_spec::ImportSpec; use sigil_stitch::spec::modifiers::{TypeKind, Visibility}; use sigil_stitch::spec::parameter_spec::ParameterSpec; use sigil_stitch::spec::type_spec::TypeSpec; use sigil_stitch::type_name::TypeName; use std::collections::HashSet; +use super::config::UntaggedUnionStrategy; +use super::plan::{ + Direction as JsonDirection, Scope, TypeScriptPlan, + representation_is_unknown as type_expr_collapses_to_unknown, union_type as union_typename, +}; + /// Flags controlling optional TS emissions (const objects, type guards). #[derive(Debug, Clone, Copy, Default)] pub struct EmitFlags { pub emit_enum_constants: bool, pub emit_type_guards: bool, pub property_naming_camel_case: bool, + pub untagged_union_strategy: UntaggedUnionStrategy, } /// Convert a PascalCase name to its lowerCamelCase function base name. @@ -56,10 +63,10 @@ pub(super) fn fn_base_name(pascal: &str) -> String { /// - Enums with `emit_enum_constants`: the type name itself (the const object). /// - Tagged unions with `emit_type_guards`: one `is{Variant}` per contentful variant. /// - Everything else: empty. -pub fn value_exports_for_schema( +pub(super) fn value_exports_for_schema( schema: &IrSchema, flags: EmitFlags, - convertible: &HashSet, + plan: &TypeScriptPlan<'_>, ir: &IrSpec, ) -> Vec { match &schema.kind { @@ -96,13 +103,13 @@ pub fn value_exports_for_schema( exports } IrSchemaKind::Alias(_) | IrSchemaKind::Intersection(_) - if convertible.contains(&schema.name) => + if plan.has_converter(&schema.name) => { let pascal = schema.name.to_pascal_case(); let base = fn_base_name(&pascal); vec![format!("{base}FromJSON"), format!("{base}ToJSON")] } - IrSchemaKind::Union(_) if convertible.contains(&schema.name) => { + IrSchemaKind::Union(_) if plan.has_converter(&schema.name) => { let pascal = schema.name.to_pascal_case(); let base = fn_base_name(&pascal); vec![format!("{base}FromJSON"), format!("{base}ToJSON")] @@ -134,10 +141,10 @@ pub fn value_exports_for_schema( /// Return extra type-export names for the barrel (beyond the primary type name). /// /// Objects in camelCase mode also export `Name$Wire`. -pub fn extra_type_exports_for_schema( +pub(super) fn extra_type_exports_for_schema( schema: &IrSchema, flags: EmitFlags, - convertible: &HashSet, + plan: &TypeScriptPlan<'_>, ) -> Vec { match &schema.kind { IrSchemaKind::Object(_) if flags.property_naming_camel_case => { @@ -149,11 +156,11 @@ pub fn extra_type_exports_for_schema( vec![format!("{}$Wire", schema.name.to_pascal_case())] } IrSchemaKind::Alias(_) | IrSchemaKind::Intersection(_) - if convertible.contains(&schema.name) => + if plan.has_wire_type(&schema.name) => { vec![format!("{}$Wire", schema.name.to_pascal_case())] } - IrSchemaKind::Union(_) if convertible.contains(&schema.name) => { + IrSchemaKind::Union(_) if plan.has_wire_type(&schema.name) => { vec![format!("{}$Wire", schema.name.to_pascal_case())] } _ => vec![], @@ -161,26 +168,40 @@ pub fn extra_type_exports_for_schema( } /// Emit a TypeScript model file for an IR schema. Dispatches on `schema.kind`. -pub fn emit_model_file( +pub(super) fn emit_model_file( schema: &IrSchema, ir: &IrSpec, flags: EmitFlags, - convertible: &HashSet, + plan: &TypeScriptPlan<'_>, unknown_aliases: &HashSet, ts: &TypeScript, -) -> Option { +) -> Result, SigilStitchError> { + if let IrSchemaKind::Union(union) = &schema.kind + && flags.property_naming_camel_case + && plan.has_converter(&schema.name) + { + return emit_union_file_camel_case( + schema, + union, + plan, + unknown_aliases, + flags.untagged_union_strategy, + ts, + ) + .map(Some); + } match &schema.kind { IrSchemaKind::Object(obj) => { - emit_object_file(schema, obj, flags, convertible, unknown_aliases, ts) + emit_object_file(schema, obj, flags, plan, unknown_aliases, ts).map(Some) } - IrSchemaKind::Enum(en) => emit_enum_file_from(schema, en, flags, ts), - IrSchemaKind::Alias(expr) => emit_alias_file(schema, expr, convertible, ts), - IrSchemaKind::Union(u) => emit_union_file(schema, u, flags, convertible, ts), + IrSchemaKind::Enum(en) => Ok(emit_enum_file_from(schema, en, flags, ts)), + IrSchemaKind::Alias(expr) => emit_alias_file(schema, expr, plan, ts).map(Some), + IrSchemaKind::Union(u) => emit_union_file(schema, u, plan, ts).map(Some), IrSchemaKind::Intersection(i) => { - emit_intersection_file(schema, ir, i, flags, convertible, unknown_aliases, ts) + emit_intersection_file(schema, ir, i, flags, plan, unknown_aliases, ts) } IrSchemaKind::TaggedUnion(tu) => { - emit_tagged_union_file(schema, ir, tu, flags, convertible, unknown_aliases, ts) + emit_tagged_union_file(schema, ir, tu, flags, plan, unknown_aliases, ts) } } } @@ -197,14 +218,14 @@ fn emit_object_file( schema: &IrSchema, obj: &IrObject, flags: EmitFlags, - convertible: &HashSet, + plan: &TypeScriptPlan<'_>, unknown_aliases: &HashSet, ts: &TypeScript, -) -> Option { +) -> Result { let name = schema.name.to_pascal_case(); if flags.property_naming_camel_case { - return emit_object_file_camel_case(schema, obj, &name, convertible, unknown_aliases, ts); + return emit_object_file_camel_case(schema, obj, &name, plan, unknown_aliases, ts); } let mut tb = TypeSpec::builder(&name, TypeKind::Interface).visibility(Visibility::Public); @@ -213,19 +234,18 @@ fn emit_object_file( } for (_json_name, prop) in &obj.properties { - tb = tb.add_field(build_field(prop, &schema.name, unknown_aliases)?); + tb = tb.add_field(build_field(prop, &schema.name, plan, unknown_aliases)?); } if let Some(dictionary) = - additional_properties_super_type(obj, &schema.name, false, convertible, unknown_aliases) + additional_properties_super_type(obj, &schema.name, false, plan, unknown_aliases) { tb = tb.extends(dictionary); } let filename = format!("{}.ts", name); FileSpec::builder_with(&filename, ts.clone()) - .add_type(tb.build().ok()?) + .add_type(tb.build()?) .build() - .ok() } /// Emit an object file with dual types: `Name$Wire` (wire format) + `Name` @@ -234,10 +254,10 @@ fn emit_object_file_camel_case( schema: &IrSchema, obj: &IrObject, name: &str, - convertible: &HashSet, + plan: &TypeScriptPlan<'_>, unknown_aliases: &HashSet, ts: &TypeScript, -) -> Option { +) -> Result { let wire_name = format!("{}$Wire", name); let filename = format!("{}.ts", name); @@ -248,15 +268,10 @@ fn emit_object_file_camel_case( wire_tb = wire_tb.doc(doc); } for (_json_name, prop) in &obj.properties { - wire_tb = wire_tb.add_field(build_field_wire( - prop, - &schema.name, - convertible, - unknown_aliases, - )?); + wire_tb = wire_tb.add_field(build_field_wire(prop, &schema.name, plan, unknown_aliases)?); } if let Some(dictionary) = - additional_properties_super_type(obj, &schema.name, true, convertible, unknown_aliases) + additional_properties_super_type(obj, &schema.name, true, plan, unknown_aliases) { wire_tb = wire_tb.extends(dictionary); } @@ -267,10 +282,15 @@ fn emit_object_file_camel_case( ergo_tb = ergo_tb.doc(doc); } for (_json_name, prop) in &obj.properties { - ergo_tb = ergo_tb.add_field(build_field_camel(prop, &schema.name, unknown_aliases)?); + ergo_tb = ergo_tb.add_field(build_field_camel( + prop, + &schema.name, + plan, + unknown_aliases, + )?); } if let Some(dictionary) = - additional_properties_super_type(obj, &schema.name, false, convertible, unknown_aliases) + additional_properties_super_type(obj, &schema.name, false, plan, unknown_aliases) { ergo_tb = ergo_tb.extends(dictionary); } @@ -281,29 +301,27 @@ fn emit_object_file_camel_case( &wire_name, &schema.name, obj, - convertible, + plan, unknown_aliases, JsonDirection::FromWire, - ) - .expect("TypeScript object FromJSON converter builds"); + )?; let to_json = build_json_converter( name, &wire_name, &schema.name, obj, - convertible, + plan, unknown_aliases, JsonDirection::ToWire, - ) - .expect("TypeScript object ToJSON converter builds"); + )?; let mut fb = FileSpec::builder_with(&filename, ts.clone()); - fb = fb.add_type(wire_tb.build().ok()?); - fb = fb.add_type(ergo_tb.build().ok()?); + fb = fb.add_type(wire_tb.build()?); + fb = fb.add_type(ergo_tb.build()?); fb = fb.add_function(from_json); fb = fb.add_function(to_json); - fb.build().ok() + fb.build() } /// A string index also covers the declared properties in TypeScript. Include @@ -314,7 +332,7 @@ fn additional_properties_super_type( obj: &IrObject, current_schema: &str, wire: bool, - convertible: &HashSet, + plan: &TypeScriptPlan<'_>, unknown_aliases: &HashSet, ) -> Option { let additional = obj.additional_properties.as_ref()?; @@ -322,9 +340,9 @@ fn additional_properties_super_type( if type_expr_collapses_to_unknown(expr, unknown_aliases) { TypeName::primitive("unknown") } else if wire { - type_expr_to_typename_wire(expr, current_schema, convertible) + type_expr_to_typename_wire(expr, current_schema, plan) } else { - type_expr_to_typename_for_schema(expr, current_schema) + plan.value(expr, Scope::model(current_schema)).public_type() } }; let mut members = vec![value_type(additional)]; @@ -352,15 +370,15 @@ fn additional_properties_super_type( fn build_field_wire( prop: &IrProperty, current_schema: &str, - convertible: &HashSet, + plan: &TypeScriptPlan<'_>, unknown_aliases: &HashSet, -) -> Option { +) -> Result { let field_name = if is_valid_ts_identifier(&prop.name) { prop.name.clone() } else { format!("'{}'", prop.name) }; - let inner_ty = type_expr_to_typename_wire(&prop.type_expr, current_schema, convertible); + let inner_ty = type_expr_to_typename_wire(&prop.type_expr, current_schema, plan); let field_ty = if prop.nullable { nullable_field_type_name(&prop.type_expr, inner_ty, unknown_aliases) } else { @@ -374,22 +392,25 @@ fn build_field_wire( if let Some(desc) = &prop.description { fb = fb.doc(desc); } - fb.build().ok() + fb.build() } /// Build a field for the ergonomic interface (camelCase property name). fn build_field_camel( prop: &IrProperty, current_schema: &str, + plan: &TypeScriptPlan<'_>, unknown_aliases: &HashSet, -) -> Option { +) -> Result { let camel = prop.name.to_lower_camel_case(); let field_name = if is_valid_ts_identifier(&camel) { camel } else { format!("'{}'", camel) }; - let inner_ty = type_expr_to_typename_for_schema(&prop.type_expr, current_schema); + let inner_ty = plan + .value(&prop.type_expr, Scope::model(current_schema)) + .public_type(); let field_ty = if prop.nullable { nullable_field_type_name(&prop.type_expr, inner_ty, unknown_aliases) } else { @@ -403,142 +424,16 @@ fn build_field_camel( if let Some(desc) = &prop.description { fb = fb.doc(desc); } - fb.build().ok() + fb.build() } -/// Like `type_expr_to_typename` but Named refs resolve to `Name$Wire` (only if convertible). +/// Named refs use `Name$Wire` when the value plan requires its wire declaration. fn type_expr_to_typename_wire( expr: &IrTypeExpr, current_schema: &str, - convertible: &HashSet, -) -> TypeName { - match expr { - IrTypeExpr::Named(name) => { - let ts_name = name.to_pascal_case(); - let rendered_name = if convertible.contains(name) { - let wire_name = format!("{}$Wire", ts_name); - wire_name - } else { - ts_name - }; - named_ref_to_typename(name, &rendered_name, Some(current_schema)) - } - IrTypeExpr::Array(inner) => TypeName::readonly_array(type_expr_to_typename_wire_nested( - inner, - current_schema, - convertible, - )), - IrTypeExpr::Nullable(inner) => union_typename(vec![ - type_expr_to_typename_wire(inner, current_schema, convertible), - TypeName::primitive("null"), - ]), - IrTypeExpr::Map(inner) => type_application( - TypeName::primitive("Record"), - vec![ - TypeName::primitive("string"), - type_expr_to_typename_wire(inner, current_schema, convertible), - ], - ), - IrTypeExpr::StringLiteral(s) => TypeName::string_literal(s.clone()), - IrTypeExpr::StringEnum(values) => union_typename( - values - .iter() - .map(|v| TypeName::string_literal(v.clone())) - .collect(), - ), - IrTypeExpr::Union(members) => union_typename( - members - .iter() - .map(|member| type_expr_to_typename_wire(member, current_schema, convertible)) - .collect(), - ), - IrTypeExpr::Primitive(p) => TypeName::primitive(primitive_to_ts(p)), - IrTypeExpr::Any => TypeName::primitive("unknown"), - } -} - -fn type_expr_to_typename_wire_nested( - expr: &IrTypeExpr, - current_schema: &str, - convertible: &HashSet, + plan: &TypeScriptPlan<'_>, ) -> TypeName { - match expr { - IrTypeExpr::Array(inner) => TypeName::array(type_expr_to_typename_wire_nested( - inner, - current_schema, - convertible, - )), - other => type_expr_to_typename_wire(other, current_schema, convertible), - } -} - -/// Render a type expression as a plain TS string (ergonomic / PascalCase names). -fn type_expr_str(expr: &IrTypeExpr) -> String { - match expr { - IrTypeExpr::Named(name) => name.to_pascal_case(), - IrTypeExpr::Primitive(p) => primitive_to_ts(p).to_string(), - IrTypeExpr::StringLiteral(s) => format!("'{s}'"), - IrTypeExpr::StringEnum(values) => { - simplify_union_strings(values.iter().map(|v| format!("'{v}'")).collect()) - } - IrTypeExpr::Array(inner) => { - let inner_str = type_expr_str(inner); - if is_compound_type(inner) { - format!("readonly ({inner_str})[]") - } else { - format!("readonly {inner_str}[]") - } - } - IrTypeExpr::Nullable(inner) => { - simplify_union_strings(vec![type_expr_str(inner), "null".to_string()]) - } - IrTypeExpr::Map(inner) => format!("Record", type_expr_str(inner)), - IrTypeExpr::Union(members) => { - simplify_union_strings(members.iter().map(type_expr_str).collect()) - } - IrTypeExpr::Any => "unknown".to_string(), - } -} - -/// Render a type expression as a plain TS string ($Wire variant — Named → Name$Wire only if convertible). -fn type_expr_str_wire(expr: &IrTypeExpr, convertible: &HashSet) -> String { - match expr { - IrTypeExpr::Named(name) => { - let pascal = name.to_pascal_case(); - if convertible.contains(name) { - format!("{pascal}$Wire") - } else { - pascal - } - } - IrTypeExpr::Primitive(p) => primitive_to_ts(p).to_string(), - IrTypeExpr::StringLiteral(s) => format!("'{s}'"), - IrTypeExpr::StringEnum(values) => { - simplify_union_strings(values.iter().map(|v| format!("'{v}'")).collect()) - } - IrTypeExpr::Array(inner) => { - let inner_str = type_expr_str_wire(inner, convertible); - if is_compound_type(inner) { - format!("readonly ({inner_str})[]") - } else { - format!("readonly {inner_str}[]") - } - } - IrTypeExpr::Nullable(inner) => simplify_union_strings(vec![ - type_expr_str_wire(inner, convertible), - "null".to_string(), - ]), - IrTypeExpr::Map(inner) => { - format!("Record", type_expr_str_wire(inner, convertible)) - } - IrTypeExpr::Union(members) => simplify_union_strings( - members - .iter() - .map(|m| type_expr_str_wire(m, convertible)) - .collect(), - ), - IrTypeExpr::Any => "unknown".to_string(), - } + plan.value(expr, Scope::model(current_schema)).wire_type() } fn simplify_union_strings(members: Vec) -> String { @@ -555,33 +450,6 @@ fn simplify_union_strings(members: Vec) -> String { out.join(" | ") } -fn union_typename(members: Vec) -> TypeName { - let mut out = Vec::new(); - let mut seen = HashSet::new(); - for member in members { - if type_name_is_unknown(&member) { - return TypeName::primitive("unknown"); - } - let key = format!("{member:?}"); - if seen.insert(key) { - out.push(member); - } - } - if out.len() == 1 { - out.pop().unwrap() - } else { - TypeName::union(out) - } -} - -fn type_name_is_unknown(type_name: &TypeName) -> bool { - match type_name { - TypeName::Primitive(value) | TypeName::Raw(value) => value == "unknown", - TypeName::Union(members) => members.iter().any(type_name_is_unknown), - _ => false, - } -} - fn nullable_field_type_name( expr: &IrTypeExpr, inner_ty: TypeName, @@ -594,55 +462,12 @@ fn nullable_field_type_name( } } -fn is_compound_type(expr: &IrTypeExpr) -> bool { - matches!( - expr, - IrTypeExpr::Nullable(_) | IrTypeExpr::Union(_) | IrTypeExpr::StringEnum(_) - ) -} - -#[derive(Clone, Copy, PartialEq, Eq)] -enum JsonDirection { - FromWire, - ToWire, -} - -impl JsonDirection { - fn source(self) -> &'static str { - match self { - Self::FromWire => "json", - Self::ToWire => "value", - } - } - - fn suffix(self) -> &'static str { - match self { - Self::FromWire => "FromJSON", - Self::ToWire => "ToJSON", - } - } - - fn source_key(self, prop: &IrProperty) -> String { - match self { - Self::FromWire => prop.name.clone(), - Self::ToWire => prop.name.to_lower_camel_case(), - } - } - - fn target_key(self, prop: &IrProperty) -> String { - match self { - Self::FromWire => prop.name.to_lower_camel_case(), - Self::ToWire => prop.name.clone(), - } - } -} - fn build_json_converter( name: &str, wire_name: &str, current_schema: &str, obj: &IrObject, - convertible: &HashSet, + plan: &TypeScriptPlan<'_>, unknown_aliases: &HashSet, direction: JsonDirection, ) -> Result { @@ -660,15 +485,20 @@ fn build_json_converter( &prop.type_expr, access.clone(), current_schema, - convertible, + plan, direction, )?; - if prop.nullable && needs_conversion(&prop.type_expr, convertible) { + if prop.nullable + && needs_conversion(&prop.type_expr, plan) + && !plan + .value(&prop.type_expr, Scope::model(current_schema)) + .accepts_null() + { value = sigil_quote!(TypeScript { $C(access.clone()) === null ? null : $C(value) })?; } - if !prop.required && needs_conversion(&prop.type_expr, convertible) { + if !prop.required && needs_conversion(&prop.type_expr, plan) { value = sigil_quote!(TypeScript { $C(access) !== undefined ? $C(value) : undefined })?; @@ -682,7 +512,7 @@ fn build_json_converter( }; let value = super::layout::inline(value); let format = - if 4 + key.chars().count() + 2 + super::layout::fragment_width(&value) + 1 > 100 { + if 4 + key.chars().count() + 2 + super::layout::fragment_width(&value)? + 1 > 100 { "%L:\n%>%L,%<\n" } else { "%L: %L,\n" @@ -699,9 +529,9 @@ fn build_json_converter( let condition = super::layout::inline(condition); let multiline = 4 + 4 - + super::layout::fragment_width(&condition) + + super::layout::fragment_width(&condition)? + 3 - + super::layout::fragment_width(&object) + + super::layout::fragment_width(&object)? + 7 > 100; CodeBlock::of( @@ -730,7 +560,7 @@ fn build_json_converter( && !obj .additional_properties .as_ref() - .is_some_and(|ty| needs_conversion(ty, convertible)) + .is_some_and(|ty| needs_conversion(ty, plan)) { sigil_quote!(TypeScript { return $N(source) as unknown as $T(TypeName::primitive(output)); })? } else if let Some(additional) = &obj.additional_properties { @@ -739,7 +569,7 @@ fn build_json_converter( additional, fields, current_schema, - convertible, + plan, unknown_aliases, direction, )? @@ -763,7 +593,7 @@ fn build_open_object_conversion( additional: &IrTypeExpr, fields: Vec, current_schema: &str, - convertible: &HashSet, + plan: &TypeScriptPlan<'_>, unknown_aliases: &HashSet, direction: JsonDirection, ) -> Result { @@ -786,10 +616,10 @@ fn build_open_object_conversion( let retain = direction == JsonDirection::FromWire && renamed; let metadata_key = "openapi-nexus.additionalProperties"; let additional_type = match direction { - JsonDirection::FromWire => { - type_expr_to_typename_wire(additional, current_schema, convertible) - } - JsonDirection::ToWire => type_expr_to_typename_for_schema(additional, current_schema), + JsonDirection::FromWire => type_expr_to_typename_wire(additional, current_schema, plan), + JsonDirection::ToWire => plan + .value(additional, Scope::model(current_schema)) + .public_type(), }; let retained_type = union_typename(vec![ type_application( @@ -804,7 +634,7 @@ fn build_open_object_conversion( additional, sigil_quote!(TypeScript { additionalValue })?, current_schema, - convertible, + plan, direction, )?; let keys = |keys: &[String]| { @@ -834,7 +664,7 @@ fn build_open_object_conversion( if cast_additional { cb.add(" as %T", additional_type); } - if matches!(additional, IrTypeExpr::Map(_)) && needs_conversion(additional, convertible) { + if matches!(additional, IrTypeExpr::Map(_)) && needs_conversion(additional, plan) { cb.add( ";\nconst convertedValue = %L;\nreturn [key, convertedValue] as const;\n%<}),\n%<);\n", super::layout::inline(converted), @@ -887,205 +717,16 @@ fn json_conversion_expr( expr: &IrTypeExpr, access: CodeBlock, current_schema: &str, - convertible: &HashSet, + plan: &TypeScriptPlan<'_>, direction: JsonDirection, ) -> Result { - match expr { - IrTypeExpr::Named(name) if convertible.contains(name) => { - let pascal = name.to_pascal_case(); - let function_name = format!("{}{}", fn_base_name(&pascal), direction.suffix()); - let function = if name == current_schema { - TypeName::primitive(&function_name) - } else { - TypeName::importable(&format!("./{pascal}"), &function_name) - }; - sigil_quote!(TypeScript { $T(function)($C(access)) }) - } - IrTypeExpr::Array(inner) if has_named_ref(inner, convertible) => { - let item = json_conversion_expr( - inner, - sigil_quote!(TypeScript { item })?, - current_schema, - convertible, - direction, - )?; - sigil_quote!(TypeScript { $C(access).map((item) => $C(item)) }) - } - IrTypeExpr::Nullable(inner) if has_named_ref(inner, convertible) => { - let value = json_conversion_expr( - inner, - access.clone(), - current_schema, - convertible, - direction, - )?; - sigil_quote!(TypeScript { $C(access) != null ? $C(value) : null }) - } - IrTypeExpr::Map(inner) if has_named_ref(inner, convertible) => { - let value = json_conversion_expr( - inner, - sigil_quote!(TypeScript { v })?, - current_schema, - convertible, - direction, - )?; - let args = vec![ - sigil_stitch::code_block::Arg::Code(super::layout::inline(access)), - sigil_stitch::code_block::Arg::Code(super::layout::inline(value)), - ]; - CodeBlock::of( - "Object.fromEntries(\n%>Object.entries(%L ?? {}).map(([k, v]) => [k, %L]),\n%<)", - args, - ) - } - _ => Ok(access), - } -} - -fn from_json_expr_inner(expr: &IrTypeExpr, access: &str, convertible: &HashSet) -> String { - match expr { - IrTypeExpr::Named(ref_name) if convertible.contains(ref_name) => { - let pascal = ref_name.to_pascal_case(); - let converter = format!("{}FromJSON", fn_base_name(&pascal)); - format!("{converter}({access})") - } - IrTypeExpr::Array(inner) if has_named_ref(inner, convertible) => { - let item_expr = from_json_expr_inner(inner, "item", convertible); - format!("{access}.map((item) => {item_expr})") - } - IrTypeExpr::Nullable(inner) if has_named_ref(inner, convertible) => { - let inner_expr = from_json_expr_inner(inner, access, convertible); - format!("{access} != null ? {inner_expr} : null") - } - IrTypeExpr::Map(inner) if has_named_ref(inner, convertible) => { - let val_expr = from_json_expr_inner(inner, "v", convertible); - format!( - "Object.fromEntries(Object.entries({access} ?? {{}}).map(([k, v]) => [k, {val_expr}]))" - ) - } - _ => access.to_string(), - } -} - -fn to_json_expr_inner(expr: &IrTypeExpr, access: &str, convertible: &HashSet) -> String { - match expr { - IrTypeExpr::Named(ref_name) if convertible.contains(ref_name) => { - let pascal = ref_name.to_pascal_case(); - let converter = format!("{}ToJSON", fn_base_name(&pascal)); - format!("{converter}({access})") - } - IrTypeExpr::Array(inner) if has_named_ref(inner, convertible) => { - let item_expr = to_json_expr_inner(inner, "item", convertible); - format!("{access}.map((item) => {item_expr})") - } - IrTypeExpr::Nullable(inner) if has_named_ref(inner, convertible) => { - let inner_expr = to_json_expr_inner(inner, access, convertible); - format!("{access} != null ? {inner_expr} : null") - } - IrTypeExpr::Map(inner) if has_named_ref(inner, convertible) => { - let val_expr = to_json_expr_inner(inner, "v", convertible); - format!( - "Object.fromEntries(Object.entries({access} ?? {{}}).map(([k, v]) => [k, {val_expr}]))" - ) - } - _ => access.to_string(), - } + plan.value(expr, Scope::model(current_schema)) + .convert(access, direction) } /// Returns true if the type expression contains a Named reference that needs conversion. -fn needs_conversion(expr: &IrTypeExpr, convertible: &HashSet) -> bool { - match expr { - IrTypeExpr::Named(name) => convertible.contains(name), - IrTypeExpr::Array(inner) => has_named_ref(inner, convertible), - IrTypeExpr::Nullable(inner) => has_named_ref(inner, convertible), - IrTypeExpr::Map(inner) => has_named_ref(inner, convertible), - _ => false, - } -} - -/// Returns true if a type expression (recursively) contains a convertible Named ref. -fn has_named_ref(expr: &IrTypeExpr, convertible: &HashSet) -> bool { - match expr { - IrTypeExpr::Named(name) => convertible.contains(name), - IrTypeExpr::Array(inner) | IrTypeExpr::Nullable(inner) | IrTypeExpr::Map(inner) => { - has_named_ref(inner, convertible) - } - _ => false, - } -} - -fn contains_any_named_ref(expr: &IrTypeExpr) -> bool { - match expr { - IrTypeExpr::Named(_) => true, - IrTypeExpr::Array(inner) | IrTypeExpr::Nullable(inner) | IrTypeExpr::Map(inner) => { - contains_any_named_ref(inner) - } - IrTypeExpr::Union(members) => members.iter().any(contains_any_named_ref), - IrTypeExpr::Primitive(_) - | IrTypeExpr::StringLiteral(_) - | IrTypeExpr::StringEnum(_) - | IrTypeExpr::Any => false, - } -} - -fn type_expr_collapses_to_unknown(expr: &IrTypeExpr, unknown_aliases: &HashSet) -> bool { - match expr { - IrTypeExpr::Any => true, - IrTypeExpr::Named(name) => unknown_aliases.contains(name), - IrTypeExpr::Nullable(inner) => type_expr_collapses_to_unknown(inner, unknown_aliases), - IrTypeExpr::Union(members) => members - .iter() - .any(|member| type_expr_collapses_to_unknown(member, unknown_aliases)), - IrTypeExpr::Array(_) - | IrTypeExpr::Map(_) - | IrTypeExpr::Primitive(_) - | IrTypeExpr::StringLiteral(_) - | IrTypeExpr::StringEnum(_) => false, - } -} - -fn build_unknown_alias_set(ir: &IrSpec) -> HashSet { - ir.schemas - .iter() - .filter_map(|(name, schema)| { - let renders_unknown = match &schema.kind { - IrSchemaKind::Alias(expr) => type_expr_str(expr) == "unknown", - IrSchemaKind::Union(union) => { - let mut members: Vec = - union.members.iter().map(type_expr_str).collect(); - if union.nullable { - members.push("null".to_string()); - } - simplify_union_strings(members) == "unknown" - } - _ => false, - }; - renders_unknown.then(|| name.clone()) - }) - .collect() -} - -fn collect_named_refs_from_expr( - expr: &IrTypeExpr, - refs: &mut HashSet, - convertible: &HashSet, -) { - match expr { - IrTypeExpr::Named(name) => { - if convertible.contains(name) { - refs.insert(name.clone()); - } - } - IrTypeExpr::Array(inner) | IrTypeExpr::Nullable(inner) | IrTypeExpr::Map(inner) => { - collect_named_refs_from_expr(inner, refs, convertible); - } - IrTypeExpr::Union(members) => { - for m in members { - collect_named_refs_from_expr(m, refs, convertible); - } - } - _ => {} - } +fn needs_conversion(expr: &IrTypeExpr, plan: &TypeScriptPlan<'_>) -> bool { + plan.has_effect(expr) } /// Enum file: `export type Name = 'a' | 'b' | 1 | 2 | null;` @@ -1186,29 +827,23 @@ fn is_valid_ts_identifier(s: &str) -> bool { fn emit_alias_file( schema: &IrSchema, expr: &IrTypeExpr, - convertible: &HashSet, + plan: &TypeScriptPlan<'_>, ts: &TypeScript, -) -> Option { +) -> Result { let name = schema.name.to_pascal_case(); - if convertible.contains(&schema.name) { + if plan.has_converter(&schema.name) { let wire_name = format!("{name}$Wire"); let mut file = FileSpec::builder_with(&format!("{name}.ts"), ts.clone()) - .add_type( - build_type_alias( - &wire_name, - type_expr_to_typename_wire(expr, &schema.name, convertible), - schema.description.as_deref(), - ) - .expect("convertible wire type alias builds"), - ) - .add_type( - build_type_alias( - &name, - type_expr_to_typename_for_schema(expr, &schema.name), - schema.description.as_deref(), - ) - .expect("convertible ergonomic type alias builds"), - ); + .add_type(build_type_alias( + &wire_name, + type_expr_to_typename_wire(expr, &schema.name, plan), + schema.description.as_deref(), + )?) + .add_type(build_type_alias( + &name, + plan.value(expr, Scope::model(&schema.name)).public_type(), + schema.description.as_deref(), + )?); for direction in [JsonDirection::FromWire, JsonDirection::ToWire] { let source = direction.source(); let (input, output) = match direction { @@ -1217,40 +852,30 @@ fn emit_alias_file( }; let value = json_conversion_expr( expr, - sigil_quote!(TypeScript { $N(source) }).expect("alias input builds"), + sigil_quote!(TypeScript { $N(source) })?, &schema.name, - convertible, + plan, direction, - ) - .expect("alias JSON conversion expression builds"); + )?; file = file.add_function( FunSpec::builder(&format!("{}{}", fn_base_name(&name), direction.suffix())) .visibility(Visibility::Public) .add_param(ParameterSpec::of(source, TypeName::primitive(input))) .returns(vec![TypeName::primitive(output)]) - .body( - sigil_quote!(TypeScript { return $C(value); }) - .expect("alias JSON converter body builds"), - ) - .build() - .expect("alias JSON converter builds"), + .body(sigil_quote!(TypeScript { return $C(value); })?) + .build()?, ); } - return file.build().ok(); + return file.build(); } - let rhs = type_expr_to_typename(expr); - - let type_alias = build_type_alias(&name, rhs, None).ok()?; - + let rhs = plan.value(expr, Scope::model(&schema.name)).public_type(); let filename = format!("{}.ts", name); - let mut fb = FileSpec::builder_with(&filename, ts.clone()); - if let Some(doc) = &schema.description { - fb = fb.add_raw(&format!("/** {doc} */\n")); - } - fb.add_type(type_alias).build().ok() + FileSpec::builder_with(&filename, ts.clone()) + .add_type(build_type_alias(&name, rhs, schema.description.as_deref())?) + .build() } -fn build_type_alias( +pub(super) fn build_type_alias( name: &str, target: TypeName, description: Option<&str>, @@ -1271,41 +896,29 @@ fn build_type_alias( fn emit_union_file( schema: &IrSchema, union: &IrUnion, - flags: EmitFlags, - convertible: &HashSet, + plan: &TypeScriptPlan<'_>, ts: &TypeScript, -) -> Option { +) -> Result { let name = schema.name.to_pascal_case(); - if flags.property_naming_camel_case && convertible.contains(&schema.name) { - if let [member] = union.members.as_slice() { - // A single non-null member is unambiguous even when lowering kept - // anyOf/oneOf as a union. Reuse alias conversion rather than the - // multi-member union's cast-through policy. - let expr = if union.nullable { - IrTypeExpr::Nullable(Box::new(member.clone())) - } else { - member.clone() - }; - return emit_alias_file(schema, &expr, convertible, ts); - } - return emit_union_file_camel_case(schema, union, &name, convertible, ts); - } - - let mut members: Vec = union.members.iter().map(type_expr_to_typename).collect(); + let mut members: Vec = union + .members + .iter() + .map(|expr| plan.value(expr, Scope::model(&schema.name)).public_type()) + .collect(); if union.nullable { members.push(TypeName::primitive("null")); } let union_ty = union_typename(members); - let type_alias = build_type_alias(&name, union_ty, None).ok()?; - let filename = format!("{}.ts", name); - let mut fb = FileSpec::builder_with(&filename, ts.clone()); - if let Some(doc) = &schema.description { - fb = fb.add_raw(&format!("/** {doc} */\n")); - } - fb.add_type(type_alias).build().ok() + FileSpec::builder_with(&filename, ts.clone()) + .add_type(build_type_alias( + &name, + union_ty, + schema.description.as_deref(), + )?) + .build() } /// Intersection file: `export type Name = A & B & C;` @@ -1317,31 +930,32 @@ fn emit_intersection_file( ir: &IrSpec, intersection: &IrIntersection, flags: EmitFlags, - convertible: &HashSet, + plan: &TypeScriptPlan<'_>, unknown_aliases: &HashSet, ts: &TypeScript, -) -> Option { +) -> Result, SigilStitchError> { if intersection.members.is_empty() { - return None; + return Ok(None); } let name = schema.name.to_pascal_case(); - if flags.property_naming_camel_case && convertible.contains(&schema.name) { + if flags.property_naming_camel_case && plan.has_converter(&schema.name) { return emit_intersection_file_camel_case( schema, ir, intersection, &name, - convertible, + plan, unknown_aliases, ts, - ); + ) + .map(Some); } let members: Vec = intersection .members .iter() - .map(type_expr_to_typename) + .map(|expr| plan.value(expr, Scope::model(&schema.name)).public_type()) .collect(); let inter_ty = TypeName::intersection(members); let inter_ty = if open_intersection_object(intersection, ir, unknown_aliases).is_some() { @@ -1350,15 +964,15 @@ fn emit_intersection_file( inter_ty }; - let type_alias = build_type_alias(&name, inter_ty, None).ok()?; - let filename = format!("{}.ts", name); - let mut fb = FileSpec::builder_with(&filename, ts.clone()); - if let Some(doc) = &schema.description { - // FileSpec has no structural doc slot for a code member. - fb = fb.add_raw(&format!("/** {doc} */\n")); - } - fb.add_type(type_alias).build().ok() + FileSpec::builder_with(&filename, ts.clone()) + .add_type(build_type_alias( + &name, + inter_ty, + schema.description.as_deref(), + )?) + .build() + .map(Some) } /// Tagged union file: discriminated `export type` across three tagging styles. @@ -1375,60 +989,24 @@ fn emit_tagged_union_file( ir: &IrSpec, tu: &IrTaggedUnion, flags: EmitFlags, - convertible: &HashSet, + plan: &TypeScriptPlan<'_>, unknown_aliases: &HashSet, ts: &TypeScript, -) -> Option { +) -> Result, SigilStitchError> { if tu.variants.is_empty() { - return None; + return Ok(None); } - let name = schema.name.to_pascal_case(); - if matches!(tu.tagging, TaggingStyle::External) && tu .variants .iter() .any(|variant| variant.additional_properties.is_some()) { - return emit_open_external_union_file( - schema, - ir, - tu, - flags, - convertible, - unknown_aliases, - ts, - ); - } - - if flags.property_naming_camel_case { - return emit_tagged_union_file_camel_case(schema, ir, tu, &name, flags, convertible, ts); - } - - let code = - tagged_union_type_alias_code(&name, ir, tu, &tu.discriminator_field, None, |expr| { - type_expr_str(expr) - })?; - - let filename = format!("{}.ts", name); - let mut fb = FileSpec::builder_with(&filename, ts.clone()); - if let Some(doc) = &schema.description { - fb = fb.add_raw(&format!("/** {doc} */\n")); - } - fb = fb.add_code(code); - for import in tagged_union_type_imports(ir, tu, convertible, false) { - fb = fb.add_import(import); - } - - if flags.emit_type_guards { - let guards = build_tagged_union_type_guards(&name, ir, tu, false); - for guard in guards { - fb = fb.add_code(guard); - } + return emit_open_external_union_file(schema, ir, tu, flags, plan, unknown_aliases, ts) + .map(Some); } - - fb.build().ok() + super::tagged::emit(schema, ir, tu, plan, ts).map(Some) } /// External tags are wire keys in both naming modes. Keep their outer maps @@ -1439,19 +1017,19 @@ fn emit_open_external_union_file( ir: &IrSpec, union: &IrTaggedUnion, flags: EmitFlags, - convertible: &HashSet, + plan: &TypeScriptPlan<'_>, unknown_aliases: &HashSet, ts: &TypeScript, -) -> Option { +) -> Result { let name = schema.name.to_pascal_case(); let wire_name = format!("{name}$Wire"); let value_type = |expr: &IrTypeExpr, wire: bool| { if type_expr_collapses_to_unknown(expr, unknown_aliases) { TypeName::primitive("unknown") } else if wire { - type_expr_to_typename_wire(expr, &schema.name, convertible) + type_expr_to_typename_wire(expr, &schema.name, plan) } else { - type_expr_to_typename_for_schema(expr, &schema.name) + plan.value(expr, Scope::model(&schema.name)).public_type() } }; let wrapper_type = |wire: bool| { @@ -1460,53 +1038,32 @@ fn emit_open_external_union_file( .variants .iter() .map(|variant| { - let content = value_type(&variant.content_type, wire); - let required = type_application( - TypeName::primitive("Readonly"), - vec![type_application( - TypeName::primitive("Record"), - vec![ - TypeName::string_literal(variant.discriminator_value.clone()), - content.clone(), - ], - )], - ); - if let Some(additional) = &variant.additional_properties { - TypeName::intersection(vec![ - required, - type_application( - TypeName::primitive("Readonly"), - vec![type_application( - TypeName::primitive("Record"), - vec![ - TypeName::primitive("string"), - union_typename(vec![value_type(additional, wire), content]), - ], - )], - ), - ]) - } else { - required - } + super::tagged::open_external_variant_type( + plan, + &variant.discriminator_value, + value_type(&variant.content_type, wire), + variant + .additional_properties + .as_ref() + .map(|expr| value_type(expr, wire)), + ) }) .collect(), ) }; let mut file = FileSpec::builder_with(&format!("{name}.ts"), ts.clone()); if flags.property_naming_camel_case { - file = file.add_type( - build_type_alias( - &wire_name, - wrapper_type(true), - schema.description.as_deref(), - ) - .expect("external wrapper wire alias builds"), - ); + file = file.add_type(build_type_alias( + &wire_name, + wrapper_type(true), + schema.description.as_deref(), + )?); } - file = file.add_type( - build_type_alias(&name, wrapper_type(false), schema.description.as_deref()) - .expect("external wrapper ergonomic alias builds"), - ); + file = file.add_type(build_type_alias( + &name, + wrapper_type(false), + schema.description.as_deref(), + )?); if flags.property_naming_camel_case { for direction in [JsonDirection::FromWire, JsonDirection::ToWire] { @@ -1517,542 +1074,168 @@ fn emit_open_external_union_file( } else { (&name, &wire_name) }; - let branches: Vec<_> = union.variants.iter().map(|variant| { + let branches = union.variants.iter().map(|variant| { let key = &variant.discriminator_value; // hasOwnProperty does not narrow a union containing a closed // alternative. Assert only the key just checked, not an index // signature on the entire wrapper. let content_record = type_application( - TypeName::primitive("Record"), + plan.utility_type("Record"), vec![TypeName::string_literal(key.clone()), value_type(&variant.content_type, wire)], ); let content = json_conversion_expr( &variant.content_type, - sigil_quote!(TypeScript { contentValue }).expect("external content input builds"), - &schema.name, convertible, direction, - ).expect("external content conversion builds"); + sigil_quote!(TypeScript { contentValue })?, + &schema.name, plan, direction, + )?; let additional = variant.additional_properties.as_ref().map(|expr| { let ty = value_type(expr, wire); let cast = !type_expr_collapses_to_unknown(expr, unknown_aliases); let converted = json_conversion_expr( expr, - sigil_quote!(TypeScript { additionalValue }).expect("external extension input builds"), - &schema.name, convertible, direction, - ).expect("external extension conversion builds"); - let mut cb = CodeBlock::builder(); - cb.add("const additionalProperties = Object.fromEntries(\n%>Object.entries(%N)\n%>.filter(([key]) => key !== %S)\n.map(([key, value]) => {\n%>const additionalValue = value", vec![sigil_stitch::code_block::Arg::Name(source.into()), sigil_stitch::code_block::Arg::StringLit(key.clone())]); - if cast { cb.add(" as %T", ty); } - cb.add(";\nreturn [key, %L] as const;\n%<}),\n%<%<);\n", super::layout::inline(converted)); - cb.build().expect("external extension collection builds") - }); - let mut cb = CodeBlock::builder(); - cb.begin_control_flow("if (Object.prototype.hasOwnProperty.call(%N, %S))", vec![sigil_stitch::code_block::Arg::Name(source.into()), sigil_stitch::code_block::Arg::StringLit(key.clone())]); - cb.add("const contentValue = (%N as %T)[%S];\n", vec![sigil_stitch::code_block::Arg::Name(source.into()), sigil_stitch::code_block::Arg::TypeName(content_record), sigil_stitch::code_block::Arg::StringLit(key.clone())]); - if let Some(additional) = additional { cb.add_code(additional); } - cb.add("return { ", ()); - if variant.additional_properties.is_some() { cb.add("...additionalProperties, ", ()); } - cb.add("[%S]: %L };\n", vec![sigil_stitch::code_block::Arg::StringLit(key.clone()), sigil_stitch::code_block::Arg::Code(super::layout::inline(content))]); - cb.end_control_flow(); - cb.build().expect("external wrapper conversion branch builds") - }).collect(); + sigil_quote!(TypeScript { additionalValue })?, + &schema.name, plan, direction, + )?; + let body = sigil_quote!(TypeScript { + const additionalValue = $if(cast) { value as $T(ty) } $else { value }; + return [key, $C(converted)] as const; + })?; + let arrow = super::layout::block_arrow(sigil_quote!(TypeScript { ([key, value]) })?, body); + let mapping = sigil_quote!(TypeScript { entries.map($C(arrow)) })?; + let collected = super::layout::call(sigil_quote!(TypeScript { Object.fromEntries })?, &[mapping], 33)?; + sigil_quote!(TypeScript { + const entries = Object.entries($N(source)).filter(([key]) => key !== $S(key)); + const additionalProperties = $C(collected); + }) + }).transpose()?; + let mut fields = Vec::new(); + if additional.is_some() { fields.push(sigil_quote!(TypeScript { ...additionalProperties })?); } + fields.push(sigil_quote!(TypeScript { [$S(key)]: $C(content) })?); + let returned = super::layout::return_object(&fields, None, 4)?; + sigil_quote!(TypeScript { + if (Object.prototype.hasOwnProperty.call($N(source), $S(key))) { + const contentValue = ($N(source) as $T(content_record))[$S(key)]; + $if(let Some(additional) = additional) { $C(additional) } + $C(returned) + } + }) + }).collect::, SigilStitchError>>()?; let body = sigil_quote!(TypeScript { $C_each(branches) throw new Error($S("Unknown variant")); - }) - .expect("external wrapper converter body builds"); + })?; file = file.add_function( FunSpec::builder(&format!("{}{}", fn_base_name(&name), direction.suffix())) .visibility(Visibility::Public) .add_param(ParameterSpec::of(source, TypeName::primitive(input))) .returns(vec![TypeName::primitive(output)]) .body(body) - .build() - .expect("external wrapper JSON converter builds"), + .build()?, ); } } if flags.emit_type_guards { - for guard in - build_tagged_union_type_guards(&name, ir, union, flags.property_naming_camel_case) - { + for guard in super::tagged::guards(schema, ir, union, plan)? { file = file.add_code(guard); } } - file.build().ok() + file.build() } -/// Emit a tagged union file with dual types + fromJSON/toJSON when camelCase. -fn emit_tagged_union_file_camel_case( +// --------------------------------------------------------------------------- +// Union camelCase: structural member selection and semantic conversion +// --------------------------------------------------------------------------- + +fn emit_union_file_camel_case( schema: &IrSchema, - ir: &IrSpec, - tu: &IrTaggedUnion, - name: &str, - flags: EmitFlags, - convertible: &HashSet, + union: &IrUnion, + plan: &TypeScriptPlan<'_>, + unknown_aliases: &HashSet, + strategy: UntaggedUnionStrategy, ts: &TypeScript, -) -> Option { - let wire_name = format!("{}$Wire", name); - let filename = format!("{}.ts", name); - let disc_field = &tu.discriminator_field; - let disc_field_camel = disc_field.to_lower_camel_case(); - - // --- $Wire type (wire discriminator field name + $Wire content refs) --- - let wire_code = tagged_union_type_alias_code(&wire_name, ir, tu, disc_field, None, |expr| { - type_expr_str_wire(expr, convertible) - })?; - - // --- Ergonomic type (camelCase discriminator field name + ergonomic content refs) --- - let ergo_code = tagged_union_type_alias_code( - name, - ir, - tu, - &disc_field_camel, - Some(PropertyNamingMode::CamelCase), - type_expr_str, - )?; - - // --- fromJSON / toJSON --- - let from_json = build_tagged_union_from_json(name, &wire_name, ir, tu, convertible)?; - let to_json = build_tagged_union_to_json(name, &wire_name, ir, tu, convertible)?; - - let mut fb = FileSpec::builder_with(&filename, ts.clone()); - if let Some(doc) = &schema.description { - fb = fb.add_raw(&format!("/** {doc} */\n")); - } - fb = fb.add_code(wire_code); - fb = fb.add_code(ergo_code); - for import in tagged_union_type_imports(ir, tu, convertible, true) { - fb = fb.add_import(import); - } - - // Add converter imports for Named content types (only if convertible) - let mut converter_refs = HashSet::new(); - for variant in &tu.variants { - if is_tag_only_variant( - ir, - &tu.discriminator_field, - &variant.discriminator_value, - &variant.content_type, - ) { - continue; - } - collect_named_refs_from_expr(&variant.content_type, &mut converter_refs, convertible); - } - let mut converter_refs: Vec = converter_refs.into_iter().collect(); - converter_refs.sort(); - for ref_name in converter_refs { - let pascal = ref_name.to_pascal_case(); - let module = format!("./{pascal}"); - let base = fn_base_name(&pascal); - let from_fn = format!("{base}FromJSON"); - let to_fn = format!("{base}ToJSON"); - fb = fb.add_import(ImportSpec::named(&module, &from_fn)); - fb = fb.add_import(ImportSpec::named(&module, &to_fn)); - } - - fb = fb.add_code(from_json); - fb = fb.add_code(to_json); - - if flags.emit_type_guards { - let guards = build_tagged_union_type_guards(name, ir, tu, true); - for guard in guards { - fb = fb.add_code(guard); - } - } - - fb.build().ok() -} - -/// Build fromJSON for a tagged union: switch on the wire discriminator field. -fn build_tagged_union_from_json( - name: &str, - wire_name: &str, - ir: &IrSpec, - tu: &IrTaggedUnion, - convertible: &HashSet, -) -> Option { - let fn_name = format!("{}FromJSON", fn_base_name(name)); - - if matches!(tu.tagging, TaggingStyle::External) { - return build_external_tagged_from_json(&fn_name, name, wire_name, tu, convertible); - } - - let disc_field = &tu.discriminator_field; - let disc_field_camel = disc_field.to_lower_camel_case(); - let disc_access = if is_valid_ts_identifier(disc_field) { - format!("json.{disc_field}") +) -> Result { + let name = schema.name.to_pascal_case(); + let wire_name = format!("{name}$Wire"); + let collapses_to_unknown = union + .members + .iter() + .any(|member| type_expr_collapses_to_unknown(member, unknown_aliases)); + let mut wire_members: Vec<_> = if collapses_to_unknown { + vec![TypeName::primitive("unknown")] } else { - format!("json['{disc_field}']") + union + .members + .iter() + .map(|member| type_expr_to_typename_wire(member, &schema.name, plan)) + .collect() }; - - let mut case_lines: Vec = Vec::new(); - for variant in &tu.variants { - let val = &variant.discriminator_value; - let case_body = internal_or_adjacent_from_json_case( - &tu.tagging, - val, - &disc_field_camel, - &variant.content_type, - ir, - &tu.discriminator_field, - convertible, - ); - case_lines.push(format!( - "case '{val}':\n {}", - case_body.replace('\n', "\n ") - )); - } - sigil_quote!(TypeScript { - export function $N(fn_name)(json: $N(wire_name)): $N(name) { - switch ($L(disc_access)) { - $for(case_line in &case_lines) { - $L(case_line.as_str()) - } - } - } - }) - .ok() -} - -fn internal_or_adjacent_from_json_case( - tagging: &TaggingStyle, - val: &str, - disc_field_camel: &str, - content_type: &IrTypeExpr, - ir: &IrSpec, - disc_field: &str, - convertible: &HashSet, -) -> String { - match tagging { - TaggingStyle::Internal => { - if is_tag_only_variant(ir, disc_field, val, content_type) { - return format!("return {{ {disc_field_camel}: '{val}' }};"); - } - if let IrTypeExpr::Named(ref_name) = content_type { - if convertible.contains(ref_name) { - let pascal = ref_name.to_pascal_case(); - let converter = format!("{}FromJSON", fn_base_name(&pascal)); - super::layout::return_object( - &[ - format!("...{converter}(json)"), - format!("{disc_field_camel}: '{val}'"), - ], - None, - 6, - ) - } else { - format!("return {{ ...json, {disc_field_camel}: '{val}' }};") - } - } else { - format!("return {{ ...json, {disc_field_camel}: '{val}' }};") - } - } - TaggingStyle::Adjacent { content_field } => { - if is_tag_only_variant(ir, disc_field, val, content_type) { - return format!("return {{ {disc_field_camel}: '{val}' }};"); - } - let content_camel = content_field.to_lower_camel_case(); - let content_access = if is_valid_ts_identifier(content_field) { - format!("json.{content_field}") - } else { - format!("json['{content_field}']") - }; - let content_expr = from_json_expr_inner(content_type, &content_access, convertible); - super::layout::return_object( - &[ - format!("{disc_field_camel}: '{val}'"), - format!("{content_camel}: {content_expr}"), - ], - None, - 6, - ) - } - TaggingStyle::External => unreachable!(), - } -} - -fn build_external_tagged_from_json( - fn_name: &str, - name: &str, - wire_name: &str, - tu: &IrTaggedUnion, - convertible: &HashSet, -) -> Option { - let mut body_lines: Vec = Vec::new(); - for variant in &tu.variants { - let val = &variant.discriminator_value; - let ret_expr = external_variant_from_json_expr(val, &variant.content_type, convertible); - body_lines.push(format!("if ('{val}' in json) return {ret_expr};")); - } - body_lines.push("throw new Error('Unknown variant');".to_string()); - sigil_quote!(TypeScript { - export function $N(fn_name)(json: $N(wire_name)): $N(name) { - $for(body_line in &body_lines) { - $L(body_line.as_str()) - } - } - }) - .ok() -} - -fn external_variant_from_json_expr( - val: &str, - content_type: &IrTypeExpr, - convertible: &HashSet, -) -> String { - let val_access = format!("json['{val}']"); - let content_expr = from_json_expr_inner(content_type, &val_access, convertible); - format!( - "{{ {}: {content_expr} }}", - if is_valid_ts_identifier(val) { - val.to_string() - } else { - format!("'{val}'") - } - ) -} - -/// Build toJSON for a tagged union: switch on the camelCase discriminator field. -fn build_tagged_union_to_json( - name: &str, - wire_name: &str, - ir: &IrSpec, - tu: &IrTaggedUnion, - convertible: &HashSet, -) -> Option { - let fn_name = format!("{}ToJSON", fn_base_name(name)); - - if matches!(tu.tagging, TaggingStyle::External) { - return build_external_tagged_to_json(&fn_name, name, wire_name, tu, convertible); + if union.nullable && !collapses_to_unknown { + wire_members.push(TypeName::primitive("null")); } - - let disc_field = &tu.discriminator_field; - let disc_field_camel = disc_field.to_lower_camel_case(); - let disc_access = if is_valid_ts_identifier(&disc_field_camel) { - format!("value.{disc_field_camel}") + let mut ergonomic_members: Vec<_> = if collapses_to_unknown { + vec![TypeName::primitive("unknown")] } else { - format!("value['{disc_field_camel}']") + union + .members + .iter() + .map(|member| plan.value(member, Scope::model(&schema.name)).public_type()) + .collect() }; - let disc_wire_key = if is_valid_ts_identifier(disc_field) { - disc_field.clone() + if union.nullable && !collapses_to_unknown { + ergonomic_members.push(TypeName::primitive("null")); + } + let ergonomic_type = if strategy == UntaggedUnionStrategy::Raw { + // Raw mode exposes the wire union honestly, rather than promising + // ergonomic members while leaving their field names unchanged. + TypeName::primitive(&wire_name) } else { - format!("'{disc_field}'") + union_typename(ergonomic_members) }; - - let mut case_lines: Vec = Vec::new(); - for variant in &tu.variants { - let val = &variant.discriminator_value; - let tag_only = is_tag_only_variant(ir, &tu.discriminator_field, val, &variant.content_type); - let case_body = internal_or_adjacent_to_json_case( - &tu.tagging, - val, - &disc_wire_key, - wire_name, - &variant.content_type, - tag_only, - convertible, - ); - case_lines.push(format!( - "case '{val}':\n {}", - case_body.replace('\n', "\n ") - )); - } - sigil_quote!(TypeScript { - export function $N(fn_name)(value: $N(name)): $N(wire_name) { - switch ($L(disc_access)) { - $for(case_line in &case_lines) { - $L(case_line.as_str()) - } - } - } - }) - .ok() -} - -fn internal_or_adjacent_to_json_case( - tagging: &TaggingStyle, - val: &str, - disc_wire_key: &str, - wire_name: &str, - content_type: &IrTypeExpr, - tag_only: bool, - convertible: &HashSet, -) -> String { - match tagging { - TaggingStyle::Internal => { - if tag_only { - return format!("return {{ {disc_wire_key}: '{val}' }} as {wire_name};"); - } - if let IrTypeExpr::Named(ref_name) = content_type { - if convertible.contains(ref_name) { - let pascal = ref_name.to_pascal_case(); - let converter = format!("{}ToJSON", fn_base_name(&pascal)); - super::layout::return_object( - &[ - format!("...{converter}(value)"), - format!("{disc_wire_key}: '{val}'"), - ], - Some(wire_name), - 6, - ) - } else { - format!("return {{ ...value, {disc_wire_key}: '{val}' }} as {wire_name};") - } - } else { - format!("return {{ ...value, {disc_wire_key}: '{val}' }} as {wire_name};") - } - } - TaggingStyle::Adjacent { content_field } => { - if tag_only { - return format!("return {{ {disc_wire_key}: '{val}' }};"); - } - let content_camel = content_field.to_lower_camel_case(); - let content_wire_key = if is_valid_ts_identifier(content_field) { - content_field.clone() - } else { - format!("'{content_field}'") - }; - let content_access = if is_valid_ts_identifier(&content_camel) { - format!("value.{content_camel}") - } else { - format!("value['{content_camel}']") - }; - let content_expr = to_json_expr_inner(content_type, &content_access, convertible); - super::layout::return_object( - &[ - format!("{disc_wire_key}: '{val}'"), - format!("{content_wire_key}: {content_expr}"), - ], - None, - 6, - ) - } - TaggingStyle::External => unreachable!(), + let mut file = FileSpec::builder_with(&format!("{name}.ts"), ts.clone()) + .add_type(build_type_alias( + &wire_name, + union_typename(wire_members), + schema.description.as_deref(), + )?) + .add_type(build_type_alias( + &name, + ergonomic_type, + schema.description.as_deref(), + )?); + for direction in [JsonDirection::FromWire, JsonDirection::ToWire] { + file = file.add_function(build_union_converter(schema, union, plan, direction)?); } + file.build() } -fn build_external_tagged_to_json( - fn_name: &str, - name: &str, - wire_name: &str, - tu: &IrTaggedUnion, - convertible: &HashSet, -) -> Option { - let mut body_lines: Vec = Vec::new(); - for variant in &tu.variants { - let val = &variant.discriminator_value; - let ret_expr = external_variant_to_json_expr(val, &variant.content_type, convertible); - body_lines.push(format!("if ('{val}' in value) return {ret_expr};")); - } - body_lines.push("throw new Error('Unknown variant');".to_string()); - sigil_quote!(TypeScript { - export function $N(fn_name)(value: $N(name)): $N(wire_name) { - $for(body_line in &body_lines) { - $L(body_line.as_str()) - } - } - }) - .ok() -} - -fn external_variant_to_json_expr( - val: &str, - content_type: &IrTypeExpr, - convertible: &HashSet, -) -> String { - let val_access = format!("value['{val}']"); - let content_expr = to_json_expr_inner(content_type, &val_access, convertible); - format!( - "{{ {}: {content_expr} }}", - if is_valid_ts_identifier(val) { - val.to_string() - } else { - format!("'{val}'") - } - ) -} - -// --------------------------------------------------------------------------- -// Union camelCase: cast-through pattern -// --------------------------------------------------------------------------- - -fn emit_union_file_camel_case( +fn build_union_converter( schema: &IrSchema, union: &IrUnion, - name: &str, - convertible: &HashSet, - ts: &TypeScript, -) -> Option { - let filename = format!("{}.ts", name); - let mut fb = FileSpec::builder_with(&filename, ts.clone()); - - if let Some(doc) = &schema.description { - fb = fb.add_raw(&format!("/** {doc} */\n")); - } - - // $Wire type alias + plan: &TypeScriptPlan<'_>, + direction: JsonDirection, +) -> Result { + let name = schema.name.to_pascal_case(); let wire_name = format!("{name}$Wire"); - let mut wire_members: Vec = union - .members - .iter() - .map(|m| type_expr_str_wire(m, convertible)) - .collect(); - if union.nullable { - wire_members.push("null".to_string()); - } - let wire_rhs = simplify_union_strings(wire_members); - let wire_type = sigil_quote!(TypeScript { - export type $N(wire_name.as_str()) = $L(wire_rhs.as_str()); - }) - .ok()?; - fb = fb.add_code(wire_type); - - // Ergonomic type alias - let mut ergo_members: Vec = union.members.iter().map(type_expr_str).collect(); - if union.nullable { - ergo_members.push("null".to_string()); - } - let ergo_rhs = simplify_union_strings(ergo_members); - let ergo_type = sigil_quote!(TypeScript { - export type $N(name) = $L(ergo_rhs.as_str()); - }) - .ok()?; - fb = fb.add_code(ergo_type); - - // fromJSON: cast-through - let base = fn_base_name(name); - let from_fn = format!("{base}FromJSON"); - let to_fn = format!("{base}ToJSON"); - let from_json = sigil_quote!(TypeScript { - export function $N(from_fn)(json: $N(wire_name.as_str())): $N(name) { - return json as unknown as $N(name); - } - }) - .ok()?; - fb = fb.add_code(from_json); - - // toJSON: cast-through - let to_json = sigil_quote!(TypeScript { - export function $N(to_fn)(value: $N(name)): $N(wire_name.as_str()) { - return value as unknown as $N(wire_name.as_str()); - } - }) - .ok()?; - fb = fb.add_code(to_json); - - // Add imports for Named members (wire + ergonomic types only if convertible) - let mut seen_imports = HashSet::new(); - let mut imports = Vec::new(); - for member in &union.members { - collect_type_imports_from_expr(member, &mut seen_imports, &mut imports, convertible, true); - } - if ergo_rhs != "unknown" || wire_rhs != "unknown" { - for import in imports { - fb = fb.add_import(import); - } - } - - fb.build().ok() + let (input, output) = match direction { + JsonDirection::FromWire => (&wire_name, &name), + JsonDirection::ToWire => (&name, &wire_name), + }; + let source = direction.source(); + let output_type = TypeName::primitive(output); + let body = plan.union_body( + union, + Scope::model(&schema.name), + output_type.clone(), + direction, + &name, + true, + )?; + FunSpec::builder(&format!("{}{}", fn_base_name(&name), direction.suffix())) + .visibility(Visibility::Public) + .add_param(ParameterSpec::of(source, TypeName::primitive(input))) + .returns(vec![output_type]) + .body(body) + .build() } // --------------------------------------------------------------------------- @@ -2064,10 +1247,17 @@ fn emit_intersection_file_camel_case( ir: &IrSpec, intersection: &IrIntersection, name: &str, - convertible: &HashSet, + plan: &TypeScriptPlan<'_>, unknown_aliases: &HashSet, ts: &TypeScript, -) -> Option { +) -> Result { + if let [member] = intersection.members.as_slice() + && open_intersection_object(intersection, ir, unknown_aliases).is_none() + { + // A single-member allOf is a delegation boundary, not an object spread. + // Its converter can return any supported shape, including arrays/null. + return emit_alias_file(schema, member, plan, ts); + } let filename = format!("{}.ts", name); let mut fb = FileSpec::builder_with(&filename, ts.clone()); @@ -2077,167 +1267,110 @@ fn emit_intersection_file_camel_case( intersection .members .iter() - .map(|member| type_expr_to_typename_wire(member, &schema.name, convertible)) + .map(|member| type_expr_to_typename_wire(member, &schema.name, plan)) .collect(), ); let ergonomic_type = TypeName::intersection( intersection .members .iter() - .map(|member| type_expr_to_typename_for_schema(member, &schema.name)) + .map(|member| plan.value(member, Scope::model(&schema.name)).public_type()) .collect(), ); let mut file = fb - .add_type( - build_type_alias( - &wire_name, - type_application(TypeName::primitive("Readonly"), vec![wire_type]), - schema.description.as_deref(), - ) - .expect("open intersection wire type alias builds"), - ) - .add_type( - build_type_alias( - name, - type_application(TypeName::primitive("Readonly"), vec![ergonomic_type]), - schema.description.as_deref(), - ) - .expect("open intersection ergonomic type alias builds"), - ); + .add_type(build_type_alias( + &wire_name, + type_application(TypeName::primitive("Readonly"), vec![wire_type]), + schema.description.as_deref(), + )?) + .add_type(build_type_alias( + name, + type_application(TypeName::primitive("Readonly"), vec![ergonomic_type]), + schema.description.as_deref(), + )?); for direction in [JsonDirection::FromWire, JsonDirection::ToWire] { - file = file.add_function( - build_json_converter( - name, - &wire_name, - &schema.name, - &object, - convertible, - unknown_aliases, - direction, - ) - .expect("open intersection JSON converter builds"), - ); + file = file.add_function(build_json_converter( + name, + &wire_name, + &schema.name, + &object, + plan, + unknown_aliases, + direction, + )?); } - return file.build().ok(); + return file.build(); } - // $Wire type alias: A$Wire & B$Wire & ... (only $Wire for convertible refs) + // $Wire type alias: A$Wire & B$Wire & ... (wire symbols come from the plan). let wire_name = format!("{name}$Wire"); let wire_members = intersection .members .iter() - .map(|member| type_expr_to_typename_wire(member, &schema.name, convertible)) + .map(|member| type_expr_to_typename_wire(member, &schema.name, plan)) .collect(); - fb = fb.add_type( - build_type_alias( - &wire_name, - TypeName::intersection(wire_members), - schema.description.as_deref(), - ) - .expect("intersection wire type alias builds"), - ); + fb = fb.add_type(build_type_alias( + &wire_name, + TypeName::intersection(wire_members), + schema.description.as_deref(), + )?); // Ergonomic type alias: A & B & ... let ergo_members = intersection .members .iter() - .map(|member| type_expr_to_typename_for_schema(member, &schema.name)) + .map(|member| plan.value(member, Scope::model(&schema.name)).public_type()) .collect(); - fb = fb.add_type( - build_type_alias(name, TypeName::intersection(ergo_members), None) - .expect("intersection ergonomic type alias builds"), - ); - - // fromJSON: spread each convertible Named member's converter - let base = fn_base_name(name); - let from_fn = format!("{base}FromJSON"); - let to_fn = format!("{base}ToJSON"); - let from_spreads: Vec = intersection - .members - .iter() - .filter_map(|m| { - if let IrTypeExpr::Named(ref_name) = m { - if convertible.contains(ref_name) { - let member_pascal = ref_name.to_pascal_case(); - let member_base = fn_base_name(&member_pascal); - Some(format!( - "...{member_base}FromJSON(json as {member_pascal}$Wire)" - )) - } else { - None - } - } else { - None - } - }) - .collect(); - - let from_json = sigil_quote!(TypeScript { - export function $N(from_fn)(json: $N(wire_name.as_str())): $N(name) { - return $L("{ ")$for(spread in &from_spreads; separator = ", ") { $L(spread.as_str()) }$L(format!(" }} as {name};")) - } - }) - .ok()?; - fb = fb.add_code(from_json); - - // toJSON: spread each convertible Named member's converter - let to_spreads: Vec = intersection - .members - .iter() - .filter_map(|m| { - if let IrTypeExpr::Named(ref_name) = m { - if convertible.contains(ref_name) { - let member_pascal = ref_name.to_pascal_case(); - let member_base = fn_base_name(&member_pascal); - Some(format!("...{member_base}ToJSON(value as {member_pascal})")) - } else { - None - } - } else { - None - } - }) - .collect(); - - let to_json = sigil_quote!(TypeScript { - export function $N(to_fn)(value: $N(name)): $N(wire_name.as_str()) { - return $L("{ ")$for(spread in &to_spreads; separator = ", ") { $L(spread.as_str()) }$L(format!(" }} as {name}$Wire;")) - } - }) - .ok()?; - fb = fb.add_code(to_json); - - // Add imports for Named members - for member in &intersection.members { - if let IrTypeExpr::Named(ref_name) = member { - let member_pascal = ref_name.to_pascal_case(); - fb = fb.add_import(ImportSpec::named_type( - &format!("./{member_pascal}"), - &member_pascal, - )); - if convertible.contains(ref_name) { - let member_base = fn_base_name(&member_pascal); - fb = fb.add_import(ImportSpec::named_type( - &format!("./{member_pascal}"), - &format!("{member_pascal}$Wire"), - )); - fb = fb.add_import(ImportSpec::named( - &format!("./{member_pascal}"), - &format!("{member_base}FromJSON"), - )); - fb = fb.add_import(ImportSpec::named( - &format!("./{member_pascal}"), - &format!("{member_base}ToJSON"), - )); - } - } + fb = fb.add_type(build_type_alias( + name, + TypeName::intersection(ergo_members), + None, + )?); + + // Keep the existing multi-member allOf spread behavior. Only its emission + // and child delegation move to the value plan; intersection semantics are + // not redesigned here. + for direction in [JsonDirection::FromWire, JsonDirection::ToWire] { + let source = direction.source(); + let (input, output): (&str, &str) = match direction { + JsonDirection::FromWire => (&wire_name, name), + JsonDirection::ToWire => (name, &wire_name), + }; + let spreads = intersection + .members + .iter() + .filter(|expr| matches!(expr, IrTypeExpr::Named(name) if plan.has_converter(name))) + .map(|expr| { + let value = plan.value(expr, Scope::model(&schema.name)); + let ty = match direction { + JsonDirection::FromWire => value.wire_type(), + JsonDirection::ToWire => value.public_type(), + }; + let converted = value.convert( + sigil_quote!(TypeScript { $N(source) as $T(ty) })?, + direction, + )?; + sigil_quote!(TypeScript { ...$C(converted) }) + }) + .collect::, SigilStitchError>>()?; + let output_type = TypeName::primitive(output); + let body = sigil_quote!(TypeScript { + return { $for(spread in &spreads; separator = ", ") { $C(spread.clone()) } } as $T(output_type.clone()); + })?; + fb = fb.add_function( + FunSpec::builder(&format!("{}{}", fn_base_name(name), direction.suffix())) + .visibility(Visibility::Public) + .add_param(ParameterSpec::of(source, TypeName::primitive(input))) + .returns(vec![output_type]) + .body(body) + .build()?, + ); } - - fb.build().ok() + fb.build() } /// Plan named fields and a dictionary together, so allOf map conversion never /// interprets a declared field as an extension or leaves its wire spelling behind. -fn open_intersection_object( +pub(super) fn open_intersection_object( intersection: &IrIntersection, ir: &IrSpec, unknown_aliases: &HashSet, @@ -2384,125 +1517,18 @@ fn intersect_dictionary_values( IrTypeExpr::Primitive(IrPrimitive::String), value @ (IrTypeExpr::StringLiteral(_) | IrTypeExpr::StringEnum(_)), ) - | ( - value @ (IrTypeExpr::StringLiteral(_) | IrTypeExpr::StringEnum(_)), - IrTypeExpr::Primitive(IrPrimitive::String), - ) => Some(value.clone()), - _ => None, - } - } - intersect(left, right, ir, unknown_aliases, &mut HashSet::new()) -} - -fn tagged_union_type_imports( - ir: &IrSpec, - tu: &IrTaggedUnion, - convertible: &HashSet, - include_wire: bool, -) -> Vec { - let mut seen = HashSet::new(); - let mut imports = Vec::new(); - for variant in &tu.variants { - if is_tag_only_variant( - ir, - &tu.discriminator_field, - &variant.discriminator_value, - &variant.content_type, - ) { - continue; - } - collect_type_imports_from_expr( - &variant.content_type, - &mut seen, - &mut imports, - convertible, - include_wire, - ); - } - imports -} - -fn collect_type_imports_from_expr( - expr: &IrTypeExpr, - seen: &mut HashSet, - imports: &mut Vec, - convertible: &HashSet, - include_wire: bool, -) { - match expr { - IrTypeExpr::Named(ref_name) => { - let pascal = ref_name.to_pascal_case(); - if seen.insert(pascal.clone()) { - imports.push(ImportSpec::named_type(&format!("./{pascal}"), &pascal)); - } - if include_wire && convertible.contains(ref_name) { - let wire = format!("{pascal}$Wire"); - if seen.insert(wire.clone()) { - imports.push(ImportSpec::named_type(&format!("./{pascal}"), &wire)); - } - } - } - IrTypeExpr::Array(inner) | IrTypeExpr::Nullable(inner) | IrTypeExpr::Map(inner) => { - collect_type_imports_from_expr(inner, seen, imports, convertible, include_wire); - } - IrTypeExpr::Union(members) => { - for member in members { - collect_type_imports_from_expr(member, seen, imports, convertible, include_wire); - } - } - IrTypeExpr::Primitive(_) - | IrTypeExpr::StringLiteral(_) - | IrTypeExpr::StringEnum(_) - | IrTypeExpr::Any => {} - } -} - -/// Build `is*` type guard functions for a tagged union. -/// -/// Returns one CodeBlock per contentful (non-empty) variant. -fn build_tagged_union_type_guards( - name: &str, - ir: &IrSpec, - tu: &IrTaggedUnion, - camel_case: bool, -) -> Vec { - tu.variants - .iter() - .filter(|variant| { - !is_tag_only_variant( - ir, - &tu.discriminator_field, - &variant.discriminator_value, - &variant.content_type, - ) && !is_unspecified_variant( - &variant.discriminator_value, - &tu.discriminator_field, - &variant.content_type, - ) - }) - .filter_map(|variant| { - let guard_name = is_guard_name(&variant.discriminator_value); - let (check_body, guard_type) = guard_check_and_type( - &tu.tagging, - &tu.discriminator_field, - &variant.discriminator_value, - &variant.content_type, - camel_case, - ); - - let predicate = format!("value is {guard_type}"); - sigil_quote!(TypeScript { - export function $N(guard_name)(value: $N(name)): $L(predicate) { - return $L(check_body); - } - }) - .ok() - }) - .collect() + | ( + value @ (IrTypeExpr::StringLiteral(_) | IrTypeExpr::StringEnum(_)), + IrTypeExpr::Primitive(IrPrimitive::String), + ) => Some(value.clone()), + _ => None, + } + } + intersect(left, right, ir, unknown_aliases, &mut HashSet::new()) } /// Derive `isVariantName` from a discriminator value. -fn is_guard_name(disc_value: &str) -> String { +pub(super) fn is_guard_name(disc_value: &str) -> String { format!("is{}", disc_value.to_pascal_case()) } @@ -2511,7 +1537,11 @@ fn is_guard_name(disc_value: &str) -> String { /// - discriminator values containing "UNSPECIFIED" (explicit sentinel) /// - discriminator values equal to the discriminator field name (e.g. `kind` /// key in an External-tagged union where the `kind` variant is the empty case) -fn is_unspecified_variant(disc_value: &str, disc_field: &str, content_type: &IrTypeExpr) -> bool { +pub(super) fn is_unspecified_variant( + disc_value: &str, + disc_field: &str, + content_type: &IrTypeExpr, +) -> bool { if disc_value.to_uppercase().contains("UNSPECIFIED") { return true; } @@ -2524,249 +1554,6 @@ fn is_unspecified_variant(disc_value: &str, disc_field: &str, content_type: &IrT matches!(content_type, IrTypeExpr::StringLiteral(_)) } -fn is_tag_only_variant( - ir: &IrSpec, - disc_field: &str, - disc_value: &str, - content_type: &IrTypeExpr, -) -> bool { - let IrTypeExpr::Named(name) = content_type else { - return false; - }; - let Some(schema) = ir.schemas.get(name) else { - return false; - }; - let IrSchemaKind::Object(obj) = &schema.kind else { - return false; - }; - if obj.additional_properties.is_some() { - return false; - } - if obj.properties.len() != 1 { - return false; - } - let Some(prop) = obj.properties.get(disc_field) else { - return false; - }; - matches!(&prop.type_expr, IrTypeExpr::StringLiteral(value) if value == disc_value) -} - -// --------------------------------------------------------------------------- -// Variant helpers — shared between tagged-union type emission and guard emission -// --------------------------------------------------------------------------- - -#[derive(Debug, Clone, Copy)] -enum PropertyNamingMode { - CamelCase, -} - -/// Build the TS type expression for one variant, with `content` slotted in. -fn variant_type_format( - tagging: &TaggingStyle, - disc_field: &str, - disc_value: &str, - content_field_mode: Option, - content: &str, - multiline: bool, -) -> String { - let (padding, closing_padding) = if multiline { - ("\n ", ";\n") - } else { - (" ", " ") - }; - match tagging { - TaggingStyle::Internal => { - if multiline { - format!("{{{padding}{disc_field}: '{disc_value}'{closing_padding}}} & {content}") - } else { - format!("({{{padding}{disc_field}: '{disc_value}'{closing_padding}}} & {content})") - } - } - TaggingStyle::Adjacent { content_field } => { - let emitted_content_field = match content_field_mode { - Some(PropertyNamingMode::CamelCase) => content_field.to_lower_camel_case(), - None => content_field.clone(), - }; - format!( - "{{{padding}{disc_field}: '{disc_value}';{padding}{emitted_content_field}: {content}{closing_padding}}}" - ) - } - TaggingStyle::External => { - let key = if is_valid_ts_identifier(disc_value) { - disc_value.to_string() - } else { - format!("'{disc_value}'") - }; - format!("{{{padding}{key}: {content}{closing_padding}}}") - } - } -} - -fn tagged_variant_type_string( - tagging: &TaggingStyle, - disc_field: &str, - disc_value: &str, - content_field_mode: Option, - content: &str, - tag_only: bool, -) -> String { - if tag_only - && matches!( - tagging, - TaggingStyle::Internal | TaggingStyle::Adjacent { .. } - ) - { - return format!("{{ {disc_field}: '{disc_value}' }}"); - } - variant_type_format( - tagging, - disc_field, - disc_value, - content_field_mode, - content, - false, - ) -} - -/// Build the runtime narrowing expression for one variant. -fn variant_check_body(tagging: &TaggingStyle, disc_field: &str, disc_value: &str) -> String { - match tagging { - TaggingStyle::Internal | TaggingStyle::Adjacent { .. } => { - format!("value.{disc_field} === '{disc_value}'") - } - TaggingStyle::External => { - format!("'{disc_value}' in value") - } - } -} - -// --------------------------------------------------------------------------- -// TsTypeDisplay — Display impl for IrTypeExpr as a raw TS type string -// --------------------------------------------------------------------------- - -struct TsTypeDisplay<'a>(&'a IrTypeExpr); - -impl std::fmt::Display for TsTypeDisplay<'_> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self.0 { - IrTypeExpr::Named(name) => write!(f, "{}", name.to_pascal_case()), - IrTypeExpr::Primitive(p) => write!(f, "{}", primitive_to_ts(p)), - IrTypeExpr::Nullable(inner) => write!( - f, - "{}", - simplify_union_strings(vec![TsTypeDisplay(inner).to_string(), "null".to_string()]) - ), - IrTypeExpr::Array(inner) => write!(f, "readonly {}[]", TsTypeDisplay(inner)), - IrTypeExpr::Map(inner) => { - write!(f, "Record", TsTypeDisplay(inner)) - } - IrTypeExpr::StringLiteral(s) => write!(f, "'{s}'"), - IrTypeExpr::StringEnum(values) => write!( - f, - "{}", - simplify_union_strings(values.iter().map(|v| format!("'{v}'")).collect()) - ), - IrTypeExpr::Union(members) => { - let parts: Vec = members - .iter() - .map(|m| TsTypeDisplay(m).to_string()) - .collect(); - write!(f, "{}", simplify_union_strings(parts)) - } - IrTypeExpr::Any => write!(f, "unknown"), - } - } -} - -// --------------------------------------------------------------------------- -// Guard emission helpers -// --------------------------------------------------------------------------- - -/// Produce `(check_body, guard_type_text)` for one variant. -fn guard_check_and_type( - tagging: &TaggingStyle, - disc_field: &str, - disc_value: &str, - content_type: &IrTypeExpr, - camel_case: bool, -) -> (String, String) { - let emitted_disc_field = if camel_case { - disc_field.to_lower_camel_case() - } else { - disc_field.to_string() - }; - let content_field_mode = if camel_case { - Some(PropertyNamingMode::CamelCase) - } else { - None - }; - let check = variant_check_body(tagging, &emitted_disc_field, disc_value); - // Multiline object predicates let Oxfmt lay out long signatures in one pass. - let ty = variant_type_format( - tagging, - &emitted_disc_field, - disc_value, - content_field_mode, - &TsTypeDisplay(content_type).to_string(), - true, - ); - (check, ty) -} - -fn tagged_union_type_alias_code( - name: &str, - ir: &IrSpec, - tu: &IrTaggedUnion, - discriminator_field: &str, - content_field_mode: Option, - type_name_for: F, -) -> Option -where - F: Fn(&IrTypeExpr) -> String, -{ - let members: Vec = tu - .variants - .iter() - .map(|variant| { - let tag_only = is_tag_only_variant( - ir, - &tu.discriminator_field, - &variant.discriminator_value, - &variant.content_type, - ); - let member = tagged_variant_type_string( - &tu.tagging, - discriminator_field, - &variant.discriminator_value, - content_field_mode, - &type_name_for(&variant.content_type), - tag_only, - ); - if member.chars().count() + 4 > 100 && matches!(tu.tagging, TaggingStyle::Internal) { - let object = variant_type_format( - &tu.tagging, - discriminator_field, - &variant.discriminator_value, - content_field_mode, - &type_name_for(&variant.content_type), - true, - ); - format!("({})", object.replace('\n', "\n ")) - } else { - member - } - }) - .collect(); - - let inline = format!("export type {name} = {};\n", members.join(" | ")); - let text = if inline.trim_end().chars().count() <= 100 { - inline - } else { - format!("export type {name} =\n | {};\n", members.join("\n | ")) - }; - CodeBlock::of("%L", text).ok() -} - /// Build the pipe-joined literal body for an enum: `'a' | 1 | true | null`. /// Returns `None` if any value can't be represented as a TS literal. fn enum_union_body(en: &IrEnum) -> Option { @@ -2800,14 +1587,17 @@ fn json_value_to_ts_literal(v: &serde_json::Value) -> Option { fn build_field( prop: &IrProperty, current_schema: &str, + plan: &TypeScriptPlan<'_>, unknown_aliases: &HashSet, -) -> Option { +) -> Result { let field_name = if is_valid_ts_identifier(&prop.name) { prop.name.clone() } else { format!("'{}'", prop.name) }; - let inner_ty = type_expr_to_typename_for_schema(&prop.type_expr, current_schema); + let inner_ty = plan + .value(&prop.type_expr, Scope::model(current_schema)) + .public_type(); let field_ty = if prop.nullable { nullable_field_type_name(&prop.type_expr, inner_ty, unknown_aliases) } else { @@ -2821,136 +1611,14 @@ fn build_field( if let Some(desc) = &prop.description { fb = fb.doc(desc); } - fb.build().ok() -} - -fn type_expr_to_typename(expr: &IrTypeExpr) -> TypeName { - type_expr_to_typename_with_current(expr, None) -} - -fn type_expr_to_typename_for_schema(expr: &IrTypeExpr, current_schema: &str) -> TypeName { - type_expr_to_typename_with_current(expr, Some(current_schema)) -} - -fn type_expr_to_typename_with_current(expr: &IrTypeExpr, current_schema: Option<&str>) -> TypeName { - match expr { - IrTypeExpr::Named(name) => { - let ts_name = name.to_pascal_case(); - named_ref_to_typename(name, &ts_name, current_schema) - } - IrTypeExpr::Primitive(p) => TypeName::primitive(primitive_to_ts(p)), - // Outer array is `readonly X[]`; nested arrays stay plain `X[]` to - // avoid sigil's invalid-TS `readonly readonly X[][]` when both layers - // are readonly. Field-level readonly on the property is still applied - // by `FieldSpec::is_readonly()` elsewhere. - IrTypeExpr::Array(inner) => { - TypeName::readonly_array(type_expr_to_typename_nested(inner, current_schema)) - } - IrTypeExpr::Nullable(inner) => union_typename(vec![ - type_expr_to_typename_with_current(inner, current_schema), - TypeName::primitive("null"), - ]), - IrTypeExpr::StringLiteral(s) => TypeName::string_literal(s.clone()), - IrTypeExpr::StringEnum(values) => union_typename( - values - .iter() - .map(|v| TypeName::string_literal(v.clone())) - .collect(), - ), - IrTypeExpr::Map(inner) => type_application( - TypeName::primitive("Record"), - vec![ - TypeName::primitive("string"), - type_expr_to_typename_with_current(inner, current_schema), - ], - ), - IrTypeExpr::Union(_) if !contains_any_named_ref(expr) => { - TypeName::raw(&type_expr_str(expr)) - } - IrTypeExpr::Union(members) => union_typename( - members - .iter() - .map(|member| type_expr_to_typename_with_current(member, current_schema)) - .collect(), - ), - IrTypeExpr::Any => TypeName::primitive("unknown"), - } -} - -fn named_ref_to_typename( - schema_name: &str, - rendered_name: &str, - current_schema: Option<&str>, -) -> TypeName { - if current_schema == Some(schema_name) { - TypeName::primitive(rendered_name) - } else { - let module = format!("./{}", schema_name.to_pascal_case()); - TypeName::importable_type(&module, rendered_name) - } -} - -/// Same as [`type_expr_to_typename`] but nested arrays render as plain `X[]`. -fn type_expr_to_typename_nested(expr: &IrTypeExpr, current_schema: Option<&str>) -> TypeName { - match expr { - IrTypeExpr::Array(inner) => { - TypeName::array(type_expr_to_typename_nested(inner, current_schema)) - } - // Array element unions must stay structured so the renderer can add - // parentheses; a raw `T | null` would render as `T | null[]`. - IrTypeExpr::Nullable(inner) => union_typename(vec![ - type_expr_to_typename_nested(inner, current_schema), - TypeName::primitive("null"), - ]), - IrTypeExpr::Union(members) => union_typename( - members - .iter() - .map(|member| type_expr_to_typename_nested(member, current_schema)) - .collect(), - ), - other => type_expr_to_typename_with_current(other, current_schema), - } + fb.build() } -pub fn build_convertible_set(ir: &IrSpec, flags: EmitFlags) -> HashSet { - if !flags.property_naming_camel_case { - return HashSet::new(); - } - // Pass 1: leaf convertibles (Object, TaggedUnion) — always convertible. - let leaf: HashSet = ir - .schemas - .iter() - .filter_map(|(name, schema)| { - let dominated = match &schema.kind { - IrSchemaKind::Object(_) => true, - IrSchemaKind::TaggedUnion(tu) => !tu.variants.is_empty(), - _ => false, - }; - dominated.then(|| name.clone()) - }) - .collect(); - - // Resolve aliases and composites to a fixed point so a chain of aliases - // cannot hide a model's wire type or required JSON conversion. - let mut set = leaf; - loop { - let previous_len = set.len(); - for (name, schema) in &ir.schemas { - let dominated = match &schema.kind { - IrSchemaKind::Alias(expr) => has_named_ref(expr, &set), - IrSchemaKind::Intersection(i) => i.members.iter().any(|m| has_named_ref(m, &set)), - IrSchemaKind::Union(u) => u.members.iter().any(|m| has_named_ref(m, &set)), - _ => false, - }; - if dominated { - set.insert(name.clone()); - } - } - if set.len() == previous_len { - break; - } - } - set +#[cfg(test)] +fn build_unknown_alias_set(ir: &IrSpec) -> HashSet { + TypeScriptPlan::new(ir, EmitFlags::default()) + .unknown_aliases() + .clone() } /// Lower every `IrSchema` in the spec into a sigil-rendered `FileInfo`. @@ -2959,13 +1627,23 @@ pub fn generate_model_files( flags: EmitFlags, ts: &TypeScript, ) -> Result, GeneratorError> { + let plan = TypeScriptPlan::new(ir, flags); + generate_model_files_with_plan(&plan, ts) +} + +pub(super) fn generate_model_files_with_plan( + plan: &TypeScriptPlan<'_>, + ts: &TypeScript, +) -> Result, GeneratorError> { + let ir = plan.ir(); + let flags = plan.flags(); let header = super::project_files::render_file_header(&ir.info); - let convertible = build_convertible_set(ir, flags); - let unknown_aliases = build_unknown_alias_set(ir); + let unknown_aliases = plan.unknown_aliases(); let mut files = Vec::with_capacity(ir.schemas.len()); for (name, schema) in &ir.schemas { - let file_spec = emit_model_file(schema, ir, flags, &convertible, &unknown_aliases, ts) + let file_spec = emit_model_file(schema, ir, flags, plan, unknown_aliases, ts) + .map_err(|source| GeneratorError::emission(ArtifactKind::Model, name, source))? .ok_or_else(|| GeneratorError::UnsupportedSchema { schema: name.clone(), kind: format!("{:?}", schema.kind), @@ -2979,6 +1657,28 @@ pub fn generate_model_files( files.push(FileInfo::model(filename, content)); } + let mut runtime_files = Vec::new(); + if let Some(file) = plan + .matching_file(ts) + .map_err(|source| GeneratorError::emission(ArtifactKind::Model, "union matching", source))? + { + runtime_files.push(file); + } + runtime_files.extend(plan.conversion_files(ts).map_err(|source| { + GeneratorError::emission(ArtifactKind::Model, "compound value conversion", source) + })?); + for file in runtime_files { + let filename = file.filename().to_owned(); + let body = file + .with_lang(super::layout::TypeScript::from(ts.clone())) + .render(100) + .map_err(|source| GeneratorError::emission(ArtifactKind::Model, &filename, source))?; + files.push(FileInfo::runtime( + filename.to_string(), + format!("{header}{body}"), + )); + } + Ok(files) } @@ -3003,6 +1703,132 @@ mod render_tests { use super::*; use sigil_stitch::assert_rendered; + #[test] + fn inline_union_conversion_has_a_typed_lexical_boundary() { + let expr = IrTypeExpr::Union(vec![IrTypeExpr::Primitive(IrPrimitive::Boolean)]); + let mut ir = additional_properties_ir(); + ir.schemas.clear(); + ir.schemas.insert( + "Inline".into(), + IrSchema { + name: "Inline".into(), + description: None, + deprecated: false, + kind: IrSchemaKind::Alias(expr.clone()), + is_component: true, + }, + ); + let plan = TypeScriptPlan::new( + &ir, + EmitFlags { + property_naming_camel_case: true, + ..Default::default() + }, + ); + let conversion = json_conversion_expr( + &expr, + sigil_quote!(TypeScript { input }).unwrap(), + "Inline", + &plan, + JsonDirection::FromWire, + ) + .unwrap(); + assert_rendered!( + TypeScript::new(), + conversion, + "import { convert$0FromJSON } from '../runtime/valueConversion$0';\n\nconvert$0FromJSON(input)\n" + ); + let file = plan.conversion_files(&TypeScript::new()).unwrap().remove(0); + assert_eq!( + file.with_lang(super::super::layout::TypeScript::from(TypeScript::new())) + .render(100) + .unwrap(), + "import { matches$0FromJSON, matches$0ToJSON } from '../runtime/unionMatch';\n\nexport type Value$0Wire = boolean;\n\nexport type Value$0 = boolean;\n\nexport function convert$0FromJSON(json: boolean): Value$0 {\n const candidate: unknown = json;\n if (matches$0FromJSON(candidate)) {\n return json as unknown as boolean;\n }\n return json as unknown as Value$0;\n}\n\nexport function convert$0ToJSON(value: boolean): Value$0Wire {\n const candidate: unknown = value;\n if (matches$0ToJSON(candidate)) {\n return value as unknown as boolean;\n }\n return value as unknown as Value$0Wire;\n}\n" + ); + } + + #[test] + fn unknown_union_aliases_resolve_without_schema_order_dependence() { + let parsed = crate::parser::parse_content_yaml(include_str!( + "../../../../tests/fixtures/valid/typescript-untagged-union.yaml" + )) + .unwrap(); + let mut ir = crate::ir::lower::lower(parsed).unwrap(); + for _ in 0..2 { + let unknown = build_unknown_alias_set(&ir); + for name in [ + "OpenInner", + "OpenAlias", + "AliasOuter", + "Outer", + "AmbiguousInner", + "AmbiguousOuter", + ] { + assert!( + unknown.contains(name), + "{name} must retain the raw JSON type contract" + ); + } + assert!(!unknown.contains("Detail")); + assert!(!unknown.contains("NullableMapFallback")); + ir.schemas.reverse(); + } + } + + #[test] + fn extracted_enum_null_permission_has_a_typed_conversion_boundary() { + let parsed = crate::parser::parse_content_yaml( + r#"openapi: 3.1.0 +info: {title: Enum null permission, version: '1'} +paths: {} +components: + schemas: + ScalarChoice: + anyOf: [{type: integer}, {type: boolean}] + Parent: + type: object + required: [scalar_value] + properties: + scalar_value: + anyOf: [{$ref: '#/components/schemas/ScalarChoice'}, {type: 'null'}] + enum: [1, null] +"#, + ) + .unwrap(); + let ir = crate::ir::lower::lower(parsed).unwrap(); + let IrSchemaKind::Object(parent) = &ir.schemas["Parent"].kind else { + panic!("parent is an object"); + }; + let plan = TypeScriptPlan::new( + &ir, + EmitFlags { + property_naming_camel_case: true, + untagged_union_strategy: UntaggedUnionStrategy::Strict, + ..Default::default() + }, + ); + let conversion = json_conversion_expr( + &parent.properties["scalar_value"].type_expr, + sigil_quote!(TypeScript { input }).unwrap(), + "Parent", + &plan, + JsonDirection::FromWire, + ) + .unwrap(); + assert_rendered!( + TypeScript::new(), + conversion, + "import { convert$0FromJSON } from '../runtime/valueConversion$0';\n\nconvert$0FromJSON(input)\n" + ); + let file = plan.conversion_files(&TypeScript::new()).unwrap().remove(0); + assert_eq!( + file.with_lang(super::super::layout::TypeScript::from(TypeScript::new())) + .render(100) + .unwrap(), + "import type { ScalarChoice, ScalarChoice$Wire } from '../models/ScalarChoice';\nimport { scalarChoiceFromJSON, scalarChoiceToJSON } from '../models/ScalarChoice';\n\nexport type Value$0Wire = ScalarChoice$Wire | null;\n\nexport type Value$0 = ScalarChoice | null;\n\nexport function convert$0FromJSON(json: ScalarChoice$Wire | null): Value$0 {\n if (json == null) {\n return null;\n }\n return scalarChoiceFromJSON(json);\n}\n\nexport function convert$0ToJSON(value: ScalarChoice | null): Value$0Wire {\n if (value == null) {\n return null;\n }\n return scalarChoiceToJSON(value);\n}\n" + ); + } + fn additional_properties_ir() -> IrSpec { let parsed = crate::parser::parse_content_yaml(include_str!( "../../../../tests/fixtures/valid/typescript-additional-properties.yaml" @@ -3044,7 +1870,7 @@ mod render_tests { ..Default::default() }; for _ in 0..2 { - let convertible = build_convertible_set(&ir, flags); + let plan = TypeScriptPlan::new(&ir, flags); for name in [ "DetailAlias", "DetailAliasChain", @@ -3052,12 +1878,20 @@ mod render_tests { "NullableDetailAlias", ] { assert!( - convertible.contains(name), + plan.has_converter(name), "{name} must retain model conversion" ); } - assert!(!convertible.contains("AnyJsonValue")); - assert!(build_convertible_set(&ir, EmitFlags::default()).is_empty()); + // A representation that is unchanged may still have a union + // selection action. Symbol and effect planning are independent. + assert!(plan.has_converter("AnyJsonValue")); + assert!(plan.has_wire_type("AnyJsonValue")); + let preserved = TypeScriptPlan::new(&ir, EmitFlags::default()); + assert!( + ir.schemas + .keys() + .all(|name| !preserved.has_converter(name) && !preserved.has_wire_type(name)) + ); ir.schemas.reverse(); } } @@ -3069,15 +1903,16 @@ mod render_tests { property_naming_camel_case: true, ..Default::default() }; - let convertible = build_convertible_set(&ir, flags); + let plan = TypeScriptPlan::new(&ir, flags); let file = emit_model_file( &ir.schemas["DetailAliasChain"], &ir, flags, - &convertible, + &plan, &HashSet::new(), &TypeScript::new(), ) + .unwrap() .unwrap(); assert_eq!( file.render(100).unwrap(), @@ -3106,19 +1941,21 @@ export function detailAliasChainToJSON(value: DetailAliasChain): DetailAliasChai property_naming_camel_case: true, ..Default::default() }; - let convertible = build_convertible_set(&ir, flags); + let plan = TypeScriptPlan::new(&ir, flags); let file = emit_model_file( &ir.schemas["NullableDetailAlias"], &ir, flags, - &convertible, + &plan, &HashSet::new(), &TypeScript::new(), ) + .unwrap() .unwrap(); assert_eq!( file.render(100).unwrap(), - r#"import type { DetailAliasChain, DetailAliasChain$Wire } from './DetailAliasChain'; + r#"import { matches$5FromJSON, matches$5ToJSON } from '../runtime/unionMatch'; +import type { DetailAliasChain, DetailAliasChain$Wire } from './DetailAliasChain'; import { detailAliasChainFromJSON, detailAliasChainToJSON } from './DetailAliasChain'; export type NullableDetailAlias$Wire = DetailAliasChain$Wire | null; @@ -3126,11 +1963,25 @@ export type NullableDetailAlias$Wire = DetailAliasChain$Wire | null; export type NullableDetailAlias = DetailAliasChain | null; export function nullableDetailAliasFromJSON(json: NullableDetailAlias$Wire): NullableDetailAlias { - return json != null ? detailAliasChainFromJSON(json) : null; + const candidate: unknown = json; + if (matches$5FromJSON(candidate)) { + return detailAliasChainFromJSON(json as unknown as DetailAliasChain$Wire); + } + if (candidate === null) { + return null; + } + return json as unknown as NullableDetailAlias; } export function nullableDetailAliasToJSON(value: NullableDetailAlias): NullableDetailAlias$Wire { - return value != null ? detailAliasChainToJSON(value) : null; + const candidate: unknown = value; + if (matches$5ToJSON(candidate)) { + return detailAliasChainToJSON(value as unknown as DetailAliasChain); + } + if (candidate === null) { + return null; + } + return value as unknown as NullableDetailAlias$Wire; } "# ); @@ -3139,14 +1990,16 @@ export function nullableDetailAliasToJSON(value: NullableDetailAlias): NullableD #[test] fn planned_allof_type_keeps_readonly_dictionary_and_imports() { let ir = additional_properties_ir(); + let plan = TypeScriptPlan::new(&ir, EmitFlags::default()); let file = emit_model_file( &ir.schemas["AllOfMixedStringMap"], &ir, EmitFlags::default(), - &HashSet::new(), + &plan, &build_unknown_alias_set(&ir), &TypeScript::new(), ) + .unwrap() .unwrap(); assert_eq!( file.render(100).unwrap(), @@ -3169,34 +2022,35 @@ export function nullableDetailAliasToJSON(value: NullableDetailAlias): NullableD property_naming_camel_case: true, ..Default::default() }; - let convertible = build_convertible_set(&ir, flags); + let plan = TypeScriptPlan::new(&ir, flags); let mut schema = ir.schemas["AllOfJsonDictionary"].clone(); schema.kind = IrSchemaKind::Intersection(intersection); let file = emit_model_file( &schema, &ir, flags, - &convertible, + &plan, &build_unknown_alias_set(&ir), &TypeScript::new(), ) + .unwrap() .unwrap(); assert_eq!( file.render(100).unwrap(), - r#"import type { AnyJsonValue } from './AnyJsonValue'; + r#"import type { AnyJsonValue, AnyJsonValue$Wire } from './AnyJsonValue'; import type { Detail, Detail$Wire } from './Detail'; import { detailFromJSON, detailToJSON } from './Detail'; -export type AllOfJsonDictionary$Wire = Detail$Wire & Record & string; +export type AllOfJsonDictionary$Wire = Detail$Wire & Record & string; export type AllOfJsonDictionary = Detail & Record & string; export function allOfJsonDictionaryFromJSON(json: AllOfJsonDictionary$Wire): AllOfJsonDictionary { - return { ...detailFromJSON(json as Detail$Wire) } as AllOfJsonDictionary; + return {...detailFromJSON(json as Detail$Wire)} as AllOfJsonDictionary; } export function allOfJsonDictionaryToJSON(value: AllOfJsonDictionary): AllOfJsonDictionary$Wire { - return { ...detailToJSON(value as Detail) } as AllOfJsonDictionary$Wire; + return {...detailToJSON(value as Detail)} as AllOfJsonDictionary$Wire; } "# ); @@ -3395,9 +2249,22 @@ components: ))))), "import type { Detail } from './Detail';\n\nexport type Values = readonly (Detail | null)[];\n", ), + ( + IrTypeExpr::Array(Box::new(IrTypeExpr::Union(vec![ + IrTypeExpr::Primitive(IrPrimitive::Integer), + IrTypeExpr::Primitive(IrPrimitive::Number), + ]))), + "export type Values = readonly number[];\n", + ), + ( + IrTypeExpr::Nullable(Box::new(IrTypeExpr::Nullable(Box::new(IrTypeExpr::Any)))), + "export type Values = unknown;\n", + ), ]; for (expr, expected) in cases { - let ty = type_expr_to_typename(&expr); + let ir = additional_properties_ir(); + let plan = TypeScriptPlan::new(&ir, EmitFlags::default()); + let ty = plan.value(&expr, Scope::model("Values")).public_type(); let block = sigil_quote!(TypeScript { export type Values = $T(ty); }) @@ -3411,22 +2278,117 @@ components: let expr = IrTypeExpr::Map(Box::new(IrTypeExpr::Nullable(Box::new(IrTypeExpr::Named( "Detail".to_string(), ))))); - let convertible = HashSet::from(["Detail".to_string()]); + let mut ir = additional_properties_ir(); + ir.schemas.retain(|name, _| name == "Detail"); + ir.schemas.insert( + "Values".into(), + IrSchema { + name: "Values".into(), + description: None, + deprecated: false, + kind: IrSchemaKind::Alias(expr.clone()), + is_component: true, + }, + ); + let plan = TypeScriptPlan::new( + &ir, + EmitFlags { + property_naming_camel_case: true, + ..Default::default() + }, + ); for (direction, expected) in [ ( JsonDirection::FromWire, - "import { detailFromJSON } from './Detail';\n\nObject.fromEntries(\n Object.entries(values ?? {}).map(([k, v]) => [k, v != null ? detailFromJSON(v) : null]),\n)\n", + "import { convert$0FromJSON } from '../runtime/valueConversion$0';\n\nconvert$0FromJSON(values)\n", ), ( JsonDirection::ToWire, - "import { detailToJSON } from './Detail';\n\nObject.fromEntries(\n Object.entries(values ?? {}).map(([k, v]) => [k, v != null ? detailToJSON(v) : null]),\n)\n", + "import { convert$0ToJSON } from '../runtime/valueConversion$0';\n\nconvert$0ToJSON(values)\n", ), ] { let access = sigil_quote!(TypeScript { values }).unwrap(); - let block = - json_conversion_expr(&expr, access, "Envelope", &convertible, direction).unwrap(); + let block = json_conversion_expr(&expr, access, "Envelope", &plan, direction).unwrap(); assert_rendered!(TypeScript::new(), width = 200, block, expected); } + let mut helper_files = plan.conversion_files(&TypeScript::new()).unwrap(); + assert_eq!(helper_files.len(), 2); + let file = helper_files.remove(0); + assert_eq!( + file.with_lang(super::super::layout::TypeScript::from(TypeScript::new())) + .render(100) + .unwrap(), + "import type { Detail, Detail$Wire } from '../models/Detail';\nimport { convert$1FromJSON, convert$1ToJSON } from './valueConversion$1';\n\nexport type Value$0Wire = Record;\n\nexport type Value$0 = Record;\n\nexport function convert$0FromJSON(json: Record): Value$0 {\n return Object.fromEntries(\n Object.entries(json ?? {}).map(([key, item]) => [key, convert$1FromJSON(item)]),\n );\n}\n\nexport function convert$0ToJSON(value: Record): Value$0Wire {\n return Object.fromEntries(\n Object.entries(value ?? {}).map(([key, item]) => [key, convert$1ToJSON(item)]),\n );\n}\n" + ); + assert_eq!( + helper_files + .remove(0) + .with_lang(super::super::layout::TypeScript::from(TypeScript::new())) + .render(100) + .unwrap(), + "import type { Detail, Detail$Wire } from '../models/Detail';\nimport { detailFromJSON, detailToJSON } from '../models/Detail';\n\nexport type Value$1Wire = Detail$Wire | null;\n\nexport type Value$1 = Detail | null;\n\nexport function convert$1FromJSON(json: Detail$Wire | null): Value$1 {\n if (json == null) {\n return null;\n }\n return detailFromJSON(json);\n}\n\nexport function convert$1ToJSON(value: Detail | null): Value$1Wire {\n if (value == null) {\n return null;\n }\n return detailToJSON(value);\n}\n" + ); + } + + #[test] + fn open_external_types_qualify_utilities_and_track_payload_imports() { + let parsed = crate::parser::parse_content_yaml(include_str!( + "../../../../tests/fixtures/valid/typescript-untagged-union.yaml" + )) + .unwrap(); + let ir = crate::ir::lower::lower(parsed).unwrap(); + let plan = TypeScriptPlan::new( + &ir, + EmitFlags { + property_naming_camel_case: true, + ..Default::default() + }, + ); + let detail = IrTypeExpr::Named("Detail".to_string()); + let items = IrTypeExpr::Array(Box::new(detail.clone())); + let ty = super::super::tagged::open_external_variant_type( + &plan, + "extension_key", + plan.value(&detail, Scope::model("Readonly")).wire_type(), + Some(plan.value(&items, Scope::model("Readonly")).wire_type()), + ); + let block = sigil_quote!(TypeScript { export type Readonly = $T(ty); }) + .expect("qualified open external type quote builds"); + assert_rendered!( + TypeScript::new(), + width = 200, + block, + "import type { Detail$Wire } from './Detail';\n\nexport type Readonly = globalThis.Readonly> & globalThis.Readonly>;\n" + ); + } + + #[test] + fn raw_tagged_guard_keeps_semantic_payload_types_and_imports() { + let parsed = crate::parser::parse_content_yaml(include_str!( + "../../../../tests/fixtures/valid/typescript-untagged-union.yaml" + )) + .unwrap(); + let ir = crate::ir::lower::lower(parsed).unwrap(); + let schema = &ir.schemas["AdjacentContainer"]; + let IrSchemaKind::TaggedUnion(union) = &schema.kind else { + panic!("adjacent fixture") + }; + let plan = TypeScriptPlan::new( + &ir, + EmitFlags { + property_naming_camel_case: true, + untagged_union_strategy: UntaggedUnionStrategy::Raw, + ..Default::default() + }, + ); + let guards = super::super::tagged::guards(schema, &ir, union, &plan).unwrap(); + assert_eq!(guards.len(), 1); + assert_rendered!( + TypeScript::new(), + width = 100, + guards[0].clone(), + "import type { Detail$Wire } from './Detail';\n\nexport function isData(\n value: AdjacentContainer,\n): value is globalThis.Record<'eventType', 'data'> &\n globalThis.Record<'eventData', readonly (globalThis.Record | string)[]> {\n return value['eventType'] === 'data';\n}\n" + ); } #[test] @@ -3439,7 +2401,13 @@ components: let IrSchemaKind::Object(obj) = &ir.schemas["MixedModelMap"].kind else { panic!("object fixture") }; - let convertible = HashSet::from(["Detail".to_string()]); + let plan = TypeScriptPlan::new( + &ir, + EmitFlags { + property_naming_camel_case: true, + ..Default::default() + }, + ); for (wire, expected) in [ ( false, @@ -3454,7 +2422,7 @@ components: obj, "MixedModelMap", wire, - &convertible, + &plan, &HashSet::new(), ) .unwrap(); diff --git a/src/generators/typescript/fetch/sigil_emit_api.rs b/src/generators/typescript/fetch/sigil_emit_api.rs index 740e91186..28e77fb55 100644 --- a/src/generators/typescript/fetch/sigil_emit_api.rs +++ b/src/generators/typescript/fetch/sigil_emit_api.rs @@ -28,7 +28,7 @@ use crate::generators::response_names::response_entry_kind as response_detail_ki use crate::generators::type_application; use crate::generators::{ArtifactKind, GeneratorError}; use crate::ir::types::{ - IrOperation, IrParameter, IrPrimitive, IrRequestBody, IrResponse, IrSpec, IrTypeExpr, + IrOperation, IrParameter, IrRequestBody, IrResponse, IrSpec, IrTypeExpr, ParameterLocation as IrParameterLocation, }; use heck::{ToLowerCamelCase as _, ToPascalCase as _}; @@ -45,7 +45,8 @@ use sigil_stitch::spec::type_spec::TypeSpec; use sigil_stitch::spec::where_spec::{CallableParam, CallableParamPresence}; use sigil_stitch::type_name::TypeName; -use super::sigil_emit::{EmitFlags, build_convertible_set, fn_base_name}; +use super::plan::{Direction, Scope, TypeScriptPlan}; +use super::sigil_emit::EmitFlags; const RUNTIME_MOD: &str = "../runtime/runtime"; @@ -55,14 +56,26 @@ pub fn generate_api_files( property_naming_camel_case: bool, request_inputs: &RequestInputPlan, ts: &TypeScript, +) -> Result, GeneratorError> { + let plan = TypeScriptPlan::new( + ir, + EmitFlags { + property_naming_camel_case, + ..EmitFlags::default() + }, + ); + generate_api_files_with_plan(ir, request_inputs, &plan, ts) +} + +pub(super) fn generate_api_files_with_plan( + ir: &IrSpec, + request_inputs: &RequestInputPlan, + plan: &TypeScriptPlan<'_>, + ts: &TypeScript, ) -> Result, GeneratorError> { let header = super::project_files::render_file_header(&ir.info); let by_tag = group_by_tag(&ir.operations); - let flags = EmitFlags { - property_naming_camel_case, - ..EmitFlags::default() - }; - let convertible = build_convertible_set(ir, flags); + let property_naming_camel_case = plan.flags().property_naming_camel_case; let mut files = Vec::with_capacity(by_tag.len()); for (tag, ops) in &by_tag { @@ -72,7 +85,7 @@ pub fn generate_api_files( ir, property_naming_camel_case, request_inputs, - &convertible, + plan, ts, ) .map_err(|source| { @@ -160,7 +173,7 @@ fn emit_api_file( ir: &IrSpec, property_naming_camel_case: bool, request_inputs: &RequestInputPlan, - convertible: &HashSet, + plan: &TypeScriptPlan<'_>, ts: &TypeScript, ) -> Result { let class_name = format!("{}Api", tag.to_pascal_case()); @@ -170,7 +183,7 @@ fn emit_api_file( // Request interfaces — one per op that has at least one parameter / body. for op in ops { - if let Some(req_iface) = build_request_interface(op, request_inputs) { + if let Some(req_iface) = build_request_interface(op, request_inputs, plan) { fb = fb.add_type(req_iface); } } @@ -178,49 +191,16 @@ fn emit_api_file( // Per-operation raw response type aliases — emit each union member on its // own line so readers can scan each `Wrapper & { status: N }` pair without // the pretty printer splitting intersections across lines. - fb = fb.add_code(build_response_aliases_block(ops)?); + fb = fb.add_code(build_response_aliases_block(ops, plan)?); for accessor in build_success_header_accessors(ops, ir)? { fb = fb.add_function(accessor); } - for (detail_alias, error_class) in build_error_types(ops, ir)? { + for (detail_alias, error_class) in build_error_types(ops, ir, plan)? { fb = fb.add_code(detail_alias); fb = fb.add_type(error_class); } - fb = fb.add_type(build_api_interface(&interface_name, ops)?); - - // When property_naming_camel_case is enabled, add explicit imports for - // converter functions and $Wire types referenced in raw code blocks. - if property_naming_camel_case { - let request_types = - collect_request_named_types(ops, ir, property_naming_camel_case, convertible); - let response_types = collect_response_named_types(ops, convertible); - - for name in request_types.union(&response_types) { - let pascal = name.to_pascal_case(); - let module = format!("../models/{pascal}"); - let base = fn_base_name(&pascal); - let in_request = request_types.contains(name); - let in_response = response_types.contains(name); - - if in_response { - let from_fn = format!("{base}FromJSON"); - let wire_name = format!("{}$Wire", pascal); - fb = fb.add_import(sigil_stitch::spec::import_spec::ImportSpec::named( - &module, &from_fn, - )); - fb = fb.add_import(sigil_stitch::spec::import_spec::ImportSpec::named_type( - &module, &wire_name, - )); - } - if in_request { - let to_fn = format!("{base}ToJSON"); - fb = fb.add_import(sigil_stitch::spec::import_spec::ImportSpec::named( - &module, &to_fn, - )); - } - } - } + fb = fb.add_type(build_api_interface(&interface_name, ops, plan)?); // ApiClass stays structural so modifiers / docs / constructor delegation // use sigil's machinery. @@ -230,7 +210,7 @@ fn emit_api_file( ops, ir, property_naming_camel_case, - convertible, + plan, )?); fb.build() @@ -260,11 +240,14 @@ fn error_detail_alias_name(op: &IrOperation) -> String { /// Each member is a `%T` slot so imports still flow through the collector, /// and each sits on its own line so intersections (`Wrapper & { status: N }`) /// stay intact. -fn build_response_aliases_block(ops: &[&IrOperation]) -> Result { +fn build_response_aliases_block( + ops: &[&IrOperation], + plan: &TypeScriptPlan<'_>, +) -> Result { let mut cb = CodeBlock::builder(); for op in ops { let alias = raw_response_alias_name(op); - let members = raw_response_members(op); + let members = raw_response_members(op, plan); let alias_block = if members.len() == 1 { let member = members.into_iter().next().unwrap(); sigil_quote!(TypeScript { @@ -322,6 +305,7 @@ fn build_success_header_accessors( fn build_error_types( ops: &[&IrOperation], ir: &IrSpec, + plan: &TypeScriptPlan<'_>, ) -> Result, SigilStitchError> { let mut error_types = Vec::new(); for op in ops { @@ -332,7 +316,8 @@ fn build_error_types( .iter() .filter(|resp| !is_success_status(&resp.status)) .collect(); - let detail_alias_block = build_error_detail_alias_block(&detail_alias, &error_responses)?; + let detail_alias_block = + build_error_detail_alias_block(&detail_alias, &error_responses, plan)?; let error_class = build_error_class(&class_name, &detail_alias, op, ir)?; error_types.push((detail_alias_block, error_class)); } @@ -342,6 +327,7 @@ fn build_error_types( fn build_error_detail_alias_block( detail_alias: &str, error_responses: &[&IrResponse], + plan: &TypeScriptPlan<'_>, ) -> Result { let mut cb = CodeBlock::builder(); let mut used = HashSet::new(); @@ -354,7 +340,7 @@ fn build_error_detail_alias_block( .ok() .map(|n| n.to_string()) .unwrap_or_else(|| "number".to_string()); - members.push((kind, status_ty, response_value_type(resp))); + members.push((kind, status_ty, response_value_type(resp, plan))); } members.push(( "unexpected".to_string(), @@ -536,7 +522,7 @@ fn ts_header_body( /// Compute the deduplicated list of union members for an operation's raw /// response type (wrapper intersected with status literal). -fn raw_response_members(op: &IrOperation) -> Vec { +fn raw_response_members(op: &IrOperation, plan: &TypeScriptPlan<'_>) -> Vec { let mut members: Vec = Vec::new(); let mut any_body = false; let mut has_default = false; @@ -549,7 +535,7 @@ fn raw_response_members(op: &IrOperation) -> Vec { if resp.status.eq_ignore_ascii_case("default") { has_default = true; } - let kind = classify_response(resp); + let kind = classify_response(resp, plan); if !matches!(kind, ResponseKind::None) { any_body = true; } @@ -572,6 +558,7 @@ fn raw_response_members(op: &IrOperation) -> Vec { fn build_request_interface( op: &IrOperation, request_inputs: &RequestInputPlan, + plan: &TypeScriptPlan<'_>, ) -> Option { let has_params = !op.parameters.is_empty() || op.request_body.is_some(); if !has_params { @@ -588,7 +575,11 @@ fn build_request_interface( if matches!(param.location, IrParameterLocation::Cookie) { continue; } - tb = tb.add_field(build_param_field(param, &resolved_param(&names, param))); + tb = tb.add_field(build_param_field( + param, + &resolved_param(&names, param), + plan, + )); } if let Some(rb) = &op.request_body { tb = tb.add_field(build_body_field( @@ -596,14 +587,15 @@ fn build_request_interface( rb, &resolved_body(&names), request_inputs, + plan, )); } tb.build().ok() } -fn build_param_field(param: &IrParameter, name: &str) -> FieldSpec { - let ty = type_expr_to_typename(¶m.type_expr); +fn build_param_field(param: &IrParameter, name: &str, plan: &TypeScriptPlan<'_>) -> FieldSpec { + let ty = plan.value(¶m.type_expr, Scope::api()).public_type(); let mut fb = FieldSpec::builder(name, ty); if !param.required { fb = fb.is_optional(); @@ -652,6 +644,7 @@ fn build_body_field( rb: &IrRequestBody, name: &str, request_inputs: &RequestInputPlan, + plan: &TypeScriptPlan<'_>, ) -> FieldSpec { let ty = preferred_request_media_type(rb) .and_then(|mt| { @@ -661,7 +654,9 @@ fn build_body_field( TypeName::importable_type(&format!("../models/{ts_name}"), &ts_name) }) } else { - rb.content.get(mt.as_str()).map(type_expr_to_typename) + rb.content + .get(mt.as_str()) + .map(|expr| plan.value(expr, Scope::api()).public_type()) } }) .unwrap_or_else(|| TypeName::primitive("unknown")); @@ -682,6 +677,7 @@ fn build_body_field( fn build_api_interface( interface_name: &str, ops: &[&IrOperation], + plan: &TypeScriptPlan<'_>, ) -> Result { let mut interface = TypeSpec::builder(interface_name, TypeKind::Interface).visibility(Visibility::Public); @@ -695,7 +691,7 @@ fn build_api_interface( ); let return_type = type_application( TypeName::primitive("Promise"), - vec![convenience_body_type(op)], + vec![convenience_body_type(op, plan)], ); interface = interface.add_field( FieldSpec::builder(&method_base, api_callable_type(op, return_type)).build()?, @@ -732,7 +728,7 @@ fn build_api_class( ops: &[&IrOperation], ir: &IrSpec, property_naming_camel_case: bool, - convertible: &HashSet, + plan: &TypeScriptPlan<'_>, ) -> Result { let mut tb = TypeSpec::builder(class_name, TypeKind::Class) .visibility(Visibility::Public) @@ -741,13 +737,8 @@ fn build_api_class( .add_method(build_constructor()); for op in ops { - tb = tb.add_method(build_raw_method( - op, - ir, - property_naming_camel_case, - convertible, - )?); - tb = tb.add_method(build_convenience_method(op)?); + tb = tb.add_method(build_raw_method(op, ir, property_naming_camel_case, plan)?); + tb = tb.add_method(build_convenience_method(op, plan)?); } tb.build() @@ -780,7 +771,7 @@ fn build_raw_method( op: &IrOperation, ir: &IrSpec, property_naming_camel_case: bool, - convertible: &HashSet, + plan: &TypeScriptPlan<'_>, ) -> Result { let method_base = op.operation_id.to_lower_camel_case(); let method_name = format!("{}Raw", method_base); @@ -793,13 +784,13 @@ fn build_raw_method( fb = fb.returns(vec![raw_return_type(op)]); let mut body = CodeBlock::builder(); - emit_required_param_checks(&mut body, op, &method_name); + emit_required_param_checks(&mut body, op, &method_name, plan)?; emit_url_path(&mut body, op)?; emit_query_params(&mut body, op)?; emit_headers(&mut body, op)?; - emit_request_body(&mut body, op, ir, property_naming_camel_case, convertible)?; + emit_request_body(&mut body, op, ir, property_naming_camel_case, plan)?; emit_make_request(&mut body, op, op.request_body.is_some()); - emit_response_handler(&mut body, op, property_naming_camel_case, convertible)?; + emit_response_handler(&mut body, op, property_naming_camel_case, plan)?; fb = fb.body(body.build()?); @@ -834,31 +825,55 @@ fn emit_required_param_checks( cb: &mut sigil_stitch::code_block::CodeBlockBuilder, op: &IrOperation, method_name: &str, -) { + plan: &TypeScriptPlan<'_>, +) -> Result<(), SigilStitchError> { let names = resolve_param_names(op); let required_named = op .parameters .iter() .filter(|p| p.required && !matches!(p.location, IrParameterLocation::Cookie)) - .map(|p| resolved_param(&names, p)) + .map(|p| (resolved_param(&names, p), false)) .collect::>(); let mut all_required = required_named; if let Some(rb) = &op.request_body && rb.required { - all_required.push(resolved_body(&names)); + let nullable = preferred_request_media_type(rb) + .filter(|media| is_json_media_type(media)) + .and_then(|media| rb.content.get(media.as_str())) + .is_some_and(|expr| plan.value(expr, Scope::api()).accepts_null()); + all_required.push((resolved_body(&names), nullable)); } - for pname in all_required { - let access = request_parameters_access(&pname); - cb.add( - &format!( - "if ({0} === undefined || {0} === null) {{\n throw new %T(\n '{1}',\n 'Required parameter \"{1}\" was null or undefined when calling {2}().',\n );\n}}\n", - access, pname, method_name - ), - vec![Arg::TypeName(rt_value("RequiredError"))], - ); + for (pname, nullable) in all_required { + let access = sigil_quote!(TypeScript { requestParameters[$S(pname.as_str())] })?; + let condition = if nullable { + sigil_quote!(TypeScript { $C(access) === undefined })? + } else { + sigil_quote!(TypeScript { $C(access.clone()) === undefined || $C(access) === null })? + }; + let missing = if nullable { + "undefined" + } else { + "null or undefined" + }; + let message = + format!("Required parameter \"{pname}\" was {missing} when calling {method_name}()."); + let error = super::layout::call( + sigil_quote!(TypeScript { new $T(rt_value("RequiredError")) })?, + &[ + sigil_quote!(TypeScript { $S(pname) })?, + sigil_quote!(TypeScript { $S(message) })?, + ], + 12, + )?; + cb.add_code(sigil_quote!(TypeScript { + if ($C(condition)) { + throw $C(error); + } + })?); } + Ok(()) } fn emit_url_path( @@ -972,7 +987,7 @@ fn emit_request_body( op: &IrOperation, ir: &IrSpec, property_naming_camel_case: bool, - convertible: &HashSet, + plan: &TypeScriptPlan<'_>, ) -> Result<(), SigilStitchError> { if let Some(ref body) = op.request_body { cb.add("// Prepare request body\n", vec![]); @@ -986,36 +1001,41 @@ fn emit_request_body( if let Some(parts) = multipart_parts_for(body, &media_type, ir, property_naming_camel_case) { - if !body.required { - cb.add_code(sigil_quote!(TypeScript { - let requestBody: Blob | undefined = undefined; - })?); - cb.add( - &format!("if ({access} !== undefined && {access} !== null) {{\n"), - vec![], - ); - emit_multipart_blob_setup(cb)?; - } else { - emit_multipart_blob_setup(cb)?; - } - for part in parts { - let part_access = format!("{access}{}", ts_property_access(&part.field_name)); + cb.add_code(sigil_quote!(TypeScript { const multipartBody = requestParameters[$S(body_name.as_str())]; })?); + let mut multipart = CodeBlock::builder(); + emit_multipart_blob_setup(&mut multipart)?; + for (index, part) in parts.into_iter().enumerate() { + let part_name = format!("multipartValue${index}"); + multipart.add_code(sigil_quote!(TypeScript { const $N(part_name.clone()) = multipartBody[$S(part.field_name.as_str())]; })?); + let mut part_body = CodeBlock::builder(); + let column = + if body.required { 4 } else { 6 } + if part.required { 0 } else { 2 }; + emit_multipart_blob_part( + &mut part_body, + &part, + sigil_quote!(TypeScript { $N(part_name.clone()) })?, + plan, + index, + column, + )?; + let part_body = part_body.build()?; if part.required { - emit_multipart_blob_part(cb, &part, &part_access, convertible)?; + multipart.add_code(part_body); } else { - cb.add( - &format!( - "if ({part_access} !== undefined && {part_access} !== null) {{\n" - ), - vec![], - ); - emit_multipart_blob_part(cb, &part, &part_access, convertible)?; - cb.add("}\n", vec![]); + multipart.add_code(sigil_quote!(TypeScript { + if ($N(part_name.clone()) !== undefined && $N(part_name) !== null) { $C(part_body) } + })?); } } - emit_multipart_blob_finish(cb, body.required)?; + emit_multipart_blob_finish(&mut multipart, body.required)?; + let multipart = multipart.build()?; if !body.required { - cb.add("}\n", vec![]); + cb.add_code(sigil_quote!(TypeScript { + let requestBody: Blob | undefined = undefined; + if (multipartBody !== undefined && multipartBody !== null) { $C(multipart) } + })?); + } else { + cb.add_code(multipart); } } else { emit_unsupported_ts_body( @@ -1042,11 +1062,30 @@ fn emit_request_body( None } }); - if let Some(to_json) = - json_type.and_then(|ty| body_to_json_expr(ty, &access, convertible)) - { + if let Some(ty) = json_type { + let access = + sigil_quote!(TypeScript { requestParameters[$S(body_name.as_str())] })?; + let value_plan = plan.value(ty, Scope::api()); + let value = if body.required { + value_plan.convert_at(access, Direction::ToWire, 23)? + } else { + let converted = value_plan.convert_at( + sigil_quote!(TypeScript { bodyValue })?, + Direction::ToWire, + 13, + )?; + let body = sigil_quote!(TypeScript { + const bodyValue = $C(access); + if (bodyValue === undefined) { + return undefined; + } + return $C(converted); + })?; + let arrow = super::layout::block_arrow(sigil_quote!(TypeScript { () })?, body); + sigil_quote!(TypeScript { ($C(arrow))() })? + }; cb.add_code(sigil_quote!(TypeScript { - const requestBody = $L(to_json.as_str()); + const requestBody = $C(value); })?); } else { cb.add_code(sigil_quote!(TypeScript { @@ -1119,8 +1158,10 @@ fn emit_multipart_blob_finish( fn emit_multipart_blob_part( cb: &mut sigil_stitch::code_block::CodeBlockBuilder, part: &MultipartPart, - part_access: &str, - convertible: &HashSet, + part_expr: CodeBlock, + plan: &TypeScriptPlan<'_>, + index: usize, + column: usize, ) -> Result<(), SigilStitchError> { if part.value_encoding == MultipartValueEncoding::Unsupported { cb.add_code(sigil_quote!(TypeScript { @@ -1129,7 +1170,6 @@ fn emit_multipart_blob_part( return Ok(()); } if part.is_binary { - cb.add("{\n", vec![]); let header_prefix = format!( "\r\nContent-Disposition: form-data; name=\"{}\"; filename=\"", multipart_header_quoted(&part.wire_name) @@ -1138,21 +1178,32 @@ fn emit_multipart_blob_part( "\"\r\nContent-Type: {}\r\n\r\n", multipart_header_value(&part.content_type) ); - let filename_literal = ts_string_literal(&part.wire_name); - cb.add_code( - sigil_quote!(TypeScript { - const multipartFilename = $T(rt_value("uploadFileFilename"))($L(part_access), $L(filename_literal.as_str())); - }) - ?, - ); - let header_prefix_literal = ts_string_literal(&header_prefix); - let header_suffix_literal = ts_string_literal(&header_suffix); - cb.add_code( - sigil_quote!(TypeScript { - multipartChunks.push($S("--") + multipartBoundary + $L(header_prefix_literal.as_str()) + $T(rt_value("multipartHeaderValue"))(multipartFilename) + $L(header_suffix_literal.as_str())); - }) - ?, - ); + let filename_name = format!("multipartFilename${index}"); + let filename = super::layout::call( + sigil_quote!(TypeScript { $T(rt_value("uploadFileFilename")) })?, + &[ + part_expr.clone(), + sigil_quote!(TypeScript { $S(part.wire_name.clone()) })?, + ], + column + filename_name.len() + 9, + )?; + cb.add_code(super::layout::const_value( + &filename_name, + filename, + column, + )?); + let header = super::layout::call( + sigil_quote!(TypeScript { multipartChunks.push })?, + &[ + sigil_quote!(TypeScript { $S("--") })?, + sigil_quote!(TypeScript { multipartBoundary })?, + sigil_quote!(TypeScript { $S(header_prefix) })?, + sigil_quote!(TypeScript { $T(rt_value("multipartHeaderValue"))($N(filename_name)) })?, + sigil_quote!(TypeScript { $S(header_suffix) })?, + ], + column, + )?; + cb.add_code(sigil_quote!(TypeScript { $C(header); })?); } else { let disposition = format!( "form-data; name=\"{}\"", @@ -1163,34 +1214,45 @@ fn emit_multipart_blob_part( disposition, multipart_header_value(&part.content_type) ); - let header_tail_literal = ts_string_literal(&header_tail); - cb.add_code(sigil_quote!(TypeScript { - multipartChunks.push($S("--") + multipartBoundary + $L(header_tail_literal)); - })?); + let header = super::layout::call( + sigil_quote!(TypeScript { multipartChunks.push })?, + &[ + sigil_quote!(TypeScript { $S("--") })?, + sigil_quote!(TypeScript { multipartBoundary })?, + sigil_quote!(TypeScript { $S(header_tail) })?, + ], + column, + )?; + cb.add_code(sigil_quote!(TypeScript { $C(header); })?); } if part.is_binary { - cb.add_code(sigil_quote!(TypeScript { - multipartChunks.push($T(rt_value("uploadFileData"))($L(part_access))); - })?); + let data = sigil_quote!(TypeScript { $T(rt_value("uploadFileData"))($C(part_expr)) })?; + let pushed = super::layout::call( + sigil_quote!(TypeScript { multipartChunks.push })?, + &[data], + column, + )?; + cb.add_code(sigil_quote!(TypeScript { $C(pushed); })?); } else { let value_expr = if part.value_encoding == MultipartValueEncoding::Json { - let json_value = multipart_part_to_json_expr(part, part_access, convertible) - .unwrap_or_else(|| part_access.to_string()); - format!("JSON.stringify({json_value})") + let json_value = plan + .value(&part.type_expr, Scope::api()) + .convert(part_expr, Direction::ToWire)?; + sigil_quote!(TypeScript { JSON.stringify($C(json_value)) })? } else { - format!("String({part_access})") + sigil_quote!(TypeScript { String($C(part_expr)) })? }; - cb.add_code(sigil_quote!(TypeScript { - multipartChunks.push($L(value_expr)); - })?); + let pushed = super::layout::call( + sigil_quote!(TypeScript { multipartChunks.push })?, + &[value_expr], + column, + )?; + cb.add_code(sigil_quote!(TypeScript { $C(pushed); })?); } let crlf_literal = ts_string_literal("\r\n"); cb.add_code(sigil_quote!(TypeScript { multipartChunks.push($L(crlf_literal)); })?); - if part.is_binary { - cb.add("}\n", vec![]); - } Ok(()) } @@ -1209,16 +1271,12 @@ fn emit_response_handler( cb: &mut sigil_stitch::code_block::CodeBlockBuilder, op: &IrOperation, property_naming_camel_case: bool, - convertible: &HashSet, + plan: &TypeScriptPlan<'_>, ) -> Result<(), SigilStitchError> { cb.add_code(sigil_quote!(TypeScript { $comment("Handle responses") })?); - cb.add_code(build_error_dispatch( - op, - property_naming_camel_case, - convertible, - )?); + cb.add_code(build_error_dispatch(op, property_naming_camel_case, plan)?); let mut numeric: Vec<(u16, &IrResponse)> = Vec::new(); let mut wildcards: Vec<(&str, &IrResponse)> = Vec::new(); @@ -1245,7 +1303,7 @@ fn emit_response_handler( .any(|r| !r.content.is_empty()); let fallback = if let Some(response) = default { - build_response_return(response, property_naming_camel_case, convertible)? + build_response_return(response, property_naming_camel_case, plan)? } else { build_fallback_return(fallback_has_body)? }; @@ -1262,7 +1320,7 @@ fn emit_response_handler( { branches.push(( response_status_condition(&response.status)?, - build_response_return(response, property_naming_camel_case, convertible)?, + build_response_return(response, property_naming_camel_case, plan)?, )); } cb.add_code(build_status_dispatch(branches, fallback)?); @@ -1327,7 +1385,7 @@ fn is_success_status(status: &str) -> bool { fn build_error_dispatch( op: &IrOperation, property_naming_camel_case: bool, - convertible: &HashSet, + plan: &TypeScriptPlan<'_>, ) -> Result { let mut numeric = Vec::new(); let mut wildcards = Vec::new(); @@ -1357,19 +1415,13 @@ fn build_error_dispatch( let kind = unique_ts_kind(&response_detail_kind(&resp.status), &mut used); branches.push(( response_status_condition(&resp.status)?, - build_error_throw_for_response( - op, - resp, - &kind, - property_naming_camel_case, - convertible, - )?, + build_error_throw_for_response(op, resp, &kind, property_naming_camel_case, plan)?, )); } let fallback = if let Some(resp) = default { let kind = unique_ts_kind(&response_detail_kind(&resp.status), &mut used); - build_error_throw_for_response(op, resp, &kind, property_naming_camel_case, convertible)? + build_error_throw_for_response(op, resp, &kind, property_naming_camel_case, plan)? } else { build_error_throw( op, @@ -1393,7 +1445,7 @@ fn build_error_throw_for_response( resp: &IrResponse, kind: &str, property_naming_camel_case: bool, - convertible: &HashSet, + plan: &TypeScriptPlan<'_>, ) -> Result { let status = if let Ok(code) = resp.status.parse::() { sigil_quote!(TypeScript { response.status as $L(code.to_string()) }) @@ -1404,8 +1456,8 @@ fn build_error_throw_for_response( op, kind, status, - response_value_type(resp), - error_value_expr(resp, property_naming_camel_case, convertible)?, + response_value_type(resp, plan), + error_value_expr(resp, property_naming_camel_case, plan)?, ) } @@ -1436,14 +1488,17 @@ fn build_error_throw( fn error_value_expr( resp: &IrResponse, property_naming_camel_case: bool, - convertible: &HashSet, + plan: &TypeScriptPlan<'_>, ) -> Result { - let kind = classify_response(resp); + let kind = classify_response(resp, plan); if property_naming_camel_case && let ResponseKind::Json(Some(_)) = &kind - && let Some(from_json) = response_from_json_transformer(resp, convertible)? + && let Some(from_json) = response_from_json_transformer(resp, plan)? { - let wrapper_type = rt_value("JSONApiResponse"); + let wrapper_type = type_application( + rt_value("JSONApiResponse"), + vec![response_value_type(resp, plan)], + ); return sigil_quote!(TypeScript { new $T(wrapper_type)(errorRaw.clone(), $C(from_json)).value() }); @@ -1464,9 +1519,9 @@ fn error_value_expr( fn build_response_return( resp: &IrResponse, property_naming_camel_case: bool, - convertible: &HashSet, + plan: &TypeScriptPlan<'_>, ) -> Result { - let kind = classify_response(resp); + let kind = classify_response(resp, plan); let status_ty = match resp.status.parse::() { Ok(n) => n.to_string(), Err(_) => "number".to_string(), @@ -1476,7 +1531,7 @@ fn build_response_return( // inject a fromJSON transformer: `new JSONApiResponse(response, (json) => petFromJSON(json as Pet$Wire))` if property_naming_camel_case && let ResponseKind::Json(Some(_)) = &kind - && let Some(from_json) = response_from_json_transformer(resp, convertible)? + && let Some(from_json) = response_from_json_transformer(resp, plan)? { let wrapper_type = match kind { ResponseKind::Json(Some(body_ty)) => { @@ -1510,7 +1565,7 @@ fn build_response_return( let flat = sigil_quote!(TypeScript { return new $T(wrapper_value.clone())(response) as $T(wrapper_type.clone())$L(" & ")$T(status_type.clone()); })?; - if super::layout::fragment_width(&flat) + 6 > 100 { + if super::layout::fragment_width(&flat)? + 6 > 100 { let status_type = TypeName::raw(&format!("{{\n status: {status_ty};\n}}")); sigil_quote!(TypeScript { return new $T(wrapper_value)(response) as $T(wrapper_type)$L(" & ")$T(status_type); }) } else { @@ -1544,7 +1599,10 @@ fn build_fallback_return(any_body: bool) -> Result // Convenience method // ============================================================================ -fn build_convenience_method(op: &IrOperation) -> Result { +fn build_convenience_method( + op: &IrOperation, + plan: &TypeScriptPlan<'_>, +) -> Result { let method_base = op.operation_id.to_lower_camel_case(); let raw_name = format!("{}Raw", method_base); @@ -1553,7 +1611,7 @@ fn build_convenience_method(op: &IrOperation) -> Result TypeName { TypeName::intersection(vec![wrapper, rt_type("HttpSuccessResponse")]) } -fn convenience_body_type(op: &IrOperation) -> TypeName { +fn convenience_body_type(op: &IrOperation, plan: &TypeScriptPlan<'_>) -> TypeName { let mut members: Vec = Vec::new(); let mut any_body = false; for resp in op @@ -1700,7 +1758,7 @@ fn convenience_body_type(op: &IrOperation) -> TypeName { .iter() .filter(|resp| is_success_status(&resp.status)) { - match classify_response(resp) { + match classify_response(resp, plan) { ResponseKind::Json(Some(body)) => { any_body = true; members.push(body); @@ -1729,8 +1787,8 @@ fn convenience_body_type(op: &IrOperation) -> TypeName { } } -fn response_value_type(resp: &IrResponse) -> TypeName { - match classify_response(resp) { +fn response_value_type(resp: &IrResponse, plan: &TypeScriptPlan<'_>) -> TypeName { + match classify_response(resp, plan) { ResponseKind::Json(Some(body)) => body, ResponseKind::Json(None) => TypeName::primitive("unknown"), ResponseKind::Text => TypeName::primitive("string"), @@ -1777,12 +1835,12 @@ enum ResponseKind { None, } -fn classify_response(resp: &IrResponse) -> ResponseKind { +fn classify_response(resp: &IrResponse, plan: &TypeScriptPlan<'_>) -> ResponseKind { let Some((media_type, ty)) = pick_response_content(resp) else { return ResponseKind::None; }; if is_json_media_type(&media_type) { - return ResponseKind::Json(Some(type_expr_to_typename(ty))); + return ResponseKind::Json(Some(plan.value(ty, Scope::api()).public_type())); } match media_type_base(&media_type).as_str() { "text/plain" @@ -1913,52 +1971,6 @@ fn rt_type(name: &str) -> TypeName { // IrTypeExpr → TypeName // ============================================================================ -fn type_expr_to_typename(expr: &IrTypeExpr) -> TypeName { - match expr { - IrTypeExpr::Named(name) => { - let ts_name = name.to_pascal_case(); - TypeName::importable_type(&format!("../models/{ts_name}"), &ts_name) - } - IrTypeExpr::Primitive(p) => TypeName::primitive(primitive_to_ts(p)), - IrTypeExpr::Array(inner) => TypeName::array(type_expr_to_typename(inner)), - IrTypeExpr::Nullable(inner) => TypeName::union(vec![ - type_expr_to_typename(inner), - TypeName::primitive("null"), - ]), - IrTypeExpr::StringLiteral(s) => TypeName::string_literal(s.clone()), - IrTypeExpr::StringEnum(values) => TypeName::union( - values - .iter() - .map(|v| TypeName::string_literal(v.clone())) - .collect(), - ), - IrTypeExpr::Map(inner) => type_application( - TypeName::primitive("Record"), - vec![TypeName::primitive("string"), type_expr_to_typename(inner)], - ), - IrTypeExpr::Union(members) => { - TypeName::union(members.iter().map(type_expr_to_typename).collect()) - } - IrTypeExpr::Any => TypeName::primitive("unknown"), - } -} - -fn primitive_to_ts(p: &IrPrimitive) -> &'static str { - match p { - IrPrimitive::Binary => "Blob | File", - IrPrimitive::String - | IrPrimitive::Date - | IrPrimitive::DateTime - | IrPrimitive::Uuid - | IrPrimitive::StringWithFormat(_) => "string", - IrPrimitive::Integer - | IrPrimitive::Number - | IrPrimitive::IntegerWithFormat(_) - | IrPrimitive::NumberWithFormat(_) => "number", - IrPrimitive::Boolean => "boolean", - } -} - // ============================================================================ // Property naming (camelCase) helpers for API layer // ============================================================================ @@ -1969,7 +1981,7 @@ fn primitive_to_ts(p: &IrPrimitive) -> &'static str { /// Returns None for non-Named types (no conversion needed). fn response_from_json_transformer( resp: &IrResponse, - convertible: &HashSet, + plan: &TypeScriptPlan<'_>, ) -> Result, SigilStitchError> { let Some((media_type, type_expr)) = pick_response_content(resp) else { return Ok(None); @@ -1977,66 +1989,17 @@ fn response_from_json_transformer( if !is_json_media_type(&media_type) { return Ok(None); } - let (name, is_array) = match type_expr { - IrTypeExpr::Named(name) if convertible.contains(name) => (name, false), - IrTypeExpr::Array(inner) => match inner.as_ref() { - IrTypeExpr::Named(name) if convertible.contains(name) => (name, true), - _ => return Ok(None), - }, - _ => return Ok(None), - }; - let pascal = name.to_pascal_case(); - let module = format!("../models/{pascal}"); - let from_fn = TypeName::importable(&module, &format!("{}FromJSON", fn_base_name(&pascal))); - let wire = TypeName::importable_type(&module, &format!("{pascal}$Wire")); - let transformer = if is_array { - sigil_quote!(TypeScript { - (json) => { - return (json as $T(TypeName::array(wire))).map($T(from_fn)); - } - }) - } else { - sigil_quote!(TypeScript { - (json) => { - return $T(from_fn)(json as $T(wire)); - } - }) - }?; - Ok(Some(transformer)) -} - -/// For a request body with a Named type, produce the toJSON call: -/// `petToJSON(requestParameters.body)` or `requestParameters.body.map(petToJSON)` -/// Returns None for non-Named types (no conversion needed). -fn body_to_json_expr( - content_type: &IrTypeExpr, - access: &str, - convertible: &HashSet, -) -> Option { - match content_type { - IrTypeExpr::Named(name) if convertible.contains(name) => { - let pascal = name.to_pascal_case(); - let to_fn = format!("{}ToJSON", fn_base_name(&pascal)); - Some(format!("{to_fn}({access})")) - } - IrTypeExpr::Array(inner) => match inner.as_ref() { - IrTypeExpr::Named(name) if convertible.contains(name) => { - let pascal = name.to_pascal_case(); - let to_fn = format!("{}ToJSON", fn_base_name(&pascal)); - Some(format!("{access}.map({to_fn})")) - } - _ => None, - }, - _ => None, + if !plan.has_effect(type_expr) { + return Ok(None); } -} - -fn multipart_part_to_json_expr( - part: &MultipartPart, - access: &str, - convertible: &HashSet, -) -> Option { - body_to_json_expr(&part.type_expr, access, convertible) + let value = plan.value(type_expr, Scope::api()); + let wire = value.wire_type(); + let converted = value.convert( + sigil_quote!(TypeScript { json as $T(wire) })?, + Direction::FromWire, + )?; + let transformer = sigil_quote!(TypeScript { (json) => { return $C(converted); } })?; + Ok(Some(transformer)) } struct MultipartPart { @@ -2075,14 +2038,6 @@ fn multipart_parts_for( }) } -fn ts_property_access(field_name: &str) -> String { - if is_js_identifier(field_name) { - format!(".{field_name}") - } else { - format!("[{}]", ts_string_literal(field_name)) - } -} - fn ts_string_literal(value: &str) -> String { format!( "'{}'", @@ -2104,38 +2059,6 @@ fn multipart_header_value(value: &str) -> String { value.replace(['\r', '\n'], "") } -/// Collect unique Named type references from request bodies only -/// that are in the convertible set (i.e., have $Wire/fromJSON/toJSON emitted). -fn collect_request_named_types( - ops: &[&IrOperation], - ir: &IrSpec, - property_naming_camel_case: bool, - convertible: &HashSet, -) -> BTreeSet { - let mut names = BTreeSet::new(); - for op in ops { - if let Some(ref body) = op.request_body - && let Some(media_type) = preferred_request_media_type(body) - { - if is_json_media_type(&media_type) { - if let Some(ty) = body.content.get(media_type.as_str()) { - collect_convertible_named_refs(ty, convertible, &mut names); - } - } else if media_type_base(&media_type) == "multipart/form-data" - && let Some(parts) = - multipart_parts_for(body, &media_type, ir, property_naming_camel_case) - { - for part in parts { - if part.value_encoding == MultipartValueEncoding::Json { - collect_convertible_named_refs(&part.type_expr, convertible, &mut names); - } - } - } - } - } - names -} - fn pick_response_content(resp: &IrResponse) -> Option<(String, &IrTypeExpr)> { pick_media_type_ref(&resp.content, |media_type| { media_type_base(media_type) == "application/json" @@ -2207,46 +2130,6 @@ fn is_xml_media_type(media_type: &str) -> bool { base == "application/xml" || base == "text/xml" || base.ends_with("+xml") } -/// Collect unique Named type references from JSON responses only -/// that are in the convertible set (i.e., have $Wire/fromJSON/toJSON emitted). -fn collect_response_named_types( - ops: &[&IrOperation], - convertible: &HashSet, -) -> BTreeSet { - let mut names = BTreeSet::new(); - for op in ops { - for resp in &op.responses { - if let Some((media_type, ty)) = pick_response_content(resp) - && is_json_media_type(&media_type) - { - collect_convertible_named_refs(ty, convertible, &mut names); - } - } - } - names -} - -fn collect_convertible_named_refs( - expr: &IrTypeExpr, - convertible: &HashSet, - names: &mut BTreeSet, -) { - match expr { - IrTypeExpr::Named(name) if convertible.contains(name) => { - names.insert(name.clone()); - } - IrTypeExpr::Array(inner) | IrTypeExpr::Nullable(inner) | IrTypeExpr::Map(inner) => { - collect_convertible_named_refs(inner, convertible, names); - } - IrTypeExpr::Union(members) => { - for member in members { - collect_convertible_named_refs(member, convertible, names); - } - } - _ => {} - } -} - #[cfg(test)] mod tests { use super::*; @@ -2298,15 +2181,22 @@ mod tests { #[test] fn string_singletons_use_structured_string_literal_type_names() { + let ir = response_dispatch_ir(); + let plan = TypeScriptPlan::new(&ir, EmitFlags::default()); assert_eq!( - type_expr_to_typename(&IrTypeExpr::StringLiteral("quoted'".to_string())), + plan.value( + &IrTypeExpr::StringLiteral("quoted'".to_string()), + Scope::api() + ) + .public_type(), TypeName::string_literal("quoted'") ); assert_eq!( - type_expr_to_typename(&IrTypeExpr::StringEnum(vec![ - "a".to_string(), - "b".to_string() - ])), + plan.value( + &IrTypeExpr::StringEnum(vec!["a".to_string(), "b".to_string()]), + Scope::api() + ) + .public_type(), TypeName::union(vec![ TypeName::string_literal("a"), TypeName::string_literal("b"), @@ -2355,6 +2245,39 @@ mod tests { crate::ir::lower::lower(parsed).expect("typed error fixture lowers") } + #[test] + fn optional_json_request_delegates_supplied_null_with_stable_layout() + -> Result<(), SigilStitchError> { + let parsed = crate::parser::parse_content_yaml(include_str!( + "../../../../tests/fixtures/valid/typescript-untagged-union.yaml" + )) + .unwrap(); + let ir = crate::ir::lower::lower(parsed).unwrap(); + let plan = TypeScriptPlan::new( + &ir, + EmitFlags { + property_naming_camel_case: true, + untagged_union_strategy: super::super::config::UntaggedUnionStrategy::Strict, + ..Default::default() + }, + ); + let op = ir + .operations + .iter() + .find(|op| op.operation_id == "sendOptionalScalar") + .unwrap(); + let mut body = CodeBlock::builder(); + emit_request_body(&mut body, op, &ir, true, &plan)?; + let block = body.build()?; + assert_rendered!( + super::super::layout::TypeScript::from(TypeScript::new()), + width = 100, + block, + "import { nullableScalarInnerToJSON } from '../models/NullableScalarInner';\n\n// Prepare request body\nconst requestBody = (() => {\n const bodyValue = requestParameters['body'];\n if (bodyValue === undefined) {\n return undefined;\n }\n return nullableScalarInnerToJSON(bodyValue);\n})();\n" + ); + Ok(()) + } + #[test] fn api_callable_parameters_preserve_request_and_override_presence() { let ir = api_interface_ir(); @@ -2407,7 +2330,8 @@ mod tests { .expect("interface operation exists") }) .collect::>(); - let interface = build_api_interface("EntriesApiInterface", &ops)?; + let plan = TypeScriptPlan::new(&ir, EmitFlags::default()); + let interface = build_api_interface("EntriesApiInterface", &ops, &plan)?; let fragments = interface.emit(&TypeScript::new())?; let block = sigil_quote!(TypeScript { $C_each(fragments) })?; assert_rendered!( @@ -2441,7 +2365,8 @@ export interface EntriesApiInterface { .iter() .find(|op| op.operation_id == "getEntries") .expect("composite response operation exists"); - let interface = build_api_interface("EntriesApiInterface", &[op])?; + let plan = TypeScriptPlan::new(&ir, EmitFlags::default()); + let interface = build_api_interface("EntriesApiInterface", &[op], &plan)?; let fragments = interface.emit(&TypeScript::new())?; let block = sigil_quote!(TypeScript { $C_each(fragments) })?; assert_rendered!( @@ -2550,17 +2475,22 @@ export class CreateResourceError extends ResponseError { .iter() .find(|response| response.status == "401") .expect("array error exists"); - let convertible = HashSet::from(["Detail".to_string()]); - let block = - build_error_throw_for_response(op, response, "unauthorized", true, &convertible)?; + let plan = TypeScriptPlan::new( + &ir, + EmitFlags { + property_naming_camel_case: true, + ..Default::default() + }, + ); + let block = build_error_throw_for_response(op, response, "unauthorized", true, &plan)?; assert_rendered!( TypeScript::new(), width = 100, block, r#"import type { Detail, Detail$Wire } from '../models/Detail'; -import { detailFromJSON } from '../models/Detail'; import { JSONApiResponse } from '../runtime/runtime'; +import { convert$0FromJSON } from '../runtime/valueConversion$0'; let errorValue: Promise | undefined; throw new ReadMixedError(response, { @@ -2568,8 +2498,8 @@ throw new ReadMixedError(response, { status: response.status as 401, raw: response, value: () => { - errorValue ??= new JSONApiResponse(errorRaw.clone(), (json) => { - return (json as Detail$Wire[]).map(detailFromJSON); + errorValue ??= new JSONApiResponse(errorRaw.clone(), (json) => { + return convert$0FromJSON(json as Detail$Wire[]); }).value(); return errorValue; }, @@ -2583,6 +2513,7 @@ throw new ReadMixedError(response, { fn status_dispatch_composes_conditions_and_returns_without_extra_newlines() -> Result<(), SigilStitchError> { let ir = response_dispatch_ir(); + let plan = TypeScriptPlan::new(&ir, EmitFlags::default()); let op = ir .operations .iter() @@ -2597,7 +2528,7 @@ throw new ReadMixedError(response, { .expect("success response exists"); branches.push(( response_status_condition(status)?, - build_response_return(response, false, &HashSet::new())?, + build_response_return(response, false, &plan)?, )); } let block = build_status_dispatch(branches, build_fallback_return(true)?)?; @@ -2630,7 +2561,14 @@ if (response.status === 201) { .iter() .find(|op| op.operation_id == "readDefault") .expect("default-only operation exists"); - let block = build_error_dispatch(op, true, &HashSet::from(["Detail".to_string()]))?; + let plan = TypeScriptPlan::new( + &ir, + EmitFlags { + property_naming_camel_case: true, + ..Default::default() + }, + ); + let block = build_error_dispatch(op, true, &plan)?; assert_rendered!( TypeScript::new(), width = 100, @@ -2648,7 +2586,7 @@ if (!(response.status >= 200 && response.status < 300)) { status: response.status, raw: response, value: () => { - errorValue ??= new JSONApiResponse(errorRaw.clone(), (json) => { + errorValue ??= new JSONApiResponse(errorRaw.clone(), (json) => { return detailFromJSON(json as Detail$Wire); }).value(); return errorValue; diff --git a/src/generators/typescript/fetch/tagged.rs b/src/generators/typescript/fetch/tagged.rs new file mode 100644 index 000000000..eb19944c3 --- /dev/null +++ b/src/generators/typescript/fetch/tagged.rs @@ -0,0 +1,297 @@ +//! Structured tagged declarations and payload delegation through the value plan. + +use heck::{ToLowerCamelCase, ToPascalCase}; +use sigil_stitch::error::SigilStitchError; +use sigil_stitch::lang::typescript::TypeScript; +use sigil_stitch::prelude::{CodeBlock, TypeName, sigil_quote}; +use sigil_stitch::spec::modifiers::Visibility; +use sigil_stitch::spec::{file_spec::FileSpec, fun_spec::FunSpec, parameter_spec::ParameterSpec}; + +use crate::generators::{closed_sum::is_tagged_unit_variant, type_application}; +use crate::ir::types::{IrSchema, IrSpec, IrTaggedUnion, IrTaggedVariant, TaggingStyle}; + +use super::layout; +use super::plan::{Direction, Scope, TypeScriptPlan, union_type}; +use super::sigil_emit::{build_type_alias, fn_base_name, is_guard_name, is_unspecified_variant}; + +fn record(plan: &TypeScriptPlan<'_>, key: &str, value: TypeName) -> TypeName { + type_application( + plan.utility_type("Record"), + vec![TypeName::string_literal(key.to_string()), value], + ) +} + +pub(super) fn open_external_variant_type( + plan: &TypeScriptPlan<'_>, + key: &str, + payload: TypeName, + additional: Option, +) -> TypeName { + let required = type_application( + plan.utility_type("Readonly"), + vec![record(plan, key, payload.clone())], + ); + match additional { + Some(additional) => { + let dictionary = type_application( + plan.utility_type("Record"), + vec![ + TypeName::primitive("string"), + union_type(vec![additional, payload]), + ], + ); + TypeName::intersection(vec![ + required, + type_application(plan.utility_type("Readonly"), vec![dictionary]), + ]) + } + None => required, + } +} + +fn variant_type( + schema: &IrSchema, + ir: &IrSpec, + union: &IrTaggedUnion, + variant: &IrTaggedVariant, + plan: &TypeScriptPlan<'_>, + wire: bool, +) -> TypeName { + let rename = !wire && plan.flags().property_naming_camel_case; + let disc = if rename { + union.discriminator_field.to_lower_camel_case() + } else { + union.discriminator_field.clone() + }; + let value = plan.value(&variant.content_type, Scope::model(&schema.name)); + let payload = if wire { + value.wire_type() + } else { + value.public_type() + }; + let tag = record( + plan, + &disc, + TypeName::string_literal(variant.discriminator_value.clone()), + ); + match &union.tagging { + TaggingStyle::Internal if is_tagged_unit_variant(ir, union, variant) => tag, + TaggingStyle::Internal => TypeName::intersection(vec![tag, payload]), + TaggingStyle::Adjacent { .. } if is_tagged_unit_variant(ir, union, variant) => tag, + TaggingStyle::Adjacent { content_field } => { + let content = if rename { + content_field.to_lower_camel_case() + } else { + content_field.clone() + }; + TypeName::intersection(vec![tag, record(plan, &content, payload)]) + } + TaggingStyle::External => { + if !union + .variants + .iter() + .any(|variant| variant.additional_properties.is_some()) + { + return record(plan, &variant.discriminator_value, payload); + } + let additional = variant.additional_properties.as_ref().map(|expr| { + let value = plan.value(expr, Scope::model(&schema.name)); + if wire { + value.wire_type() + } else { + value.public_type() + } + }); + open_external_variant_type(plan, &variant.discriminator_value, payload, additional) + } + } +} + +pub(super) fn guards( + schema: &IrSchema, + ir: &IrSpec, + union: &IrTaggedUnion, + plan: &TypeScriptPlan<'_>, +) -> Result, SigilStitchError> { + union + .variants + .iter() + .filter(|variant| { + !is_tagged_unit_variant(ir, union, variant) + && !is_unspecified_variant( + &variant.discriminator_value, + &union.discriminator_field, + &variant.content_type, + ) + }) + .map(|variant| { + let guard = is_guard_name(&variant.discriminator_value); + let parent = TypeName::primitive(&schema.name.to_pascal_case()); + let narrowed = variant_type(schema, ir, union, variant, plan, false); + let disc = if plan.flags().property_naming_camel_case { + union.discriminator_field.to_lower_camel_case() + } else { + union.discriminator_field.clone() + }; + let tag = &variant.discriminator_value; + let check = match union.tagging { + TaggingStyle::External => sigil_quote!(TypeScript { $S(tag) in value })?, + _ => sigil_quote!(TypeScript { value[$S(disc)] === $S(tag) })?, + }; + layout::type_guard( + &guard, + parent, + narrowed, + sigil_quote!(TypeScript { return $C(check); })?, + ) + }) + .collect() +} + +pub(super) fn emit( + schema: &IrSchema, + ir: &IrSpec, + union: &IrTaggedUnion, + plan: &TypeScriptPlan<'_>, + ts: &TypeScript, +) -> Result { + let name = schema.name.to_pascal_case(); + let wire_name = format!("{name}$Wire"); + let mut file = FileSpec::builder_with(&format!("{name}.ts"), ts.clone()); + let camel = plan.flags().property_naming_camel_case; + for wire in [true, false].into_iter().filter(|wire| !*wire || camel) { + let variants = union + .variants + .iter() + .map(|variant| variant_type(schema, ir, union, variant, plan, wire)) + .collect(); + file = file.add_type(build_type_alias( + if wire { &wire_name } else { &name }, + union_type(variants), + schema.description.as_deref(), + )?); + } + for direction in [Direction::FromWire, Direction::ToWire] + .into_iter() + .filter(|_| camel) + { + let source = direction.source(); + let (input, output) = match direction { + Direction::FromWire => (&wire_name, &name), + Direction::ToWire => (&name, &wire_name), + }; + let output_type = TypeName::primitive(output); + let mut branches = Vec::new(); + for variant in &union.variants { + let disc = match direction { + Direction::FromWire => union.discriminator_field.clone(), + Direction::ToWire => union.discriminator_field.to_lower_camel_case(), + }; + let target_disc = match direction { + Direction::FromWire => union.discriminator_field.to_lower_camel_case(), + Direction::ToWire => union.discriminator_field.clone(), + }; + let tag = &variant.discriminator_value; + let value = plan.value(&variant.content_type, Scope::model(&schema.name)); + let content_type = match direction { + Direction::FromWire => value.wire_type(), + Direction::ToWire => value.public_type(), + }; + let body = if is_tagged_unit_variant(ir, union, variant) + && !matches!(union.tagging, TaggingStyle::External) + { + layout::return_object( + &[sigil_quote!(TypeScript { [$S(target_disc)]: $S(tag) })?], + Some(output_type.clone()), + 4, + )? + } else { + match &union.tagging { + TaggingStyle::Internal => { + let content = value.convert( + sigil_quote!(TypeScript { $N(source) as unknown as $T(content_type) })?, + direction, + )?; + layout::return_object( + &[ + sigil_quote!(TypeScript { ...$C(content) })?, + sigil_quote!(TypeScript { [$S(target_disc)]: $S(tag) })?, + ], + Some(output_type.clone()), + 4, + )? + } + TaggingStyle::Adjacent { content_field } => { + let source_key = match direction { + Direction::FromWire => content_field.clone(), + Direction::ToWire => content_field.to_lower_camel_case(), + }; + let target_key = match direction { + Direction::FromWire => content_field.to_lower_camel_case(), + Direction::ToWire => content_field.clone(), + }; + let column = + 6 + layout::fragment_width( + &sigil_quote!(TypeScript { [$S(target_key.clone())]: })?, + )? + 1; + let access = layout::indexed_assertion( + sigil_quote!(TypeScript { $N(source) })?, + record(plan, &source_key, content_type), + &source_key, + 8, + )?; + let content = value.convert_at(access, direction, column)?; + layout::return_object( + &[ + sigil_quote!(TypeScript { [$S(target_disc)]: $S(tag) })?, + sigil_quote!(TypeScript { [$S(target_key)]: $C(content) })?, + ], + Some(output_type.clone()), + 4, + )? + } + TaggingStyle::External => { + let column = 6 + + layout::fragment_width(&sigil_quote!(TypeScript { [$S(tag)]: })?)? + + 1; + let access = layout::indexed_assertion( + sigil_quote!(TypeScript { $N(source) })?, + record(plan, tag, content_type), + tag, + 8, + )?; + let content = value.convert_at(access, direction, column)?; + layout::return_object( + &[sigil_quote!(TypeScript { [$S(tag)]: $C(content) })?], + Some(output_type.clone()), + 4, + )? + } + } + }; + let condition = match union.tagging { + TaggingStyle::External => { + sigil_quote!(TypeScript { Object.prototype.hasOwnProperty.call($N(source), $S(tag)) })? + } + _ => sigil_quote!(TypeScript { $N(source)[$S(disc)] === $S(tag) })?, + }; + branches.push(sigil_quote!(TypeScript { if ($C(condition)) { $C(body) } })?); + } + let body = + sigil_quote!(TypeScript { $C_each(branches) throw new Error($S("Unknown variant")); })?; + file = file.add_function( + FunSpec::builder(&format!("{}{}", fn_base_name(&name), direction.suffix())) + .visibility(Visibility::Public) + .add_param(ParameterSpec::of(source, TypeName::primitive(input))) + .returns(vec![output_type]) + .body(body) + .build()?, + ); + } + if plan.flags().emit_type_guards { + for guard in guards(schema, ir, union, plan)? { + file = file.add_code(guard); + } + } + file.build() +} diff --git a/src/generators/union_match.rs b/src/generators/union_match.rs new file mode 100644 index 000000000..bb72beaa9 --- /dev/null +++ b/src/generators/union_match.rs @@ -0,0 +1,959 @@ +//! Target-independent structural member selection for untagged conversion. +//! +//! This is deliberately not a JSON Schema validator: formats, numeric bounds, +//! and other validation keywords do not select conversion functions. Property +//! names stay in their wire form; each renderer owns its naming convention. + +use std::collections::HashMap; + +use crate::generators::closed_sum::is_tagged_unit_variant; +use crate::ir::types::{IrPrimitive, IrSchemaKind, IrSpec, IrTypeExpr, IrUnion, TaggingStyle}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum MatchScope { + Any, + Concrete, + Fallback, +} + +#[derive(Debug, Clone, PartialEq)] +pub(crate) enum JsonShape { + Any, + Never, + Null, + Boolean, + String, + Binary, + Number { + integer: bool, + }, + Literal(serde_json::Value), + /// A nullable wrapper grants permission without inventing an authored member. + Nullable(Box), + Array(Box), + Map(Box), + MapWithConcreteValue { + values: Box, + concrete: Box, + }, + Object(Vec), + ObjectWithAdditional { + properties: Vec, + values: Box, + }, + /// Productive recursion retains a graph edge rather than weakening to Any. + Reference { + schema: String, + scope: MatchScope, + unrestricted: bool, + accepts_null: bool, + }, + AnyOf(Vec), + AllOf(Vec), +} + +#[derive(Debug, Clone, PartialEq)] +pub(crate) struct PropertyMatch { + pub wire_name: String, + pub rename: bool, + pub required: bool, + pub shape: JsonShape, +} + +#[derive(Debug, Clone)] +pub(crate) struct UnionMemberMatch { + pub type_expr: IrTypeExpr, + pub shape: JsonShape, + pub fallback: bool, +} + +pub(crate) fn plan_union_members( + name: &str, + union: &IrUnion, + ir: &IrSpec, +) -> Vec { + plan_members(&union.members, ir, Some(name)) +} + +/// Inline unions inside containers select a new value, not the enclosing schema. +pub(crate) fn plan_inline_union_members( + members: &[IrTypeExpr], + ir: &IrSpec, +) -> Vec { + plan_members(members, ir, None) +} + +fn plan_members( + types: &[IrTypeExpr], + ir: &IrSpec, + current_schema: Option<&str>, +) -> Vec { + let mut members: Vec<_> = types + .iter() + .flat_map(|type_expr| { + let mut visiting = current_schema + .map(|name| HashMap::from([(name.to_string(), 0)])) + .unwrap_or_default(); + let shape = plan_shape(type_expr, ir, &mut visiting, 0); + let (concrete, fallback) = selection_shapes(shape); + [(concrete, false), (fallback, true)] + .into_iter() + .filter_map(move |(shape, fallback)| { + (shape != JsonShape::Never).then(|| UnionMemberMatch { + type_expr: type_expr.clone(), + shape, + fallback, + }) + }) + }) + .collect(); + // Stable sorting retains schema order within both groups. An unrestricted + // JSON alternative must not hide a concrete convertible object after it. + members.sort_by_key(|member| member.fallback); + members +} + +fn any_of(shapes: Vec) -> JsonShape { + let mut flattened = Vec::new(); + for shape in shapes { + match shape { + JsonShape::Never => {} + JsonShape::AnyOf(shapes) => { + for shape in shapes { + if !flattened.contains(&shape) { + flattened.push(shape); + } + } + } + shape => { + if !flattened.contains(&shape) { + flattened.push(shape); + } + } + } + } + // Unlike a boolean validator, a conversion plan must retain the concrete + // alternatives next to Any so their converters can still be selected. + match flattened.len() { + 0 => JsonShape::Never, + 1 => flattened.into_iter().next().expect("one union shape"), + _ => JsonShape::AnyOf(flattened), + } +} + +fn all_of(shapes: Vec) -> JsonShape { + let mut flattened = Vec::new(); + for shape in shapes { + match shape { + JsonShape::Never => return JsonShape::Never, + JsonShape::Any => {} + JsonShape::AllOf(shapes) => flattened.extend(shapes), + shape => flattened.push(shape), + } + } + match flattened.len() { + 0 => JsonShape::Any, + 1 => flattened + .into_iter() + .next() + .expect("one intersection shape"), + _ => JsonShape::AllOf(flattened), + } +} + +fn nullable(shape: JsonShape) -> JsonShape { + match shape { + JsonShape::Any | JsonShape::Null | JsonShape::Nullable(_) => shape, + JsonShape::Never => JsonShape::Null, + shape => JsonShape::Nullable(Box::new(shape)), + } +} + +fn shape_accepts_null(shape: &JsonShape) -> bool { + match shape { + JsonShape::Any | JsonShape::Null | JsonShape::Nullable(_) => true, + JsonShape::Literal(value) => value.is_null(), + JsonShape::Reference { + scope: MatchScope::Any, + accepts_null, + .. + } => *accepts_null, + JsonShape::AnyOf(shapes) => shapes.iter().any(shape_accepts_null), + JsonShape::AllOf(shapes) => shapes.iter().all(shape_accepts_null), + _ => false, + } +} + +pub(crate) fn accepts_any(shape: &JsonShape) -> bool { + match shape { + JsonShape::Any => true, + JsonShape::Reference { + scope: MatchScope::Any, + unrestricted, + .. + } => *unrestricted, + JsonShape::Nullable(inner) => accepts_any(inner), + JsonShape::AnyOf(shapes) => shapes.iter().any(accepts_any), + JsonShape::AllOf(shapes) => shapes.iter().all(accepts_any), + _ => false, + } +} + +/// Keep dispatch priority separate from the original member's conversion +/// boundary. A named open union has both a concrete guard and a fallback, +/// but both branches must call that named converter (including its policy). +pub(crate) fn selection_shapes(shape: JsonShape) -> (JsonShape, JsonShape) { + match shape { + JsonShape::Never => (JsonShape::Never, JsonShape::Never), + JsonShape::Any => (JsonShape::Never, JsonShape::Any), + JsonShape::Reference { + schema, + unrestricted, + accepts_null, + .. + } => ( + JsonShape::Reference { + schema: schema.clone(), + scope: MatchScope::Concrete, + unrestricted, + accepts_null, + }, + JsonShape::Reference { + schema, + scope: MatchScope::Fallback, + unrestricted, + accepts_null, + }, + ), + JsonShape::Nullable(inner) => { + let already_nullable = shape_accepts_null(&inner); + let (concrete, fallback) = selection_shapes(*inner); + if already_nullable { + // Preserve authored concrete null paths beside an opaque fallback. + // A repeated wrapper adds permission, not a new candidate. + (concrete, fallback) + } else if concrete == JsonShape::Never && fallback != JsonShape::Never { + (concrete, any_of(vec![JsonShape::Null, fallback])) + } else { + (any_of(vec![JsonShape::Null, concrete]), fallback) + } + } + JsonShape::Map(inner) if accepts_any(&inner) => { + let (concrete, fallback) = selection_shapes((*inner).clone()); + let concrete = if concrete == JsonShape::Never { + JsonShape::Never + } else { + // Mixed dictionaries can contain both concrete and opaque values. + // Selecting only all-concrete dictionaries would lose conversion + // (and nested strict errors) for their concrete entries. + JsonShape::MapWithConcreteValue { + values: inner, + concrete: Box::new(concrete), + } + }; + (concrete, JsonShape::Map(Box::new(fallback))) + } + JsonShape::Object(ref properties) if properties.is_empty() => (JsonShape::Never, shape), + JsonShape::AnyOf(shapes) => { + let (concrete, fallback): (Vec<_>, Vec<_>) = + shapes.into_iter().map(selection_shapes).unzip(); + (any_of(concrete), any_of(fallback)) + } + JsonShape::AllOf(shapes) => { + let shape = all_of(shapes); + let JsonShape::AllOf(shapes) = &shape else { + return selection_shapes(shape); + }; + let (concrete, fallback): (Vec<_>, Vec<_>) = + shapes.iter().cloned().map(selection_shapes).unzip(); + // Every conjunct must accept a candidate; a concrete path must also + // match at least one concrete conjunct. No distributive expansion. + (all_of(vec![shape, any_of(concrete)]), all_of(fallback)) + } + shape => (shape, JsonShape::Never), + } +} + +fn plan_shape( + expr: &IrTypeExpr, + ir: &IrSpec, + visiting: &mut HashMap, + depth: usize, +) -> JsonShape { + match expr { + IrTypeExpr::ScalarEnum { + representation, + representation_is_constraint, + representation_nullable, + values, + } => { + let shape = if *representation_is_constraint { + plan_shape(representation, ir, visiting, depth) + } else { + JsonShape::Any + }; + all_of(vec![ + if *representation_nullable { + nullable(shape) + } else { + shape + }, + any_of(values.iter().cloned().map(JsonShape::Literal).collect()), + ]) + } + IrTypeExpr::Any => JsonShape::Any, + IrTypeExpr::Primitive(primitive) => match primitive { + IrPrimitive::Boolean => JsonShape::Boolean, + IrPrimitive::Binary => JsonShape::Binary, + IrPrimitive::Integer | IrPrimitive::IntegerWithFormat(_) => { + JsonShape::Number { integer: true } + } + IrPrimitive::Number | IrPrimitive::NumberWithFormat(_) => { + JsonShape::Number { integer: false } + } + _ => JsonShape::String, + }, + IrTypeExpr::StringLiteral(value) => { + JsonShape::Literal(serde_json::Value::String(value.clone())) + } + IrTypeExpr::StringEnum(values) => any_of( + values + .iter() + .map(|value| JsonShape::Literal(serde_json::Value::String(value.clone()))) + .collect(), + ), + IrTypeExpr::Array(inner) => { + JsonShape::Array(Box::new(plan_shape(inner, ir, visiting, depth + 1))) + } + IrTypeExpr::Map(inner) => { + JsonShape::Map(Box::new(plan_shape(inner, ir, visiting, depth + 1))) + } + IrTypeExpr::Nullable(inner) => nullable(plan_shape(inner, ir, visiting, depth)), + IrTypeExpr::Union(members) => any_of( + members + .iter() + .map(|member| plan_shape(member, ir, visiting, depth)) + .collect(), + ), + IrTypeExpr::Named(name) => { + // Recursive payloads are still converted recursively by the member + // converter. Stop expanding the selection guard at a cycle. + if let Some(previous_depth) = visiting.get(name) { + // A cycle through an array/map/property has consumed a value; + // only its guard expansion stops. An alias cycle at the same + // value cannot make progress and must not invoke itself. + return if depth > *previous_depth { + JsonShape::Reference { + schema: name.clone(), + scope: MatchScope::Any, + unrestricted: unrestricted_type(expr, ir, &mut Default::default()), + accepts_null: accepts_null(expr, ir), + } + } else { + JsonShape::Never + }; + } + visiting.insert(name.clone(), depth); + let shape = match ir.schemas.get(name).map(|schema| &schema.kind) { + Some(IrSchemaKind::Object(object)) => { + if object.properties.is_empty() { + object.additional_properties.as_ref().map_or_else( + || JsonShape::Object(Vec::new()), + |value| { + JsonShape::Map(Box::new(plan_shape(value, ir, visiting, depth + 1))) + }, + ) + } else { + let properties = object + .properties + .iter() + .map(|(wire_name, property)| { + let shape = + plan_shape(&property.type_expr, ir, visiting, depth + 1); + PropertyMatch { + wire_name: wire_name.clone(), + rename: true, + required: property.required, + // Nullability describes the type's value + // kind, but a scalar enum at this same node + // can still exclude null. Its shape already + // includes null exactly when listed. + shape: if property.nullable + && !matches!( + property.type_expr, + IrTypeExpr::ScalarEnum { .. } + | IrTypeExpr::StringLiteral(_) + | IrTypeExpr::StringEnum(_) + ) { + nullable(shape) + } else { + shape + }, + } + }) + .collect(); + match &object.additional_properties { + Some(value) => JsonShape::ObjectWithAdditional { + properties, + values: Box::new(plan_shape(value, ir, visiting, depth + 1)), + }, + None => JsonShape::Object(properties), + } + } + } + Some(IrSchemaKind::Enum(enumeration)) => any_of( + enumeration + .values + .iter() + .map(|value| JsonShape::Literal(value.value.clone())) + .collect(), + ), + Some(IrSchemaKind::Alias(inner)) => plan_shape(inner, ir, visiting, depth), + Some(IrSchemaKind::Union(union)) => { + let shape = any_of( + union + .members + .iter() + .map(|member| plan_shape(member, ir, visiting, depth)) + .collect(), + ); + if union.nullable { + nullable(shape) + } else { + shape + } + } + Some(IrSchemaKind::Intersection(intersection)) => all_of( + intersection + .members + .iter() + .map(|member| plan_shape(member, ir, visiting, depth)) + .collect(), + ), + Some(IrSchemaKind::TaggedUnion(union)) => any_of( + union + .variants + .iter() + .map(|variant| { + let tag = PropertyMatch { + wire_name: union.discriminator_field.clone(), + rename: true, + required: true, + shape: JsonShape::Literal(serde_json::Value::String( + variant.discriminator_value.clone(), + )), + }; + let tag_only = is_tagged_unit_variant(ir, union, variant); + match &union.tagging { + TaggingStyle::Internal | TaggingStyle::Adjacent { .. } + if tag_only => + { + JsonShape::Object(vec![tag]) + } + TaggingStyle::Internal => all_of(vec![ + JsonShape::Object(vec![tag]), + plan_shape(&variant.content_type, ir, visiting, depth), + ]), + TaggingStyle::Adjacent { content_field } => { + JsonShape::Object(vec![ + tag, + PropertyMatch { + wire_name: content_field.clone(), + rename: true, + required: true, + shape: plan_shape( + &variant.content_type, + ir, + visiting, + depth + 1, + ), + }, + ]) + } + TaggingStyle::External => { + let properties = vec![PropertyMatch { + wire_name: variant.discriminator_value.clone(), + rename: false, + required: true, + shape: plan_shape( + &variant.content_type, + ir, + visiting, + depth + 1, + ), + }]; + match &variant.additional_properties { + Some(expr) => JsonShape::ObjectWithAdditional { + properties, + values: Box::new(plan_shape( + expr, + ir, + visiting, + depth + 1, + )), + }, + None => JsonShape::Object(properties), + } + } + } + }) + .collect(), + ), + None => JsonShape::Any, + }; + visiting.remove(name); + shape + } + } +} + +/// Shapes are shared semantic data; target renderers decide property spelling +/// and runtime representations, including binary values. +pub(crate) fn plan_named_shape(name: &str, ir: &IrSpec) -> JsonShape { + plan_shape( + &IrTypeExpr::Named(name.to_string()), + ir, + &mut HashMap::new(), + 0, + ) +} + +/// Fixed-value null acceptance never follows container payloads. This fact also +/// travels on productive reference edges, where guard expansion has stopped. +pub(crate) fn accepts_null(expr: &IrTypeExpr, ir: &IrSpec) -> bool { + fn visit(expr: &IrTypeExpr, ir: &IrSpec, seen: &mut std::collections::HashSet) -> bool { + match expr { + IrTypeExpr::Any | IrTypeExpr::Nullable(_) => true, + IrTypeExpr::ScalarEnum { + representation, + representation_is_constraint, + representation_nullable, + values, + } => { + values.iter().any(serde_json::Value::is_null) + && (!representation_is_constraint + || *representation_nullable + || visit(representation, ir, seen)) + } + IrTypeExpr::Union(members) => members.iter().any(|member| visit(member, ir, seen)), + IrTypeExpr::Named(name) => { + if !seen.insert(name.clone()) { + return false; + } + let accepts = match ir.schemas.get(name).map(|schema| &schema.kind) { + Some(IrSchemaKind::Alias(inner)) => visit(inner, ir, seen), + Some(IrSchemaKind::Union(union)) => { + union.nullable || union.members.iter().any(|member| visit(member, ir, seen)) + } + Some(IrSchemaKind::Intersection(all)) => { + all.members.iter().all(|member| visit(member, ir, seen)) + } + Some(IrSchemaKind::Enum(enumeration)) => { + enumeration.values.iter().any(|value| value.value.is_null()) + } + None => true, + _ => false, + }; + seen.remove(name); + accepts + } + _ => false, + } + } + visit(expr, ir, &mut Default::default()) +} + +/// Whether the schema accepts arbitrary values, independently of conversion +/// evidence. This fixed-value traversal never follows container payloads. +fn unrestricted_type( + expr: &IrTypeExpr, + ir: &IrSpec, + seen: &mut std::collections::HashSet, +) -> bool { + match expr { + IrTypeExpr::Any => true, + IrTypeExpr::Nullable(inner) => unrestricted_type(inner, ir, seen), + IrTypeExpr::Union(members) => members + .iter() + .any(|member| unrestricted_type(member, ir, seen)), + IrTypeExpr::Named(name) => { + if !seen.insert(name.clone()) { + return false; + } + let unrestricted = match ir.schemas.get(name).map(|schema| &schema.kind) { + Some(IrSchemaKind::Alias(inner)) => unrestricted_type(inner, ir, seen), + Some(IrSchemaKind::Union(union)) => union + .members + .iter() + .any(|member| unrestricted_type(member, ir, seen)), + Some(IrSchemaKind::Intersection(all)) => all + .members + .iter() + .all(|member| unrestricted_type(member, ir, seen)), + None => true, + _ => false, + }; + seen.remove(name); + unrestricted + } + _ => false, + } +} + +pub(crate) fn referenced_shapes(shape: &JsonShape, names: &mut std::collections::BTreeSet) { + match shape { + JsonShape::Reference { schema, .. } => { + names.insert(schema.clone()); + } + JsonShape::Array(value) | JsonShape::Map(value) | JsonShape::Nullable(value) => { + referenced_shapes(value, names) + } + JsonShape::MapWithConcreteValue { values, concrete } => { + referenced_shapes(values, names); + referenced_shapes(concrete, names); + } + JsonShape::Object(properties) | JsonShape::ObjectWithAdditional { properties, .. } => { + for property in properties { + referenced_shapes(&property.shape, names); + } + if let JsonShape::ObjectWithAdditional { values, .. } = shape { + referenced_shapes(values, names); + } + } + JsonShape::AnyOf(shapes) | JsonShape::AllOf(shapes) => { + for shape in shapes { + referenced_shapes(shape, names); + } + } + _ => {} + } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn fixture() -> IrSpec { + let parsed = crate::parser::parse_content_yaml(include_str!( + "../../tests/fixtures/valid/typescript-untagged-union.yaml" + )) + .unwrap(); + crate::ir::lower::lower(parsed).unwrap() + } + + #[test] + fn nullable_wrappers_preserve_authored_null_beside_opaque_fallbacks() { + let ir = fixture(); + let inner = plan_named_shape("NullableOpenInner", &ir); + let (concrete, fallback) = selection_shapes(inner.clone()); + assert!(shape_accepts_null(&concrete)); + assert!(accepts_any(&fallback)); + assert_eq!( + selection_shapes(nullable(nullable(inner))), + (concrete, fallback) + ); + for name in ["NullableOpaque", "TwiceNullableOpaque"] { + let (concrete, fallback) = selection_shapes(plan_named_shape(name, &ir)); + assert_eq!(concrete, JsonShape::Never); + assert!(accepts_any(&fallback)); + } + } + + #[test] + fn nullable_reference_does_not_invent_concrete_null_evidence() { + let ir = fixture(); + let reference = JsonShape::Reference { + schema: "NullableMapFallback".into(), + scope: MatchScope::Any, + unrestricted: false, + accepts_null: accepts_null(&IrTypeExpr::Named("NullableMapFallback".into()), &ir), + }; + assert!(shape_accepts_null(&reference)); + assert_eq!( + selection_shapes(nullable(reference.clone())), + selection_shapes(reference) + ); + } + + #[test] + fn null_acceptance_intersects_every_scalar_enum_constraint() { + let ir = fixture(); + let inner = IrTypeExpr::ScalarEnum { + representation: Box::new(IrTypeExpr::Nullable(Box::new(IrTypeExpr::Primitive( + IrPrimitive::Integer, + )))), + representation_is_constraint: true, + representation_nullable: false, + values: vec![1.into()], + }; + let outer = IrTypeExpr::ScalarEnum { + representation: Box::new(inner), + representation_is_constraint: true, + representation_nullable: false, + values: vec![1.into(), serde_json::Value::Null], + }; + assert!(!accepts_null(&outer, &ir)); + let (extracted, nullable) = outer.clone().split_nullable(); + assert!(nullable); + assert!(!accepts_null(&extracted, &ir)); + assert!(accepts_null(&IrTypeExpr::Nullable(Box::new(outer)), &ir)); + } + + #[test] + fn mixed_members_retain_required_wire_fields_and_literal_constraints() { + let ir = fixture(); + let IrSchemaKind::Union(union) = &ir.schemas["Tool"].kind else { + panic!("mixed tool remains untagged"); + }; + let members = plan_union_members("Tool", union, &ir); + let JsonShape::Object(properties) = &members[0].shape else { + panic!("client member is an object"); + }; + assert!( + properties + .iter() + .any(|property| property.wire_name == "input_schema" && property.required) + ); + let JsonShape::Object(properties) = &members[1].shape else { + panic!("search member is an object"); + }; + assert!( + properties + .iter() + .any(|property| property.wire_name == "type" + && property.shape == JsonShape::Literal("web_search_20250305".into())) + ); + } + + #[test] + fn raw_json_fallback_does_not_shadow_a_concrete_member() { + let ir = fixture(); + let union = IrUnion { + members: vec![IrTypeExpr::Any, IrTypeExpr::Named("ClientTool".into())], + nullable: false, + }; + let members = plan_union_members("FallbackUnion", &union, &ir); + assert!(!members[0].fallback); + assert!(matches!(&members[0].type_expr, IrTypeExpr::Named(name) if name == "ClientTool")); + assert!(members[1].fallback); + } + + #[test] + fn unrestricted_wrappers_do_not_precede_outer_concrete_members() { + let ir = fixture(); + let IrSchemaKind::Union(union) = &ir.schemas["AliasOuter"].kind else { + panic!("outer alias union"); + }; + let members = plan_union_members("AliasOuter", union, &ir); + assert_eq!(members.len(), 3); + assert!(matches!(&members[0].type_expr, IrTypeExpr::Named(name) if name == "OpenAlias")); + assert!(!members[0].fallback); + assert!(matches!(&members[1].type_expr, IrTypeExpr::Named(name) if name == "SearchTool")); + assert!(!members[1].fallback); + assert!(matches!(&members[2].type_expr, IrTypeExpr::Named(name) if name == "OpenAlias")); + assert!(members[2].fallback); + assert_eq!(members[2].shape, JsonShape::Any); + let IrSchemaKind::Union(union) = &ir.schemas["NullableMapFallback"].kind else { + panic!("nullable map union"); + }; + let members = plan_union_members("NullableMapFallback", union, &ir); + assert!(!members[0].fallback); + assert!(matches!(&members[0].type_expr, IrTypeExpr::Named(name) if name == "Detail")); + assert!(members[1].fallback); + assert_eq!( + members[1].shape, + JsonShape::AnyOf(vec![ + JsonShape::Null, + JsonShape::Map(Box::new(JsonShape::Any)) + ]) + ); + } + + #[test] + fn named_nested_unions_keep_both_delegation_paths() { + let ir = fixture(); + let IrSchemaKind::Union(union) = &ir.schemas["Outer"].kind else { + panic!("outer union") + }; + let members = plan_union_members("Outer", union, &ir); + assert_eq!(members.len(), 3); + assert!(matches!(&members[0].type_expr, IrTypeExpr::Named(name) if name == "OpenInner")); + assert!( + matches!(&members[0].shape, JsonShape::Object(properties) if properties[0].wire_name == "wire_name") + ); + assert!(!members[0].fallback); + assert_eq!(members[1].shape, JsonShape::String); + assert!(matches!(&members[2].type_expr, IrTypeExpr::Named(name) if name == "OpenInner")); + assert!(members[2].fallback); + assert_eq!(members[2].shape, JsonShape::Any); + } + + #[test] + fn open_map_values_retain_a_concrete_delegation_path() { + let ir = fixture(); + let IrSchemaKind::Union(union) = &ir.schemas["MapOuter"].kind else { + panic!("outer map union") + }; + let members = plan_union_members("MapOuter", union, &ir); + assert_eq!(members.len(), 3); + assert!(matches!(&members[0].type_expr, IrTypeExpr::Named(name) if name == "OpenMap")); + assert!(!members[0].fallback); + assert!( + matches!(&members[0].shape, JsonShape::MapWithConcreteValue { values, concrete } + if accepts_any(values) && matches!(**concrete, JsonShape::Object(_))) + ); + assert_eq!(members[1].shape, JsonShape::Any); + assert!(members[1].fallback); + assert_eq!(members[2].shape, JsonShape::Map(Box::new(JsonShape::Any))); + assert!(members[2].fallback); + } + + #[test] + fn inline_union_selection_can_delegate_to_its_enclosing_schema() { + let ir = fixture(); + let members = plan_inline_union_members(&[IrTypeExpr::Named("Outer".into())], &ir); + assert_eq!(members.len(), 2); + assert!( + members.iter().all( + |member| matches!(&member.type_expr, IrTypeExpr::Named(name) if name == "Outer") + ) + ); + assert!(!members[0].fallback); + assert!(members[1].fallback); + } + + #[test] + fn binary_and_adjacent_unit_shapes_keep_their_semantics() { + let ir = fixture(); + let binary = plan_shape( + &IrTypeExpr::Named("BinaryModel".into()), + &ir, + &mut HashMap::new(), + 0, + ); + let JsonShape::Object(properties) = binary else { + panic!("binary model") + }; + assert!( + properties + .iter() + .any(|property| property.wire_name == "binary_data" + && property.shape == JsonShape::Binary) + ); + let adjacent = plan_shape( + &IrTypeExpr::Named("Adjacent".into()), + &ir, + &mut HashMap::new(), + 0, + ); + let JsonShape::AnyOf(variants) = adjacent else { + panic!("adjacent variants") + }; + let JsonShape::Object(unit) = &variants[0] else { + panic!("unit variant") + }; + assert_eq!(unit.len(), 1); + assert_eq!(unit[0].wire_name, "event_type"); + let JsonShape::Object(payload) = &variants[1] else { + panic!("payload variant") + }; + assert_eq!(payload.len(), 2); + assert_eq!(payload[1].wire_name, "event_data"); + assert!(matches!(payload[1].shape, JsonShape::Object(_))); + } + + #[test] + fn intersection_partition_retains_constraints_on_fallback_paths() { + let constrained = JsonShape::Object(vec![PropertyMatch { + wire_name: "kind".into(), + rename: true, + required: true, + shape: JsonShape::Literal("item".into()), + }]); + let (concrete, fallback) = + selection_shapes(JsonShape::AllOf(vec![JsonShape::Any, constrained.clone()])); + assert_eq!(concrete, constrained); + assert_eq!(fallback, JsonShape::Never); + let (_, fallback) = selection_shapes(JsonShape::AllOf(vec![ + JsonShape::Any, + JsonShape::Map(Box::new(JsonShape::Any)), + ])); + assert_eq!(fallback, JsonShape::Map(Box::new(JsonShape::Any))); + } + + #[test] + fn recursive_alias_guard_expansion_terminates() { + let mut ir = fixture(); + ir.schemas.get_mut("ClientTool").unwrap().kind = + IrSchemaKind::Alias(IrTypeExpr::Named("ClientTool".into())); + assert_eq!( + plan_shape( + &IrTypeExpr::Named("ClientTool".into()), + &ir, + &mut HashMap::new(), + 0 + ), + JsonShape::Never + ); + } + + #[test] + fn same_value_alias_cycles_do_not_shadow_productive_members() { + let mut ir = fixture(); + ir.schemas.get_mut("OpenAlias").unwrap().kind = + IrSchemaKind::Alias(IrTypeExpr::Named("Outer".into())); + let union = IrUnion { + members: vec![ + IrTypeExpr::Named("OpenAlias".into()), + IrTypeExpr::Named("Detail".into()), + ], + nullable: false, + }; + let members = plan_union_members("Outer", &union, &ir); + assert_eq!(members.len(), 1); + assert!(matches!(&members[0].type_expr, IrTypeExpr::Named(name) if name == "Detail")); + } + + #[test] + fn inline_nested_union_retains_its_own_selection_and_conversion_identity() { + let ir = fixture(); + let union = IrUnion { + members: vec![IrTypeExpr::Union(vec![ + IrTypeExpr::Named("ClientTool".into()), + IrTypeExpr::Named("SearchTool".into()), + ])], + nullable: false, + }; + let members = plan_union_members("InlineUnion", &union, &ir); + assert_eq!(members.len(), 1); + assert!(matches!(&members[0].type_expr, IrTypeExpr::Union(_))); + assert!(members.iter().all(|member| !member.fallback)); + } + + #[test] + fn external_variant_keys_are_not_ergonomic_property_names() { + use crate::ir::types::{IrTaggedUnion, IrTaggedVariant}; + let mut ir = fixture(); + ir.schemas.get_mut("ClientTool").unwrap().kind = IrSchemaKind::TaggedUnion(IrTaggedUnion { + discriminator_field: String::new(), + tagging: TaggingStyle::External, + variants: vec![IrTaggedVariant { + discriminator_value: "snake_tag".into(), + content_type: IrTypeExpr::Named("SearchTool".into()), + description: None, + additional_properties: None, + }], + }); + let shape = plan_shape( + &IrTypeExpr::Named("ClientTool".into()), + &ir, + &mut HashMap::new(), + 0, + ); + let JsonShape::Object(properties) = shape else { + panic!("external wrapper") + }; + assert_eq!(properties[0].wire_name, "snake_tag"); + assert!(!properties[0].rename); + } +} diff --git a/src/ir/lower/v30.rs b/src/ir/lower/v30.rs index d9656ea0b..0dd7b86b3 100644 --- a/src/ir/lower/v30.rs +++ b/src/ir/lower/v30.rs @@ -785,6 +785,7 @@ impl<'a> LowerCtx<'a> { } }; with_schema_nullability(obj, base) + .with_scalar_enum(&obj.enum_values, obj.schema_type.is_some()) } fn lower_schema_value_with_promotion( @@ -1127,7 +1128,7 @@ fn extract_component_name(ref_path: &str) -> String { /// Preserve nullability without changing the existing untyped-schema fallback. fn nested_type_to_expr(obj: &ObjectSchema) -> IrTypeExpr { let base = type_string_to_type_expr(obj.schema_type.as_deref().unwrap_or("string"), obj); - with_schema_nullability(obj, base) + with_schema_nullability(obj, base).with_scalar_enum(&obj.enum_values, obj.schema_type.is_some()) } fn with_schema_nullability(obj: &ObjectSchema, base: IrTypeExpr) -> IrTypeExpr { @@ -1298,10 +1299,7 @@ fn classify_tagging_style( } fn unwrap_nullable(expr: IrTypeExpr) -> (IrTypeExpr, bool) { - match expr { - IrTypeExpr::Nullable(inner) => (*inner, true), - other => (other, false), - } + expr.split_nullable() } fn extract_validation(obj: &ObjectSchema) -> Option { diff --git a/src/ir/lower/v31.rs b/src/ir/lower/v31.rs index c2c2d3d6e..78a5b16e6 100644 --- a/src/ir/lower/v31.rs +++ b/src/ir/lower/v31.rs @@ -719,9 +719,10 @@ impl<'a> LowerCtx<'a> { } ObjectOrReference::Object(obj) => { if let Some(inner) = nullable_wrapper_inner(obj) { - return Ok(IrTypeExpr::Nullable(Box::new( - self.lower_schema_ref(inner)?, - ))); + return Ok( + IrTypeExpr::Nullable(Box::new(self.lower_schema_ref(inner)?)) + .with_scalar_enum(&obj.enum_values, true), + ); } Ok(self.lower_type_from_schema(obj)) } @@ -775,7 +776,9 @@ impl<'a> LowerCtx<'a> { return Ok(None); }; let inner = self.lower_schema_ref_with_promotion(candidate_name, inner_ref)?; - Ok(Some(IrTypeExpr::Nullable(Box::new(inner)))) + Ok(Some( + IrTypeExpr::Nullable(Box::new(inner)).with_scalar_enum(&obj.enum_values, true), + )) } /// Lower array items, promoting inline object items to named schemas. @@ -816,7 +819,7 @@ impl<'a> LowerCtx<'a> { } } - match &obj.schema_type { + let representation = match &obj.schema_type { Some(type_set) => type_set_to_type_expr(type_set, obj), None => { // No type specified, infer from context @@ -828,7 +831,8 @@ impl<'a> LowerCtx<'a> { IrTypeExpr::Any } } - } + }; + representation.with_scalar_enum(&obj.enum_values, obj.schema_type.is_some()) } fn lower_schema_value_with_promotion( @@ -1284,12 +1288,13 @@ fn nested_type_to_expr(obj: &ObjectSchema) -> IrTypeExpr { } ObjectOrReference::Object(inner) => nested_type_to_expr(inner), }; - return IrTypeExpr::Nullable(Box::new(inner)); + return IrTypeExpr::Nullable(Box::new(inner)).with_scalar_enum(&obj.enum_values, true); } - match &obj.schema_type { + let representation = match &obj.schema_type { Some(types) => type_set_to_type_expr(types, obj), None => single_type_to_expr(SchemaType::String, obj), - } + }; + representation.with_scalar_enum(&obj.enum_values, obj.schema_type.is_some()) } fn with_schema_nullability(obj: &ObjectSchema, base: IrTypeExpr) -> IrTypeExpr { @@ -1428,10 +1433,7 @@ fn classify_tagging_style( } fn unwrap_nullable(expr: IrTypeExpr) -> (IrTypeExpr, bool) { - match expr { - IrTypeExpr::Nullable(inner) => (*inner, true), - other => (other, false), - } + expr.split_nullable() } fn extract_validation(obj: &ObjectSchema) -> Option { diff --git a/src/ir/lower/v32.rs b/src/ir/lower/v32.rs index ae6e67029..afcc374f6 100644 --- a/src/ir/lower/v32.rs +++ b/src/ir/lower/v32.rs @@ -716,9 +716,10 @@ impl<'a> LowerCtx<'a> { } ObjectOrReference::Object(obj) => { if let Some(inner) = nullable_wrapper_inner(obj) { - return Ok(IrTypeExpr::Nullable(Box::new( - self.lower_schema_ref(inner)?, - ))); + return Ok( + IrTypeExpr::Nullable(Box::new(self.lower_schema_ref(inner)?)) + .with_scalar_enum(&obj.enum_values, true), + ); } Ok(self.lower_type_from_schema(obj)) } @@ -772,7 +773,9 @@ impl<'a> LowerCtx<'a> { return Ok(None); }; let inner = self.lower_schema_ref_with_promotion(candidate_name, inner_ref)?; - Ok(Some(IrTypeExpr::Nullable(Box::new(inner)))) + Ok(Some( + IrTypeExpr::Nullable(Box::new(inner)).with_scalar_enum(&obj.enum_values, true), + )) } /// Lower array items, promoting inline object items to named schemas. @@ -813,7 +816,7 @@ impl<'a> LowerCtx<'a> { } } - match &obj.schema_type { + let representation = match &obj.schema_type { Some(type_set) => type_set_to_type_expr(type_set, obj), None => { // No type specified, infer from context @@ -825,7 +828,8 @@ impl<'a> LowerCtx<'a> { IrTypeExpr::Any } } - } + }; + representation.with_scalar_enum(&obj.enum_values, obj.schema_type.is_some()) } fn lower_schema_value_with_promotion( @@ -1291,12 +1295,13 @@ fn nested_type_to_expr(obj: &ObjectSchema) -> IrTypeExpr { } ObjectOrReference::Object(inner) => nested_type_to_expr(inner), }; - return IrTypeExpr::Nullable(Box::new(inner)); + return IrTypeExpr::Nullable(Box::new(inner)).with_scalar_enum(&obj.enum_values, true); } - match &obj.schema_type { + let representation = match &obj.schema_type { Some(types) => type_set_to_type_expr(types, obj), None => single_type_to_expr(SchemaType::String, obj), - } + }; + representation.with_scalar_enum(&obj.enum_values, obj.schema_type.is_some()) } fn with_schema_nullability(obj: &ObjectSchema, base: IrTypeExpr) -> IrTypeExpr { @@ -1432,10 +1437,7 @@ fn classify_tagging_style( } fn unwrap_nullable(expr: IrTypeExpr) -> (IrTypeExpr, bool) { - match expr { - IrTypeExpr::Nullable(inner) => (*inner, true), - other => (other, false), - } + expr.split_nullable() } fn extract_validation(obj: &ObjectSchema) -> Option { diff --git a/src/ir/types/type_expr.rs b/src/ir/types/type_expr.rs index 534c8dd78..a30df785b 100644 --- a/src/ir/types/type_expr.rs +++ b/src/ir/types/type_expr.rs @@ -16,6 +16,18 @@ pub enum IrTypeExpr { /// Inline string enum (e.g., `"active" | "inactive"`). Used for multi-value string /// enums in property types that don't warrant a separate named schema. StringEnum(Vec), + /// Finite scalar values with the pre-existing target representation retained. + /// Generators may use the values for selection without changing public types. + ScalarEnum { + representation: Box, + /// Whether the retained representation is an authored value constraint, + /// rather than the legacy string fallback for an enum without a type. + representation_is_constraint: bool, + /// Null permission extracted from this representation by `split_nullable`. + /// Keep its original constraint boundary without changing legacy types. + representation_nullable: bool, + values: Vec, + }, /// Array of items. Array(Box), /// Map with string keys and typed values (the `additionalProperties` pattern). @@ -29,6 +41,143 @@ pub enum IrTypeExpr { Any, } +impl IrTypeExpr { + /// View the pre-existing representation without scalar selection metadata. + /// Generators that do not consume finite-value facts can keep their existing + /// type, codec and transport policies through this transparent view. + pub fn representation(&self) -> &Self { + match self { + Self::ScalarEnum { representation, .. } => representation.representation(), + other => other, + } + } + + /// Separate a property's structural nullability without moving finite-value + /// constraints across that boundary. The enum still constrains the whole + /// original node, including whether null is one of its permitted values. + pub(crate) fn split_nullable(self) -> (Self, bool) { + match self { + Self::Nullable(inner) => (*inner, true), + Self::ScalarEnum { + representation, + representation_is_constraint, + representation_nullable, + values, + } => { + let removed_here = matches!(*representation, Self::Nullable(_)); + let (representation, nullable) = representation.split_nullable(); + ( + Self::ScalarEnum { + representation: Box::new(representation), + representation_is_constraint, + representation_nullable: representation_nullable || removed_here, + values, + }, + nullable || representation_nullable, + ) + } + other => (other, false), + } + } + + /// Retain scalar enum facts without widening the supported type grammar or + /// changing the representation consumed by existing language generators. + pub(crate) fn with_scalar_enum( + self, + values: &[serde_json::Value], + representation_is_constraint: bool, + ) -> Self { + if values.is_empty() + || values + .iter() + .any(|value| value.is_array() || value.is_object()) + || matches!(&self, Self::StringLiteral(_) | Self::StringEnum(_)) + { + self + } else { + Self::ScalarEnum { + representation: Box::new(self), + representation_is_constraint, + representation_nullable: false, + values: values.to_vec(), + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn finite(representation: IrTypeExpr, values: Vec) -> IrTypeExpr { + IrTypeExpr::ScalarEnum { + representation: Box::new(representation), + representation_is_constraint: true, + representation_nullable: false, + values, + } + } + + #[test] + fn extracted_null_permission_remains_at_its_original_enum_boundary() { + let integer = IrTypeExpr::Primitive(IrPrimitive::Integer); + let inner_values = vec![1.into()]; + let outer_values = vec![1.into(), serde_json::Value::Null]; + let outside = finite( + IrTypeExpr::Nullable(Box::new(finite(integer.clone(), inner_values.clone()))), + outer_values.clone(), + ); + let inside = finite( + finite( + IrTypeExpr::Nullable(Box::new(integer.clone())), + inner_values, + ), + outer_values, + ); + let (outside, outside_nullable) = outside.split_nullable(); + let (inside, inside_nullable) = inside.split_nullable(); + assert!(outside_nullable && inside_nullable); + assert_eq!(outside.representation(), &integer); + assert_eq!(inside.representation(), &integer); + assert_ne!( + outside, inside, + "the same legacy type must not erase constraint scope" + ); + let IrTypeExpr::ScalarEnum { + representation, + representation_nullable, + .. + } = outside + else { + panic!("outer finite constraint"); + }; + assert!(representation_nullable); + assert!(matches!( + *representation, + IrTypeExpr::ScalarEnum { + representation_nullable: false, + .. + } + )); + let IrTypeExpr::ScalarEnum { + representation, + representation_nullable, + .. + } = inside + else { + panic!("outer finite constraint"); + }; + assert!(!representation_nullable); + assert!(matches!( + *representation, + IrTypeExpr::ScalarEnum { + representation_nullable: true, + .. + } + )); + } +} + /// Primitive types with optional format hints. #[derive(Debug, Clone, PartialEq, Eq, Serialize)] pub enum IrPrimitive { diff --git a/tests/fixtures/valid/typescript-helper-isolation.yaml b/tests/fixtures/valid/typescript-helper-isolation.yaml new file mode 100644 index 000000000..786a047e5 --- /dev/null +++ b/tests/fixtures/valid/typescript-helper-isolation.yaml @@ -0,0 +1,20 @@ +openapi: 3.1.0 +info: {title: TypeScript helper dependency isolation, version: 1.0.0} +paths: {} +components: + schemas: + Detail: + type: object + required: [wire_name] + properties: + wire_name: {type: string} + Useful: + type: array + items: {$ref: '#/components/schemas/Detail'} + Recursive: + anyOf: + - {$ref: '#/components/schemas/Detail'} + - type: array + items: {$ref: '#/components/schemas/Recursive'} + - type: object + additionalProperties: {$ref: '#/components/schemas/Recursive'} diff --git a/tests/fixtures/valid/typescript-untagged-union.yaml b/tests/fixtures/valid/typescript-untagged-union.yaml new file mode 100644 index 000000000..7cd41fb65 --- /dev/null +++ b/tests/fixtures/valid/typescript-untagged-union.yaml @@ -0,0 +1,818 @@ +openapi: 3.1.0 +info: + title: Untagged union conversion + version: 1.0.0 +paths: + /matrix: + post: + tags: [Conversion] + operationId: exchangeMatrix + requestBody: + required: true + content: + application/json: + schema: + type: array + items: + type: array + items: + $ref: '#/components/schemas/Tool' + responses: + '200': + description: Matrix + content: + application/json: + schema: + type: array + items: + type: array + items: + $ref: '#/components/schemas/Tool' + '400': + description: Matrix error + content: + application/json: + schema: + type: array + items: + type: array + items: + $ref: '#/components/schemas/Tool' + /nullable: + post: + tags: [Conversion] + operationId: exchangeNullable + requestBody: + required: true + content: + application/json: + schema: + type: [array, 'null'] + items: + $ref: '#/components/schemas/Tool' + responses: + '200': + description: Nullable array + content: + application/json: + schema: + type: [array, 'null'] + items: + $ref: '#/components/schemas/Tool' + /multipart: + post: + tags: [Conversion] + operationId: exchangeMultipart + requestBody: + required: true + content: + multipart/form-data: + schema: + type: object + required: [payload_values] + properties: + upload_file: + type: string + format: binary + payload_values: + type: array + items: + type: [object, string] + additionalProperties: + $ref: '#/components/schemas/Detail' + encoding: + payload_values: + contentType: application/json + responses: + '204': + description: Accepted + /optional-scalar: + post: + tags: [Conversion] + operationId: sendOptionalScalar + requestBody: + required: false + content: + application/json: + schema: {$ref: '#/components/schemas/NullableScalarInner'} + responses: + '204': {description: Accepted} + /required-scalar: + post: + tags: [Conversion] + operationId: sendRequiredScalar + requestBody: + required: true + content: + application/json: + schema: {$ref: '#/components/schemas/NullableScalarInner'} + responses: + '204': {description: Accepted} + /optional-inline-scalar: + post: + tags: [Conversion] + operationId: sendOptionalInlineScalar + requestBody: + required: false + content: + application/json: + schema: + anyOf: + - {type: [string, 'null']} + - {type: [number, 'null']} + responses: + '204': {description: Accepted} + /optional-detail: + post: + tags: [Conversion] + operationId: sendOptionalDetail + requestBody: + required: false + content: + application/json: + schema: {$ref: '#/components/schemas/NullableDetail'} + responses: + '204': {description: Accepted} +components: + schemas: + ClientTool: + type: object + required: [name, input_schema] + properties: + name: + type: string + input_schema: + type: object + additionalProperties: true + SearchTool: + type: object + required: [type, name] + properties: + type: + type: string + enum: [web_search_20250305] + name: + type: string + enum: [web_search] + max_uses: + type: integer + Tool: + oneOf: + - $ref: '#/components/schemas/ClientTool' + - $ref: '#/components/schemas/SearchTool' + NullableTool: + anyOf: + - $ref: '#/components/schemas/Tool' + - type: 'null' + ToolAlias: + allOf: + - $ref: '#/components/schemas/Tool' + ToolList: + oneOf: + - type: array + items: + $ref: '#/components/schemas/ClientTool' + - type: array + items: + $ref: '#/components/schemas/SearchTool' + ToolMap: + oneOf: + - type: object + additionalProperties: + $ref: '#/components/schemas/ClientTool' + - type: object + additionalProperties: + $ref: '#/components/schemas/SearchTool' + OpenTool: + anyOf: + - {} + - $ref: '#/components/schemas/ClientTool' + AlternativeTool: + type: object + required: [name, input_schema] + properties: + name: + type: string + input_schema: + type: object + additionalProperties: true + second_option: + type: boolean + AmbiguousTool: + anyOf: + - $ref: '#/components/schemas/ClientTool' + - $ref: '#/components/schemas/AlternativeTool' + ToolRequest: + type: object + required: [tools] + properties: + tools: + type: array + items: + $ref: '#/components/schemas/Tool' + Detail: + type: object + required: [wire_name] + properties: + wire_name: + type: string + OtherDetail: + type: object + required: [wire_name] + properties: + wire_name: + type: string + optional_flag: + type: boolean + OpenInner: + anyOf: + - {} + - $ref: '#/components/schemas/Detail' + Outer: + anyOf: + - $ref: '#/components/schemas/OpenInner' + - type: string + OpenAlias: + allOf: + - $ref: '#/components/schemas/OpenInner' + AliasOuter: + anyOf: + - $ref: '#/components/schemas/OpenAlias' + - $ref: '#/components/schemas/SearchTool' + NullableMapFallback: + anyOf: + - type: [object, 'null'] + additionalProperties: true + - $ref: '#/components/schemas/Detail' + AmbiguousInner: + anyOf: + - {} + - $ref: '#/components/schemas/Detail' + - $ref: '#/components/schemas/OtherDetail' + AmbiguousOuter: + anyOf: + - $ref: '#/components/schemas/AmbiguousInner' + - type: string + Adjacent: + oneOf: + - type: object + required: [event_type] + properties: + event_type: + type: string + enum: [empty] + - type: object + required: [event_type, event_data] + properties: + event_type: + type: string + enum: [data] + event_data: + $ref: '#/components/schemas/Detail' + AdjacentOrNumber: + anyOf: + - $ref: '#/components/schemas/Adjacent' + - type: number + BinaryModel: + type: object + required: [binary_data, field_name] + properties: + binary_data: + type: string + format: binary + field_name: + type: string + BinaryUnion: + anyOf: + - $ref: '#/components/schemas/BinaryModel' + - type: boolean + OpenMap: + type: object + additionalProperties: + $ref: '#/components/schemas/OpenInner' + MapOuter: + anyOf: + - {} + - $ref: '#/components/schemas/OpenMap' + AmbiguousMap: + type: object + additionalProperties: + $ref: '#/components/schemas/AmbiguousInner' + AmbiguousMapOuter: + anyOf: + - {} + - $ref: '#/components/schemas/AmbiguousMap' + MapWrapped: + allOf: + - $ref: '#/components/schemas/NullableMapFallback' + WrappedMapOrBoolean: + anyOf: + - $ref: '#/components/schemas/MapWrapped' + - type: boolean + DetailArrayOrObject: + anyOf: + - type: array + items: + $ref: '#/components/schemas/Detail' + - $ref: '#/components/schemas/Detail' + ArrayWrapped: + allOf: + - $ref: '#/components/schemas/DetailArrayOrObject' + WrappedArrayOrBoolean: + anyOf: + - $ref: '#/components/schemas/ArrayWrapped' + - type: boolean + InlineMixedValues: + anyOf: + - type: array + items: + type: [object, string] + additionalProperties: + $ref: '#/components/schemas/Detail' + - $ref: '#/components/schemas/Detail' + InlineDictionaryValues: + anyOf: + - type: object + additionalProperties: + type: [object, string] + additionalProperties: + $ref: '#/components/schemas/Detail' + - type: boolean + NamedValue: + anyOf: + - type: object + additionalProperties: + $ref: '#/components/schemas/Detail' + - type: string + NamedContainer: + anyOf: + - type: array + items: + $ref: '#/components/schemas/NamedValue' + - $ref: '#/components/schemas/Detail' + InlineNumericValues: + anyOf: + - type: array + items: + type: [object, integer, number] + additionalProperties: + $ref: '#/components/schemas/Detail' + ArrayPayload: + type: object + required: [wire_items] + properties: + wire_items: + type: array + items: + type: string + ArrayPayloadOrBoolean: + anyOf: + - $ref: '#/components/schemas/ArrayPayload' + - type: boolean + PrimitiveArrayChoice: + anyOf: + - type: array + items: + type: [integer, number] + - $ref: '#/components/schemas/Detail' + PrimitiveMapChoice: + anyOf: + - type: object + additionalProperties: + type: [integer, number] + - $ref: '#/components/schemas/Detail' + DictionaryAlias: + allOf: + - type: object + additionalProperties: + $ref: '#/components/schemas/Detail' + ValueAlias: + type: array + items: + type: [object, string] + additionalProperties: + $ref: '#/components/schemas/Detail' + AliasChoice: + anyOf: + - $ref: '#/components/schemas/ValueAlias' + - $ref: '#/components/schemas/Detail' + InlineProperty: + type: object + required: [entries] + properties: + entries: + type: array + items: + type: [object, string] + additionalProperties: + $ref: '#/components/schemas/Detail' + RawEnvelope: + type: object + required: [request_id, raw_choice, plain_detail] + properties: + request_id: + type: string + raw_choice: + $ref: '#/components/schemas/AliasChoice' + plain_detail: + $ref: '#/components/schemas/Detail' + MixedMapA: + type: object + required: [marker] + properties: + marker: + type: string + additionalProperties: + $ref: '#/components/schemas/Detail' + MixedMapB: + type: object + required: [marker] + properties: + marker: + type: string + additionalProperties: + type: array + items: + $ref: '#/components/schemas/Detail' + MixedMapChoice: + anyOf: + - $ref: '#/components/schemas/MixedMapA' + - $ref: '#/components/schemas/MixedMapB' + NumericOne: + type: object + required: [revision] + properties: + revision: + type: integer + enum: [1] + first_value: + type: string + NumericTwo: + type: object + required: [revision] + properties: + revision: + type: integer + enum: [2] + second_value: + type: string + NumericEnumChoice: + oneOf: + - $ref: '#/components/schemas/NumericOne' + - $ref: '#/components/schemas/NumericTwo' + NullableOpaque: + anyOf: + - {} + - type: 'null' + TwiceNullableOpaque: + anyOf: + - $ref: '#/components/schemas/NullableOpaque' + - type: 'null' + NullableDetail: + anyOf: + - $ref: '#/components/schemas/Detail' + - type: 'null' + OnceNullableOuter: + anyOf: + - $ref: '#/components/schemas/NullableOpaque' + - $ref: '#/components/schemas/NullableDetail' + TwiceNullableOuter: + anyOf: + - $ref: '#/components/schemas/TwiceNullableOpaque' + - $ref: '#/components/schemas/NullableDetail' + RecursiveList: + anyOf: + - type: array + items: + $ref: '#/components/schemas/RecursiveList' + - $ref: '#/components/schemas/Detail' + RecursiveOpenDictionary: + anyOf: + - {} + - $ref: '#/components/schemas/Detail' + - type: object + additionalProperties: + $ref: '#/components/schemas/RecursiveOpenDictionary' + RecursiveDictionaryChoice: + anyOf: + - {} + - $ref: '#/components/schemas/RecursiveOpenDictionary' + AdjacentContainer: + oneOf: + - type: object + required: [event_type] + properties: + event_type: + type: string + enum: [empty] + - type: object + required: [event_type, event_data] + properties: + event_type: + type: string + enum: [data] + event_data: + type: array + items: + type: [object, string] + additionalProperties: + $ref: '#/components/schemas/Detail' + AdjacentPrimitive: + oneOf: + - type: object + required: [event_type] + properties: + event_type: + type: string + enum: [empty] + - type: object + required: [event_type, event_data] + properties: + event_type: + type: string + enum: [data] + event_data: + type: array + items: + type: [integer, number] + Record: + oneOf: + - type: object + required: [event_type] + properties: + event_type: {type: string, enum: [empty]} + - type: object + required: [event_type, event_data] + properties: + event_type: {type: string, enum: [data]} + event_data: + $ref: '#/components/schemas/Detail' + RecordOrBoolean: + anyOf: + - $ref: '#/components/schemas/Record' + - type: boolean + Readonly: + oneOf: + - type: object + required: [extension_key] + properties: + extension_key: {$ref: '#/components/schemas/Detail'} + additionalProperties: + type: array + items: {$ref: '#/components/schemas/Detail'} + TaggedMapA: + oneOf: + - type: object + required: [payload_key] + properties: + payload_key: {$ref: '#/components/schemas/Detail'} + additionalProperties: {$ref: '#/components/schemas/Detail'} + TaggedMapB: + oneOf: + - type: object + required: [payload_key] + properties: + payload_key: {$ref: '#/components/schemas/Detail'} + additionalProperties: + type: array + items: {$ref: '#/components/schemas/Detail'} + TaggedMapChoice: + anyOf: + - $ref: '#/components/schemas/TaggedMapA' + - $ref: '#/components/schemas/TaggedMapB' + NullableNumericOne: + type: object + required: [revision] + properties: + revision: {type: [integer, 'null'], enum: [1]} + first_value: {type: string} + NullableNumericTwo: + type: object + required: [revision] + properties: + revision: {type: [integer, 'null'], enum: [2, null]} + second_value: {type: string} + NullableNumericChoice: + oneOf: + - $ref: '#/components/schemas/NullableNumericOne' + - $ref: '#/components/schemas/NullableNumericTwo' + ExplicitNullableEnumChoice: + anyOf: + - type: array + items: + anyOf: + - {type: integer, enum: [1]} + - {type: 'null'} + - $ref: '#/components/schemas/Detail' + WrapperOne: + type: object + required: [revision] + properties: + revision: + anyOf: [{type: integer}, {type: 'null'}] + enum: [1] + first_value: {type: string} + WrapperTwo: + type: object + required: [revision] + properties: + revision: + anyOf: [{type: integer}, {type: 'null'}] + enum: [2, null] + second_value: {type: string} + WrapperChoice: + oneOf: + - $ref: '#/components/schemas/WrapperOne' + - $ref: '#/components/schemas/WrapperTwo' + NullableOverlap: + anyOf: + - $ref: '#/components/schemas/NullableDetail' + - $ref: '#/components/schemas/NullableTool' + ExplicitNullOverlap: + anyOf: + - {type: 'null'} + - $ref: '#/components/schemas/NullableDetail' + - $ref: '#/components/schemas/NullableTool' + NestedExplicitNullOverlap: + anyOf: + - {type: 'null'} + - $ref: '#/components/schemas/NullableOverlap' + - {type: boolean} + NestedDetailLists: + type: array + items: + type: array + items: {$ref: '#/components/schemas/Detail'} + NullableScalarInner: + anyOf: + - {type: [string, 'null']} + - {type: [number, 'null']} + WrappedScalarInner: + anyOf: + - {$ref: '#/components/schemas/NullableScalarInner'} + - {type: 'null'} + NullableScalarParent: + type: object + required: [inner_value] + properties: + inner_value: + anyOf: + - {$ref: '#/components/schemas/NullableScalarInner'} + - {type: 'null'} + NullableScalarArray: + type: array + items: + anyOf: + - {$ref: '#/components/schemas/NullableScalarInner'} + - {type: 'null'} + NullableScalarMap: + type: object + additionalProperties: + anyOf: + - {$ref: '#/components/schemas/NullableScalarInner'} + - {type: 'null'} + NullableNonNullScalars: + type: array + items: + anyOf: + - anyOf: [{type: integer}, {type: number}] + - {type: 'null'} + NestedEnumNarrow: + type: object + required: [revision] + properties: + revision: + anyOf: [{type: integer, enum: [1]}, {type: 'null'}] + enum: [1, 2] + first_value: {type: string} + NestedEnumChoice: + anyOf: + - {$ref: '#/components/schemas/NestedEnumNarrow'} + - {$ref: '#/components/schemas/NumericTwo'} + NestedNullableEnumNarrow: + type: object + required: [revision] + properties: + revision: + anyOf: [{type: integer, enum: [1]}, {type: 'null'}] + enum: [1, null] + first_value: {type: string} + NestedNullableEnumChoice: + anyOf: + - {$ref: '#/components/schemas/NestedNullableEnumNarrow'} + - {$ref: '#/components/schemas/NumericTwo'} + NullableOpenInner: + anyOf: + - {} + - {type: [string, 'null']} + - {type: [number, 'null']} + NullableOpenExplicitOuter: + anyOf: + - {$ref: '#/components/schemas/NullableOpenInner'} + - {type: 'null'} + NullableOpenSiblingOuter: + anyOf: + - {$ref: '#/components/schemas/NullableOpenInner'} + - {type: [boolean, 'null']} + NullableOpaqueSiblingOuter: + anyOf: + - {$ref: '#/components/schemas/NullableOpaque'} + - {type: [boolean, 'null']} + UntypedNumericOne: + type: object + required: [revision] + properties: + revision: {enum: [1]} + first_value: {type: string} + UntypedNumericTwo: + type: object + required: [revision] + properties: + revision: {enum: [2]} + second_value: {type: string} + UntypedNumericChoice: + anyOf: + - {$ref: '#/components/schemas/UntypedNumericOne'} + - {$ref: '#/components/schemas/UntypedNumericTwo'} + ScalarEnumChoice: + anyOf: + - {type: integer} + - {type: boolean} + NullableEnumParent: + type: object + required: [scalar_value] + properties: + scalar_value: + anyOf: + - {$ref: '#/components/schemas/ScalarEnumChoice'} + - {type: 'null'} + enum: [1, null] + NullableInlineEnumParent: + type: object + required: [scalar_value] + properties: + scalar_value: + type: [integer, boolean, 'null'] + enum: [1, null] + NullablePlainEnumParent: + type: object + required: [scalar_value] + properties: + scalar_value: + anyOf: + - {$ref: '#/components/schemas/ScalarEnumChoice'} + - {type: 'null'} + AlreadyNullableEnumParent: + type: object + required: [scalar_value] + properties: + scalar_value: + anyOf: + - {$ref: '#/components/schemas/NullableScalarInner'} + - {type: 'null'} + enum: [1, null] + ConstrainedOuterEnumParent: + type: object + required: [scalar_value] + properties: + scalar_value: + anyOf: + - anyOf: + - {$ref: '#/components/schemas/NullableScalarInner'} + - {type: 'null'} + enum: [1] + - {type: 'null'} + enum: [1, null] + EnumFreeOuterEnumParent: + type: object + required: [scalar_value] + properties: + scalar_value: + anyOf: + - anyOf: + - {$ref: '#/components/schemas/NullableScalarInner'} + - {type: 'null'} + enum: [1] + - {type: 'null'} + ContainerOuterEnum: + type: array + items: + anyOf: + - anyOf: + - {$ref: '#/components/schemas/NullableScalarInner'} + - {type: 'null'} + enum: [1] + - {type: 'null'} + enum: [1, null] + AlreadyNullableOuterEnumParent: + type: object + required: [scalar_value] + properties: + scalar_value: + anyOf: + - anyOf: + - {$ref: '#/components/schemas/NullableScalarInner'} + - {type: 'null'} + enum: [1, null] + - {type: 'null'} + enum: [1, null] diff --git a/tests/generated-clients/jvm/README.md b/tests/generated-clients/jvm/README.md index 415af2810..d3463134b 100644 --- a/tests/generated-clients/jvm/README.md +++ b/tests/generated-clients/jvm/README.md @@ -30,7 +30,7 @@ win before `Any`, that fallback payloads are preserved, and that discriminator, adjacent, and external round-trips retain complete JSON and decoded payloads. The runner respects `JAVA_HOME` or Gradle's active JVM. Current local validation -uses Homebrew JDK 27 and Gradle 9.8.1; CI also runs the JVM validation on JDK 27. +uses Homebrew JDK 27.0.1 and Gradle 9.8.2; CI also runs the JVM validation on JDK 27. Generated libraries keep their declared Java 17 compilation target. The Foojay resolver supplies that compiler where needed, without forcing the consumer tests to run on an older JVM. No patch-versioned JDK installation path is baked into diff --git a/tests/generated-clients/typescript/README.md b/tests/generated-clients/typescript/README.md index 85c13c747..62e399c89 100644 --- a/tests/generated-clients/typescript/README.md +++ b/tests/generated-clients/typescript/README.md @@ -4,11 +4,11 @@ just golden-typescript-clients::test ``` -Requires Rust, Node.js `^22.18.0 || ^24.11.0 || >=26.0.0`, and Vite+ 1.5.0 +Requires Rust, Node.js `^22.18.0 || ^24.11.0 || >=26.0.0`, and Vite+ 1.5.1 installed alongside TypeScript 7.0.2: ```sh -npm install -g typescript@7.0.2 vite-plus@1.5.0 +npm install -g typescript@7.0.2 vite-plus@1.5.1 ``` The current global Vite+ CLI resolves the TypeScript API beside the CLI when @@ -16,7 +16,8 @@ packing declarations, so the generated package's compiler alone is insufficient. The strict consumer check itself uses that package's installed compiler. - `prepare` runs the public generator CLI with an explicit empty configuration, - producing fresh preserve and camelCase clients. + producing fresh, unmodified preserve and camelCase clients plus untagged-union + clients using the default, strict, and raw policies with tagged type guards. - `build` copies each client to a temporary build directory, runs `vp install`, reports the resolved Vite+ component versions with `vp --version`, runs full `vp check` and `vp pack`, then checks again after packing and @@ -27,11 +28,46 @@ The strict consumer check itself uses that package's installed compiler. configuration excludes `dist/`, so checks do not reformat build output. - `typecheck.ts` checks the actual packed declarations with strict typing and `exactOptionalPropertyTypes`, including negative dictionary-type and readonly - `allOf` assertions for preserve, ergonomic, and wire declarations. + `allOf` assertions for preserve, ergonomic, and wire declarations, as well as + ergonomic versus raw-wire union contracts and readonly union arrays. - `tests/*.test.mjs` uses Node's native test runner against the packed camelCase client. It asserts full payload equality in both JSON conversion directions, typed/nested/recursive dictionaries, JSON/model allOf composition, search-event variants, omitted fields, collisions with renamed fields, and prototype-sensitive keys. + Union cases check member and containing-request conversion independently in + both directions, nullable aliases, arrays/maps, raw fallback priority, + deterministic ambiguity, strict failures, literal checks, and own fields. + Nested unions also retain inner strict errors; nullable/allOf fallbacks cannot + shadow concrete members. Adjacent-tagged unit/payload cases, actual Blob/File + members, and arbitrary-JSON aliases run in all relevant conversion policies. + Inline array/dictionary member unions share the configured dispatch policy; + mixed concrete/opaque open dictionaries retain nested conversion and strict + errors. Single-member allOf aliases preserve nullable maps and arrays. + Conversion contracts also cover moving a union through an alias/property, + inline numeric enums, typed additional-value selection, recursive open + dictionaries/lists, and raw subtrees inside converting parents. Tagged models + named `Record` or `Readonly` retain their public converters; external tagged + selection checks typed extensions, and nullable enums retain their finite + constraints. HTTP tests exercise nested request/success/error containers, + required/optional nullable bodies, and multipart JSON parts. Selection controls + retain nested enum intersections, authored concrete null matches beside opaque + fallbacks, untyped scalar enum values, and supplied-null ambiguity without + confusing body omission. Enum metadata retains a typed nullable boundary around + nonnullable delegates while preserving already-nullable inner strict errors. + Nested finite constraints keep outer-only null permission consistent between + extracted property wrappers and array items. + Packed declarations pin + raw multipart inputs, raw aliases, tagged guards, singleton dictionary readonly + indexes, and the established readonly-outer/plain-inner nested array contract. + Frozen nested inputs remain usable; static conversion/matching helpers do not + leak into the public barrel. +- `isolation-consumer.ts` is an ordinary consumer of one generated model. Its + native `tsc` project and Node test prove helper imports do not pull unrelated + model graphs into that consumer. The separate isolation fixture intentionally + contains an unsupported same-value circular alias: only the selected usable + graph is compiled here, not its entire barrel. The five supported packages + above still receive their full unmodified Vite+ checks and packed-declaration + checks. Compilation stays in the just recipe, never inside a test program. Generation and build orchestration stays in just/CI, not in the tests. After a build, the runtime suite can be rerun directly: diff --git a/tests/generated-clients/typescript/isolation-consumer.ts b/tests/generated-clients/typescript/isolation-consumer.ts new file mode 100644 index 000000000..598ab0ce2 --- /dev/null +++ b/tests/generated-clients/typescript/isolation-consumer.ts @@ -0,0 +1,2 @@ +// Import only the usable graph; the unrelated same-value alias remains unsupported. +export { usefulFromJSON, usefulToJSON } from './generated/isolation/models/Useful'; diff --git a/tests/generated-clients/typescript/tests/conversion-contract.test.mjs b/tests/generated-clients/typescript/tests/conversion-contract.test.mjs new file mode 100644 index 000000000..6c6b5dd29 --- /dev/null +++ b/tests/generated-clients/typescript/tests/conversion-contract.test.mjs @@ -0,0 +1,218 @@ +import assert from 'node:assert/strict'; +import test from 'node:test'; +import * as first from '../generated/union-default/dist/index.mjs'; +import * as strict from '../generated/union-strict/dist/index.mjs'; +import * as raw from '../generated/union-raw/dist/index.mjs'; + +const value = { wireName: 'Ada' }; +const wire = { wire_name: 'Ada' }; + +for (const [policy, generated] of [['firstMatch', first], ['strict', strict]]) { + test(`${policy}: moving an inline union through an alias or property preserves conversion`, () => { + const input = [{ keep_key: wire }, 'opaque']; + const output = [{ keep_key: value }, 'opaque']; + for (const base of ['inlineMixedValues', 'aliasChoice', 'valueAlias']) { + assert.deepEqual(generated[`${base}FromJSON`](input), output); + assert.deepEqual(generated[`${base}ToJSON`](output), input); + } + assert.deepEqual(generated.inlinePropertyFromJSON({ entries: input }), { entries: output }); + assert.deepEqual(generated.inlinePropertyToJSON({ entries: output }), { entries: input }); + }); + + test(`${policy}: additional-value shapes distinguish otherwise identical object members`, () => { + const input = { marker: 'array', extra_key: [wire] }; + const output = { marker: 'array', extra_key: [value] }; + assert.deepEqual(generated.mixedMapBFromJSON(input), output); + assert.deepEqual(generated.mixedMapBToJSON(output), input); + assert.deepEqual(generated.mixedMapChoiceFromJSON(input), output); + assert.deepEqual(generated.mixedMapChoiceToJSON(output), input); + }); + + test(`${policy}: inline numeric enum facts select the unique member without dropping its fields`, () => { + assert.deepEqual(generated.numericEnumChoiceFromJSON({ revision: 2, second_value: 'two' }), { revision: 2, secondValue: 'two' }); + assert.deepEqual(generated.numericEnumChoiceToJSON({ revision: 2, secondValue: 'two' }), { revision: 2, second_value: 'two' }); + }); + + test(`${policy}: finite recursive values retain both directions of member conversion`, () => { + assert.deepEqual(generated.recursiveListFromJSON([[wire], wire]), [[value], value]); + assert.deepEqual(generated.recursiveListToJSON([[value], value]), [[wire], wire]); + }); + + test(`${policy}: recursive dictionaries preserve concrete evidence and opaque fallback identity`, () => { + const input = { keep_key: wire, nested_key: { keep_key: wire } }; + const output = { keep_key: value, nested_key: { keep_key: value } }; + assert.deepEqual(generated.recursiveDictionaryChoiceFromJSON(input), output); + assert.deepEqual(generated.recursiveDictionaryChoiceToJSON(output), input); + for (const opaque of [{}, { keep_key: 'opaque' }]) { + assert.equal(generated.recursiveDictionaryChoiceFromJSON(opaque), opaque); + assert.equal(generated.recursiveDictionaryChoiceToJSON(opaque), opaque); + } + }); + + test(`${policy}: direct tagged payloads delegate through the same inline value plan`, () => { + const input = { event_type: 'data', event_data: [{ keep_key: wire }, 'opaque'] }; + const output = { eventType: 'data', eventData: [{ keep_key: value }, 'opaque'] }; + assert.deepEqual(generated.adjacentContainerFromJSON(input), output); + assert.deepEqual(generated.adjacentContainerToJSON(output), input); + }); + + test(`${policy}: a tagged model named Record keeps its generated converter contract`, () => { + assert.deepEqual(generated.recordFromJSON({ event_type: 'data', event_data: wire }), { eventType: 'data', eventData: value }); + assert.deepEqual(generated.recordToJSON({ eventType: 'data', eventData: value }), { event_type: 'data', event_data: wire }); + assert.deepEqual(generated.recordOrBooleanFromJSON({ event_type: 'data', event_data: wire }), { eventType: 'data', eventData: value }); + assert.deepEqual(generated.recordOrBooleanToJSON({ eventType: 'data', eventData: value }), { event_type: 'data', event_data: wire }); + }); + + test(`${policy}: external tagged candidates check typed extensions separately from their payload key`, () => { + const input = { payload_key: wire, extra_key: [wire] }; + const output = { payload_key: value, extra_key: [value] }; + assert.deepEqual(generated.taggedMapBFromJSON(input), output); + assert.deepEqual(generated.taggedMapBToJSON(output), input); + assert.deepEqual(generated.taggedMapChoiceFromJSON(input), output); + assert.deepEqual(generated.taggedMapChoiceToJSON(output), input); + }); + + test(`${policy}: an open external model named Readonly preserves payloads and extensions`, () => { + const input = { extension_key: wire, extra_key: [wire] }; + const output = { extension_key: value, extra_key: [value] }; + assert.deepEqual(generated.readonlyFromJSON(input), output); + assert.deepEqual(generated.readonlyToJSON(output), input); + }); + + test(`${policy}: nullability does not broaden an enum that excludes null`, () => { + const input = { revision: null, second_value: 'two' }; + const output = { revision: null, secondValue: 'two' }; + assert.deepEqual(generated.nullableNumericTwoFromJSON(input), output); + assert.deepEqual(generated.nullableNumericTwoToJSON(output), input); + assert.deepEqual(generated.nullableNumericChoiceFromJSON(input), output); + assert.deepEqual(generated.nullableNumericChoiceToJSON(output), input); + assert.deepEqual(generated.explicitNullableEnumChoiceFromJSON([null, 1]), [null, 1]); + assert.deepEqual(generated.explicitNullableEnumChoiceToJSON([null, 1]), [null, 1]); + }); + + test(`${policy}: nullable wrappers retain their outer enum constraint`, () => { + const input = { revision: null, second_value: 'two' }; + const output = { revision: null, secondValue: 'two' }; + assert.deepEqual(generated.wrapperTwoFromJSON(input), output); + assert.deepEqual(generated.wrapperTwoToJSON(output), input); + assert.deepEqual(generated.wrapperChoiceFromJSON(input), output); + assert.deepEqual(generated.wrapperChoiceToJSON(output), input); + }); +} + +for (const suffix of ['FromJSON', 'ToJSON']) { + test(`strict: nullable properties and containers delegate inner null ambiguity ${suffix}`, () => { + assert.throws(() => strict[`nullableScalarInner${suffix}`](null), /Ambiguous NullableScalarInner/); + assert.throws(() => strict[`wrappedScalarInner${suffix}`](null), /Ambiguous NullableScalarInner/); + const parent = suffix === 'FromJSON' ? { inner_value: null } : { innerValue: null }; + assert.throws(() => strict[`nullableScalarParent${suffix}`](parent), /Ambiguous NullableScalarInner/); + assert.throws(() => strict[`nullableScalarArray${suffix}`]([null]), /Ambiguous NullableScalarInner/); + assert.throws(() => strict[`nullableScalarMap${suffix}`]({ keep_key: null }), /Ambiguous NullableScalarInner/); + assert.deepEqual(strict[`nullableNonNullScalars${suffix}`]([null]), [null]); + assert.throws(() => strict[`nullableNonNullScalars${suffix}`]([7]), /Ambiguous/); + }); + + test(`strict: explicit null keeps original and nested ambiguity ${suffix}`, () => { + assert.throws(() => strict[`nullableOverlap${suffix}`](null), /Ambiguous/); + assert.throws(() => strict[`explicitNullOverlap${suffix}`](null), /Ambiguous/); + assert.throws(() => strict[`nestedExplicitNullOverlap${suffix}`](null), /Ambiguous/); + }); +} + +for (const [policy, generated] of [['firstMatch', first], ['raw', raw]]) { + test(`${policy}: nullable delegation retains permissive and raw null controls`, () => { + assert.equal(generated.nullableScalarInnerFromJSON(null), null); + assert.equal(generated.wrappedScalarInnerToJSON(null), null); + assert.deepEqual(generated.nullableScalarParentFromJSON({ inner_value: null }), { innerValue: null }); + assert.deepEqual(generated.nullableScalarParentToJSON({ innerValue: null }), { inner_value: null }); + assert.deepEqual(generated.nullableScalarArrayFromJSON([null]), [null]); + assert.deepEqual(generated.nullableScalarMapToJSON({ keep_key: null }), { keep_key: null }); + }); +} + +test('strict: primitive-only inline unions check ambiguity in arrays and dictionaries', () => { + for (const [base, input] of [['primitiveArrayChoice', [7]], ['primitiveMapChoice', { keep_key: 7 }]]) { + assert.throws(() => strict[`${base}FromJSON`](input), /Ambiguous/); + assert.throws(() => strict[`${base}ToJSON`](input), /Ambiguous/); + } + assert.throws(() => strict.adjacentPrimitiveFromJSON({ event_type: 'data', event_data: [7] }), /Ambiguous/); + assert.throws(() => strict.adjacentPrimitiveToJSON({ eventType: 'data', eventData: [7] }), /Ambiguous/); +}); + +test('strict: nullable wrapping is idempotent and never promotes an opaque fallback', () => { + for (const base of ['onceNullableOuter', 'twiceNullableOuter']) { + assert.equal(strict[`${base}FromJSON`](null), null); + assert.equal(strict[`${base}ToJSON`](null), null); + assert.deepEqual(strict[`${base}FromJSON`](wire), value); + assert.deepEqual(strict[`${base}ToJSON`](value), wire); + } +}); + +test('raw: aliases and inline containers retain wire values and reference identity', () => { + const input = [{ keep_key: wire }, 'opaque']; + for (const base of ['inlineMixedValues', 'aliasChoice', 'valueAlias']) { + assert.equal(raw[`${base}FromJSON`](input), input); + assert.equal(raw[`${base}ToJSON`](input), input); + } +}); + +test('raw: ordinary parents and siblings convert outside the raw union subtree', () => { + const input = { request_id: 'r1', raw_choice: [{ keep_key: wire }], plain_detail: wire }; + const output = { requestId: 'r1', rawChoice: [{ keep_key: wire }], plainDetail: value }; + assert.deepEqual(raw.rawEnvelopeFromJSON(input), output); + assert.deepEqual(raw.rawEnvelopeToJSON(output), input); + const taggedWire = { event_type: 'data', event_data: [{ keep_key: wire }, 'opaque'] }; + const taggedValue = { eventType: 'data', eventData: taggedWire.event_data }; + assert.deepEqual(raw.adjacentContainerFromJSON(taggedWire), taggedValue); + assert.deepEqual(raw.adjacentContainerToJSON(taggedValue), taggedWire); +}); + +for (const [policy, generated] of [['firstMatch', first], ['strict', strict], ['raw', raw]]) { + test(`${policy}: nested array conversion reads frozen inputs without widening declarations`, () => { + const input = Object.freeze([Object.freeze([Object.freeze({ ...wire })])]); + const output = Object.freeze([Object.freeze([Object.freeze({ ...value })])]); + assert.deepEqual(generated.nestedDetailListsFromJSON(input), output); + assert.deepEqual(generated.nestedDetailListsToJSON(output), input); + }); + + test(`${policy}: implementation helpers are not public barrel exports`, () => { + assert.deepEqual(Object.keys(generated).filter((key) => /^(convert|matches)\$/.test(key)), []); + }); + + test(`${policy}: multipart JSON values share the model conversion contract`, async () => { + const input = [{ keep_key: wire }, 'opaque']; + const payload = policy === 'raw' ? input : [{ keep_key: value }, 'opaque']; + let sent; + const api = new generated.ConversionApi(new generated.Configuration({ + fetchApi: async (url, init) => { + const form = await new Request(url, init).formData(); + assert.equal(form.has('upload_file'), false); + sent = JSON.parse(form.get('payload_values')); + return new Response(null, { status: 204 }); + }, + })); + await api.exchangeMultipart({ body: { payloadValues: payload } }); + assert.deepEqual(sent, input); + }); + + test(`${policy}: multipart uploads retain supplied filenames and exact binary bytes`, async () => { + const bytes = Uint8Array.of(0, 13, 10, 127, 255); + const input = [{ keep_key: wire }, 'opaque']; + const payload = policy === 'raw' ? input : [{ keep_key: value }, 'opaque']; + let received = false; + const api = new generated.ConversionApi(new generated.Configuration({ + fetchApi: async (url, init) => { + const form = await new Request(url, init).formData(); + const file = form.get('upload_file'); + assert.equal(file.name, 'custom-file.bin'); + assert.equal(file.type, 'application/octet-stream'); + assert.deepEqual(new Uint8Array(await file.arrayBuffer()), bytes); + assert.deepEqual(JSON.parse(form.get('payload_values')), input); + received = true; + return new Response(null, { status: 204 }); + }, + })); + await api.exchangeMultipart({ body: { payloadValues: payload, uploadFile: { data: new Blob([bytes]), filename: 'custom-file.bin' } } }); + assert.equal(received, true); + }); +} diff --git a/tests/generated-clients/typescript/tests/helper-isolation.test.mjs b/tests/generated-clients/typescript/tests/helper-isolation.test.mjs new file mode 100644 index 000000000..67b5f7be4 --- /dev/null +++ b/tests/generated-clients/typescript/tests/helper-isolation.test.mjs @@ -0,0 +1,13 @@ +import assert from 'node:assert/strict'; +import { createRequire } from 'node:module'; +import test from 'node:test'; + +const require = createRequire(import.meta.url); +const client = require('../../../../target/generated-clients/typescript-isolation/isolation-consumer.js'); + +test('selected model helpers do not load unrelated model graphs', () => { + const wire = [{ wire_name: 'Ada' }]; + const value = [{ wireName: 'Ada' }]; + assert.deepEqual(client.usefulFromJSON(wire), value); + assert.deepEqual(client.usefulToJSON(value), wire); +}); diff --git a/tests/generated-clients/typescript/tests/selection-boundaries.test.mjs b/tests/generated-clients/typescript/tests/selection-boundaries.test.mjs new file mode 100644 index 000000000..05d083c33 --- /dev/null +++ b/tests/generated-clients/typescript/tests/selection-boundaries.test.mjs @@ -0,0 +1,141 @@ +import assert from 'node:assert/strict'; +import test from 'node:test'; +import * as first from '../generated/union-default/dist/index.mjs'; +import * as strict from '../generated/union-strict/dist/index.mjs'; +import * as raw from '../generated/union-raw/dist/index.mjs'; + +for (const [policy, generated] of [['firstMatch', first], ['strict', strict]]) { + test(`${policy}: untyped numeric enums do not inherit the legacy string constraint`, () => { + for (const [wire, value] of [ + [{ revision: 1, first_value: 'one' }, { revision: 1, firstValue: 'one' }], + [{ revision: 2, second_value: 'two' }, { revision: 2, secondValue: 'two' }], + ]) { + assert.deepEqual(generated.untypedNumericChoiceFromJSON(wire), value); + assert.deepEqual(generated.untypedNumericChoiceToJSON(value), wire); + } + }); + + test(`${policy}: nested and outer enum constraints retain their intersection`, () => { + for (const [wire, value] of [ + [{ revision: 1, first_value: 'one' }, { revision: 1, firstValue: 'one' }], + [{ revision: 2, second_value: 'two' }, { revision: 2, secondValue: 'two' }], + ]) { + assert.deepEqual(generated.nestedEnumChoiceFromJSON(wire), value); + assert.deepEqual(generated.nestedEnumChoiceToJSON(value), wire); + } + assert.deepEqual(generated.numericTwoFromJSON({ revision: 2, second_value: 'two' }), { revision: 2, secondValue: 'two' }); + assert.deepEqual(generated.numericTwoToJSON({ revision: 2, secondValue: 'two' }), { revision: 2, second_value: 'two' }); + assert.deepEqual(generated.nestedNullableEnumChoiceFromJSON({ revision: null, first_value: 'one' }), { revision: null, firstValue: 'one' }); + assert.deepEqual(generated.nestedNullableEnumChoiceToJSON({ revision: null, firstValue: 'one' }), { revision: null, first_value: 'one' }); + assert.deepEqual(generated.nestedNullableEnumChoiceFromJSON({ revision: 2, second_value: 'two' }), { revision: 2, secondValue: 'two' }); + }); +} + +for (const [policy, generated] of [['firstMatch', first], ['strict', strict], ['raw', raw]]) { + test(`${policy}: extracted enum null permission guards a nonnullable delegate`, () => { + for (const base of ['nullableEnumParent', 'nullableInlineEnumParent', 'nullablePlainEnumParent']) { + for (const scalar of [null, 1]) { + assert.deepEqual(generated[`${base}FromJSON`]({ scalar_value: scalar }), { scalarValue: scalar }); + assert.deepEqual(generated[`${base}ToJSON`]({ scalarValue: scalar }), { scalar_value: scalar }); + } + } + if (policy === 'strict') { + assert.throws(() => generated.alreadyNullableEnumParentFromJSON({ scalar_value: null }), /Ambiguous NullableScalarInner/); + assert.throws(() => generated.alreadyNullableEnumParentToJSON({ scalarValue: null }), /Ambiguous NullableScalarInner/); + } else { + assert.deepEqual(generated.alreadyNullableEnumParentFromJSON({ scalar_value: null }), { scalarValue: null }); + assert.deepEqual(generated.alreadyNullableEnumParentToJSON({ scalarValue: null }), { scalar_value: null }); + } + }); +} + +for (const direction of ['FromJSON', 'ToJSON']) { + test(`strict: open nested unions keep authored concrete null ambiguity ${direction}`, () => { + assert.throws(() => strict[`nullableOpenInner${direction}`](null), /Ambiguous NullableOpenInner/); + assert.throws(() => strict[`nullableOpenExplicitOuter${direction}`](null), /Ambiguous NullableOpenInner/); + assert.throws(() => strict[`nullableOpenSiblingOuter${direction}`](null), /Ambiguous NullableOpenSiblingOuter/); + assert.equal(strict[`nullableOpaqueSiblingOuter${direction}`](null), null); + for (const value of ['text', 1.5]) { + assert.equal(strict[`nullableOpenExplicitOuter${direction}`](value), value); + } + assert.equal(strict[`nullableOpenSiblingOuter${direction}`](true), true); + const opaque = { keep_key: 'opaque' }; + assert.equal(strict[`nullableOpenExplicitOuter${direction}`](opaque), opaque); + }); +} + +for (const [policy, generated] of [['firstMatch', first], ['raw', raw]]) { + test(`${policy}: open nullable unions retain permissive and raw controls`, () => { + for (const base of ['nullableOpenInner', 'nullableOpenExplicitOuter', 'nullableOpenSiblingOuter', 'nullableOpaqueSiblingOuter']) { + assert.equal(generated[`${base}FromJSON`](null), null); + assert.equal(generated[`${base}ToJSON`](null), null); + } + const input = { revision: 2, second_value: 'two' }; + if (policy === 'raw') { + assert.equal(generated.nestedEnumChoiceFromJSON(input), input); + assert.equal(generated.nestedEnumChoiceToJSON(input), input); + } + }); +} + +function http(generated) { + const sent = []; + const api = new generated.ConversionApi(new generated.Configuration({ + fetchApi: async (_url, init) => { + sent.push(init.body); + return new Response(null, { status: 204 }); + }, + })); + return { api, sent }; +} + +for (const [direction, input, expected] of [ + ['FromJSON', { scalar_value: null }, { scalarValue: null }], + ['ToJSON', { scalarValue: null }, { scalar_value: null }], +]) { + test(`strict: nested finite boundary retains outer-only null permission ${direction}`, () => { + assert.deepEqual(strict[`containerOuterEnum${direction}`]([null, 1]), [null, 1]); + assert.deepEqual(strict[`enumFreeOuterEnumParent${direction}`](input), expected); + assert.deepEqual(strict[`constrainedOuterEnumParent${direction}`](input), expected); + }); + test(`strict: nested finite boundary retains inner-owned null ambiguity ${direction}`, () => { + assert.throws(() => strict[`alreadyNullableOuterEnumParent${direction}`](input), /Ambiguous NullableScalarInner/); + }); + for (const [policy, generated] of [['firstMatch', first], ['raw', raw]]) { + test(`${policy}: nested finite null controls remain usable ${direction}`, () => { + assert.deepEqual(generated[`constrainedOuterEnumParent${direction}`](input), expected); + assert.deepEqual(generated[`containerOuterEnum${direction}`]([null, 1]), [null, 1]); + }); + } +} + +test('strict: direct and required JSON body retain null ambiguity', async () => { + assert.throws(() => strict.nullableScalarInnerToJSON(null), /Ambiguous NullableScalarInner/); + const { api, sent } = http(strict); + await assert.rejects(api.sendRequiredScalar({ body: null }), /Ambiguous NullableScalarInner/); + assert.deepEqual(sent, []); +}); + +for (const method of ['sendOptionalScalar', 'sendOptionalInlineScalar']) { + test(`strict: ${method} delegates supplied null rather than treating it as omission`, async () => { + const { api, sent } = http(strict); + await assert.rejects(api[method]({ body: null }), /Ambiguous/); + assert.deepEqual(sent, []); + }); +} + +for (const [policy, generated] of [['firstMatch', first], ['strict', strict], ['raw', raw]]) { + test(`${policy}: optional JSON bodies preserve omission, allowed null and converted values`, async () => { + const { api, sent } = http(generated); + await api.sendOptionalScalar({}); + await api.sendOptionalInlineScalar({}); + await api.sendOptionalDetail({}); + await api.sendOptionalDetail({ body: null }); + await api.sendOptionalDetail({ body: policy === 'raw' ? { wire_name: 'Ada' } : { wireName: 'Ada' } }); + assert.deepEqual(sent, [undefined, undefined, undefined, 'null', '{"wire_name":"Ada"}']); + if (policy !== 'strict') { + await api.sendOptionalScalar({ body: null }); + assert.equal(sent.at(-1), 'null'); + } + }); +} diff --git a/tests/generated-clients/typescript/tests/untagged-union.test.mjs b/tests/generated-clients/typescript/tests/untagged-union.test.mjs new file mode 100644 index 000000000..851cd7ae7 --- /dev/null +++ b/tests/generated-clients/typescript/tests/untagged-union.test.mjs @@ -0,0 +1,290 @@ +import assert from 'node:assert/strict'; +import test from 'node:test'; +import * as client from '../generated/union-default/dist/index.mjs'; +import * as strict from '../generated/union-strict/dist/index.mjs'; +import * as raw from '../generated/union-raw/dist/index.mjs'; + +const clientValue = { name: 'weather', inputSchema: { type: 'object', opaque_key: { keep_name: true } } }; +const clientWire = { name: 'weather', input_schema: { type: 'object', opaque_key: { keep_name: true } } }; +const searchValue = { type: 'web_search_20250305', name: 'web_search', maxUses: 1 }; +const searchWire = { type: 'web_search_20250305', name: 'web_search', max_uses: 1 }; + +for (const [name, value, wire] of [['client', clientValue, clientWire], ['search', searchValue, searchWire]]) { + test(`${name} union conversion independently matches its member in both directions`, () => { + assert.deepEqual(client.toolFromJSON(wire), value); + assert.deepEqual(client.toolToJSON(value), wire); + assert.deepEqual(strict.toolFromJSON(wire), value); + assert.deepEqual(strict.toolToJSON(value), wire); + }); +} + +test('a containing request converts its typed union elements', () => { + const value = { tools: [clientValue, searchValue] }; + const wire = { tools: [clientWire, searchWire] }; + assert.deepEqual(client.toolRequestFromJSON(wire), value); + assert.deepEqual(client.toolRequestToJSON(value), wire); +}); + +test('nullable and alias unions delegate to the same conversion', () => { + assert.equal(client.nullableToolFromJSON(null), null); + assert.equal(client.nullableToolToJSON(null), null); + assert.deepEqual(client.nullableToolFromJSON(clientWire), clientValue); + assert.deepEqual(client.toolAliasToJSON(searchValue), searchWire); +}); + +test('array and dictionary alternatives convert values, not dictionary keys', () => { + assert.deepEqual(client.toolListFromJSON([clientWire]), [clientValue]); + assert.deepEqual(client.toolListToJSON([searchValue]), [searchWire]); + assert.deepEqual(client.toolMapFromJSON({ opaque_key: clientWire }), { opaque_key: clientValue }); + assert.deepEqual(client.toolMapToJSON({ opaque_key: searchValue }), { opaque_key: searchWire }); +}); + +test('unconstrained JSON remains a fallback after a concrete convertible member', () => { + assert.deepEqual(client.openToolFromJSON(clientWire), clientValue); + assert.deepEqual(client.openToolToJSON(clientValue), clientWire); + const unmatched = { arbitrary_key: [7, null, 'opaque'] }; + assert.deepEqual(client.openToolFromJSON(unmatched), unmatched); + assert.deepEqual(client.openToolToJSON(unmatched), unmatched); +}); + +test('default ambiguous selection is deterministic and unmatched values stay intact', () => { + assert.deepEqual(client.ambiguousToolFromJSON(clientWire), clientValue); + assert.deepEqual(client.ambiguousToolToJSON(clientValue), clientWire); + const unmatched = { arbitrary_key: true }; + assert.deepEqual(client.toolFromJSON(unmatched), unmatched); + assert.deepEqual(client.toolToJSON(unmatched), unmatched); +}); + +test('strict policy rejects ambiguous and unmatched shapes without treating raw fallback as ambiguity', () => { + assert.throws(() => strict.ambiguousToolFromJSON(clientWire), TypeError); + assert.throws(() => strict.ambiguousToolToJSON(clientValue), TypeError); + assert.throws(() => strict.toolFromJSON({ arbitrary_key: true }), TypeError); + assert.throws(() => strict.toolToJSON({ arbitrary_key: true }), TypeError); + assert.deepEqual(strict.openToolFromJSON(clientWire), clientValue); +}); + +test('required literals, primitive kinds, and own fields determine member matches', () => { + assert.throws(() => strict.toolFromJSON({ type: 'wrong', name: 'web_search', max_uses: 1 }), TypeError); + assert.throws(() => strict.toolFromJSON({ name: 7, input_schema: {} }), TypeError); + assert.throws(() => strict.toolFromJSON(Object.create(clientWire)), TypeError); +}); + +test('raw policy explicitly preserves the original representation', () => { + assert.equal(raw.toolFromJSON(clientWire), clientWire); + assert.equal(raw.toolToJSON(searchWire), searchWire); + assert.deepEqual(raw.toolRequestToJSON({ tools: [clientWire, searchWire] }), { tools: [clientWire, searchWire] }); +}); + +const detailValue = { wireName: 'Ada' }; +const detailWire = { wire_name: 'Ada' }; + +for (const [policy, generated] of [['default', client], ['strict', strict]]) { + test(`${policy}: array-valued object members compile and convert in both directions`, () => { + assert.deepEqual(generated.arrayPayloadOrBooleanFromJSON({ wire_items: ['one'] }), { wireItems: ['one'] }); + assert.deepEqual(generated.arrayPayloadOrBooleanToJSON({ wireItems: ['one'] }), { wire_items: ['one'] }); + }); +} + +for (const [policy, generated] of [['default', client], ['strict', strict]]) { + test(`${policy}: allOf fallback aliases do not shadow a concrete outer member`, () => { + assert.deepEqual(generated.aliasOuterFromJSON(searchWire), searchValue); + assert.deepEqual(generated.aliasOuterToJSON(searchValue), searchWire); + assert.deepEqual(generated.aliasOuterFromJSON(detailWire), detailValue); + assert.deepEqual(generated.aliasOuterToJSON(detailValue), detailWire); + for (const unmatched of [null, 'opaque', 7, ['opaque'], { arbitrary_key: true }]) { + assert.deepEqual(generated.aliasOuterFromJSON(unmatched), unmatched); + assert.deepEqual(generated.aliasOuterToJSON(unmatched), unmatched); + } + }); + + test(`${policy}: nullable raw maps remain fallbacks instead of concrete ambiguity`, () => { + assert.deepEqual(generated.nullableMapFallbackFromJSON(detailWire), detailValue); + assert.deepEqual(generated.nullableMapFallbackToJSON(detailValue), detailWire); + assert.equal(generated.nullableMapFallbackFromJSON(null), null); + assert.equal(generated.nullableMapFallbackToJSON(null), null); + const unmatched = { arbitrary_key: [1, null, { keep_name: true }] }; + assert.deepEqual(generated.nullableMapFallbackFromJSON(unmatched), unmatched); + assert.deepEqual(generated.nullableMapFallbackToJSON(unmatched), unmatched); + }); + + test(`${policy}: named inner unions retain conversion through an unrestricted alternative`, () => { + assert.deepEqual(generated.openInnerFromJSON(detailWire), detailValue); + assert.deepEqual(generated.openInnerToJSON(detailValue), detailWire); + assert.deepEqual(generated.outerFromJSON(detailWire), detailValue); + assert.deepEqual(generated.outerToJSON(detailValue), detailWire); + const unmatched = { arbitrary_key: [7, null, 'opaque'] }; + assert.equal(generated.outerFromJSON(unmatched), unmatched); + assert.equal(generated.outerToJSON(unmatched), unmatched); + }); + + test(`${policy}: adjacent tagged unit and payload variants match their direct converters`, () => { + for (const [wire, value] of [ + [{ event_type: 'empty' }, { eventType: 'empty' }], + [{ event_type: 'data', event_data: detailWire }, { eventType: 'data', eventData: detailValue }], + ]) { + assert.deepEqual(generated.adjacentFromJSON(wire), value); + assert.deepEqual(generated.adjacentToJSON(value), wire); + assert.deepEqual(generated.adjacentOrNumberFromJSON(wire), value); + assert.deepEqual(generated.adjacentOrNumberToJSON(value), wire); + } + assert.equal(generated.adjacentOrNumberFromJSON(7), 7); + }); + + for (const binary of [new Blob(['x']), new File(['y'], 'sample.txt')]) { + test(`${policy}: a ${binary.constructor.name} member uses its actual TypeScript representation`, () => { + const value = { binaryData: binary, fieldName: 'name' }; + const wire = { binary_data: binary, field_name: 'name' }; + assert.deepEqual(generated.binaryModelToJSON(value), wire); + assert.deepEqual(generated.binaryUnionToJSON(value), wire); + assert.deepEqual(generated.binaryUnionFromJSON(wire), value); + }); + } +} + +test('outer strict unions retain inner ambiguity errors in both directions', () => { + assert.throws(() => strict.ambiguousInnerFromJSON(detailWire), /Ambiguous/); + assert.throws(() => strict.ambiguousInnerToJSON(detailValue), /Ambiguous/); + assert.throws(() => strict.ambiguousOuterFromJSON(detailWire), /Ambiguous/); + assert.throws(() => strict.ambiguousOuterToJSON(detailValue), /Ambiguous/); + assert.deepEqual(client.ambiguousOuterFromJSON(detailWire), detailValue); + assert.deepEqual(client.ambiguousOuterToJSON(detailValue), detailWire); +}); + +test('raw policy keeps nested, nullable, tagged, and binary union values unchanged', () => { + for (const [base, wire] of [ + ['outer', detailWire], ['aliasOuter', searchWire], + ['nullableMapFallback', detailWire], ['ambiguousOuter', detailWire], + ['adjacentOrNumber', { event_type: 'empty' }], + ['binaryUnion', { binary_data: new Blob(['x']), field_name: 'name' }], + ]) { + assert.equal(raw[`${base}FromJSON`](wire), wire); + assert.equal(raw[`${base}ToJSON`](wire), wire); + } +}); + +for (const [policy, generated] of [['default', client], ['strict', strict]]) { + test(`${policy}: single-member allOf preserves nullable map values`, () => { + for (const base of ['mapWrapped', 'wrappedMapOrBoolean']) { + assert.equal(generated[`${base}FromJSON`](null), null); + assert.equal(generated[`${base}ToJSON`](null), null); + assert.deepEqual(generated[`${base}FromJSON`](detailWire), detailValue); + assert.deepEqual(generated[`${base}ToJSON`](detailValue), detailWire); + } + }); + + test(`${policy}: single-member allOf preserves arrays and converts their elements`, () => { + for (const base of ['arrayWrapped', 'wrappedArrayOrBoolean']) { + assert.deepEqual(generated[`${base}FromJSON`]([detailWire]), [detailValue]); + assert.deepEqual(generated[`${base}ToJSON`]([detailValue]), [detailWire]); + } + }); + + test(`${policy}: open dictionary values retain concrete nested conversion before raw fallback`, () => { + const value = { concrete_key: detailValue, opaque_key: { keep_name: true }, string_key: 'opaque' }; + const wire = { concrete_key: detailWire, opaque_key: { keep_name: true }, string_key: 'opaque' }; + assert.deepEqual(generated.openMapFromJSON(wire), value); + assert.deepEqual(generated.openMapToJSON(value), wire); + assert.deepEqual(generated.mapOuterFromJSON(wire), value); + assert.deepEqual(generated.mapOuterToJSON(value), wire); + for (const opaque of [{}, { opaque_key: { keep_name: true } }]) { + assert.deepEqual(generated.mapOuterFromJSON(opaque), opaque); + assert.deepEqual(generated.mapOuterToJSON(opaque), opaque); + } + }); + + test(`${policy}: inline array unions convert as their named-union control`, () => { + const value = [{ keep_key: detailValue }, 'opaque']; + const wire = [{ keep_key: detailWire }, 'opaque']; + assert.deepEqual(generated.namedContainerFromJSON(wire), value); + assert.deepEqual(generated.namedContainerToJSON(value), wire); + assert.deepEqual(generated.inlineMixedValuesFromJSON(wire), value); + assert.deepEqual(generated.inlineMixedValuesToJSON(value), wire); + }); + + test(`${policy}: inline dictionary unions preserve keys and convert nested values`, () => { + const value = { object_key: { keep_key: detailValue }, string_key: 'opaque' }; + const wire = { object_key: { keep_key: detailWire }, string_key: 'opaque' }; + assert.deepEqual(generated.inlineDictionaryValuesFromJSON(wire), value); + assert.deepEqual(generated.inlineDictionaryValuesToJSON(value), wire); + }); +} + +test('strict: open dictionary selection retains inner ambiguity errors', () => { + for (const [suffix, input] of [['FromJSON', detailWire], ['ToJSON', detailValue]]) { + assert.throws(() => strict[`ambiguousMap${suffix}`]({ keep_key: input }), /Ambiguous/); + assert.throws(() => strict[`ambiguousMapOuter${suffix}`]({ keep_key: input }), /Ambiguous/); + } +}); + +test('inline singleton container unions retain their configured ambiguity policy', () => { + const value = [{ keep_key: detailValue }, 7]; + const wire = [{ keep_key: detailWire }, 7]; + assert.deepEqual(client.inlineNumericValuesFromJSON(wire), value); + assert.deepEqual(client.inlineNumericValuesToJSON(value), wire); + assert.throws(() => strict.inlineNumericValuesFromJSON(wire), /Ambiguous/); + assert.throws(() => strict.inlineNumericValuesToJSON(value), /Ambiguous/); + assert.equal(raw.inlineNumericValuesFromJSON(wire), wire); + assert.equal(raw.inlineNumericValuesToJSON(wire), wire); +}); + +test('raw: container unions preserve their wire values without nested conversion', () => { + for (const [base, wire] of [ + ['inlineMixedValues', [{ keep_key: detailWire }, 'opaque']], + ['inlineDictionaryValues', { keep_key: { nested_key: detailWire } }], + ['mapOuter', { keep_key: detailWire }], + ['ambiguousMapOuter', { keep_key: detailWire }], + ['wrappedMapOrBoolean', null], + ['wrappedArrayOrBoolean', [detailWire]], + ]) { + assert.equal(raw[`${base}FromJSON`](wire), wire); + assert.equal(raw[`${base}ToJSON`](wire), wire); + } +}); + +for (const [policy, generated] of [['default', client], ['strict', strict], ['raw', raw]]) { + test(`${policy}: HTTP nested containers use the same value conversion as models`, async () => { + const wire = [[clientWire, searchWire]]; + const value = policy === 'raw' ? wire : [[clientValue, searchValue]]; + let sent; + const api = new generated.ConversionApi(new generated.Configuration({ + fetchApi: async (_url, init) => { + sent = JSON.parse(init.body); + return new Response(JSON.stringify(wire), { status: 200, headers: { 'Content-Type': 'application/json' } }); + }, + })); + assert.deepEqual(await api.exchangeMatrix({ body: value }), value); + assert.deepEqual(sent, wire); + }); + + test(`${policy}: lazy HTTP errors use the same nested conversion`, async () => { + const wire = [[clientWire]]; + const value = policy === 'raw' ? wire : [[clientValue]]; + const api = new generated.ConversionApi(new generated.Configuration({ + fetchApi: async () => new Response(JSON.stringify(wire), { status: 400, headers: { 'Content-Type': 'application/json' } }), + })); + let caught; + try { + await api.exchangeMatrix({ body: value }); + } catch (error) { + caught = error; + } + assert.ok(caught instanceof generated.ExchangeMatrixError); + assert.deepEqual(await caught.detail.value(), value); + }); + + test(`${policy}: required nullable HTTP containers distinguish null from a missing body`, async () => { + for (const input of [null, [clientWire]]) { + const value = input === null || policy === 'raw' ? input : [clientValue]; + let sent; + const api = new generated.ConversionApi(new generated.Configuration({ + fetchApi: async (_url, init) => { + sent = JSON.parse(init.body); + return new Response(JSON.stringify(input), { status: 200, headers: { 'Content-Type': 'application/json' } }); + }, + })); + assert.deepEqual(await api.exchangeNullable({ body: value }), value); + assert.deepEqual(sent, input); + await assert.rejects(api.exchangeNullable({}), generated.RequiredError); + } + }); +} diff --git a/tests/generated-clients/typescript/tsconfig.isolation.json b/tests/generated-clients/typescript/tsconfig.isolation.json new file mode 100644 index 000000000..c3ddfd3d4 --- /dev/null +++ b/tests/generated-clients/typescript/tsconfig.isolation.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "CommonJS", + "strict": true, + "exactOptionalPropertyTypes": true, + "noEmitOnError": true, + "rootDir": ".", + "outDir": "../../../target/generated-clients/typescript-isolation", + "lib": ["ES2022", "DOM", "DOM.Iterable"] + }, + "files": ["isolation-consumer.ts"] +} diff --git a/tests/generated-clients/typescript/typecheck.ts b/tests/generated-clients/typescript/typecheck.ts index 9af7962e0..746ec7ee3 100644 --- a/tests/generated-clients/typescript/typecheck.ts +++ b/tests/generated-clients/typescript/typecheck.ts @@ -30,9 +30,68 @@ import type { MixedModelMap as PreservedModelMap, MixedStringMap as PreservedStringMap, } from './generated/preserve/dist/index.mjs'; +import type { Tool, Tool$Wire, ToolList, ToolMap, ToolRequest, BinaryUnion, AdjacentOrNumber, Outer, OpenAlias, InlineMixedValues, InlineMixedValues$Wire, MapWrapped, ArrayWrapped, DictionaryAlias, DictionaryAlias$Wire } from './generated/union-default/dist/index.mjs'; +import type { Tool as RawTool, BinaryUnion as RawBinaryUnion, AdjacentOrNumber as RawAdjacentOrNumber, AliasChoice as RawAliasChoice, ValueAlias as RawValueAlias, RawEnvelope } from './generated/union-raw/dist/index.mjs'; +import type { DictionaryAlias as StrictDictionaryAlias } from './generated/union-strict/dist/index.mjs'; +import type { ExchangeMultipartMultipartRequestBody as RawMultipartInput } from './generated/union-raw/dist/index.mjs'; +import type { NestedDetailLists } from './generated/union-default/dist/index.mjs'; + +const unionTool: Tool = { name: 'weather', inputSchema: { type: 'object' } }; +const unionWire: Tool$Wire = { name: 'weather', input_schema: { type: 'object' } }; +const unionRequest: ToolRequest = { tools: [unionTool] }; +const unionList: ToolList = [{ name: 'weather', inputSchema: {} }]; +const unionMap: ToolMap = { arbitrary_key: { name: 'weather', inputSchema: {} } }; +const rawUnion: RawTool = unionWire; +const binaryUnion: BinaryUnion = { binaryData: new Blob(['x']), fieldName: 'name' }; +const binaryUnionWire: RawBinaryUnion = { binary_data: new Blob(['x']), field_name: 'name' }; +const unitUnion: AdjacentOrNumber = { eventType: 'empty' }; +const unitUnionWire: RawAdjacentOrNumber = { event_type: 'empty' }; +const openOuter: Outer = { arbitrary_key: [null, 7] }; +const openAlias: OpenAlias = 'opaque'; +const inlineValues: InlineMixedValues = [{ keep_key: { wireName: 'Ada' } }, 'opaque']; +const inlineWire: InlineMixedValues$Wire = [{ keep_key: { wire_name: 'Ada' } }, 'opaque']; +const wrappedNull: MapWrapped = null; +const wrappedArray: ArrayWrapped = [{ wireName: 'Ada' }]; +const rawAlias: RawAliasChoice = [{ keep_key: { wire_name: 'Ada' } }, 'opaque']; +const rawValueAlias: RawValueAlias = [{ keep_key: { wire_name: 'Ada' } }]; +const rawMultipart: RawMultipartInput = { payloadValues: [{ keep_key: { wire_name: 'Ada' } }, 'opaque'] }; +// @ts-expect-error Multipart declarations use the same raw union wire projection. +const invalidRawMultipart: RawMultipartInput = { payloadValues: [{ keep_key: { wireName: 'Ada' } }] }; +const rawEnvelope: RawEnvelope = { requestId: 'r1', rawChoice: rawAlias, plainDetail: { wireName: 'Ada' } }; +const dictionaryAlias: DictionaryAlias = { keep_key: { wireName: 'Ada' } }; +const dictionaryAliasWire: DictionaryAlias$Wire = { keep_key: { wire_name: 'Ada' } }; +const strictDictionary: StrictDictionaryAlias = dictionaryAlias; +// @ts-expect-error Raw aliases require the wire representation through inline unions. +const invalidRawAlias: RawAliasChoice = [{ keep_key: { wireName: 'Ada' } }]; +// @ts-expect-error Singleton allOf dictionaries preserve their readonly index signature. +dictionaryAlias.new_key = { wireName: 'changed' }; +// @ts-expect-error Singleton allOf wire dictionaries preserve their readonly index signature. +dictionaryAliasWire.new_key = { wire_name: 'changed' }; +// @ts-expect-error Strict selection does not change readonly declaration contracts. +strictDictionary.new_key = { wireName: 'changed' }; +// @ts-expect-error Container member conversion retains the ergonomic element type. +const invalidInlineValues: InlineMixedValues = [{ keep_key: { wire_name: 'Ada' } }]; +// @ts-expect-error Single-member allOf array aliases remain readonly. +wrappedArray[0] = { wireName: 'changed' }; +// @ts-expect-error Binary members still require Blob/File, not strings. +const invalidBinaryUnion: BinaryUnion = { binaryData: 'wrong', fieldName: 'name' }; +// @ts-expect-error Normal tagged members keep ergonomic names. +const invalidUnitUnion: AdjacentOrNumber = { event_type: 'empty' }; +// @ts-expect-error Normal unions retain the ergonomic member contract. +const invalidUnion: Tool = { name: 'weather', input_schema: {} }; +// @ts-expect-error Raw unions expose wire members rather than ergonomic members. +const invalidRawUnion: RawTool = { name: 'weather', inputSchema: {} }; +// @ts-expect-error Union array alternatives remain readonly. +unionList[0] = { name: 'changed', inputSchema: {} }; function expectType(_value: T): void {} +declare const nestedLists: NestedDetailLists; +expectType(nestedLists[0][0].wireName); +nestedLists[0].push({ wireName: 'inner arrays retain their existing mutable type' }); +// @ts-expect-error The outer model array remains readonly. +nestedLists[0] = []; + declare const allOf: AllOfMixedStringMap; declare const allOfWire: AllOfMixedStringMap$Wire; declare const preservedAllOf: PreservedAllOfMap; diff --git a/tests/golden/typescript/typescript-fetch/additional-properties/apis/AdditionalPropertiesApi.ts.golden b/tests/golden/typescript/typescript-fetch/additional-properties/apis/AdditionalPropertiesApi.ts.golden index 8a3645aa1..777dc5334 100644 --- a/tests/golden/typescript/typescript-fetch/additional-properties/apis/AdditionalPropertiesApi.ts.golden +++ b/tests/golden/typescript/typescript-fetch/additional-properties/apis/AdditionalPropertiesApi.ts.golden @@ -164,7 +164,7 @@ export class AdditionalPropertiesApi extends BaseAPI implements AdditionalProper requestParameters: ApiPostLeafRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling postLeafRaw().', @@ -239,7 +239,7 @@ export class AdditionalPropertiesApi extends BaseAPI implements AdditionalProper requestParameters: ApiPostMiddleRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling postMiddleRaw().', @@ -314,7 +314,7 @@ export class AdditionalPropertiesApi extends BaseAPI implements AdditionalProper requestParameters: ApiPostRootRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling postRootRaw().', diff --git a/tests/golden/typescript/typescript-fetch/api-interface/apis/EntriesApi.ts.golden b/tests/golden/typescript/typescript-fetch/api-interface/apis/EntriesApi.ts.golden index 67f577704..b3e68bdfa 100644 --- a/tests/golden/typescript/typescript-fetch/api-interface/apis/EntriesApi.ts.golden +++ b/tests/golden/typescript/typescript-fetch/api-interface/apis/EntriesApi.ts.golden @@ -247,7 +247,7 @@ export class EntriesApi extends BaseAPI implements EntriesApiInterface { requestParameters: ApiGetEntriesRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.id === undefined || requestParameters.id === null) { + if (requestParameters['id'] === undefined || requestParameters['id'] === null) { throw new RequiredError( 'id', 'Required parameter "id" was null or undefined when calling getEntriesRaw().', diff --git a/tests/golden/typescript/typescript-fetch/binary-transfer-media-types/apis/TransferApi.ts.golden b/tests/golden/typescript/typescript-fetch/binary-transfer-media-types/apis/TransferApi.ts.golden index 1c6d8a283..ddbf733fc 100644 --- a/tests/golden/typescript/typescript-fetch/binary-transfer-media-types/apis/TransferApi.ts.golden +++ b/tests/golden/typescript/typescript-fetch/binary-transfer-media-types/apis/TransferApi.ts.golden @@ -125,7 +125,7 @@ export class TransferApi extends BaseAPI implements TransferApiInterface { requestParameters: ApiDownloadAssetRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.assetId === undefined || requestParameters.assetId === null) { + if (requestParameters['assetId'] === undefined || requestParameters['assetId'] === null) { throw new RequiredError( 'assetId', 'Required parameter "assetId" was null or undefined when calling downloadAssetRaw().', @@ -197,7 +197,7 @@ export class TransferApi extends BaseAPI implements TransferApiInterface { requestParameters: ApiUploadAssetRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling uploadAssetRaw().', @@ -211,20 +211,36 @@ export class TransferApi extends BaseAPI implements TransferApiInterface { const headerParameters: Record = {}; // Prepare request body + const multipartBody = requestParameters['body']; const multipartBoundary = '----openapi-nexus-' + Math.random().toString(16).slice(2); const multipartChunks: Array = []; headerParameters['Content-Type'] = 'multipart/form-data; boundary=' + multipartBoundary; - { - const multipartFilename = uploadFileFilename(requestParameters.body.file, 'file'); - multipartChunks.push('--' + multipartBoundary + '\r\nContent-Disposition: form-data; name="file"; filename="' + multipartHeaderValue(multipartFilename) + '"\r\nContent-Type: application/octet-stream\r\n\r\n'); - multipartChunks.push(uploadFileData(requestParameters.body.file)); + const multipartValue$0 = multipartBody['file']; + const multipartFilename$0 = uploadFileFilename(multipartValue$0, 'file'); + multipartChunks.push( + '--', + multipartBoundary, + '\r\nContent-Disposition: form-data; name="file"; filename="', + multipartHeaderValue(multipartFilename$0), + '"\r\nContent-Type: application/octet-stream\r\n\r\n', + ); + multipartChunks.push(uploadFileData(multipartValue$0)); multipartChunks.push('\r\n'); - } - multipartChunks.push('--' + multipartBoundary + '\r\nContent-Disposition: form-data; name="metadata"\r\nContent-Type: application/json\r\n\r\n'); - multipartChunks.push(JSON.stringify(requestParameters.body.metadata)); + const multipartValue$1 = multipartBody['metadata']; + multipartChunks.push( + '--', + multipartBoundary, + '\r\nContent-Disposition: form-data; name="metadata"\r\nContent-Type: application/json\r\n\r\n', + ); + multipartChunks.push(JSON.stringify(multipartValue$1)); multipartChunks.push('\r\n'); - multipartChunks.push('--' + multipartBoundary + '\r\nContent-Disposition: form-data; name="purpose"\r\nContent-Type: text/plain\r\n\r\n'); - multipartChunks.push(String(requestParameters.body.purpose)); + const multipartValue$2 = multipartBody['purpose']; + multipartChunks.push( + '--', + multipartBoundary, + '\r\nContent-Disposition: form-data; name="purpose"\r\nContent-Type: text/plain\r\n\r\n', + ); + multipartChunks.push(String(multipartValue$2)); multipartChunks.push('\r\n'); multipartChunks.push('--' + multipartBoundary + '--\r\n'); const requestBody = new Blob(multipartChunks); diff --git a/tests/golden/typescript/typescript-fetch/binary-transfer-media-types/runtime/runtime.ts.golden b/tests/golden/typescript/typescript-fetch/binary-transfer-media-types/runtime/runtime.ts.golden index 6f798fdb7..fe6259c0d 100644 --- a/tests/golden/typescript/typescript-fetch/binary-transfer-media-types/runtime/runtime.ts.golden +++ b/tests/golden/typescript/typescript-fetch/binary-transfer-media-types/runtime/runtime.ts.golden @@ -114,7 +114,10 @@ export function uploadFileFilename(value: UploadFileInput, fallback: string): st } export function multipartHeaderValue(value: string): string { - return value.replace(/[\r\n]/g, '').replace(/\\/g, '\\\\').replace(/"/g, '\\"'); + return value + .replace(/[\r\n]/g, '') + .replace(/\\/g, '\\\\') + .replace(/"/g, '\\"'); } function isUploadFile(value: UploadFileInput): value is UploadFile { diff --git a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/AllOfObjects.ts.golden b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/AllOfObjects.ts.golden index 9ad0a2df1..ec2a4dbd5 100644 --- a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/AllOfObjects.ts.golden +++ b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/AllOfObjects.ts.golden @@ -7,6 +7,7 @@ import type { AllOfObjectsAllOf2 } from './AllOfObjectsAllOf2'; import type { BasicObject } from './BasicObject'; -/** Intersection of object types (merge) */ - +/** + * Intersection of object types (merge) + */ export type AllOfObjects = BasicObject & AllOfObjectsAllOf2; diff --git a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/AnyOfTypes.ts.golden b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/AnyOfTypes.ts.golden index 0b8307aee..46425673a 100644 --- a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/AnyOfTypes.ts.golden +++ b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/AnyOfTypes.ts.golden @@ -4,6 +4,7 @@ * Comprehensive Schema Types Example — 1.0.0 * Comprehensive test for all OpenAPI v3.1.2 schema types */ -/** Any of multiple types */ - +/** + * Any of multiple types + */ export type AnyOfTypes = string | number; diff --git a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/ArrayWithMaxItems.ts.golden b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/ArrayWithMaxItems.ts.golden index 1c3b71c6b..31733b355 100644 --- a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/ArrayWithMaxItems.ts.golden +++ b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/ArrayWithMaxItems.ts.golden @@ -4,6 +4,7 @@ * Comprehensive Schema Types Example — 1.0.0 * Comprehensive test for all OpenAPI v3.1.2 schema types */ -/** Array with maximum items constraint */ - +/** + * Array with maximum items constraint + */ export type ArrayWithMaxItems = readonly string[]; diff --git a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/ArrayWithMinItems.ts.golden b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/ArrayWithMinItems.ts.golden index 0d0c18a37..349236fbb 100644 --- a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/ArrayWithMinItems.ts.golden +++ b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/ArrayWithMinItems.ts.golden @@ -4,6 +4,7 @@ * Comprehensive Schema Types Example — 1.0.0 * Comprehensive test for all OpenAPI v3.1.2 schema types */ -/** Array with minimum items constraint */ - +/** + * Array with minimum items constraint + */ export type ArrayWithMinItems = readonly string[]; diff --git a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/BinaryString.ts.golden b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/BinaryString.ts.golden index 458ee098e..685e67c24 100644 --- a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/BinaryString.ts.golden +++ b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/BinaryString.ts.golden @@ -4,6 +4,7 @@ * Comprehensive Schema Types Example — 1.0.0 * Comprehensive test for all OpenAPI v3.1.2 schema types */ -/** String with binary format */ - +/** + * String with binary format + */ export type BinaryString = Blob | File; diff --git a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/BooleanArray.ts.golden b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/BooleanArray.ts.golden index 717eb2440..530a550ed 100644 --- a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/BooleanArray.ts.golden +++ b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/BooleanArray.ts.golden @@ -4,6 +4,7 @@ * Comprehensive Schema Types Example — 1.0.0 * Comprehensive test for all OpenAPI v3.1.2 schema types */ -/** Array of booleans */ - +/** + * Array of booleans + */ export type BooleanArray = readonly boolean[]; diff --git a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/BooleanType.ts.golden b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/BooleanType.ts.golden index 2c397635b..a67dd27dc 100644 --- a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/BooleanType.ts.golden +++ b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/BooleanType.ts.golden @@ -4,6 +4,7 @@ * Comprehensive Schema Types Example — 1.0.0 * Comprehensive test for all OpenAPI v3.1.2 schema types */ -/** Boolean type */ - +/** + * Boolean type + */ export type BooleanType = boolean; diff --git a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/ByteString.ts.golden b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/ByteString.ts.golden index 5127db307..ef7f545cf 100644 --- a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/ByteString.ts.golden +++ b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/ByteString.ts.golden @@ -4,6 +4,7 @@ * Comprehensive Schema Types Example — 1.0.0 * Comprehensive test for all OpenAPI v3.1.2 schema types */ -/** String with byte format (base64) */ - +/** + * String with byte format (base64) + */ export type ByteString = string; diff --git a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/DateTimeString.ts.golden b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/DateTimeString.ts.golden index 89a847530..2d581caa9 100644 --- a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/DateTimeString.ts.golden +++ b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/DateTimeString.ts.golden @@ -4,6 +4,7 @@ * Comprehensive Schema Types Example — 1.0.0 * Comprehensive test for all OpenAPI v3.1.2 schema types */ -/** String with date-time format */ - +/** + * String with date-time format + */ export type DateTimeString = string; diff --git a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/DoubleType.ts.golden b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/DoubleType.ts.golden index 85df54e8c..9397af818 100644 --- a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/DoubleType.ts.golden +++ b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/DoubleType.ts.golden @@ -4,6 +4,7 @@ * Comprehensive Schema Types Example — 1.0.0 * Comprehensive test for all OpenAPI v3.1.2 schema types */ -/** Double precision floating point */ - +/** + * Double precision floating point + */ export type DoubleType = number; diff --git a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/EmailString.ts.golden b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/EmailString.ts.golden index fdc47e7b6..1187a607e 100644 --- a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/EmailString.ts.golden +++ b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/EmailString.ts.golden @@ -4,6 +4,7 @@ * Comprehensive Schema Types Example — 1.0.0 * Comprehensive test for all OpenAPI v3.1.2 schema types */ -/** String with email format */ - +/** + * String with email format + */ export type EmailString = string; diff --git a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/EmptyObject.ts.golden b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/EmptyObject.ts.golden index 9a93ff401..03b4acc34 100644 --- a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/EmptyObject.ts.golden +++ b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/EmptyObject.ts.golden @@ -4,6 +4,7 @@ * Comprehensive Schema Types Example — 1.0.0 * Comprehensive test for all OpenAPI v3.1.2 schema types */ -/** Object without properties */ - +/** + * Object without properties + */ export type EmptyObject = unknown; diff --git a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/FloatType.ts.golden b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/FloatType.ts.golden index b7abf054d..b31dbb72b 100644 --- a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/FloatType.ts.golden +++ b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/FloatType.ts.golden @@ -4,6 +4,7 @@ * Comprehensive Schema Types Example — 1.0.0 * Comprehensive test for all OpenAPI v3.1.2 schema types */ -/** Floating point number */ - +/** + * Floating point number + */ export type FloatType = number; diff --git a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/Integer32.ts.golden b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/Integer32.ts.golden index f93ca7e03..44aaf6f35 100644 --- a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/Integer32.ts.golden +++ b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/Integer32.ts.golden @@ -4,6 +4,7 @@ * Comprehensive Schema Types Example — 1.0.0 * Comprehensive test for all OpenAPI v3.1.2 schema types */ -/** 32-bit integer */ - +/** + * 32-bit integer + */ export type Integer32 = number; diff --git a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/Integer64.ts.golden b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/Integer64.ts.golden index d4f7bde2f..f6b73892c 100644 --- a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/Integer64.ts.golden +++ b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/Integer64.ts.golden @@ -4,6 +4,7 @@ * Comprehensive Schema Types Example — 1.0.0 * Comprehensive test for all OpenAPI v3.1.2 schema types */ -/** 64-bit integer */ - +/** + * 64-bit integer + */ export type Integer64 = number; diff --git a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/IntegerType.ts.golden b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/IntegerType.ts.golden index 01e1f3b2c..c120bcb6b 100644 --- a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/IntegerType.ts.golden +++ b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/IntegerType.ts.golden @@ -4,6 +4,7 @@ * Comprehensive Schema Types Example — 1.0.0 * Comprehensive test for all OpenAPI v3.1.2 schema types */ -/** Basic integer type */ - +/** + * Basic integer type + */ export type IntegerType = number; diff --git a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/NestedArray.ts.golden b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/NestedArray.ts.golden index 18bb7b11d..2a6fbc2fe 100644 --- a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/NestedArray.ts.golden +++ b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/NestedArray.ts.golden @@ -4,6 +4,7 @@ * Comprehensive Schema Types Example — 1.0.0 * Comprehensive test for all OpenAPI v3.1.2 schema types */ -/** Array of arrays (nested) */ - +/** + * Array of arrays (nested) + */ export type NestedArray = readonly string[][]; diff --git a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/NullableString.ts.golden b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/NullableString.ts.golden index e5e82d2a6..ca5a999c3 100644 --- a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/NullableString.ts.golden +++ b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/NullableString.ts.golden @@ -4,6 +4,7 @@ * Comprehensive Schema Types Example — 1.0.0 * Comprehensive test for all OpenAPI v3.1.2 schema types */ -/** Nullable string type (OpenAPI 3.1.2 style) */ - +/** + * Nullable string type (OpenAPI 3.1.2 style) + */ export type NullableString = string | null; diff --git a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/NumberArray.ts.golden b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/NumberArray.ts.golden index aaa993aa2..c2c4b7d76 100644 --- a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/NumberArray.ts.golden +++ b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/NumberArray.ts.golden @@ -4,6 +4,7 @@ * Comprehensive Schema Types Example — 1.0.0 * Comprehensive test for all OpenAPI v3.1.2 schema types */ -/** Array of numbers */ - +/** + * Array of numbers + */ export type NumberArray = readonly number[]; diff --git a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/NumberType.ts.golden b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/NumberType.ts.golden index 00aa3d34e..7ed3e4f2d 100644 --- a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/NumberType.ts.golden +++ b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/NumberType.ts.golden @@ -4,6 +4,7 @@ * Comprehensive Schema Types Example — 1.0.0 * Comprehensive test for all OpenAPI v3.1.2 schema types */ -/** Basic number type */ - +/** + * Basic number type + */ export type NumberType = number; diff --git a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/NumberWithMaximum.ts.golden b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/NumberWithMaximum.ts.golden index 87ae22b8b..0b763dfd3 100644 --- a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/NumberWithMaximum.ts.golden +++ b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/NumberWithMaximum.ts.golden @@ -4,6 +4,7 @@ * Comprehensive Schema Types Example — 1.0.0 * Comprehensive test for all OpenAPI v3.1.2 schema types */ -/** Number with maximum value */ - +/** + * Number with maximum value + */ export type NumberWithMaximum = number; diff --git a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/NumberWithMinimum.ts.golden b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/NumberWithMinimum.ts.golden index 70427c5d3..dc5653df5 100644 --- a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/NumberWithMinimum.ts.golden +++ b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/NumberWithMinimum.ts.golden @@ -4,6 +4,7 @@ * Comprehensive Schema Types Example — 1.0.0 * Comprehensive test for all OpenAPI v3.1.2 schema types */ -/** Number with minimum value */ - +/** + * Number with minimum value + */ export type NumberWithMinimum = number; diff --git a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/NumberWithMultipleOf.ts.golden b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/NumberWithMultipleOf.ts.golden index 6066650ad..d6359c710 100644 --- a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/NumberWithMultipleOf.ts.golden +++ b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/NumberWithMultipleOf.ts.golden @@ -4,6 +4,7 @@ * Comprehensive Schema Types Example — 1.0.0 * Comprehensive test for all OpenAPI v3.1.2 schema types */ -/** Number divisible by multipleOf */ - +/** + * Number divisible by multipleOf + */ export type NumberWithMultipleOf = number; diff --git a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/ObjectArray.ts.golden b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/ObjectArray.ts.golden index fab136418..ad480898e 100644 --- a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/ObjectArray.ts.golden +++ b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/ObjectArray.ts.golden @@ -6,6 +6,7 @@ */ import type { ObjectArrayItems } from './ObjectArrayItems'; -/** Array of objects */ - +/** + * Array of objects + */ export type ObjectArray = readonly ObjectArrayItems[]; diff --git a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/OneOfObjects.ts.golden b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/OneOfObjects.ts.golden index 7701b84d1..c9dcc338d 100644 --- a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/OneOfObjects.ts.golden +++ b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/OneOfObjects.ts.golden @@ -7,6 +7,7 @@ import type { BasicObject } from './BasicObject'; import type { NestedObject } from './NestedObject'; -/** Union of object types */ - +/** + * Union of object types + */ export type OneOfObjects = BasicObject | NestedObject; diff --git a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/OneOfTypes.ts.golden b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/OneOfTypes.ts.golden index 1424d9043..8440bb2bf 100644 --- a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/OneOfTypes.ts.golden +++ b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/OneOfTypes.ts.golden @@ -4,6 +4,7 @@ * Comprehensive Schema Types Example — 1.0.0 * Comprehensive test for all OpenAPI v3.1.2 schema types */ -/** Union of multiple primitive types */ - +/** + * Union of multiple primitive types + */ export type OneOfTypes = string | number | boolean; diff --git a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/OneOfWithNull.ts.golden b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/OneOfWithNull.ts.golden index a92d0ad58..74cc1dfbe 100644 --- a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/OneOfWithNull.ts.golden +++ b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/OneOfWithNull.ts.golden @@ -6,6 +6,7 @@ */ import type { BasicObject } from './BasicObject'; -/** Union of string, null, and schema reference */ - +/** + * Union of string, null, and schema reference + */ export type OneOfWithNull = string | BasicObject | null; diff --git a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/PasswordString.ts.golden b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/PasswordString.ts.golden index 1cdbbcee3..fd6144e8f 100644 --- a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/PasswordString.ts.golden +++ b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/PasswordString.ts.golden @@ -4,6 +4,7 @@ * Comprehensive Schema Types Example — 1.0.0 * Comprehensive test for all OpenAPI v3.1.2 schema types */ -/** String with password format */ - +/** + * String with password format + */ export type PasswordString = string; diff --git a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/ReferenceArray.ts.golden b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/ReferenceArray.ts.golden index e0ec43dc1..61877296b 100644 --- a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/ReferenceArray.ts.golden +++ b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/ReferenceArray.ts.golden @@ -6,6 +6,7 @@ */ import type { StringType } from './StringType'; -/** Array of references */ - +/** + * Array of references + */ export type ReferenceArray = readonly StringType[]; diff --git a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/StringArray.ts.golden b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/StringArray.ts.golden index 4d6f71a88..d7d991777 100644 --- a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/StringArray.ts.golden +++ b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/StringArray.ts.golden @@ -4,6 +4,7 @@ * Comprehensive Schema Types Example — 1.0.0 * Comprehensive test for all OpenAPI v3.1.2 schema types */ -/** Array of strings */ - +/** + * Array of strings + */ export type StringArray = readonly string[]; diff --git a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/StringType.ts.golden b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/StringType.ts.golden index 64a45ab04..ccf15b5fa 100644 --- a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/StringType.ts.golden +++ b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/StringType.ts.golden @@ -4,6 +4,7 @@ * Comprehensive Schema Types Example — 1.0.0 * Comprehensive test for all OpenAPI v3.1.2 schema types */ -/** Basic string type */ - +/** + * Basic string type + */ export type StringType = string; diff --git a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/StringWithMaxLength.ts.golden b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/StringWithMaxLength.ts.golden index e5baf1264..8bfeb6c48 100644 --- a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/StringWithMaxLength.ts.golden +++ b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/StringWithMaxLength.ts.golden @@ -4,6 +4,7 @@ * Comprehensive Schema Types Example — 1.0.0 * Comprehensive test for all OpenAPI v3.1.2 schema types */ -/** String with maximum length */ - +/** + * String with maximum length + */ export type StringWithMaxLength = string; diff --git a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/StringWithMinLength.ts.golden b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/StringWithMinLength.ts.golden index 2e283cb6b..686dd5a75 100644 --- a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/StringWithMinLength.ts.golden +++ b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/StringWithMinLength.ts.golden @@ -4,6 +4,7 @@ * Comprehensive Schema Types Example — 1.0.0 * Comprehensive test for all OpenAPI v3.1.2 schema types */ -/** String with minimum length */ - +/** + * String with minimum length + */ export type StringWithMinLength = string; diff --git a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/StringWithPattern.ts.golden b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/StringWithPattern.ts.golden index 2875cc681..eb10dad83 100644 --- a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/StringWithPattern.ts.golden +++ b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/StringWithPattern.ts.golden @@ -4,6 +4,7 @@ * Comprehensive Schema Types Example — 1.0.0 * Comprehensive test for all OpenAPI v3.1.2 schema types */ -/** String with regex pattern */ - +/** + * String with regex pattern + */ export type StringWithPattern = string; diff --git a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/UniqueItemsArray.ts.golden b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/UniqueItemsArray.ts.golden index 01090fa8f..0693273f5 100644 --- a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/UniqueItemsArray.ts.golden +++ b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/UniqueItemsArray.ts.golden @@ -4,6 +4,7 @@ * Comprehensive Schema Types Example — 1.0.0 * Comprehensive test for all OpenAPI v3.1.2 schema types */ -/** Array with unique items constraint */ - +/** + * Array with unique items constraint + */ export type UniqueItemsArray = readonly string[]; diff --git a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/UriString.ts.golden b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/UriString.ts.golden index 2cfa7b11b..b357be2f4 100644 --- a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/UriString.ts.golden +++ b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/UriString.ts.golden @@ -4,6 +4,7 @@ * Comprehensive Schema Types Example — 1.0.0 * Comprehensive test for all OpenAPI v3.1.2 schema types */ -/** String with URI format */ - +/** + * String with URI format + */ export type UriString = string; diff --git a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/UuidString.ts.golden b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/UuidString.ts.golden index a6db94f48..3125d2270 100644 --- a/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/UuidString.ts.golden +++ b/tests/golden/typescript/typescript-fetch/comprehensive-schemas/models/UuidString.ts.golden @@ -4,6 +4,7 @@ * Comprehensive Schema Types Example — 1.0.0 * Comprehensive test for all OpenAPI v3.1.2 schema types */ -/** String with UUID format */ - +/** + * String with UUID format + */ export type UuidString = string; diff --git a/tests/golden/typescript/typescript-fetch/delete-with-response-schema/apis/TestResourceApi.ts.golden b/tests/golden/typescript/typescript-fetch/delete-with-response-schema/apis/TestResourceApi.ts.golden index d2431956b..f9d24f365 100644 --- a/tests/golden/typescript/typescript-fetch/delete-with-response-schema/apis/TestResourceApi.ts.golden +++ b/tests/golden/typescript/typescript-fetch/delete-with-response-schema/apis/TestResourceApi.ts.golden @@ -124,10 +124,10 @@ export class TestResourceApi extends BaseAPI implements TestResourceApiInterface requestParameters: ApiCreateTestResourceRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined) { throw new RequiredError( 'body', - 'Required parameter "body" was null or undefined when calling createTestResourceRaw().', + 'Required parameter "body" was undefined when calling createTestResourceRaw().', ); } // Build path with path parameters @@ -190,7 +190,7 @@ export class TestResourceApi extends BaseAPI implements TestResourceApiInterface requestParameters: ApiDeleteTestResourceRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.resourceId === undefined || requestParameters.resourceId === null) { + if (requestParameters['resourceId'] === undefined || requestParameters['resourceId'] === null) { throw new RequiredError( 'resourceId', 'Required parameter "resourceId" was null or undefined when calling deleteTestResourceRaw().', diff --git a/tests/golden/typescript/typescript-fetch/delete-with-response-schema/models/CreateTestResourceRequest.ts.golden b/tests/golden/typescript/typescript-fetch/delete-with-response-schema/models/CreateTestResourceRequest.ts.golden index 34cfa87bf..1aed88336 100644 --- a/tests/golden/typescript/typescript-fetch/delete-with-response-schema/models/CreateTestResourceRequest.ts.golden +++ b/tests/golden/typescript/typescript-fetch/delete-with-response-schema/models/CreateTestResourceRequest.ts.golden @@ -4,6 +4,7 @@ * Test API with DELETE Response Schema — 1.0.0 * Test fixture for DELETE operations with JSON response schemas and type alias request bodies */ -/** Request to create a new test resource (type alias - no properties) */ - +/** + * Request to create a new test resource (type alias - no properties) + */ export type CreateTestResourceRequest = unknown; diff --git a/tests/golden/typescript/typescript-fetch/duplicate-param-names/apis/ItemsApi.ts.golden b/tests/golden/typescript/typescript-fetch/duplicate-param-names/apis/ItemsApi.ts.golden index 12b9b3350..1d05f5023 100644 --- a/tests/golden/typescript/typescript-fetch/duplicate-param-names/apis/ItemsApi.ts.golden +++ b/tests/golden/typescript/typescript-fetch/duplicate-param-names/apis/ItemsApi.ts.golden @@ -87,13 +87,13 @@ export class ItemsApi extends BaseAPI implements ItemsApiInterface { requestParameters: ApiCreateItemWithBodyConflictRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.itemId === undefined || requestParameters.itemId === null) { + if (requestParameters['itemId'] === undefined || requestParameters['itemId'] === null) { throw new RequiredError( 'itemId', 'Required parameter "itemId" was null or undefined when calling createItemWithBodyConflictRaw().', ); } - if (requestParameters.bodyBody === undefined || requestParameters.bodyBody === null) { + if (requestParameters['bodyBody'] === undefined || requestParameters['bodyBody'] === null) { throw new RequiredError( 'bodyBody', 'Required parameter "bodyBody" was null or undefined when calling createItemWithBodyConflictRaw().', diff --git a/tests/golden/typescript/typescript-fetch/duplicate-param-names/apis/UsersApi.ts.golden b/tests/golden/typescript/typescript-fetch/duplicate-param-names/apis/UsersApi.ts.golden index c32822273..0f9480a13 100644 --- a/tests/golden/typescript/typescript-fetch/duplicate-param-names/apis/UsersApi.ts.golden +++ b/tests/golden/typescript/typescript-fetch/duplicate-param-names/apis/UsersApi.ts.golden @@ -95,7 +95,7 @@ export class UsersApi extends BaseAPI implements UsersApiInterface { requestParameters: ApiGetUserByIdDuplicateRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.pathId === undefined || requestParameters.pathId === null) { + if (requestParameters['pathId'] === undefined || requestParameters['pathId'] === null) { throw new RequiredError( 'pathId', 'Required parameter "pathId" was null or undefined when calling getUserByIdDuplicateRaw().', diff --git a/tests/golden/typescript/typescript-fetch/enum-repr/apis/EnumReprApi.ts.golden b/tests/golden/typescript/typescript-fetch/enum-repr/apis/EnumReprApi.ts.golden index 5cdd1da80..ab849ede1 100644 --- a/tests/golden/typescript/typescript-fetch/enum-repr/apis/EnumReprApi.ts.golden +++ b/tests/golden/typescript/typescript-fetch/enum-repr/apis/EnumReprApi.ts.golden @@ -250,7 +250,7 @@ export class EnumReprApi extends BaseAPI implements EnumReprApiInterface { requestParameters: ApiHandleAdjacentlyTaggedRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling handleAdjacentlyTaggedRaw().', @@ -327,7 +327,7 @@ export class EnumReprApi extends BaseAPI implements EnumReprApiInterface { requestParameters: ApiHandleExternallyTaggedRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling handleExternallyTaggedRaw().', @@ -404,7 +404,7 @@ export class EnumReprApi extends BaseAPI implements EnumReprApiInterface { requestParameters: ApiHandleInternallyTaggedRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling handleInternallyTaggedRaw().', @@ -481,7 +481,7 @@ export class EnumReprApi extends BaseAPI implements EnumReprApiInterface { requestParameters: ApiHandleMixedRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling handleMixedRaw().', @@ -556,7 +556,7 @@ export class EnumReprApi extends BaseAPI implements EnumReprApiInterface { requestParameters: ApiHandleUntaggedRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling handleUntaggedRaw().', diff --git a/tests/golden/typescript/typescript-fetch/enum-repr/models/AdjacentlyTaggedEnum.ts.golden b/tests/golden/typescript/typescript-fetch/enum-repr/models/AdjacentlyTaggedEnum.ts.golden index af385123d..cd6784c07 100644 --- a/tests/golden/typescript/typescript-fetch/enum-repr/models/AdjacentlyTaggedEnum.ts.golden +++ b/tests/golden/typescript/typescript-fetch/enum-repr/models/AdjacentlyTaggedEnum.ts.golden @@ -7,11 +7,12 @@ import type { VariantA } from './VariantA'; import type { VariantB } from './VariantB'; -/** Adjacently tagged enum +/** + * Adjacently tagged enum The tag and content are separate fields: -`{"ty": "VariantA", "data": {"field1": "...", "field2": 123}}` */ - +`{"ty": "VariantA", "data": {"field1": "...", "field2": 123}}` + */ export type AdjacentlyTaggedEnum = - | { ty: 'ADJACENTLY_TAGGED_ENUM_VARIANT_A'; data: VariantA } - | { ty: 'ADJACENTLY_TAGGED_ENUM_VARIANT_B'; data: VariantB }; + | (Record<'ty', 'ADJACENTLY_TAGGED_ENUM_VARIANT_A'> & Record<'data', VariantA>) + | (Record<'ty', 'ADJACENTLY_TAGGED_ENUM_VARIANT_B'> & Record<'data', VariantB>); diff --git a/tests/golden/typescript/typescript-fetch/enum-repr/models/ExternallyTaggedEnum.ts.golden b/tests/golden/typescript/typescript-fetch/enum-repr/models/ExternallyTaggedEnum.ts.golden index 09dc7debe..308da598f 100644 --- a/tests/golden/typescript/typescript-fetch/enum-repr/models/ExternallyTaggedEnum.ts.golden +++ b/tests/golden/typescript/typescript-fetch/enum-repr/models/ExternallyTaggedEnum.ts.golden @@ -7,11 +7,12 @@ import type { VariantA } from './VariantA'; import type { VariantB } from './VariantB'; -/** Externally tagged enum (default Serde representation) +/** + * Externally tagged enum (default Serde representation) This is the default representation where the variant name is the key -and the content is the value: `{"VariantA": {"field1": "...", "field2": 123}}` */ - +and the content is the value: `{"VariantA": {"field1": "...", "field2": 123}}` + */ export type ExternallyTaggedEnum = - | { EXTERNALLY_TAGGED_ENUM_VARIANT_A: VariantA } - | { EXTERNALLY_TAGGED_ENUM_VARIANT_B: VariantB }; + | Record<'EXTERNALLY_TAGGED_ENUM_VARIANT_A', VariantA> + | Record<'EXTERNALLY_TAGGED_ENUM_VARIANT_B', VariantB>; diff --git a/tests/golden/typescript/typescript-fetch/enum-repr/models/InternallyTaggedEnum.ts.golden b/tests/golden/typescript/typescript-fetch/enum-repr/models/InternallyTaggedEnum.ts.golden index 75b637a62..e6c71a7bf 100644 --- a/tests/golden/typescript/typescript-fetch/enum-repr/models/InternallyTaggedEnum.ts.golden +++ b/tests/golden/typescript/typescript-fetch/enum-repr/models/InternallyTaggedEnum.ts.golden @@ -7,11 +7,12 @@ import type { VariantA } from './VariantA'; import type { VariantB } from './VariantB'; -/** Internally tagged enum +/** + * Internally tagged enum The tag is a field inside the object alongside other fields: -`{"ty": "VariantA", "field1": "...", "field2": 123}` */ - +`{"ty": "VariantA", "field1": "...", "field2": 123}` + */ export type InternallyTaggedEnum = - | ({ ty: 'INTERNALLY_TAGGED_ENUM_VARIANT_A' } & VariantA) - | ({ ty: 'INTERNALLY_TAGGED_ENUM_VARIANT_B' } & VariantB); + | (Record<'ty', 'INTERNALLY_TAGGED_ENUM_VARIANT_A'> & VariantA) + | (Record<'ty', 'INTERNALLY_TAGGED_ENUM_VARIANT_B'> & VariantB); diff --git a/tests/golden/typescript/typescript-fetch/enum-repr/models/MixedEnum.ts.golden b/tests/golden/typescript/typescript-fetch/enum-repr/models/MixedEnum.ts.golden index 3c88b5414..c92e23f25 100644 --- a/tests/golden/typescript/typescript-fetch/enum-repr/models/MixedEnum.ts.golden +++ b/tests/golden/typescript/typescript-fetch/enum-repr/models/MixedEnum.ts.golden @@ -7,9 +7,10 @@ import type { MixedEnumMember2 } from './MixedEnumMember2'; import type { MixedEnumMember4 } from './MixedEnumMember4'; -/** Mixed enum with unit variants (SimpleA, SimpleB) and tuple variants (VariantA(VariantA), VariantB(VariantB)) +/** + * Mixed enum with unit variants (SimpleA, SimpleB) and tuple variants (VariantA(VariantA), VariantB(VariantB)) Serializes as externally tagged: `{"SimpleA": null}`, `{"VariantA": {"field1": "...", "field2": 123}}`, -`{"SimpleB": null}`, `{"VariantB": {"field3": true, "field4": 1.23}}` */ - +`{"SimpleB": null}`, `{"VariantB": {"field3": true, "field4": 1.23}}` + */ export type MixedEnum = 'SimpleA' | MixedEnumMember2 | 'SimpleB' | MixedEnumMember4; diff --git a/tests/golden/typescript/typescript-fetch/enum-repr/models/UntaggedEnum.ts.golden b/tests/golden/typescript/typescript-fetch/enum-repr/models/UntaggedEnum.ts.golden index 2de828935..738a3a672 100644 --- a/tests/golden/typescript/typescript-fetch/enum-repr/models/UntaggedEnum.ts.golden +++ b/tests/golden/typescript/typescript-fetch/enum-repr/models/UntaggedEnum.ts.golden @@ -7,9 +7,10 @@ import type { VariantA } from './VariantA'; import type { VariantB } from './VariantB'; -/** Untagged enum +/** + * Untagged enum No tag is used, variants are distinguished by their structure: -`{"field1": "...", "field2": 123}` or `{"field3": true, "field4": 1.23}` */ - +`{"field1": "...", "field2": 123}` or `{"field3": true, "field4": 1.23}` + */ export type UntaggedEnum = VariantA | VariantB; diff --git a/tests/golden/typescript/typescript-fetch/media-type-selection/apis/MediaApi.ts.golden b/tests/golden/typescript/typescript-fetch/media-type-selection/apis/MediaApi.ts.golden index c31516cb5..7b5a1fc7c 100644 --- a/tests/golden/typescript/typescript-fetch/media-type-selection/apis/MediaApi.ts.golden +++ b/tests/golden/typescript/typescript-fetch/media-type-selection/apis/MediaApi.ts.golden @@ -228,7 +228,7 @@ export class MediaApi extends BaseAPI implements MediaApiInterface { requestParameters: ApiSendJsonPreferredRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling sendJsonPreferredRaw().', @@ -292,7 +292,7 @@ export class MediaApi extends BaseAPI implements MediaApiInterface { requestParameters: ApiSendParameterizedMultipartRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling sendParameterizedMultipartRaw().', @@ -306,19 +306,30 @@ export class MediaApi extends BaseAPI implements MediaApiInterface { const headerParameters: Record = {}; // Prepare request body + const multipartBody = requestParameters['body']; const multipartBoundary = '----openapi-nexus-' + Math.random().toString(16).slice(2); const multipartChunks: Array = []; headerParameters['Content-Type'] = 'multipart/form-data; boundary=' + multipartBoundary; - { - const multipartFilename = uploadFileFilename(requestParameters.body.file, 'file'); - multipartChunks.push('--' + multipartBoundary + '\r\nContent-Disposition: form-data; name="file"; filename="' + multipartHeaderValue(multipartFilename) + '"\r\nContent-Type: application/octet-stream\r\n\r\n'); - multipartChunks.push(uploadFileData(requestParameters.body.file)); - multipartChunks.push('\r\n'); - } - if (requestParameters.body.note !== undefined && requestParameters.body.note !== null) { - multipartChunks.push('--' + multipartBoundary + '\r\nContent-Disposition: form-data; name="note"\r\nContent-Type: text/plain\r\n\r\n'); - multipartChunks.push(String(requestParameters.body.note)); + const multipartValue$0 = multipartBody['file']; + const multipartFilename$0 = uploadFileFilename(multipartValue$0, 'file'); + multipartChunks.push( + '--', + multipartBoundary, + '\r\nContent-Disposition: form-data; name="file"; filename="', + multipartHeaderValue(multipartFilename$0), + '"\r\nContent-Type: application/octet-stream\r\n\r\n', + ); + multipartChunks.push(uploadFileData(multipartValue$0)); multipartChunks.push('\r\n'); + const multipartValue$1 = multipartBody['note']; + if (multipartValue$1 !== undefined && multipartValue$1 !== null) { + multipartChunks.push( + '--', + multipartBoundary, + '\r\nContent-Disposition: form-data; name="note"\r\nContent-Type: text/plain\r\n\r\n', + ); + multipartChunks.push(String(multipartValue$1)); + multipartChunks.push('\r\n'); } multipartChunks.push('--' + multipartBoundary + '--\r\n'); const requestBody = new Blob(multipartChunks); @@ -369,7 +380,7 @@ export class MediaApi extends BaseAPI implements MediaApiInterface { requestParameters: ApiSendVendorJsonRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling sendVendorJsonRaw().', diff --git a/tests/golden/typescript/typescript-fetch/media-type-selection/runtime/runtime.ts.golden b/tests/golden/typescript/typescript-fetch/media-type-selection/runtime/runtime.ts.golden index b546f79f3..30bf1dd3f 100644 --- a/tests/golden/typescript/typescript-fetch/media-type-selection/runtime/runtime.ts.golden +++ b/tests/golden/typescript/typescript-fetch/media-type-selection/runtime/runtime.ts.golden @@ -114,7 +114,10 @@ export function uploadFileFilename(value: UploadFileInput, fallback: string): st } export function multipartHeaderValue(value: string): string { - return value.replace(/[\r\n]/g, '').replace(/\\/g, '\\\\').replace(/"/g, '\\"'); + return value + .replace(/[\r\n]/g, '') + .replace(/\\/g, '\\\\') + .replace(/"/g, '\\"'); } function isUploadFile(value: UploadFileInput): value is UploadFile { diff --git a/tests/golden/typescript/typescript-fetch/multipart-edge-cases/apis/MultipartApi.ts.golden b/tests/golden/typescript/typescript-fetch/multipart-edge-cases/apis/MultipartApi.ts.golden index da46eb60e..8cb846948 100644 --- a/tests/golden/typescript/typescript-fetch/multipart-edge-cases/apis/MultipartApi.ts.golden +++ b/tests/golden/typescript/typescript-fetch/multipart-edge-cases/apis/MultipartApi.ts.golden @@ -117,41 +117,67 @@ export class MultipartApi extends BaseAPI implements MultipartApiInterface { const headerParameters: Record = {}; // Prepare request body + const multipartBody = requestParameters['body']; let requestBody: Blob | undefined = undefined; - if (requestParameters.body !== undefined && requestParameters.body !== null) { - const multipartBoundary = '----openapi-nexus-' + Math.random().toString(16).slice(2); - const multipartChunks: Array = []; - headerParameters['Content-Type'] = 'multipart/form-data; boundary=' + multipartBoundary; - if (requestParameters.body.attributes !== undefined && requestParameters.body.attributes !== null) { - multipartChunks.push('--' + multipartBoundary + '\r\nContent-Disposition: form-data; name="attributes"\r\nContent-Type: application/json\r\n\r\n'); - multipartChunks.push(JSON.stringify(requestParameters.body.attributes)); - multipartChunks.push('\r\n'); - } - if (requestParameters.body.enabled !== undefined && requestParameters.body.enabled !== null) { - multipartChunks.push('--' + multipartBoundary + '\r\nContent-Disposition: form-data; name="enabled"\r\nContent-Type: text/plain\r\n\r\n'); - multipartChunks.push(String(requestParameters.body.enabled)); - multipartChunks.push('\r\n'); - } - if (requestParameters.body.file !== undefined && requestParameters.body.file !== null) { - { - const multipartFilename = uploadFileFilename(requestParameters.body.file, 'file'); - multipartChunks.push('--' + multipartBoundary + '\r\nContent-Disposition: form-data; name="file"; filename="' + multipartHeaderValue(multipartFilename) + '"\r\nContent-Type: application/octet-stream\r\n\r\n'); - multipartChunks.push(uploadFileData(requestParameters.body.file)); - multipartChunks.push('\r\n'); - } - } - if (requestParameters.body.retry_count !== undefined && requestParameters.body.retry_count !== null) { - multipartChunks.push('--' + multipartBoundary + '\r\nContent-Disposition: form-data; name="retry_count"\r\nContent-Type: text/plain\r\n\r\n'); - multipartChunks.push(String(requestParameters.body.retry_count)); - multipartChunks.push('\r\n'); - } - if (requestParameters.body.title !== undefined && requestParameters.body.title !== null) { - multipartChunks.push('--' + multipartBoundary + '\r\nContent-Disposition: form-data; name="title"\r\nContent-Type: text/plain\r\n\r\n'); - multipartChunks.push(String(requestParameters.body.title)); - multipartChunks.push('\r\n'); - } - multipartChunks.push('--' + multipartBoundary + '--\r\n'); - requestBody = new Blob(multipartChunks); + if (multipartBody !== undefined && multipartBody !== null) { + const multipartBoundary = '----openapi-nexus-' + Math.random().toString(16).slice(2); + const multipartChunks: Array = []; + headerParameters['Content-Type'] = 'multipart/form-data; boundary=' + multipartBoundary; + const multipartValue$0 = multipartBody['attributes']; + if (multipartValue$0 !== undefined && multipartValue$0 !== null) { + multipartChunks.push( + '--', + multipartBoundary, + '\r\nContent-Disposition: form-data; name="attributes"\r\nContent-Type: application/json\r\n\r\n', + ); + multipartChunks.push(JSON.stringify(multipartValue$0)); + multipartChunks.push('\r\n'); + } + const multipartValue$1 = multipartBody['enabled']; + if (multipartValue$1 !== undefined && multipartValue$1 !== null) { + multipartChunks.push( + '--', + multipartBoundary, + '\r\nContent-Disposition: form-data; name="enabled"\r\nContent-Type: text/plain\r\n\r\n', + ); + multipartChunks.push(String(multipartValue$1)); + multipartChunks.push('\r\n'); + } + const multipartValue$2 = multipartBody['file']; + if (multipartValue$2 !== undefined && multipartValue$2 !== null) { + const multipartFilename$2 = uploadFileFilename(multipartValue$2, 'file'); + multipartChunks.push( + '--', + multipartBoundary, + '\r\nContent-Disposition: form-data; name="file"; filename="', + multipartHeaderValue(multipartFilename$2), + '"\r\nContent-Type: application/octet-stream\r\n\r\n', + ); + multipartChunks.push(uploadFileData(multipartValue$2)); + multipartChunks.push('\r\n'); + } + const multipartValue$3 = multipartBody['retry_count']; + if (multipartValue$3 !== undefined && multipartValue$3 !== null) { + multipartChunks.push( + '--', + multipartBoundary, + '\r\nContent-Disposition: form-data; name="retry_count"\r\nContent-Type: text/plain\r\n\r\n', + ); + multipartChunks.push(String(multipartValue$3)); + multipartChunks.push('\r\n'); + } + const multipartValue$4 = multipartBody['title']; + if (multipartValue$4 !== undefined && multipartValue$4 !== null) { + multipartChunks.push( + '--', + multipartBoundary, + '\r\nContent-Disposition: form-data; name="title"\r\nContent-Type: text/plain\r\n\r\n', + ); + multipartChunks.push(String(multipartValue$4)); + multipartChunks.push('\r\n'); + } + multipartChunks.push('--' + multipartBoundary + '--\r\n'); + requestBody = new Blob(multipartChunks); } // Make request const response = await this.request( @@ -200,7 +226,7 @@ export class MultipartApi extends BaseAPI implements MultipartApiInterface { requestParameters: ApiSendTextFieldsRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling sendTextFieldsRaw().', @@ -214,17 +240,33 @@ export class MultipartApi extends BaseAPI implements MultipartApiInterface { const headerParameters: Record = {}; // Prepare request body + const multipartBody = requestParameters['body']; const multipartBoundary = '----openapi-nexus-' + Math.random().toString(16).slice(2); const multipartChunks: Array = []; headerParameters['Content-Type'] = 'multipart/form-data; boundary=' + multipartBoundary; - multipartChunks.push('--' + multipartBoundary + '\r\nContent-Disposition: form-data; name="enabled"\r\nContent-Type: text/plain\r\n\r\n'); - multipartChunks.push(String(requestParameters.body.enabled)); + const multipartValue$0 = multipartBody['enabled']; + multipartChunks.push( + '--', + multipartBoundary, + '\r\nContent-Disposition: form-data; name="enabled"\r\nContent-Type: text/plain\r\n\r\n', + ); + multipartChunks.push(String(multipartValue$0)); multipartChunks.push('\r\n'); - multipartChunks.push('--' + multipartBoundary + '\r\nContent-Disposition: form-data; name="note"\r\nContent-Type: text/plain\r\n\r\n'); - multipartChunks.push(String(requestParameters.body.note)); + const multipartValue$1 = multipartBody['note']; + multipartChunks.push( + '--', + multipartBoundary, + '\r\nContent-Disposition: form-data; name="note"\r\nContent-Type: text/plain\r\n\r\n', + ); + multipartChunks.push(String(multipartValue$1)); multipartChunks.push('\r\n'); - multipartChunks.push('--' + multipartBoundary + '\r\nContent-Disposition: form-data; name="retry_count"\r\nContent-Type: text/plain\r\n\r\n'); - multipartChunks.push(String(requestParameters.body.retry_count)); + const multipartValue$2 = multipartBody['retry_count']; + multipartChunks.push( + '--', + multipartBoundary, + '\r\nContent-Disposition: form-data; name="retry_count"\r\nContent-Type: text/plain\r\n\r\n', + ); + multipartChunks.push(String(multipartValue$2)); multipartChunks.push('\r\n'); multipartChunks.push('--' + multipartBoundary + '--\r\n'); const requestBody = new Blob(multipartChunks); diff --git a/tests/golden/typescript/typescript-fetch/multipart-edge-cases/models/SendTextFieldsMultipartRequestBody.ts.golden b/tests/golden/typescript/typescript-fetch/multipart-edge-cases/models/SendTextFieldsMultipartRequestBody.ts.golden index 9df1a2466..0c73daff9 100644 --- a/tests/golden/typescript/typescript-fetch/multipart-edge-cases/models/SendTextFieldsMultipartRequestBody.ts.golden +++ b/tests/golden/typescript/typescript-fetch/multipart-edge-cases/models/SendTextFieldsMultipartRequestBody.ts.golden @@ -4,7 +4,6 @@ * Multipart Edge Cases — 1.0.0 * Covers optional multipart bodies, optional parts, and text-only multipart fields. */ - export interface SendTextFieldsMultipartRequestBody { enabled: boolean; note: string; diff --git a/tests/golden/typescript/typescript-fetch/multipart-edge-cases/runtime/runtime.ts.golden b/tests/golden/typescript/typescript-fetch/multipart-edge-cases/runtime/runtime.ts.golden index 9cfc74b13..8c36fcca0 100644 --- a/tests/golden/typescript/typescript-fetch/multipart-edge-cases/runtime/runtime.ts.golden +++ b/tests/golden/typescript/typescript-fetch/multipart-edge-cases/runtime/runtime.ts.golden @@ -114,7 +114,10 @@ export function uploadFileFilename(value: UploadFileInput, fallback: string): st } export function multipartHeaderValue(value: string): string { - return value.replace(/[\r\n]/g, '').replace(/\\/g, '\\\\').replace(/"/g, '\\"'); + return value + .replace(/[\r\n]/g, '') + .replace(/\\/g, '\\\\') + .replace(/"/g, '\\"'); } function isUploadFile(value: UploadFileInput): value is UploadFile { diff --git a/tests/golden/typescript/typescript-fetch/multipart-explicit-encoding/apis/TransferApi.ts.golden b/tests/golden/typescript/typescript-fetch/multipart-explicit-encoding/apis/TransferApi.ts.golden index 7b9ef2a8c..7dce3bce4 100644 --- a/tests/golden/typescript/typescript-fetch/multipart-explicit-encoding/apis/TransferApi.ts.golden +++ b/tests/golden/typescript/typescript-fetch/multipart-explicit-encoding/apis/TransferApi.ts.golden @@ -73,7 +73,7 @@ export class TransferApi extends BaseAPI implements TransferApiInterface { requestParameters: ApiUploadEncodedAssetRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling uploadEncodedAssetRaw().', @@ -87,23 +87,44 @@ export class TransferApi extends BaseAPI implements TransferApiInterface { const headerParameters: Record = {}; // Prepare request body + const multipartBody = requestParameters['body']; const multipartBoundary = '----openapi-nexus-' + Math.random().toString(16).slice(2); const multipartChunks: Array = []; headerParameters['Content-Type'] = 'multipart/form-data; boundary=' + multipartBoundary; - multipartChunks.push('--' + multipartBoundary + '\r\nContent-Disposition: form-data; name="audit"\r\nContent-Type: application/json\r\n\r\n'); - multipartChunks.push(JSON.stringify(requestParameters.body.audit)); + const multipartValue$0 = multipartBody['audit']; + multipartChunks.push( + '--', + multipartBoundary, + '\r\nContent-Disposition: form-data; name="audit"\r\nContent-Type: application/json\r\n\r\n', + ); + multipartChunks.push(JSON.stringify(multipartValue$0)); multipartChunks.push('\r\n'); - { - const multipartFilename = uploadFileFilename(requestParameters.body.file, 'file'); - multipartChunks.push('--' + multipartBoundary + '\r\nContent-Disposition: form-data; name="file"; filename="' + multipartHeaderValue(multipartFilename) + '"\r\nContent-Type: application/pdf\r\n\r\n'); - multipartChunks.push(uploadFileData(requestParameters.body.file)); + const multipartValue$1 = multipartBody['file']; + const multipartFilename$1 = uploadFileFilename(multipartValue$1, 'file'); + multipartChunks.push( + '--', + multipartBoundary, + '\r\nContent-Disposition: form-data; name="file"; filename="', + multipartHeaderValue(multipartFilename$1), + '"\r\nContent-Type: application/pdf\r\n\r\n', + ); + multipartChunks.push(uploadFileData(multipartValue$1)); multipartChunks.push('\r\n'); - } - multipartChunks.push('--' + multipartBoundary + '\r\nContent-Disposition: form-data; name="metadata"\r\nContent-Type: application/vnd.openapi-nexus.metadata+json\r\n\r\n'); - multipartChunks.push(JSON.stringify(requestParameters.body.metadata)); + const multipartValue$2 = multipartBody['metadata']; + multipartChunks.push( + '--', + multipartBoundary, + '\r\nContent-Disposition: form-data; name="metadata"\r\nContent-Type: application/vnd.openapi-nexus.metadata+json\r\n\r\n', + ); + multipartChunks.push(JSON.stringify(multipartValue$2)); multipartChunks.push('\r\n'); - multipartChunks.push('--' + multipartBoundary + '\r\nContent-Disposition: form-data; name="purpose"\r\nContent-Type: text/plain\r\n\r\n'); - multipartChunks.push(String(requestParameters.body.purpose)); + const multipartValue$3 = multipartBody['purpose']; + multipartChunks.push( + '--', + multipartBoundary, + '\r\nContent-Disposition: form-data; name="purpose"\r\nContent-Type: text/plain\r\n\r\n', + ); + multipartChunks.push(String(multipartValue$3)); multipartChunks.push('\r\n'); multipartChunks.push('--' + multipartBoundary + '--\r\n'); const requestBody = new Blob(multipartChunks); diff --git a/tests/golden/typescript/typescript-fetch/multipart-explicit-encoding/runtime/runtime.ts.golden b/tests/golden/typescript/typescript-fetch/multipart-explicit-encoding/runtime/runtime.ts.golden index 723b7cd6c..00cf68f5f 100644 --- a/tests/golden/typescript/typescript-fetch/multipart-explicit-encoding/runtime/runtime.ts.golden +++ b/tests/golden/typescript/typescript-fetch/multipart-explicit-encoding/runtime/runtime.ts.golden @@ -114,7 +114,10 @@ export function uploadFileFilename(value: UploadFileInput, fallback: string): st } export function multipartHeaderValue(value: string): string { - return value.replace(/[\r\n]/g, '').replace(/\\/g, '\\\\').replace(/"/g, '\\"'); + return value + .replace(/[\r\n]/g, '') + .replace(/\\/g, '\\\\') + .replace(/"/g, '\\"'); } function isUploadFile(value: UploadFileInput): value is UploadFile { diff --git a/tests/golden/typescript/typescript-fetch/multipart-nested-object-parts/apis/MultipartApi.ts.golden b/tests/golden/typescript/typescript-fetch/multipart-nested-object-parts/apis/MultipartApi.ts.golden index 47d598092..bf1e97fbe 100644 --- a/tests/golden/typescript/typescript-fetch/multipart-nested-object-parts/apis/MultipartApi.ts.golden +++ b/tests/golden/typescript/typescript-fetch/multipart-nested-object-parts/apis/MultipartApi.ts.golden @@ -73,7 +73,7 @@ export class MultipartApi extends BaseAPI implements MultipartApiInterface { requestParameters: ApiSendNestedObjectPartRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling sendNestedObjectPartRaw().', @@ -87,17 +87,28 @@ export class MultipartApi extends BaseAPI implements MultipartApiInterface { const headerParameters: Record = {}; // Prepare request body + const multipartBody = requestParameters['body']; const multipartBoundary = '----openapi-nexus-' + Math.random().toString(16).slice(2); const multipartChunks: Array = []; headerParameters['Content-Type'] = 'multipart/form-data; boundary=' + multipartBoundary; - { - const multipartFilename = uploadFileFilename(requestParameters.body.file, 'file'); - multipartChunks.push('--' + multipartBoundary + '\r\nContent-Disposition: form-data; name="file"; filename="' + multipartHeaderValue(multipartFilename) + '"\r\nContent-Type: application/octet-stream\r\n\r\n'); - multipartChunks.push(uploadFileData(requestParameters.body.file)); + const multipartValue$0 = multipartBody['file']; + const multipartFilename$0 = uploadFileFilename(multipartValue$0, 'file'); + multipartChunks.push( + '--', + multipartBoundary, + '\r\nContent-Disposition: form-data; name="file"; filename="', + multipartHeaderValue(multipartFilename$0), + '"\r\nContent-Type: application/octet-stream\r\n\r\n', + ); + multipartChunks.push(uploadFileData(multipartValue$0)); multipartChunks.push('\r\n'); - } - multipartChunks.push('--' + multipartBoundary + '\r\nContent-Disposition: form-data; name="item_config"\r\nContent-Type: application/json\r\n\r\n'); - multipartChunks.push(JSON.stringify(requestParameters.body.item_config)); + const multipartValue$1 = multipartBody['item_config']; + multipartChunks.push( + '--', + multipartBoundary, + '\r\nContent-Disposition: form-data; name="item_config"\r\nContent-Type: application/json\r\n\r\n', + ); + multipartChunks.push(JSON.stringify(multipartValue$1)); multipartChunks.push('\r\n'); multipartChunks.push('--' + multipartBoundary + '--\r\n'); const requestBody = new Blob(multipartChunks); diff --git a/tests/golden/typescript/typescript-fetch/multipart-nested-object-parts/runtime/runtime.ts.golden b/tests/golden/typescript/typescript-fetch/multipart-nested-object-parts/runtime/runtime.ts.golden index 1b4f8c0d8..a5f666ec5 100644 --- a/tests/golden/typescript/typescript-fetch/multipart-nested-object-parts/runtime/runtime.ts.golden +++ b/tests/golden/typescript/typescript-fetch/multipart-nested-object-parts/runtime/runtime.ts.golden @@ -114,7 +114,10 @@ export function uploadFileFilename(value: UploadFileInput, fallback: string): st } export function multipartHeaderValue(value: string): string { - return value.replace(/[\r\n]/g, '').replace(/\\/g, '\\\\').replace(/"/g, '\\"'); + return value + .replace(/[\r\n]/g, '') + .replace(/\\/g, '\\\\') + .replace(/"/g, '\\"'); } function isUploadFile(value: UploadFileInput): value is UploadFile { diff --git a/tests/golden/typescript/typescript-fetch/multipart-unsupported-schema/apis/TransferApi.ts.golden b/tests/golden/typescript/typescript-fetch/multipart-unsupported-schema/apis/TransferApi.ts.golden index c1c31920b..842c062f1 100644 --- a/tests/golden/typescript/typescript-fetch/multipart-unsupported-schema/apis/TransferApi.ts.golden +++ b/tests/golden/typescript/typescript-fetch/multipart-unsupported-schema/apis/TransferApi.ts.golden @@ -69,7 +69,7 @@ export class TransferApi extends BaseAPI implements TransferApiInterface { requestParameters: ApiUploadRawMultipartRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling uploadRawMultipartRaw().', diff --git a/tests/golden/typescript/typescript-fetch/multiple-similar-request-schemas/apis/TestApiApi.ts.golden b/tests/golden/typescript/typescript-fetch/multiple-similar-request-schemas/apis/TestApiApi.ts.golden index c977e160b..7557713a8 100644 --- a/tests/golden/typescript/typescript-fetch/multiple-similar-request-schemas/apis/TestApiApi.ts.golden +++ b/tests/golden/typescript/typescript-fetch/multiple-similar-request-schemas/apis/TestApiApi.ts.golden @@ -108,7 +108,7 @@ export class TestApiApi extends BaseAPI implements TestApiApiInterface { requestParameters: ApiCreateTypeARequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling createTypeARaw().', @@ -172,7 +172,7 @@ export class TestApiApi extends BaseAPI implements TestApiApiInterface { requestParameters: ApiCreateTypeBRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling createTypeBRaw().', diff --git a/tests/golden/typescript/typescript-fetch/naming-conventions/apis/DefaultApi.ts.golden b/tests/golden/typescript/typescript-fetch/naming-conventions/apis/DefaultApi.ts.golden index f7adb8148..1c5f66f78 100644 --- a/tests/golden/typescript/typescript-fetch/naming-conventions/apis/DefaultApi.ts.golden +++ b/tests/golden/typescript/typescript-fetch/naming-conventions/apis/DefaultApi.ts.golden @@ -117,7 +117,7 @@ export class DefaultApi extends BaseAPI implements DefaultApiInterface { requestParameters: ApiGetWithQueryParamsRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.snakeCaseParam === undefined || requestParameters.snakeCaseParam === null) { + if (requestParameters['snakeCaseParam'] === undefined || requestParameters['snakeCaseParam'] === null) { throw new RequiredError( 'snakeCaseParam', 'Required parameter "snakeCaseParam" was null or undefined when calling getWithQueryParamsRaw().', @@ -213,7 +213,7 @@ export class DefaultApi extends BaseAPI implements DefaultApiInterface { requestParameters: ApiTestNamingConventionsRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling testNamingConventionsRaw().', diff --git a/tests/golden/typescript/typescript-fetch/open-tagged-additional-properties/models/Event.ts.golden b/tests/golden/typescript/typescript-fetch/open-tagged-additional-properties/models/Event.ts.golden index 8ec3839ac..57dd6596a 100644 --- a/tests/golden/typescript/typescript-fetch/open-tagged-additional-properties/models/Event.ts.golden +++ b/tests/golden/typescript/typescript-fetch/open-tagged-additional-properties/models/Event.ts.golden @@ -7,5 +7,5 @@ import type { EventCompleted } from './EventCompleted'; import type { EventSearching } from './EventSearching'; export type Event = - | ({ type: 'searching' } & EventSearching) - | ({ type: 'completed' } & EventCompleted); + | (Record<'type', 'searching'> & EventSearching) + | (Record<'type', 'completed'> & EventCompleted); diff --git a/tests/golden/typescript/typescript-fetch/optional-request-bodies/apis/DefaultApi.ts.golden b/tests/golden/typescript/typescript-fetch/optional-request-bodies/apis/DefaultApi.ts.golden index 73585833c..04657e0c6 100644 --- a/tests/golden/typescript/typescript-fetch/optional-request-bodies/apis/DefaultApi.ts.golden +++ b/tests/golden/typescript/typescript-fetch/optional-request-bodies/apis/DefaultApi.ts.golden @@ -349,7 +349,7 @@ export class DefaultApi extends BaseAPI implements DefaultApiInterface { requestParameters: ApiSendRequiredJsonRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling sendRequiredJsonRaw().', diff --git a/tests/golden/typescript/typescript-fetch/petstore/apis/PetApi.ts.golden b/tests/golden/typescript/typescript-fetch/petstore/apis/PetApi.ts.golden index b16c06363..123173b09 100644 --- a/tests/golden/typescript/typescript-fetch/petstore/apis/PetApi.ts.golden +++ b/tests/golden/typescript/typescript-fetch/petstore/apis/PetApi.ts.golden @@ -420,7 +420,7 @@ export class PetApi extends BaseAPI implements PetApiInterface { requestParameters: ApiUpdatePetRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling updatePetRaw().', @@ -517,7 +517,7 @@ export class PetApi extends BaseAPI implements PetApiInterface { requestParameters: ApiAddPetRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling addPetRaw().', @@ -603,7 +603,7 @@ export class PetApi extends BaseAPI implements PetApiInterface { requestParameters: ApiFindPetsByStatusRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.status === undefined || requestParameters.status === null) { + if (requestParameters['status'] === undefined || requestParameters['status'] === null) { throw new RequiredError( 'status', 'Required parameter "status" was null or undefined when calling findPetsByStatusRaw().', @@ -677,7 +677,7 @@ export class PetApi extends BaseAPI implements PetApiInterface { requestParameters: ApiFindPetsByTagsRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.tags === undefined || requestParameters.tags === null) { + if (requestParameters['tags'] === undefined || requestParameters['tags'] === null) { throw new RequiredError( 'tags', 'Required parameter "tags" was null or undefined when calling findPetsByTagsRaw().', @@ -751,7 +751,7 @@ export class PetApi extends BaseAPI implements PetApiInterface { requestParameters: ApiGetPetByIdRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.petId === undefined || requestParameters.petId === null) { + if (requestParameters['petId'] === undefined || requestParameters['petId'] === null) { throw new RequiredError( 'petId', 'Required parameter "petId" was null or undefined when calling getPetByIdRaw().', @@ -834,7 +834,7 @@ export class PetApi extends BaseAPI implements PetApiInterface { requestParameters: ApiUpdatePetWithFormRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.petId === undefined || requestParameters.petId === null) { + if (requestParameters['petId'] === undefined || requestParameters['petId'] === null) { throw new RequiredError( 'petId', 'Required parameter "petId" was null or undefined when calling updatePetWithFormRaw().', @@ -912,7 +912,7 @@ export class PetApi extends BaseAPI implements PetApiInterface { requestParameters: ApiDeletePetRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.petId === undefined || requestParameters.petId === null) { + if (requestParameters['petId'] === undefined || requestParameters['petId'] === null) { throw new RequiredError( 'petId', 'Required parameter "petId" was null or undefined when calling deletePetRaw().', @@ -984,7 +984,7 @@ export class PetApi extends BaseAPI implements PetApiInterface { requestParameters: ApiUploadFileRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.petId === undefined || requestParameters.petId === null) { + if (requestParameters['petId'] === undefined || requestParameters['petId'] === null) { throw new RequiredError( 'petId', 'Required parameter "petId" was null or undefined when calling uploadFileRaw().', diff --git a/tests/golden/typescript/typescript-fetch/petstore/apis/StoreApi.ts.golden b/tests/golden/typescript/typescript-fetch/petstore/apis/StoreApi.ts.golden index 0b89a495c..5a58ee953 100644 --- a/tests/golden/typescript/typescript-fetch/petstore/apis/StoreApi.ts.golden +++ b/tests/golden/typescript/typescript-fetch/petstore/apis/StoreApi.ts.golden @@ -263,7 +263,7 @@ export class StoreApi extends BaseAPI implements StoreApiInterface { requestParameters: ApiPlaceOrderRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling placeOrderRaw().', @@ -338,7 +338,7 @@ export class StoreApi extends BaseAPI implements StoreApiInterface { requestParameters: ApiGetOrderByIdRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.orderId === undefined || requestParameters.orderId === null) { + if (requestParameters['orderId'] === undefined || requestParameters['orderId'] === null) { throw new RequiredError( 'orderId', 'Required parameter "orderId" was null or undefined when calling getOrderByIdRaw().', @@ -421,7 +421,7 @@ export class StoreApi extends BaseAPI implements StoreApiInterface { requestParameters: ApiDeleteOrderRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.orderId === undefined || requestParameters.orderId === null) { + if (requestParameters['orderId'] === undefined || requestParameters['orderId'] === null) { throw new RequiredError( 'orderId', 'Required parameter "orderId" was null or undefined when calling deleteOrderRaw().', diff --git a/tests/golden/typescript/typescript-fetch/petstore/apis/UserApi.ts.golden b/tests/golden/typescript/typescript-fetch/petstore/apis/UserApi.ts.golden index dbbbcc76b..2b6739318 100644 --- a/tests/golden/typescript/typescript-fetch/petstore/apis/UserApi.ts.golden +++ b/tests/golden/typescript/typescript-fetch/petstore/apis/UserApi.ts.golden @@ -336,7 +336,7 @@ export class UserApi extends BaseAPI implements UserApiInterface { requestParameters: ApiCreateUserRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling createUserRaw().', @@ -400,7 +400,7 @@ export class UserApi extends BaseAPI implements UserApiInterface { requestParameters: ApiCreateUsersWithListInputRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling createUsersWithListInputRaw().', @@ -585,7 +585,7 @@ export class UserApi extends BaseAPI implements UserApiInterface { requestParameters: ApiGetUserByNameRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.username === undefined || requestParameters.username === null) { + if (requestParameters['username'] === undefined || requestParameters['username'] === null) { throw new RequiredError( 'username', 'Required parameter "username" was null or undefined when calling getUserByNameRaw().', @@ -668,13 +668,13 @@ export class UserApi extends BaseAPI implements UserApiInterface { requestParameters: ApiUpdateUserRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.username === undefined || requestParameters.username === null) { + if (requestParameters['username'] === undefined || requestParameters['username'] === null) { throw new RequiredError( 'username', 'Required parameter "username" was null or undefined when calling updateUserRaw().', ); } - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling updateUserRaw().', @@ -761,7 +761,7 @@ export class UserApi extends BaseAPI implements UserApiInterface { requestParameters: ApiDeleteUserRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.username === undefined || requestParameters.username === null) { + if (requestParameters['username'] === undefined || requestParameters['username'] === null) { throw new RequiredError( 'username', 'Required parameter "username" was null or undefined when calling deleteUserRaw().', diff --git a/tests/golden/typescript/typescript-fetch/request-body-content-types/apis/DefaultApi.ts.golden b/tests/golden/typescript/typescript-fetch/request-body-content-types/apis/DefaultApi.ts.golden index 53ff91953..a5e3a4dc0 100644 --- a/tests/golden/typescript/typescript-fetch/request-body-content-types/apis/DefaultApi.ts.golden +++ b/tests/golden/typescript/typescript-fetch/request-body-content-types/apis/DefaultApi.ts.golden @@ -309,7 +309,7 @@ export class DefaultApi extends BaseAPI implements DefaultApiInterface { requestParameters: ApiPostBinaryRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling postBinaryRaw().', @@ -373,7 +373,7 @@ export class DefaultApi extends BaseAPI implements DefaultApiInterface { requestParameters: ApiPostFormRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling postFormRaw().', @@ -437,7 +437,7 @@ export class DefaultApi extends BaseAPI implements DefaultApiInterface { requestParameters: ApiPostJsonRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling postJsonRaw().', @@ -501,7 +501,7 @@ export class DefaultApi extends BaseAPI implements DefaultApiInterface { requestParameters: ApiPostJsonOrXmlRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling postJsonOrXmlRaw().', @@ -565,7 +565,7 @@ export class DefaultApi extends BaseAPI implements DefaultApiInterface { requestParameters: ApiPatchMergeJsonRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling patchMergeJsonRaw().', @@ -629,7 +629,7 @@ export class DefaultApi extends BaseAPI implements DefaultApiInterface { requestParameters: ApiPostTextRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling postTextRaw().', @@ -693,7 +693,7 @@ export class DefaultApi extends BaseAPI implements DefaultApiInterface { requestParameters: ApiPostXmlRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling postXmlRaw().', diff --git a/tests/golden/typescript/typescript-fetch/response-body-no-response-body/apis/FooApi.ts.golden b/tests/golden/typescript/typescript-fetch/response-body-no-response-body/apis/FooApi.ts.golden index 495087267..8dd745936 100644 --- a/tests/golden/typescript/typescript-fetch/response-body-no-response-body/apis/FooApi.ts.golden +++ b/tests/golden/typescript/typescript-fetch/response-body-no-response-body/apis/FooApi.ts.golden @@ -85,13 +85,13 @@ export class FooApi extends BaseAPI implements FooApiInterface { requestParameters: ApiUpdateFooBarRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.fooId === undefined || requestParameters.fooId === null) { + if (requestParameters['fooId'] === undefined || requestParameters['fooId'] === null) { throw new RequiredError( 'fooId', 'Required parameter "fooId" was null or undefined when calling updateFooBarRaw().', ); } - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling updateFooBarRaw().', diff --git a/tests/golden/typescript/typescript-fetch/server-object/apis/DefaultApi.ts.golden b/tests/golden/typescript/typescript-fetch/server-object/apis/DefaultApi.ts.golden index 2b5292135..d1f81b904 100644 --- a/tests/golden/typescript/typescript-fetch/server-object/apis/DefaultApi.ts.golden +++ b/tests/golden/typescript/typescript-fetch/server-object/apis/DefaultApi.ts.golden @@ -165,7 +165,7 @@ export class DefaultApi extends BaseAPI implements DefaultApiInterface { requestParameters: ApiGetUserByIdRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.id === undefined || requestParameters.id === null) { + if (requestParameters['id'] === undefined || requestParameters['id'] === null) { throw new RequiredError( 'id', 'Required parameter "id" was null or undefined when calling getUserByIdRaw().', diff --git a/tests/golden/typescript/typescript-fetch/server-path-prefix/apis/DefaultApi.ts.golden b/tests/golden/typescript/typescript-fetch/server-path-prefix/apis/DefaultApi.ts.golden index 7f70c5871..4b0934fa3 100644 --- a/tests/golden/typescript/typescript-fetch/server-path-prefix/apis/DefaultApi.ts.golden +++ b/tests/golden/typescript/typescript-fetch/server-path-prefix/apis/DefaultApi.ts.golden @@ -223,7 +223,7 @@ export class DefaultApi extends BaseAPI implements DefaultApiInterface { requestParameters: ApiGetUserRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.userId === undefined || requestParameters.userId === null) { + if (requestParameters['userId'] === undefined || requestParameters['userId'] === null) { throw new RequiredError( 'userId', 'Required parameter "userId" was null or undefined when calling getUserRaw().', diff --git a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/AllOfJsonDictionary.ts.golden b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/AllOfJsonDictionary.ts.golden index 86612d59d..876389f16 100644 --- a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/AllOfJsonDictionary.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/AllOfJsonDictionary.ts.golden @@ -7,11 +7,12 @@ import type { AllOfJsonDictionaryAllOf1, AllOfJsonDictionaryAllOf1$Wire, } from './AllOfJsonDictionaryAllOf1'; -import type { AnyJsonValue } from './AnyJsonValue'; +import type { AnyJsonValue, AnyJsonValue$Wire } from './AnyJsonValue'; +import { anyJsonValueFromJSON, anyJsonValueToJSON } from './AnyJsonValue'; import { detailFromJSON, detailToJSON } from './Detail'; export type AllOfJsonDictionary$Wire = Readonly< - AllOfJsonDictionaryAllOf1$Wire & Record + AllOfJsonDictionaryAllOf1$Wire & Record >; export type AllOfJsonDictionary = Readonly< @@ -26,7 +27,7 @@ export function allOfJsonDictionaryFromJSON(json: AllOfJsonDictionary$Wire): All const additionalProperties = Object.fromEntries( additionalEntries.map(([key, value]) => { const additionalValue = value; - return [key, additionalValue] as const; + return [key, anyJsonValueFromJSON(additionalValue)] as const; }), ); const visibleProperties = Object.fromEntries( @@ -65,7 +66,7 @@ export function allOfJsonDictionaryToJSON(value: AllOfJsonDictionary): AllOfJson const additionalProperties = Object.fromEntries( additionalEntries.map(([key, value]) => { const additionalValue = value; - return [key, additionalValue] as const; + return [key, anyJsonValueToJSON(additionalValue)] as const; }), ); const result = { diff --git a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/AnyJsonValue.ts.golden b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/AnyJsonValue.ts.golden index 5372bf2af..3e7800b88 100644 --- a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/AnyJsonValue.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/AnyJsonValue.ts.golden @@ -3,4 +3,16 @@ * * TypeScript additional property preservation — 1.0.0 */ +import { convert$1FromJSON, convert$1ToJSON } from '../runtime/valueConversion$1'; + +export type AnyJsonValue$Wire = unknown; + export type AnyJsonValue = unknown; + +export function anyJsonValueFromJSON(json: AnyJsonValue$Wire): AnyJsonValue { + return convert$1FromJSON(json); +} + +export function anyJsonValueToJSON(value: AnyJsonValue): AnyJsonValue$Wire { + return convert$1ToJSON(value); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/ArrayModelMap.ts.golden b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/ArrayModelMap.ts.golden index 085653a15..b36a0e678 100644 --- a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/ArrayModelMap.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/ArrayModelMap.ts.golden @@ -3,8 +3,8 @@ * * TypeScript additional property preservation — 1.0.0 */ +import { convert$3FromJSON, convert$3ToJSON } from '../runtime/valueConversion$3'; import type { Detail, Detail$Wire } from './Detail'; -import { detailFromJSON, detailToJSON } from './Detail'; export interface ArrayModelMap$Wire extends Readonly< Record @@ -24,7 +24,7 @@ export function arrayModelMapFromJSON(json: ArrayModelMap$Wire): ArrayModelMap { const additionalProperties = Object.fromEntries( additionalEntries.map(([key, value]) => { const additionalValue = value as readonly Detail$Wire[]; - return [key, additionalValue.map((item) => detailFromJSON(item))] as const; + return [key, convert$3FromJSON(additionalValue)] as const; }), ); const visibleProperties = Object.fromEntries( @@ -63,7 +63,7 @@ export function arrayModelMapToJSON(value: ArrayModelMap): ArrayModelMap$Wire { const additionalProperties = Object.fromEntries( additionalEntries.map(([key, value]) => { const additionalValue = value as readonly Detail[]; - return [key, additionalValue.map((item) => detailToJSON(item))] as const; + return [key, convert$3ToJSON(additionalValue)] as const; }), ); const result = { diff --git a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/ClosedExternalEvent.ts.golden b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/ClosedExternalEvent.ts.golden index 42ffcc23b..55e3c50c4 100644 --- a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/ClosedExternalEvent.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/ClosedExternalEvent.ts.golden @@ -6,18 +6,36 @@ import type { Detail, Detail$Wire } from './Detail'; import { detailFromJSON, detailToJSON } from './Detail'; -export type ClosedExternalEvent$Wire = { started: Detail$Wire } | { finished: Detail$Wire }; +export type ClosedExternalEvent$Wire = + | Record<'started', Detail$Wire> + | Record<'finished', Detail$Wire>; -export type ClosedExternalEvent = { started: Detail } | { finished: Detail }; +export type ClosedExternalEvent = Record<'started', Detail> | Record<'finished', Detail>; export function closedExternalEventFromJSON(json: ClosedExternalEvent$Wire): ClosedExternalEvent { - if ('started' in json) return { started: detailFromJSON(json['started']) }; - if ('finished' in json) return { finished: detailFromJSON(json['finished']) }; + if (Object.prototype.hasOwnProperty.call(json, 'started')) { + return { + ['started']: detailFromJSON((json as Record<'started', Detail$Wire>)['started']), + } as ClosedExternalEvent; + } + if (Object.prototype.hasOwnProperty.call(json, 'finished')) { + return { + ['finished']: detailFromJSON((json as Record<'finished', Detail$Wire>)['finished']), + } as ClosedExternalEvent; + } throw new Error('Unknown variant'); } export function closedExternalEventToJSON(value: ClosedExternalEvent): ClosedExternalEvent$Wire { - if ('started' in value) return { started: detailToJSON(value['started']) }; - if ('finished' in value) return { finished: detailToJSON(value['finished']) }; + if (Object.prototype.hasOwnProperty.call(value, 'started')) { + return { + ['started']: detailToJSON((value as Record<'started', Detail>)['started']), + } as ClosedExternalEvent$Wire; + } + if (Object.prototype.hasOwnProperty.call(value, 'finished')) { + return { + ['finished']: detailToJSON((value as Record<'finished', Detail>)['finished']), + } as ClosedExternalEvent$Wire; + } throw new Error('Unknown variant'); } diff --git a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/DetailListAlias.ts.golden b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/DetailListAlias.ts.golden index 7b53ca264..4664f1eae 100644 --- a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/DetailListAlias.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/DetailListAlias.ts.golden @@ -3,17 +3,17 @@ * * TypeScript additional property preservation — 1.0.0 */ +import { convert$4FromJSON, convert$4ToJSON } from '../runtime/valueConversion$4'; import type { DetailAliasChain, DetailAliasChain$Wire } from './DetailAliasChain'; -import { detailAliasChainFromJSON, detailAliasChainToJSON } from './DetailAliasChain'; export type DetailListAlias$Wire = readonly DetailAliasChain$Wire[]; export type DetailListAlias = readonly DetailAliasChain[]; export function detailListAliasFromJSON(json: DetailListAlias$Wire): DetailListAlias { - return json.map((item) => detailAliasChainFromJSON(item)); + return convert$4FromJSON(json); } export function detailListAliasToJSON(value: DetailListAlias): DetailListAlias$Wire { - return value.map((item) => detailAliasChainToJSON(item)); + return convert$4ToJSON(value); } diff --git a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/JsonModelAllOfAllOf1.ts.golden b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/JsonModelAllOfAllOf1.ts.golden index 7394b479a..b4b7080c3 100644 --- a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/JsonModelAllOfAllOf1.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/JsonModelAllOfAllOf1.ts.golden @@ -4,6 +4,7 @@ * TypeScript additional property preservation — 1.0.0 */ import type { AnyJsonValue } from './AnyJsonValue'; +import { anyJsonValueFromJSON, anyJsonValueToJSON } from './AnyJsonValue'; import type { Detail, Detail$Wire } from './Detail'; import { detailFromJSON, detailToJSON } from './Detail'; @@ -25,7 +26,7 @@ export function jsonModelAllOfAllOf1FromJSON( const additionalProperties = Object.fromEntries( additionalEntries.map(([key, value]) => { const additionalValue = value; - return [key, additionalValue] as const; + return [key, anyJsonValueFromJSON(additionalValue)] as const; }), ); const visibleProperties = Object.fromEntries( @@ -64,7 +65,7 @@ export function jsonModelAllOfAllOf1ToJSON(value: JsonModelAllOfAllOf1): JsonMod const additionalProperties = Object.fromEntries( additionalEntries.map(([key, value]) => { const additionalValue = value; - return [key, additionalValue] as const; + return [key, anyJsonValueToJSON(additionalValue)] as const; }), ); const result = { diff --git a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/MixedExternalEvent.ts.golden b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/MixedExternalEvent.ts.golden index 23d7911d0..b26ba6661 100644 --- a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/MixedExternalEvent.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/MixedExternalEvent.ts.golden @@ -17,13 +17,12 @@ export type MixedExternalEvent = export function mixedExternalEventFromJSON(json: MixedExternalEvent$Wire): MixedExternalEvent { if (Object.prototype.hasOwnProperty.call(json, 'started')) { const contentValue = (json as Record<'started', Detail$Wire>)['started']; + const entries = Object.entries(json).filter(([key]) => key !== 'started'); const additionalProperties = Object.fromEntries( - Object.entries(json) - .filter(([key]) => key !== 'started') - .map(([key, value]) => { - const additionalValue = value; - return [key, additionalValue] as const; - }), + entries.map(([key, value]) => { + const additionalValue = value; + return [key, additionalValue] as const; + }), ); return { ...additionalProperties, ['started']: detailFromJSON(contentValue) }; } @@ -37,13 +36,12 @@ export function mixedExternalEventFromJSON(json: MixedExternalEvent$Wire): Mixed export function mixedExternalEventToJSON(value: MixedExternalEvent): MixedExternalEvent$Wire { if (Object.prototype.hasOwnProperty.call(value, 'started')) { const contentValue = (value as Record<'started', Detail>)['started']; + const entries = Object.entries(value).filter(([key]) => key !== 'started'); const additionalProperties = Object.fromEntries( - Object.entries(value) - .filter(([key]) => key !== 'started') - .map(([key, value]) => { - const additionalValue = value; - return [key, additionalValue] as const; - }), + entries.map(([key, value]) => { + const additionalValue = value; + return [key, additionalValue] as const; + }), ); return { ...additionalProperties, ['started']: detailToJSON(contentValue) }; } diff --git a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/MixedInlineAnyMap.ts.golden b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/MixedInlineAnyMap.ts.golden index 36696a863..1ace3ef32 100644 --- a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/MixedInlineAnyMap.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/MixedInlineAnyMap.ts.golden @@ -3,6 +3,8 @@ * * TypeScript additional property preservation — 1.0.0 */ +import { convert$1FromJSON, convert$1ToJSON } from '../runtime/valueConversion$1'; + export interface MixedInlineAnyMap$Wire extends Readonly> { readonly known?: string; } @@ -19,7 +21,7 @@ export function mixedInlineAnyMapFromJSON(json: MixedInlineAnyMap$Wire): MixedIn const additionalProperties = Object.fromEntries( additionalEntries.map(([key, value]) => { const additionalValue = value; - return [key, additionalValue] as const; + return [key, convert$1FromJSON(additionalValue)] as const; }), ); const result = { @@ -37,7 +39,7 @@ export function mixedInlineAnyMapToJSON(value: MixedInlineAnyMap): MixedInlineAn const additionalProperties = Object.fromEntries( additionalEntries.map(([key, value]) => { const additionalValue = value; - return [key, additionalValue] as const; + return [key, convert$1ToJSON(additionalValue)] as const; }), ); const result = { diff --git a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/MixedJsonMap.ts.golden b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/MixedJsonMap.ts.golden index 253b9f74c..6c56efed0 100644 --- a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/MixedJsonMap.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/MixedJsonMap.ts.golden @@ -3,6 +3,8 @@ * * TypeScript additional property preservation — 1.0.0 */ +import { anyJsonValueFromJSON, anyJsonValueToJSON } from './AnyJsonValue'; + export interface MixedJsonMap$Wire extends Readonly> { readonly known?: string; } @@ -19,7 +21,7 @@ export function mixedJsonMapFromJSON(json: MixedJsonMap$Wire): MixedJsonMap { const additionalProperties = Object.fromEntries( additionalEntries.map(([key, value]) => { const additionalValue = value; - return [key, additionalValue] as const; + return [key, anyJsonValueFromJSON(additionalValue)] as const; }), ); const result = { @@ -37,7 +39,7 @@ export function mixedJsonMapToJSON(value: MixedJsonMap): MixedJsonMap$Wire { const additionalProperties = Object.fromEntries( additionalEntries.map(([key, value]) => { const additionalValue = value; - return [key, additionalValue] as const; + return [key, anyJsonValueToJSON(additionalValue)] as const; }), ); const result = { diff --git a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/NestedJsonMap.ts.golden b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/NestedJsonMap.ts.golden index d0c8d9c5c..5c26b016f 100644 --- a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/NestedJsonMap.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/NestedJsonMap.ts.golden @@ -3,10 +3,11 @@ * * TypeScript additional property preservation — 1.0.0 */ -import type { AnyJsonValue } from './AnyJsonValue'; +import { convert$0FromJSON, convert$0ToJSON } from '../runtime/valueConversion$0'; +import type { AnyJsonValue, AnyJsonValue$Wire } from './AnyJsonValue'; export interface NestedJsonMap$Wire { - readonly extra?: Record; + readonly extra?: Record; readonly known?: string; } @@ -17,14 +18,14 @@ export interface NestedJsonMap { export function nestedJsonMapFromJSON(json: NestedJsonMap$Wire): NestedJsonMap { return { - extra: json.extra, + extra: json.extra !== undefined ? convert$0FromJSON(json.extra) : undefined, known: json.known, }; } export function nestedJsonMapToJSON(value: NestedJsonMap): NestedJsonMap$Wire { return { - extra: value.extra, + extra: value.extra !== undefined ? convert$0ToJSON(value.extra) : undefined, known: value.known, }; } diff --git a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/NestedModelMap.ts.golden b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/NestedModelMap.ts.golden index 5424df00f..3d6291ce6 100644 --- a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/NestedModelMap.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/NestedModelMap.ts.golden @@ -3,8 +3,8 @@ * * TypeScript additional property preservation — 1.0.0 */ +import { convert$5FromJSON, convert$5ToJSON } from '../runtime/valueConversion$5'; import type { Detail, Detail$Wire } from './Detail'; -import { detailFromJSON, detailToJSON } from './Detail'; export interface NestedModelMap$Wire extends Readonly< Record | number> @@ -24,9 +24,7 @@ export function nestedModelMapFromJSON(json: NestedModelMap$Wire): NestedModelMa const additionalProperties = Object.fromEntries( additionalEntries.map(([key, value]) => { const additionalValue = value as Record; - const convertedValue = Object.fromEntries( - Object.entries(additionalValue ?? {}).map(([k, v]) => [k, detailFromJSON(v)]), - ); + const convertedValue = convert$5FromJSON(additionalValue); return [key, convertedValue] as const; }), ); @@ -66,9 +64,7 @@ export function nestedModelMapToJSON(value: NestedModelMap): NestedModelMap$Wire const additionalProperties = Object.fromEntries( additionalEntries.map(([key, value]) => { const additionalValue = value as Record; - const convertedValue = Object.fromEntries( - Object.entries(additionalValue ?? {}).map(([k, v]) => [k, detailToJSON(v)]), - ); + const convertedValue = convert$5ToJSON(additionalValue); return [key, convertedValue] as const; }), ); diff --git a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/NullableDetailAlias.ts.golden b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/NullableDetailAlias.ts.golden index 86a4807f4..c459c3c78 100644 --- a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/NullableDetailAlias.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/NullableDetailAlias.ts.golden @@ -3,6 +3,7 @@ * * TypeScript additional property preservation — 1.0.0 */ +import { matches$5FromJSON, matches$5ToJSON } from '../runtime/unionMatch'; import type { DetailAliasChain, DetailAliasChain$Wire } from './DetailAliasChain'; import { detailAliasChainFromJSON, detailAliasChainToJSON } from './DetailAliasChain'; @@ -11,9 +12,23 @@ export type NullableDetailAlias$Wire = DetailAliasChain$Wire | null; export type NullableDetailAlias = DetailAliasChain | null; export function nullableDetailAliasFromJSON(json: NullableDetailAlias$Wire): NullableDetailAlias { - return json != null ? detailAliasChainFromJSON(json) : null; + const candidate: unknown = json; + if (matches$5FromJSON(candidate)) { + return detailAliasChainFromJSON(json as unknown as DetailAliasChain$Wire); + } + if (candidate === null) { + return null; + } + return json as unknown as NullableDetailAlias; } export function nullableDetailAliasToJSON(value: NullableDetailAlias): NullableDetailAlias$Wire { - return value != null ? detailAliasChainToJSON(value) : null; + const candidate: unknown = value; + if (matches$5ToJSON(candidate)) { + return detailAliasChainToJSON(value as unknown as DetailAliasChain); + } + if (candidate === null) { + return null; + } + return value as unknown as NullableDetailAlias$Wire; } diff --git a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/NullableModelMap.ts.golden b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/NullableModelMap.ts.golden index 7a8a6b34f..95c312391 100644 --- a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/NullableModelMap.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/NullableModelMap.ts.golden @@ -3,8 +3,8 @@ * * TypeScript additional property preservation — 1.0.0 */ +import { convert$6FromJSON, convert$6ToJSON } from '../runtime/valueConversion$6'; import type { Detail, Detail$Wire } from './Detail'; -import { detailFromJSON, detailToJSON } from './Detail'; export interface NullableModelMap$Wire extends Readonly< Record @@ -24,7 +24,7 @@ export function nullableModelMapFromJSON(json: NullableModelMap$Wire): NullableM const additionalProperties = Object.fromEntries( additionalEntries.map(([key, value]) => { const additionalValue = value as Detail$Wire | null; - return [key, additionalValue != null ? detailFromJSON(additionalValue) : null] as const; + return [key, convert$6FromJSON(additionalValue)] as const; }), ); const visibleProperties = Object.fromEntries( @@ -63,7 +63,7 @@ export function nullableModelMapToJSON(value: NullableModelMap): NullableModelMa const additionalProperties = Object.fromEntries( additionalEntries.map(([key, value]) => { const additionalValue = value as Detail | null; - return [key, additionalValue != null ? detailToJSON(additionalValue) : null] as const; + return [key, convert$6ToJSON(additionalValue)] as const; }), ); const result = { diff --git a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/OpenExternalEvent.ts.golden b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/OpenExternalEvent.ts.golden index 74d6e6352..b8472e5b4 100644 --- a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/OpenExternalEvent.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/OpenExternalEvent.ts.golden @@ -17,25 +17,23 @@ export type OpenExternalEvent = export function openExternalEventFromJSON(json: OpenExternalEvent$Wire): OpenExternalEvent { if (Object.prototype.hasOwnProperty.call(json, 'started')) { const contentValue = (json as Record<'started', Detail$Wire>)['started']; + const entries = Object.entries(json).filter(([key]) => key !== 'started'); const additionalProperties = Object.fromEntries( - Object.entries(json) - .filter(([key]) => key !== 'started') - .map(([key, value]) => { - const additionalValue = value; - return [key, additionalValue] as const; - }), + entries.map(([key, value]) => { + const additionalValue = value; + return [key, additionalValue] as const; + }), ); return { ...additionalProperties, ['started']: detailFromJSON(contentValue) }; } if (Object.prototype.hasOwnProperty.call(json, 'finished')) { const contentValue = (json as Record<'finished', Detail$Wire>)['finished']; + const entries = Object.entries(json).filter(([key]) => key !== 'finished'); const additionalProperties = Object.fromEntries( - Object.entries(json) - .filter(([key]) => key !== 'finished') - .map(([key, value]) => { - const additionalValue = value; - return [key, additionalValue] as const; - }), + entries.map(([key, value]) => { + const additionalValue = value; + return [key, additionalValue] as const; + }), ); return { ...additionalProperties, ['finished']: detailFromJSON(contentValue) }; } @@ -45,25 +43,23 @@ export function openExternalEventFromJSON(json: OpenExternalEvent$Wire): OpenExt export function openExternalEventToJSON(value: OpenExternalEvent): OpenExternalEvent$Wire { if (Object.prototype.hasOwnProperty.call(value, 'started')) { const contentValue = (value as Record<'started', Detail>)['started']; + const entries = Object.entries(value).filter(([key]) => key !== 'started'); const additionalProperties = Object.fromEntries( - Object.entries(value) - .filter(([key]) => key !== 'started') - .map(([key, value]) => { - const additionalValue = value; - return [key, additionalValue] as const; - }), + entries.map(([key, value]) => { + const additionalValue = value; + return [key, additionalValue] as const; + }), ); return { ...additionalProperties, ['started']: detailToJSON(contentValue) }; } if (Object.prototype.hasOwnProperty.call(value, 'finished')) { const contentValue = (value as Record<'finished', Detail>)['finished']; + const entries = Object.entries(value).filter(([key]) => key !== 'finished'); const additionalProperties = Object.fromEntries( - Object.entries(value) - .filter(([key]) => key !== 'finished') - .map(([key, value]) => { - const additionalValue = value; - return [key, additionalValue] as const; - }), + entries.map(([key, value]) => { + const additionalValue = value; + return [key, additionalValue] as const; + }), ); return { ...additionalProperties, ['finished']: detailToJSON(contentValue) }; } diff --git a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/PlainJsonMap.ts.golden b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/PlainJsonMap.ts.golden index 15836bd9f..96a44f328 100644 --- a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/PlainJsonMap.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/PlainJsonMap.ts.golden @@ -3,14 +3,38 @@ * * TypeScript additional property preservation — 1.0.0 */ +import { anyJsonValueFromJSON, anyJsonValueToJSON } from './AnyJsonValue'; + export interface PlainJsonMap$Wire extends Readonly> {} export interface PlainJsonMap extends Readonly> {} export function plainJsonMapFromJSON(json: PlainJsonMap$Wire): PlainJsonMap { - return json as unknown as PlainJsonMap; + const sourceEntries = Object.entries(json); + const additionalEntries = sourceEntries; + const additionalProperties = Object.fromEntries( + additionalEntries.map(([key, value]) => { + const additionalValue = value; + return [key, anyJsonValueFromJSON(additionalValue)] as const; + }), + ); + const result = { + ...additionalProperties, + }; + return result; } export function plainJsonMapToJSON(value: PlainJsonMap): PlainJsonMap$Wire { - return value as unknown as PlainJsonMap$Wire; + const sourceEntries = Object.entries(value); + const additionalEntries = sourceEntries; + const additionalProperties = Object.fromEntries( + additionalEntries.map(([key, value]) => { + const additionalValue = value; + return [key, anyJsonValueToJSON(additionalValue)] as const; + }), + ); + const result = { + ...additionalProperties, + }; + return result; } diff --git a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/SearchItem.ts.golden b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/SearchItem.ts.golden index 470d0accf..a81d9ce50 100644 --- a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/SearchItem.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/SearchItem.ts.golden @@ -3,6 +3,8 @@ * * TypeScript additional property preservation — 1.0.0 */ +import { anyJsonValueFromJSON, anyJsonValueToJSON } from './AnyJsonValue'; + export interface SearchItem$Wire extends Readonly> { readonly id?: string | null; readonly status?: string | null; @@ -23,7 +25,7 @@ export function searchItemFromJSON(json: SearchItem$Wire): SearchItem { const additionalProperties = Object.fromEntries( additionalEntries.map(([key, value]) => { const additionalValue = value; - return [key, additionalValue] as const; + return [key, anyJsonValueFromJSON(additionalValue)] as const; }), ); const result = { @@ -43,7 +45,7 @@ export function searchItemToJSON(value: SearchItem): SearchItem$Wire { const additionalProperties = Object.fromEntries( additionalEntries.map(([key, value]) => { const additionalValue = value; - return [key, additionalValue] as const; + return [key, anyJsonValueToJSON(additionalValue)] as const; }), ); const result = { diff --git a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/SearchStreamEvent.ts.golden b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/SearchStreamEvent.ts.golden index e4a5a5e88..20f8a1c44 100644 --- a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/SearchStreamEvent.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/SearchStreamEvent.ts.golden @@ -13,37 +13,41 @@ import { } from './SearchStreamEventResponseWebSearchCallSearching'; export type SearchStreamEvent$Wire = - | ({ - type: 'response.web_search_call.searching'; - } & SearchStreamEventResponseWebSearchCallSearching$Wire) - | { type: 'response.completed' }; + | (Record<'type', 'response.web_search_call.searching'> & + SearchStreamEventResponseWebSearchCallSearching$Wire) + | Record<'type', 'response.completed'>; export type SearchStreamEvent = - | ({ - type: 'response.web_search_call.searching'; - } & SearchStreamEventResponseWebSearchCallSearching) - | { type: 'response.completed' }; + | (Record<'type', 'response.web_search_call.searching'> & + SearchStreamEventResponseWebSearchCallSearching) + | Record<'type', 'response.completed'>; export function searchStreamEventFromJSON(json: SearchStreamEvent$Wire): SearchStreamEvent { - switch (json.type) { - case 'response.web_search_call.searching': - return { - ...searchStreamEventResponseWebSearchCallSearchingFromJSON(json), - type: 'response.web_search_call.searching', - }; - case 'response.completed': - return { type: 'response.completed' }; + if (json['type'] === 'response.web_search_call.searching') { + return { + ...searchStreamEventResponseWebSearchCallSearchingFromJSON( + json as unknown as SearchStreamEventResponseWebSearchCallSearching$Wire, + ), + ['type']: 'response.web_search_call.searching', + } as SearchStreamEvent; } + if (json['type'] === 'response.completed') { + return { ['type']: 'response.completed' } as SearchStreamEvent; + } + throw new Error('Unknown variant'); } export function searchStreamEventToJSON(value: SearchStreamEvent): SearchStreamEvent$Wire { - switch (value.type) { - case 'response.web_search_call.searching': - return { - ...searchStreamEventResponseWebSearchCallSearchingToJSON(value), - type: 'response.web_search_call.searching', - } as SearchStreamEvent$Wire; - case 'response.completed': - return { type: 'response.completed' } as SearchStreamEvent$Wire; + if (value['type'] === 'response.web_search_call.searching') { + return { + ...searchStreamEventResponseWebSearchCallSearchingToJSON( + value as unknown as SearchStreamEventResponseWebSearchCallSearching, + ), + ['type']: 'response.web_search_call.searching', + } as SearchStreamEvent$Wire; + } + if (value['type'] === 'response.completed') { + return { ['type']: 'response.completed' } as SearchStreamEvent$Wire; } + throw new Error('Unknown variant'); } diff --git a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/SearchStreamEventResponseWebSearchCallSearching.ts.golden b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/SearchStreamEventResponseWebSearchCallSearching.ts.golden index e99c2748f..00d305b76 100644 --- a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/SearchStreamEventResponseWebSearchCallSearching.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/SearchStreamEventResponseWebSearchCallSearching.ts.golden @@ -4,6 +4,7 @@ * TypeScript additional property preservation — 1.0.0 */ import type { AnyJsonValue } from './AnyJsonValue'; +import { anyJsonValueFromJSON, anyJsonValueToJSON } from './AnyJsonValue'; export interface SearchStreamEventResponseWebSearchCallSearching$Wire extends Readonly< Record @@ -29,7 +30,7 @@ export function searchStreamEventResponseWebSearchCallSearchingFromJSON( const additionalProperties = Object.fromEntries( additionalEntries.map(([key, value]) => { const additionalValue = value; - return [key, additionalValue] as const; + return [key, anyJsonValueFromJSON(additionalValue)] as const; }), ); const visibleProperties = Object.fromEntries( @@ -71,7 +72,7 @@ export function searchStreamEventResponseWebSearchCallSearchingToJSON( const additionalProperties = Object.fromEntries( additionalEntries.map(([key, value]) => { const additionalValue = value; - return [key, additionalValue] as const; + return [key, anyJsonValueToJSON(additionalValue)] as const; }), ); const result = { diff --git a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/TypedExternalEvent.ts.golden b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/TypedExternalEvent.ts.golden index 723dd2ce9..6fb6f14e8 100644 --- a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/TypedExternalEvent.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/TypedExternalEvent.ts.golden @@ -19,25 +19,23 @@ export type TypedExternalEvent = export function typedExternalEventFromJSON(json: TypedExternalEvent$Wire): TypedExternalEvent { if (Object.prototype.hasOwnProperty.call(json, 'started')) { const contentValue = (json as Record<'started', DetailAliasChain$Wire>)['started']; + const entries = Object.entries(json).filter(([key]) => key !== 'started'); const additionalProperties = Object.fromEntries( - Object.entries(json) - .filter(([key]) => key !== 'started') - .map(([key, value]) => { - const additionalValue = value as DetailAliasChain$Wire; - return [key, detailAliasChainFromJSON(additionalValue)] as const; - }), + entries.map(([key, value]) => { + const additionalValue = value as DetailAliasChain$Wire; + return [key, detailAliasChainFromJSON(additionalValue)] as const; + }), ); return { ...additionalProperties, ['started']: detailAliasChainFromJSON(contentValue) }; } if (Object.prototype.hasOwnProperty.call(json, 'finished')) { const contentValue = (json as Record<'finished', DetailAliasChain$Wire>)['finished']; + const entries = Object.entries(json).filter(([key]) => key !== 'finished'); const additionalProperties = Object.fromEntries( - Object.entries(json) - .filter(([key]) => key !== 'finished') - .map(([key, value]) => { - const additionalValue = value as DetailAliasChain$Wire; - return [key, detailAliasChainFromJSON(additionalValue)] as const; - }), + entries.map(([key, value]) => { + const additionalValue = value as DetailAliasChain$Wire; + return [key, detailAliasChainFromJSON(additionalValue)] as const; + }), ); return { ...additionalProperties, ['finished']: detailAliasChainFromJSON(contentValue) }; } @@ -47,25 +45,23 @@ export function typedExternalEventFromJSON(json: TypedExternalEvent$Wire): Typed export function typedExternalEventToJSON(value: TypedExternalEvent): TypedExternalEvent$Wire { if (Object.prototype.hasOwnProperty.call(value, 'started')) { const contentValue = (value as Record<'started', DetailAliasChain>)['started']; + const entries = Object.entries(value).filter(([key]) => key !== 'started'); const additionalProperties = Object.fromEntries( - Object.entries(value) - .filter(([key]) => key !== 'started') - .map(([key, value]) => { - const additionalValue = value as DetailAliasChain; - return [key, detailAliasChainToJSON(additionalValue)] as const; - }), + entries.map(([key, value]) => { + const additionalValue = value as DetailAliasChain; + return [key, detailAliasChainToJSON(additionalValue)] as const; + }), ); return { ...additionalProperties, ['started']: detailAliasChainToJSON(contentValue) }; } if (Object.prototype.hasOwnProperty.call(value, 'finished')) { const contentValue = (value as Record<'finished', DetailAliasChain>)['finished']; + const entries = Object.entries(value).filter(([key]) => key !== 'finished'); const additionalProperties = Object.fromEntries( - Object.entries(value) - .filter(([key]) => key !== 'finished') - .map(([key, value]) => { - const additionalValue = value as DetailAliasChain; - return [key, detailAliasChainToJSON(additionalValue)] as const; - }), + entries.map(([key, value]) => { + const additionalValue = value as DetailAliasChain; + return [key, detailAliasChainToJSON(additionalValue)] as const; + }), ); return { ...additionalProperties, ['finished']: detailAliasChainToJSON(contentValue) }; } diff --git a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/index.ts.golden b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/index.ts.golden index 4476a1421..b7452424e 100644 --- a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/index.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/models/index.ts.golden @@ -36,7 +36,8 @@ export { allOfRenamedMapAllOf2FromJSON, allOfRenamedMapAllOf2ToJSON, } from './AllOfRenamedMapAllOf2'; -export type { AnyJsonValue } from './AnyJsonValue'; +export type { AnyJsonValue, AnyJsonValue$Wire } from './AnyJsonValue'; +export { anyJsonValueFromJSON, anyJsonValueToJSON } from './AnyJsonValue'; export type { ArrayModelMap, ArrayModelMap$Wire } from './ArrayModelMap'; export { arrayModelMapFromJSON, arrayModelMapToJSON } from './ArrayModelMap'; export type { ClosedExternalEvent, ClosedExternalEvent$Wire } from './ClosedExternalEvent'; diff --git a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/runtime/unionMatch.ts.golden b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/runtime/unionMatch.ts.golden new file mode 100644 index 000000000..c697de798 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/runtime/unionMatch.ts.golden @@ -0,0 +1,64 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * TypeScript additional property preservation — 1.0.0 + */ +export function matches$0FromJSON(candidate: unknown): boolean { + return typeof candidate === 'boolean'; +} + +export function matches$0ToJSON(candidate: unknown): boolean { + return typeof candidate === 'boolean'; +} + +export function matches$1FromJSON(candidate: unknown): boolean { + return Array.isArray(candidate); +} + +export function matches$1ToJSON(candidate: unknown): boolean { + return Array.isArray(candidate); +} + +export function matches$3FromJSON(candidate: unknown): boolean { + return typeof candidate === 'number' && Number.isFinite(candidate); +} + +export function matches$3ToJSON(candidate: unknown): boolean { + return typeof candidate === 'number' && Number.isFinite(candidate); +} + +export function matches$4FromJSON(candidate: unknown): boolean { + return typeof candidate === 'string'; +} + +export function matches$4ToJSON(candidate: unknown): boolean { + return typeof candidate === 'string'; +} + +export function matches$5FromJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as Record; + if (!Object.prototype.hasOwnProperty.call(candidate, 'display_name')) { + return false; + } + if (!matches$4FromJSON(object['display_name'])) { + return false; + } + return true; +} + +export function matches$5ToJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as Record; + if (!Object.prototype.hasOwnProperty.call(candidate, 'displayName')) { + return false; + } + if (!matches$4ToJSON(object['displayName'])) { + return false; + } + return true; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/runtime/valueConversion$0.ts.golden b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/runtime/valueConversion$0.ts.golden new file mode 100644 index 000000000..d6c39a5ef --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/runtime/valueConversion$0.ts.golden @@ -0,0 +1,23 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * TypeScript additional property preservation — 1.0.0 + */ +import type { AnyJsonValue, AnyJsonValue$Wire } from '../models/AnyJsonValue'; +import { anyJsonValueFromJSON, anyJsonValueToJSON } from '../models/AnyJsonValue'; + +export type Value$0Wire = Record; + +export type Value$0 = Record; + +export function convert$0FromJSON(json: Record): Value$0 { + return Object.fromEntries( + Object.entries(json ?? {}).map(([key, item]) => [key, anyJsonValueFromJSON(item)]), + ); +} + +export function convert$0ToJSON(value: Record): Value$0Wire { + return Object.fromEntries( + Object.entries(value ?? {}).map(([key, item]) => [key, anyJsonValueToJSON(item)]), + ); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/runtime/valueConversion$1.ts.golden b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/runtime/valueConversion$1.ts.golden new file mode 100644 index 000000000..3c7583c13 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/runtime/valueConversion$1.ts.golden @@ -0,0 +1,18 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * TypeScript additional property preservation — 1.0.0 + */ +import { convert$2FromJSON, convert$2ToJSON } from './valueConversion$2'; + +export type Value$1Wire = unknown; + +export type Value$1 = unknown; + +export function convert$1FromJSON(json: unknown): Value$1 { + return convert$2FromJSON(json); +} + +export function convert$1ToJSON(value: unknown): Value$1Wire { + return convert$2ToJSON(value); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/runtime/valueConversion$2.ts.golden b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/runtime/valueConversion$2.ts.golden new file mode 100644 index 000000000..e8c245689 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/runtime/valueConversion$2.ts.golden @@ -0,0 +1,53 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * TypeScript additional property preservation — 1.0.0 + */ +import { + matches$0FromJSON, + matches$0ToJSON, + matches$1FromJSON, + matches$1ToJSON, + matches$3FromJSON, + matches$3ToJSON, + matches$4FromJSON, + matches$4ToJSON, +} from '../runtime/unionMatch'; + +export type Value$2Wire = unknown; + +export type Value$2 = unknown; + +export function convert$2FromJSON(json: unknown): Value$2 { + const candidate: unknown = json; + if (matches$0FromJSON(candidate)) { + return json as unknown as boolean; + } + if (matches$1FromJSON(candidate)) { + return json as unknown as readonly unknown[]; + } + if (matches$3FromJSON(candidate)) { + return json as unknown as number; + } + if (matches$4FromJSON(candidate)) { + return json as unknown as string; + } + return json as unknown as unknown; +} + +export function convert$2ToJSON(value: unknown): Value$2Wire { + const candidate: unknown = value; + if (matches$0ToJSON(candidate)) { + return value as unknown as boolean; + } + if (matches$1ToJSON(candidate)) { + return value as unknown as readonly unknown[]; + } + if (matches$3ToJSON(candidate)) { + return value as unknown as number; + } + if (matches$4ToJSON(candidate)) { + return value as unknown as string; + } + return value as unknown as unknown; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/runtime/valueConversion$3.ts.golden b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/runtime/valueConversion$3.ts.golden new file mode 100644 index 000000000..c71348e16 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/runtime/valueConversion$3.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * TypeScript additional property preservation — 1.0.0 + */ +import type { Detail, Detail$Wire } from '../models/Detail'; +import { detailFromJSON, detailToJSON } from '../models/Detail'; + +export type Value$3Wire = readonly Detail$Wire[]; + +export type Value$3 = readonly Detail[]; + +export function convert$3FromJSON(json: ReadonlyArray): Value$3 { + return json.map(detailFromJSON); +} + +export function convert$3ToJSON(value: ReadonlyArray): Value$3Wire { + return value.map(detailToJSON); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/runtime/valueConversion$4.ts.golden b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/runtime/valueConversion$4.ts.golden new file mode 100644 index 000000000..c515bb75d --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/runtime/valueConversion$4.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * TypeScript additional property preservation — 1.0.0 + */ +import type { DetailAliasChain, DetailAliasChain$Wire } from '../models/DetailAliasChain'; +import { detailAliasChainFromJSON, detailAliasChainToJSON } from '../models/DetailAliasChain'; + +export type Value$4Wire = readonly DetailAliasChain$Wire[]; + +export type Value$4 = readonly DetailAliasChain[]; + +export function convert$4FromJSON(json: ReadonlyArray): Value$4 { + return json.map(detailAliasChainFromJSON); +} + +export function convert$4ToJSON(value: ReadonlyArray): Value$4Wire { + return value.map(detailAliasChainToJSON); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/runtime/valueConversion$5.ts.golden b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/runtime/valueConversion$5.ts.golden new file mode 100644 index 000000000..51dea5b30 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/runtime/valueConversion$5.ts.golden @@ -0,0 +1,23 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * TypeScript additional property preservation — 1.0.0 + */ +import type { Detail, Detail$Wire } from '../models/Detail'; +import { detailFromJSON, detailToJSON } from '../models/Detail'; + +export type Value$5Wire = Record; + +export type Value$5 = Record; + +export function convert$5FromJSON(json: Record): Value$5 { + return Object.fromEntries( + Object.entries(json ?? {}).map(([key, item]) => [key, detailFromJSON(item)]), + ); +} + +export function convert$5ToJSON(value: Record): Value$5Wire { + return Object.fromEntries( + Object.entries(value ?? {}).map(([key, item]) => [key, detailToJSON(item)]), + ); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/runtime/valueConversion$6.ts.golden b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/runtime/valueConversion$6.ts.golden new file mode 100644 index 000000000..d410d64e2 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-additional-properties/runtime/valueConversion$6.ts.golden @@ -0,0 +1,25 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * TypeScript additional property preservation — 1.0.0 + */ +import type { Detail, Detail$Wire } from '../models/Detail'; +import { detailFromJSON, detailToJSON } from '../models/Detail'; + +export type Value$6Wire = Detail$Wire | null; + +export type Value$6 = Detail | null; + +export function convert$6FromJSON(json: Detail$Wire | null): Value$6 { + if (json == null) { + return null; + } + return detailFromJSON(json); +} + +export function convert$6ToJSON(value: Detail | null): Value$6Wire { + if (value == null) { + return null; + } + return detailToJSON(value); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-externally-tagged/models/ExternallyTaggedEnum.ts.golden b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-externally-tagged/models/ExternallyTaggedEnum.ts.golden index f3e9d16b5..7c1eff147 100644 --- a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-externally-tagged/models/ExternallyTaggedEnum.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-externally-tagged/models/ExternallyTaggedEnum.ts.golden @@ -9,24 +9,58 @@ import { variantAFromJSON, variantAToJSON } from './VariantA'; import type { VariantB, VariantB$Wire } from './VariantB'; import { variantBFromJSON, variantBToJSON } from './VariantB'; -/** Externally tagged enum where the key determines the variant */ - +/** + * Externally tagged enum where the key determines the variant + */ export type ExternallyTaggedEnum$Wire = - | { EXTERNALLY_TAGGED_VARIANT_A: VariantA$Wire } - | { EXTERNALLY_TAGGED_VARIANT_B: VariantB$Wire }; + | Record<'EXTERNALLY_TAGGED_VARIANT_A', VariantA$Wire> + | Record<'EXTERNALLY_TAGGED_VARIANT_B', VariantB$Wire>; +/** + * Externally tagged enum where the key determines the variant + */ export type ExternallyTaggedEnum = - | { EXTERNALLY_TAGGED_VARIANT_A: VariantA } - | { EXTERNALLY_TAGGED_VARIANT_B: VariantB }; + | Record<'EXTERNALLY_TAGGED_VARIANT_A', VariantA> + | Record<'EXTERNALLY_TAGGED_VARIANT_B', VariantB>; -export function externallyTaggedEnumFromJSON(json: ExternallyTaggedEnum$Wire): ExternallyTaggedEnum { - if ('EXTERNALLY_TAGGED_VARIANT_A' in json) return { EXTERNALLY_TAGGED_VARIANT_A: variantAFromJSON(json['EXTERNALLY_TAGGED_VARIANT_A']) }; - if ('EXTERNALLY_TAGGED_VARIANT_B' in json) return { EXTERNALLY_TAGGED_VARIANT_B: variantBFromJSON(json['EXTERNALLY_TAGGED_VARIANT_B']) }; +export function externallyTaggedEnumFromJSON( + json: ExternallyTaggedEnum$Wire, +): ExternallyTaggedEnum { + if (Object.prototype.hasOwnProperty.call(json, 'EXTERNALLY_TAGGED_VARIANT_A')) { + return { + ['EXTERNALLY_TAGGED_VARIANT_A']: variantAFromJSON( + (json as Record<'EXTERNALLY_TAGGED_VARIANT_A', VariantA$Wire>)[ + 'EXTERNALLY_TAGGED_VARIANT_A' + ], + ), + } as ExternallyTaggedEnum; + } + if (Object.prototype.hasOwnProperty.call(json, 'EXTERNALLY_TAGGED_VARIANT_B')) { + return { + ['EXTERNALLY_TAGGED_VARIANT_B']: variantBFromJSON( + (json as Record<'EXTERNALLY_TAGGED_VARIANT_B', VariantB$Wire>)[ + 'EXTERNALLY_TAGGED_VARIANT_B' + ], + ), + } as ExternallyTaggedEnum; + } throw new Error('Unknown variant'); } export function externallyTaggedEnumToJSON(value: ExternallyTaggedEnum): ExternallyTaggedEnum$Wire { - if ('EXTERNALLY_TAGGED_VARIANT_A' in value) return { EXTERNALLY_TAGGED_VARIANT_A: variantAToJSON(value['EXTERNALLY_TAGGED_VARIANT_A']) }; - if ('EXTERNALLY_TAGGED_VARIANT_B' in value) return { EXTERNALLY_TAGGED_VARIANT_B: variantBToJSON(value['EXTERNALLY_TAGGED_VARIANT_B']) }; + if (Object.prototype.hasOwnProperty.call(value, 'EXTERNALLY_TAGGED_VARIANT_A')) { + return { + ['EXTERNALLY_TAGGED_VARIANT_A']: variantAToJSON( + (value as Record<'EXTERNALLY_TAGGED_VARIANT_A', VariantA>)['EXTERNALLY_TAGGED_VARIANT_A'], + ), + } as ExternallyTaggedEnum$Wire; + } + if (Object.prototype.hasOwnProperty.call(value, 'EXTERNALLY_TAGGED_VARIANT_B')) { + return { + ['EXTERNALLY_TAGGED_VARIANT_B']: variantBToJSON( + (value as Record<'EXTERNALLY_TAGGED_VARIANT_B', VariantB>)['EXTERNALLY_TAGGED_VARIANT_B'], + ), + } as ExternallyTaggedEnum$Wire; + } throw new Error('Unknown variant'); } diff --git a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-intersection-enum-ref/models/AllEnums.ts.golden b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-intersection-enum-ref/models/AllEnums.ts.golden index 5621952f7..fcc792f0d 100644 --- a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-intersection-enum-ref/models/AllEnums.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-intersection-enum-ref/models/AllEnums.ts.golden @@ -7,6 +7,7 @@ import type { AnotherEnum } from './AnotherEnum'; import type { EnumStatus } from './EnumStatus'; -/** Intersection with only enum members (should NOT emit camelCase converters) */ - +/** + * Intersection with only enum members (should NOT emit camelCase converters) + */ export type AllEnums = EnumStatus & AnotherEnum; diff --git a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-intersection-enum-ref/models/Mixed.ts.golden b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-intersection-enum-ref/models/Mixed.ts.golden index e714e04a1..ca654e96c 100644 --- a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-intersection-enum-ref/models/Mixed.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-intersection-enum-ref/models/Mixed.ts.golden @@ -16,9 +16,9 @@ export type Mixed$Wire = ObjectBase$Wire & EnumStatus; export type Mixed = ObjectBase & EnumStatus; export function mixedFromJSON(json: Mixed$Wire): Mixed { - return { ...objectBaseFromJSON(json as ObjectBase$Wire) } as Mixed; + return {...objectBaseFromJSON(json as ObjectBase$Wire)} as Mixed; } export function mixedToJSON(value: Mixed): Mixed$Wire { - return { ...objectBaseToJSON(value as ObjectBase) } as Mixed$Wire; + return {...objectBaseToJSON(value as ObjectBase)} as Mixed$Wire; } diff --git a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-intersection/apis/DefaultApi.ts.golden b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-intersection/apis/DefaultApi.ts.golden index 34d226b51..753382bab 100644 --- a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-intersection/apis/DefaultApi.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-intersection/apis/DefaultApi.ts.golden @@ -71,7 +71,7 @@ export class DefaultApi extends BaseAPI implements DefaultApiInterface { requestParameters: ApiTestIntersectionRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling testIntersectionRaw().', @@ -87,7 +87,7 @@ export class DefaultApi extends BaseAPI implements DefaultApiInterface { }; // Prepare request body - const requestBody = fooToJSON(requestParameters.body); + const requestBody = fooToJSON(requestParameters['body']); // Make request const response = await this.request( { diff --git a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-intersection/models/Foo.ts.golden b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-intersection/models/Foo.ts.golden index 99e7b70bb..b79710994 100644 --- a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-intersection/models/Foo.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-intersection/models/Foo.ts.golden @@ -14,9 +14,9 @@ export type Foo$Wire = Bar$Wire & Baz$Wire; export type Foo = Bar & Baz; export function fooFromJSON(json: Foo$Wire): Foo { - return { ...barFromJSON(json as Bar$Wire), ...bazFromJSON(json as Baz$Wire) } as Foo; + return {...barFromJSON(json as Bar$Wire), ...bazFromJSON(json as Baz$Wire)} as Foo; } export function fooToJSON(value: Foo): Foo$Wire { - return { ...barToJSON(value as Bar), ...bazToJSON(value as Baz) } as Foo$Wire; + return {...barToJSON(value as Bar), ...bazToJSON(value as Baz)} as Foo$Wire; } diff --git a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-multipart-parts/apis/MultipartApi.ts.golden b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-multipart-parts/apis/MultipartApi.ts.golden index a51c214dc..f0745d331 100644 --- a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-multipart-parts/apis/MultipartApi.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-multipart-parts/apis/MultipartApi.ts.golden @@ -74,7 +74,7 @@ export class MultipartApi extends BaseAPI implements MultipartApiInterface { requestParameters: ApiSendNestedObjectPartRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling sendNestedObjectPartRaw().', @@ -88,17 +88,28 @@ export class MultipartApi extends BaseAPI implements MultipartApiInterface { const headerParameters: Record = {}; // Prepare request body + const multipartBody = requestParameters['body']; const multipartBoundary = '----openapi-nexus-' + Math.random().toString(16).slice(2); const multipartChunks: Array = []; headerParameters['Content-Type'] = 'multipart/form-data; boundary=' + multipartBoundary; - { - const multipartFilename = uploadFileFilename(requestParameters.body.file, 'file'); - multipartChunks.push('--' + multipartBoundary + '\r\nContent-Disposition: form-data; name="file"; filename="' + multipartHeaderValue(multipartFilename) + '"\r\nContent-Type: application/octet-stream\r\n\r\n'); - multipartChunks.push(uploadFileData(requestParameters.body.file)); + const multipartValue$0 = multipartBody['file']; + const multipartFilename$0 = uploadFileFilename(multipartValue$0, 'file'); + multipartChunks.push( + '--', + multipartBoundary, + '\r\nContent-Disposition: form-data; name="file"; filename="', + multipartHeaderValue(multipartFilename$0), + '"\r\nContent-Type: application/octet-stream\r\n\r\n', + ); + multipartChunks.push(uploadFileData(multipartValue$0)); multipartChunks.push('\r\n'); - } - multipartChunks.push('--' + multipartBoundary + '\r\nContent-Disposition: form-data; name="item_config"\r\nContent-Type: application/json\r\n\r\n'); - multipartChunks.push(JSON.stringify(itemConfigToJSON(requestParameters.body.itemConfig))); + const multipartValue$1 = multipartBody['itemConfig']; + multipartChunks.push( + '--', + multipartBoundary, + '\r\nContent-Disposition: form-data; name="item_config"\r\nContent-Type: application/json\r\n\r\n', + ); + multipartChunks.push(JSON.stringify(itemConfigToJSON(multipartValue$1))); multipartChunks.push('\r\n'); multipartChunks.push('--' + multipartBoundary + '--\r\n'); const requestBody = new Blob(multipartChunks); diff --git a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-multipart-parts/runtime/runtime.ts.golden b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-multipart-parts/runtime/runtime.ts.golden index 1b4f8c0d8..a5f666ec5 100644 --- a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-multipart-parts/runtime/runtime.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-multipart-parts/runtime/runtime.ts.golden @@ -114,7 +114,10 @@ export function uploadFileFilename(value: UploadFileInput, fallback: string): st } export function multipartHeaderValue(value: string): string { - return value.replace(/[\r\n]/g, '').replace(/\\/g, '\\\\').replace(/"/g, '\\"'); + return value + .replace(/[\r\n]/g, '') + .replace(/\\/g, '\\\\') + .replace(/"/g, '\\"'); } function isUploadFile(value: UploadFileInput): value is UploadFile { diff --git a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-response-dispatch/apis/ResponsesApi.ts.golden b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-response-dispatch/apis/ResponsesApi.ts.golden index 51cb5a497..a4376afab 100644 --- a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-response-dispatch/apis/ResponsesApi.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-response-dispatch/apis/ResponsesApi.ts.golden @@ -20,6 +20,7 @@ import { TextApiResponse, VoidApiResponse, } from '../runtime/runtime'; +import { convert$0FromJSON } from '../runtime/valueConversion$0'; export type ReadDefaultRawResponse = VoidApiResponse & HttpSuccessResponse; export type ReadErrorOnlyRawResponse = VoidApiResponse & HttpSuccessResponse; @@ -272,7 +273,7 @@ export class ResponsesApi extends BaseAPI implements ResponsesApiInterface { status: response.status, raw: response, value: () => { - errorValue ??= new JSONApiResponse(errorRaw.clone(), (json) => { + errorValue ??= new JSONApiResponse(errorRaw.clone(), (json) => { return detailFromJSON(json as Detail$Wire); }).value(); return errorValue; @@ -377,7 +378,7 @@ export class ResponsesApi extends BaseAPI implements ResponsesApiInterface { status: response.status as 400, raw: response, value: () => { - errorValue ??= new JSONApiResponse(errorRaw.clone(), (json) => { + errorValue ??= new JSONApiResponse(errorRaw.clone(), (json) => { return detailFromJSON(json as Detail$Wire); }).value(); return errorValue; @@ -390,8 +391,8 @@ export class ResponsesApi extends BaseAPI implements ResponsesApiInterface { status: response.status as 401, raw: response, value: () => { - errorValue ??= new JSONApiResponse(errorRaw.clone(), (json) => { - return (json as Detail$Wire[]).map(detailFromJSON); + errorValue ??= new JSONApiResponse(errorRaw.clone(), (json) => { + return convert$0FromJSON(json as Detail$Wire[]); }).value(); return errorValue; }, @@ -436,7 +437,7 @@ export class ResponsesApi extends BaseAPI implements ResponsesApiInterface { status: response.status, raw: response, value: () => { - errorValue ??= new JSONApiResponse(errorRaw.clone(), (json) => { + errorValue ??= new JSONApiResponse(errorRaw.clone(), (json) => { return detailFromJSON(json as Detail$Wire); }).value(); return errorValue; @@ -467,7 +468,7 @@ export class ResponsesApi extends BaseAPI implements ResponsesApiInterface { return new BlobApiResponse(response) as BlobApiResponse & { status: 206 }; } else if (response.status >= 200 && response.status < 300) { return new JSONApiResponse(response, (json) => { - return (json as Detail$Wire[]).map(detailFromJSON); + return convert$0FromJSON(json as Detail$Wire[]); }) as JSONApiResponse & { status: number }; } else { return new JSONApiResponse(response) as JSONApiResponse & HttpSuccessResponse; diff --git a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-response-dispatch/runtime/valueConversion$0.ts.golden b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-response-dispatch/runtime/valueConversion$0.ts.golden new file mode 100644 index 000000000..0997d65b0 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-response-dispatch/runtime/valueConversion$0.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * TypeScript Response Dispatch — 1.0.0 + */ +import type { Detail, Detail$Wire } from '../models/Detail'; +import { detailFromJSON, detailToJSON } from '../models/Detail'; + +export type Value$0Wire = Detail$Wire[]; + +export type Value$0 = Detail[]; + +export function convert$0FromJSON(json: ReadonlyArray): Value$0 { + return json.map(detailFromJSON); +} + +export function convert$0ToJSON(value: ReadonlyArray): Value$0Wire { + return value.map(detailToJSON); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-response-only-types/apis/TestResourceApi.ts.golden b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-response-only-types/apis/TestResourceApi.ts.golden index 549b754ed..04083059a 100644 --- a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-response-only-types/apis/TestResourceApi.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-response-only-types/apis/TestResourceApi.ts.golden @@ -133,10 +133,10 @@ export class TestResourceApi extends BaseAPI implements TestResourceApiInterface requestParameters: ApiCreateTestResourceRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined) { throw new RequiredError( 'body', - 'Required parameter "body" was null or undefined when calling createTestResourceRaw().', + 'Required parameter "body" was undefined when calling createTestResourceRaw().', ); } // Build path with path parameters @@ -149,7 +149,7 @@ export class TestResourceApi extends BaseAPI implements TestResourceApiInterface }; // Prepare request body - const requestBody = requestParameters.body; + const requestBody = requestParameters['body']; // Make request const response = await this.request( { @@ -199,7 +199,7 @@ export class TestResourceApi extends BaseAPI implements TestResourceApiInterface requestParameters: ApiDeleteTestResourceRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.resourceId === undefined || requestParameters.resourceId === null) { + if (requestParameters['resourceId'] === undefined || requestParameters['resourceId'] === null) { throw new RequiredError( 'resourceId', 'Required parameter "resourceId" was null or undefined when calling deleteTestResourceRaw().', @@ -238,7 +238,7 @@ export class TestResourceApi extends BaseAPI implements TestResourceApiInterface status: response.status, raw: response, value: () => { - errorValue ??= new JSONApiResponse(errorRaw.clone(), (json) => { + errorValue ??= new JSONApiResponse(errorRaw.clone(), (json) => { return httpErrorResponseFromJSON(json as HttpErrorResponse$Wire); }).value(); return errorValue; @@ -251,7 +251,7 @@ export class TestResourceApi extends BaseAPI implements TestResourceApiInterface status: response.status, raw: response, value: () => { - errorValue ??= new JSONApiResponse(errorRaw.clone(), (json) => { + errorValue ??= new JSONApiResponse(errorRaw.clone(), (json) => { return httpErrorResponseFromJSON(json as HttpErrorResponse$Wire); }).value(); return errorValue; diff --git a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-response-only-types/models/CreateTestResourceRequest.ts.golden b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-response-only-types/models/CreateTestResourceRequest.ts.golden index 34cfa87bf..1aed88336 100644 --- a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-response-only-types/models/CreateTestResourceRequest.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-response-only-types/models/CreateTestResourceRequest.ts.golden @@ -4,6 +4,7 @@ * Test API with DELETE Response Schema — 1.0.0 * Test fixture for DELETE operations with JSON response schemas and type alias request bodies */ -/** Request to create a new test resource (type alias - no properties) */ - +/** + * Request to create a new test resource (type alias - no properties) + */ export type CreateTestResourceRequest = unknown; diff --git a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-tagged-union-plain-internal/apis/DefaultApi.ts.golden b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-tagged-union-plain-internal/apis/DefaultApi.ts.golden index 82b9761d0..ce3609273 100644 --- a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-tagged-union-plain-internal/apis/DefaultApi.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-tagged-union-plain-internal/apis/DefaultApi.ts.golden @@ -72,7 +72,7 @@ export class DefaultApi extends BaseAPI implements DefaultApiInterface { requestParameters: ApiPreviewRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling previewRaw().', @@ -88,7 +88,7 @@ export class DefaultApi extends BaseAPI implements DefaultApiInterface { }; // Prepare request body - const requestBody = previewRequestToJSON(requestParameters.body); + const requestBody = previewRequestToJSON(requestParameters['body']); // Make request const response = await this.request( { diff --git a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-tagged-union-plain-internal/models/ScheduleRule.ts.golden b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-tagged-union-plain-internal/models/ScheduleRule.ts.golden index e42161d3b..06fa3f023 100644 --- a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-tagged-union-plain-internal/models/ScheduleRule.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-tagged-union-plain-internal/models/ScheduleRule.ts.golden @@ -13,33 +13,55 @@ import type { ScheduleRuleOnce, ScheduleRuleOnce$Wire } from './ScheduleRuleOnce import { scheduleRuleOnceFromJSON, scheduleRuleOnceToJSON } from './ScheduleRuleOnce'; export type ScheduleRule$Wire = - | ({ type: 'once' } & ScheduleRuleOnce$Wire) - | ({ type: 'interval' } & ScheduleRuleInterval$Wire) - | ({ type: 'cron' } & ScheduleRuleCron$Wire); + | (Record<'type', 'once'> & ScheduleRuleOnce$Wire) + | (Record<'type', 'interval'> & ScheduleRuleInterval$Wire) + | (Record<'type', 'cron'> & ScheduleRuleCron$Wire); export type ScheduleRule = - | ({ type: 'once' } & ScheduleRuleOnce) - | ({ type: 'interval' } & ScheduleRuleInterval) - | ({ type: 'cron' } & ScheduleRuleCron); + | (Record<'type', 'once'> & ScheduleRuleOnce) + | (Record<'type', 'interval'> & ScheduleRuleInterval) + | (Record<'type', 'cron'> & ScheduleRuleCron); export function scheduleRuleFromJSON(json: ScheduleRule$Wire): ScheduleRule { - switch (json.type) { - case 'once': - return { ...scheduleRuleOnceFromJSON(json), type: 'once' }; - case 'interval': - return { ...scheduleRuleIntervalFromJSON(json), type: 'interval' }; - case 'cron': - return { ...scheduleRuleCronFromJSON(json), type: 'cron' }; + if (json['type'] === 'once') { + return { + ...scheduleRuleOnceFromJSON(json as unknown as ScheduleRuleOnce$Wire), + ['type']: 'once', + } as ScheduleRule; } + if (json['type'] === 'interval') { + return { + ...scheduleRuleIntervalFromJSON(json as unknown as ScheduleRuleInterval$Wire), + ['type']: 'interval', + } as ScheduleRule; + } + if (json['type'] === 'cron') { + return { + ...scheduleRuleCronFromJSON(json as unknown as ScheduleRuleCron$Wire), + ['type']: 'cron', + } as ScheduleRule; + } + throw new Error('Unknown variant'); } export function scheduleRuleToJSON(value: ScheduleRule): ScheduleRule$Wire { - switch (value.type) { - case 'once': - return { ...scheduleRuleOnceToJSON(value), type: 'once' } as ScheduleRule$Wire; - case 'interval': - return { ...scheduleRuleIntervalToJSON(value), type: 'interval' } as ScheduleRule$Wire; - case 'cron': - return { ...scheduleRuleCronToJSON(value), type: 'cron' } as ScheduleRule$Wire; + if (value['type'] === 'once') { + return { + ...scheduleRuleOnceToJSON(value as unknown as ScheduleRuleOnce), + ['type']: 'once', + } as ScheduleRule$Wire; + } + if (value['type'] === 'interval') { + return { + ...scheduleRuleIntervalToJSON(value as unknown as ScheduleRuleInterval), + ['type']: 'interval', + } as ScheduleRule$Wire; + } + if (value['type'] === 'cron') { + return { + ...scheduleRuleCronToJSON(value as unknown as ScheduleRuleCron), + ['type']: 'cron', + } as ScheduleRule$Wire; } + throw new Error('Unknown variant'); } diff --git a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-tagged-union-variant-with-discriminator/apis/DefaultApi.ts.golden b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-tagged-union-variant-with-discriminator/apis/DefaultApi.ts.golden index 34218d92d..bb0bfdb95 100644 --- a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-tagged-union-variant-with-discriminator/apis/DefaultApi.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-tagged-union-variant-with-discriminator/apis/DefaultApi.ts.golden @@ -74,7 +74,7 @@ export class DefaultApi extends BaseAPI implements DefaultApiInterface { requestParameters: ApiCreateSetupRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling createSetupRaw().', @@ -90,7 +90,7 @@ export class DefaultApi extends BaseAPI implements DefaultApiInterface { }; // Prepare request body - const requestBody = setupToJSON(requestParameters.body); + const requestBody = setupToJSON(requestParameters['body']); // Make request const response = await this.request( { diff --git a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-tagged-union-variant-with-discriminator/models/Setup.ts.golden b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-tagged-union-variant-with-discriminator/models/Setup.ts.golden index d80d4d95a..3605b2d82 100644 --- a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-tagged-union-variant-with-discriminator/models/Setup.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-tagged-union-variant-with-discriminator/models/Setup.ts.golden @@ -15,33 +15,55 @@ import type { SetupUnspecified, SetupUnspecified$Wire } from './SetupUnspecified import { setupUnspecifiedFromJSON, setupUnspecifiedToJSON } from './SetupUnspecified'; export type Setup$Wire = - | ({ kind: 'SETUP_UNSPECIFIED' } & SetupUnspecified$Wire) - | ({ kind: 'SETUP_AUTOMATIC' } & SetupAutomatic$Wire) - | ({ kind: 'SETUP_MANUAL' } & SetupManual$Wire); + | (Record<'kind', 'SETUP_UNSPECIFIED'> & SetupUnspecified$Wire) + | (Record<'kind', 'SETUP_AUTOMATIC'> & SetupAutomatic$Wire) + | (Record<'kind', 'SETUP_MANUAL'> & SetupManual$Wire); export type Setup = - | ({ kind: 'SETUP_UNSPECIFIED' } & SetupUnspecified) - | ({ kind: 'SETUP_AUTOMATIC' } & SetupAutomatic) - | ({ kind: 'SETUP_MANUAL' } & SetupManual); + | (Record<'kind', 'SETUP_UNSPECIFIED'> & SetupUnspecified) + | (Record<'kind', 'SETUP_AUTOMATIC'> & SetupAutomatic) + | (Record<'kind', 'SETUP_MANUAL'> & SetupManual); export function setupFromJSON(json: Setup$Wire): Setup { - switch (json.kind) { - case 'SETUP_UNSPECIFIED': - return { ...setupUnspecifiedFromJSON(json), kind: 'SETUP_UNSPECIFIED' }; - case 'SETUP_AUTOMATIC': - return { ...setupAutomaticFromJSON(json), kind: 'SETUP_AUTOMATIC' }; - case 'SETUP_MANUAL': - return { ...setupManualFromJSON(json), kind: 'SETUP_MANUAL' }; + if (json['kind'] === 'SETUP_UNSPECIFIED') { + return { + ...setupUnspecifiedFromJSON(json as unknown as SetupUnspecified$Wire), + ['kind']: 'SETUP_UNSPECIFIED', + } as Setup; } + if (json['kind'] === 'SETUP_AUTOMATIC') { + return { + ...setupAutomaticFromJSON(json as unknown as SetupAutomatic$Wire), + ['kind']: 'SETUP_AUTOMATIC', + } as Setup; + } + if (json['kind'] === 'SETUP_MANUAL') { + return { + ...setupManualFromJSON(json as unknown as SetupManual$Wire), + ['kind']: 'SETUP_MANUAL', + } as Setup; + } + throw new Error('Unknown variant'); } export function setupToJSON(value: Setup): Setup$Wire { - switch (value.kind) { - case 'SETUP_UNSPECIFIED': - return { ...setupUnspecifiedToJSON(value), kind: 'SETUP_UNSPECIFIED' } as Setup$Wire; - case 'SETUP_AUTOMATIC': - return { ...setupAutomaticToJSON(value), kind: 'SETUP_AUTOMATIC' } as Setup$Wire; - case 'SETUP_MANUAL': - return { ...setupManualToJSON(value), kind: 'SETUP_MANUAL' } as Setup$Wire; + if (value['kind'] === 'SETUP_UNSPECIFIED') { + return { + ...setupUnspecifiedToJSON(value as unknown as SetupUnspecified), + ['kind']: 'SETUP_UNSPECIFIED', + } as Setup$Wire; + } + if (value['kind'] === 'SETUP_AUTOMATIC') { + return { + ...setupAutomaticToJSON(value as unknown as SetupAutomatic), + ['kind']: 'SETUP_AUTOMATIC', + } as Setup$Wire; + } + if (value['kind'] === 'SETUP_MANUAL') { + return { + ...setupManualToJSON(value as unknown as SetupManual), + ['kind']: 'SETUP_MANUAL', + } as Setup$Wire; } + throw new Error('Unknown variant'); } diff --git a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-tagged-union/apis/DefaultApi.ts.golden b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-tagged-union/apis/DefaultApi.ts.golden index 2663e85c5..b3416c877 100644 --- a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-tagged-union/apis/DefaultApi.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-tagged-union/apis/DefaultApi.ts.golden @@ -72,7 +72,7 @@ export class DefaultApi extends BaseAPI implements DefaultApiInterface { requestParameters: ApiCreateContainerRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling createContainerRaw().', @@ -88,7 +88,7 @@ export class DefaultApi extends BaseAPI implements DefaultApiInterface { }; // Prepare request body - const requestBody = containerImageToJSON(requestParameters.body); + const requestBody = containerImageToJSON(requestParameters['body']); // Make request const response = await this.request( { diff --git a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-tagged-union/models/ContainerImage.ts.golden b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-tagged-union/models/ContainerImage.ts.golden index 4a3fb4001..e263d3d4c 100644 --- a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-tagged-union/models/ContainerImage.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-tagged-union/models/ContainerImage.ts.golden @@ -14,33 +14,41 @@ import { } from './ContainerImageManaged'; export type ContainerImage$Wire = - | ({ kind: 'CONTAINER_IMAGE_MANAGED' } & ContainerImageManaged$Wire) - | ({ kind: 'CONTAINER_IMAGE_CUSTOM' } & ContainerImageCustom$Wire); + | (Record<'kind', 'CONTAINER_IMAGE_MANAGED'> & ContainerImageManaged$Wire) + | (Record<'kind', 'CONTAINER_IMAGE_CUSTOM'> & ContainerImageCustom$Wire); export type ContainerImage = - | ({ kind: 'CONTAINER_IMAGE_MANAGED' } & ContainerImageManaged) - | ({ kind: 'CONTAINER_IMAGE_CUSTOM' } & ContainerImageCustom); + | (Record<'kind', 'CONTAINER_IMAGE_MANAGED'> & ContainerImageManaged) + | (Record<'kind', 'CONTAINER_IMAGE_CUSTOM'> & ContainerImageCustom); export function containerImageFromJSON(json: ContainerImage$Wire): ContainerImage { - switch (json.kind) { - case 'CONTAINER_IMAGE_MANAGED': - return { ...containerImageManagedFromJSON(json), kind: 'CONTAINER_IMAGE_MANAGED' }; - case 'CONTAINER_IMAGE_CUSTOM': - return { ...containerImageCustomFromJSON(json), kind: 'CONTAINER_IMAGE_CUSTOM' }; + if (json['kind'] === 'CONTAINER_IMAGE_MANAGED') { + return { + ...containerImageManagedFromJSON(json as unknown as ContainerImageManaged$Wire), + ['kind']: 'CONTAINER_IMAGE_MANAGED', + } as ContainerImage; } + if (json['kind'] === 'CONTAINER_IMAGE_CUSTOM') { + return { + ...containerImageCustomFromJSON(json as unknown as ContainerImageCustom$Wire), + ['kind']: 'CONTAINER_IMAGE_CUSTOM', + } as ContainerImage; + } + throw new Error('Unknown variant'); } export function containerImageToJSON(value: ContainerImage): ContainerImage$Wire { - switch (value.kind) { - case 'CONTAINER_IMAGE_MANAGED': - return { - ...containerImageManagedToJSON(value), - kind: 'CONTAINER_IMAGE_MANAGED', - } as ContainerImage$Wire; - case 'CONTAINER_IMAGE_CUSTOM': - return { - ...containerImageCustomToJSON(value), - kind: 'CONTAINER_IMAGE_CUSTOM', - } as ContainerImage$Wire; + if (value['kind'] === 'CONTAINER_IMAGE_MANAGED') { + return { + ...containerImageManagedToJSON(value as unknown as ContainerImageManaged), + ['kind']: 'CONTAINER_IMAGE_MANAGED', + } as ContainerImage$Wire; + } + if (value['kind'] === 'CONTAINER_IMAGE_CUSTOM') { + return { + ...containerImageCustomToJSON(value as unknown as ContainerImageCustom), + ['kind']: 'CONTAINER_IMAGE_CUSTOM', + } as ContainerImage$Wire; } + throw new Error('Unknown variant'); } diff --git a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-union/apis/DefaultApi.ts.golden b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-union/apis/DefaultApi.ts.golden index 69b808293..45b1789a0 100644 --- a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-union/apis/DefaultApi.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-union/apis/DefaultApi.ts.golden @@ -71,7 +71,7 @@ export class DefaultApi extends BaseAPI implements DefaultApiInterface { requestParameters: ApiTestUnionRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling testUnionRaw().', @@ -87,7 +87,7 @@ export class DefaultApi extends BaseAPI implements DefaultApiInterface { }; // Prepare request body - const requestBody = fooToJSON(requestParameters.body); + const requestBody = fooToJSON(requestParameters['body']); // Make request const response = await this.request( { diff --git a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-union/models/Foo.ts.golden b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-union/models/Foo.ts.golden index f5fd17d2e..bc31a5b27 100644 --- a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-union/models/Foo.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-union/models/Foo.ts.golden @@ -4,18 +4,49 @@ * Union Type with Interfaces Test — 1.0.0 * Test fixture for oneOf/anyOf union types with interface members */ +import { + matches$0FromJSON, + matches$0ToJSON, + matches$3FromJSON, + matches$3ToJSON, + matches$5FromJSON, + matches$5ToJSON, +} from '../runtime/unionMatch'; import type { Bar, Bar$Wire } from './Bar'; +import { barFromJSON, barToJSON } from './Bar'; import type { Baz, Baz$Wire } from './Baz'; +import { bazFromJSON, bazToJSON } from './Baz'; import type { Qux, Qux$Wire } from './Qux'; +import { quxFromJSON, quxToJSON } from './Qux'; export type Foo$Wire = Bar$Wire | Baz$Wire | Qux$Wire; export type Foo = Bar | Baz | Qux; export function fooFromJSON(json: Foo$Wire): Foo { + const candidate: unknown = json; + if (matches$0FromJSON(candidate)) { + return barFromJSON(json as unknown as Bar$Wire); + } + if (matches$3FromJSON(candidate)) { + return bazFromJSON(json as unknown as Baz$Wire); + } + if (matches$5FromJSON(candidate)) { + return quxFromJSON(json as unknown as Qux$Wire); + } return json as unknown as Foo; } export function fooToJSON(value: Foo): Foo$Wire { + const candidate: unknown = value; + if (matches$0ToJSON(candidate)) { + return barToJSON(value as unknown as Bar); + } + if (matches$3ToJSON(candidate)) { + return bazToJSON(value as unknown as Baz); + } + if (matches$5ToJSON(candidate)) { + return quxToJSON(value as unknown as Qux); + } return value as unknown as Foo$Wire; } diff --git a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-union/runtime/unionMatch.ts.golden b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-union/runtime/unionMatch.ts.golden new file mode 100644 index 000000000..4e11e8936 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case-union/runtime/unionMatch.ts.golden @@ -0,0 +1,181 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Union Type with Interfaces Test — 1.0.0 + * Test fixture for oneOf/anyOf union types with interface members + */ +export function matches$0FromJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as Record; + if (!Object.prototype.hasOwnProperty.call(candidate, 'bucket')) { + return false; + } + if (!matches$1FromJSON(object['bucket'])) { + return false; + } + if (!Object.prototype.hasOwnProperty.call(candidate, 'endpoint')) { + return false; + } + if (!matches$1FromJSON(object['endpoint'])) { + return false; + } + if (!Object.prototype.hasOwnProperty.call(candidate, 'type')) { + return false; + } + if (!matches$2FromJSON(object['type'])) { + return false; + } + return true; +} + +export function matches$0ToJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as Record; + if (!Object.prototype.hasOwnProperty.call(candidate, 'bucket')) { + return false; + } + if (!matches$1ToJSON(object['bucket'])) { + return false; + } + if (!Object.prototype.hasOwnProperty.call(candidate, 'endpoint')) { + return false; + } + if (!matches$1ToJSON(object['endpoint'])) { + return false; + } + if (!Object.prototype.hasOwnProperty.call(candidate, 'type')) { + return false; + } + if (!matches$2ToJSON(object['type'])) { + return false; + } + return true; +} + +export function matches$1FromJSON(candidate: unknown): boolean { + return typeof candidate === 'string'; +} + +export function matches$1ToJSON(candidate: unknown): boolean { + return typeof candidate === 'string'; +} + +export function matches$2FromJSON(candidate: unknown): boolean { + return candidate === 'bar'; +} + +export function matches$2ToJSON(candidate: unknown): boolean { + return candidate === 'bar'; +} + +export function matches$3FromJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as Record; + if (!Object.prototype.hasOwnProperty.call(candidate, 'path')) { + return false; + } + if (!matches$1FromJSON(object['path'])) { + return false; + } + if (!Object.prototype.hasOwnProperty.call(candidate, 'type')) { + return false; + } + if (!matches$4FromJSON(object['type'])) { + return false; + } + return true; +} + +export function matches$3ToJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as Record; + if (!Object.prototype.hasOwnProperty.call(candidate, 'path')) { + return false; + } + if (!matches$1ToJSON(object['path'])) { + return false; + } + if (!Object.prototype.hasOwnProperty.call(candidate, 'type')) { + return false; + } + if (!matches$4ToJSON(object['type'])) { + return false; + } + return true; +} + +export function matches$4FromJSON(candidate: unknown): boolean { + return candidate === 'baz'; +} + +export function matches$4ToJSON(candidate: unknown): boolean { + return candidate === 'baz'; +} + +export function matches$5FromJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as Record; + if (!Object.prototype.hasOwnProperty.call(candidate, 'namespace')) { + return false; + } + if (!matches$1FromJSON(object['namespace'])) { + return false; + } + if (!Object.prototype.hasOwnProperty.call(candidate, 'type')) { + return false; + } + if (!matches$6FromJSON(object['type'])) { + return false; + } + if (!Object.prototype.hasOwnProperty.call(candidate, 'url')) { + return false; + } + if (!matches$1FromJSON(object['url'])) { + return false; + } + return true; +} + +export function matches$5ToJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as Record; + if (!Object.prototype.hasOwnProperty.call(candidate, 'namespace')) { + return false; + } + if (!matches$1ToJSON(object['namespace'])) { + return false; + } + if (!Object.prototype.hasOwnProperty.call(candidate, 'type')) { + return false; + } + if (!matches$6ToJSON(object['type'])) { + return false; + } + if (!Object.prototype.hasOwnProperty.call(candidate, 'url')) { + return false; + } + if (!matches$1ToJSON(object['url'])) { + return false; + } + return true; +} + +export function matches$6FromJSON(candidate: unknown): boolean { + return candidate === 'qux'; +} + +export function matches$6ToJSON(candidate: unknown): boolean { + return candidate === 'qux'; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case/apis/DefaultApi.ts.golden b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case/apis/DefaultApi.ts.golden index 33d4b3bd1..1b8700ef6 100644 --- a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case/apis/DefaultApi.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case/apis/DefaultApi.ts.golden @@ -124,7 +124,7 @@ export class DefaultApi extends BaseAPI implements DefaultApiInterface { requestParameters: ApiGetWithQueryParamsRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.snakeCaseParam === undefined || requestParameters.snakeCaseParam === null) { + if (requestParameters['snakeCaseParam'] === undefined || requestParameters['snakeCaseParam'] === null) { throw new RequiredError( 'snakeCaseParam', 'Required parameter "snakeCaseParam" was null or undefined when calling getWithQueryParamsRaw().', @@ -220,7 +220,7 @@ export class DefaultApi extends BaseAPI implements DefaultApiInterface { requestParameters: ApiTestNamingConventionsRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling testNamingConventionsRaw().', @@ -236,7 +236,7 @@ export class DefaultApi extends BaseAPI implements DefaultApiInterface { }; // Prepare request body - const requestBody = namingConventionTestToJSON(requestParameters.body); + const requestBody = namingConventionTestToJSON(requestParameters['body']); // Make request const response = await this.request( { diff --git a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case/models/NamingConventionTest.ts.golden b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case/models/NamingConventionTest.ts.golden index 818e52eb3..0b0586198 100644 --- a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case/models/NamingConventionTest.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case/models/NamingConventionTest.ts.golden @@ -4,14 +4,11 @@ * Naming Conventions Test — 1.0.0 * Test fixture to verify language property naming conventions */ +import { convert$0FromJSON, convert$0ToJSON } from '../runtime/valueConversion$0'; import type { NamingConventionTestItemsArrayItem, NamingConventionTestItemsArrayItem$Wire, } from './NamingConventionTestItemsArrayItem'; -import { - namingConventionTestItemsArrayItemFromJSON, - namingConventionTestItemsArrayItemToJSON, -} from './NamingConventionTestItemsArrayItem'; import type { NamingConventionTestNestedObject, NamingConventionTestNestedObject$Wire, @@ -141,8 +138,7 @@ export function namingConventionTestFromJSON( camelCaseField: json.camelCaseField, camelCaseNumber: json.camelCaseNumber, id: json.id, - itemsArray: - json.items_array !== undefined ? json.items_array.map((item) => namingConventionTestItemsArrayItemFromJSON(item)) : undefined, + itemsArray: json.items_array !== undefined ? convert$0FromJSON(json.items_array) : undefined, kebabCaseArray: json['kebab-case-array'], kebabCaseField: json['kebab-case-field'], nestedObject: @@ -163,8 +159,7 @@ export function namingConventionTestToJSON(value: NamingConventionTest): NamingC camelCaseField: value.camelCaseField, camelCaseNumber: value.camelCaseNumber, id: value.id, - items_array: - value.itemsArray !== undefined ? value.itemsArray.map((item) => namingConventionTestItemsArrayItemToJSON(item)) : undefined, + items_array: value.itemsArray !== undefined ? convert$0ToJSON(value.itemsArray) : undefined, 'kebab-case-array': value.kebabCaseArray, 'kebab-case-field': value.kebabCaseField, nested_object: diff --git a/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case/runtime/valueConversion$0.ts.golden b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case/runtime/valueConversion$0.ts.golden new file mode 100644 index 000000000..6c72dff63 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-property-naming-camel-case/runtime/valueConversion$0.ts.golden @@ -0,0 +1,30 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Naming Conventions Test — 1.0.0 + * Test fixture to verify language property naming conventions + */ +import type { + NamingConventionTestItemsArrayItem, + NamingConventionTestItemsArrayItem$Wire, +} from '../models/NamingConventionTestItemsArrayItem'; +import { + namingConventionTestItemsArrayItemFromJSON, + namingConventionTestItemsArrayItemToJSON, +} from '../models/NamingConventionTestItemsArrayItem'; + +export type Value$0Wire = readonly NamingConventionTestItemsArrayItem$Wire[]; + +export type Value$0 = readonly NamingConventionTestItemsArrayItem[]; + +export function convert$0FromJSON( + json: ReadonlyArray, +): Value$0 { + return json.map(namingConventionTestItemsArrayItemFromJSON); +} + +export function convert$0ToJSON( + value: ReadonlyArray, +): Value$0Wire { + return value.map(namingConventionTestItemsArrayItemToJSON); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-camel-case-tagged-nullable-unions/apis/DefaultApi.ts.golden b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-camel-case-tagged-nullable-unions/apis/DefaultApi.ts.golden index f77f02ace..70c9289e4 100644 --- a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-camel-case-tagged-nullable-unions/apis/DefaultApi.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-camel-case-tagged-nullable-unions/apis/DefaultApi.ts.golden @@ -71,7 +71,7 @@ export class DefaultApi extends BaseAPI implements DefaultApiInterface { requestParameters: ApiCreateFooEnvelopeRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling createFooEnvelopeRaw().', @@ -87,7 +87,7 @@ export class DefaultApi extends BaseAPI implements DefaultApiInterface { }; // Prepare request body - const requestBody = fooEnvelopeToJSON(requestParameters.body); + const requestBody = fooEnvelopeToJSON(requestParameters['body']); // Make request const response = await this.request( { diff --git a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-camel-case-tagged-nullable-unions/models/FooAdjacentPayload.ts.golden b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-camel-case-tagged-nullable-unions/models/FooAdjacentPayload.ts.golden index 919a7370d..bcab6cd5b 100644 --- a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-camel-case-tagged-nullable-unions/models/FooAdjacentPayload.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-camel-case-tagged-nullable-unions/models/FooAdjacentPayload.ts.golden @@ -4,6 +4,7 @@ * TypeScript Fetch VP CamelCase Masked Regression Test — 1.0.0 * Masked fixture for TypeScript Fetch camelCase regression coverage. */ +import { convert$0FromJSON, convert$0ToJSON } from '../runtime/valueConversion$0'; import type { FooAdjacentPayloadFooInlineObjectPayloadValue, FooAdjacentPayloadFooInlineObjectPayloadValue$Wire, @@ -13,75 +14,98 @@ import { fooAdjacentPayloadFooInlineObjectPayloadValueToJSON, } from './FooAdjacentPayloadFooInlineObjectPayloadValue'; import type { FooArrayPart, FooArrayPart$Wire } from './FooArrayPart'; -import { fooArrayPartFromJSON, fooArrayPartToJSON } from './FooArrayPart'; export type FooAdjacentPayload$Wire = - | { type: 'foo_inline_none' } - | { type: 'foo_inline_text'; payload_value: string } - | { type: 'foo_inline_object'; payload_value: FooAdjacentPayloadFooInlineObjectPayloadValue$Wire } - | { type: 'foo_inline_list'; payload_value: readonly FooArrayPart$Wire[] }; + | Record<'type', 'foo_inline_none'> + | (Record<'type', 'foo_inline_text'> & Record<'payload_value', string>) + | (Record<'type', 'foo_inline_object'> & + Record<'payload_value', FooAdjacentPayloadFooInlineObjectPayloadValue$Wire>) + | (Record<'type', 'foo_inline_list'> & Record<'payload_value', readonly FooArrayPart$Wire[]>); export type FooAdjacentPayload = - | { type: 'foo_inline_none' } - | { type: 'foo_inline_text'; payloadValue: string } - | { type: 'foo_inline_object'; payloadValue: FooAdjacentPayloadFooInlineObjectPayloadValue } - | { type: 'foo_inline_list'; payloadValue: readonly FooArrayPart[] }; + | Record<'type', 'foo_inline_none'> + | (Record<'type', 'foo_inline_text'> & Record<'payloadValue', string>) + | (Record<'type', 'foo_inline_object'> & + Record<'payloadValue', FooAdjacentPayloadFooInlineObjectPayloadValue>) + | (Record<'type', 'foo_inline_list'> & Record<'payloadValue', readonly FooArrayPart[]>); export function fooAdjacentPayloadFromJSON(json: FooAdjacentPayload$Wire): FooAdjacentPayload { - switch (json.type) { - case 'foo_inline_none': - return { type: 'foo_inline_none' }; - case 'foo_inline_text': - return { type: 'foo_inline_text', payloadValue: json.payload_value }; - case 'foo_inline_object': - return { - type: 'foo_inline_object', - payloadValue: fooAdjacentPayloadFooInlineObjectPayloadValueFromJSON(json.payload_value), - }; - case 'foo_inline_list': - return { - type: 'foo_inline_list', - payloadValue: json.payload_value.map((item) => fooArrayPartFromJSON(item)), - }; + if (json['type'] === 'foo_inline_none') { + return { ['type']: 'foo_inline_none' } as FooAdjacentPayload; } + if (json['type'] === 'foo_inline_text') { + return { + ['type']: 'foo_inline_text', + ['payloadValue']: (json as Record<'payload_value', string>)['payload_value'], + } as FooAdjacentPayload; + } + if (json['type'] === 'foo_inline_object') { + return { + ['type']: 'foo_inline_object', + ['payloadValue']: fooAdjacentPayloadFooInlineObjectPayloadValueFromJSON( + (json as Record<'payload_value', FooAdjacentPayloadFooInlineObjectPayloadValue$Wire>)[ + 'payload_value' + ], + ), + } as FooAdjacentPayload; + } + if (json['type'] === 'foo_inline_list') { + return { + ['type']: 'foo_inline_list', + ['payloadValue']: convert$0FromJSON( + (json as Record<'payload_value', readonly FooArrayPart$Wire[]>)['payload_value'], + ), + } as FooAdjacentPayload; + } + throw new Error('Unknown variant'); } export function fooAdjacentPayloadToJSON(value: FooAdjacentPayload): FooAdjacentPayload$Wire { - switch (value.type) { - case 'foo_inline_none': - return { type: 'foo_inline_none' }; - case 'foo_inline_text': - return { type: 'foo_inline_text', payload_value: value.payloadValue }; - case 'foo_inline_object': - return { - type: 'foo_inline_object', - payload_value: fooAdjacentPayloadFooInlineObjectPayloadValueToJSON(value.payloadValue), - }; - case 'foo_inline_list': - return { - type: 'foo_inline_list', - payload_value: value.payloadValue.map((item) => fooArrayPartToJSON(item)), - }; + if (value['type'] === 'foo_inline_none') { + return { ['type']: 'foo_inline_none' } as FooAdjacentPayload$Wire; + } + if (value['type'] === 'foo_inline_text') { + return { + ['type']: 'foo_inline_text', + ['payload_value']: (value as Record<'payloadValue', string>)['payloadValue'], + } as FooAdjacentPayload$Wire; + } + if (value['type'] === 'foo_inline_object') { + return { + ['type']: 'foo_inline_object', + ['payload_value']: fooAdjacentPayloadFooInlineObjectPayloadValueToJSON( + (value as Record<'payloadValue', FooAdjacentPayloadFooInlineObjectPayloadValue>)[ + 'payloadValue' + ], + ), + } as FooAdjacentPayload$Wire; + } + if (value['type'] === 'foo_inline_list') { + return { + ['type']: 'foo_inline_list', + ['payload_value']: convert$0ToJSON( + (value as Record<'payloadValue', readonly FooArrayPart[]>)['payloadValue'], + ), + } as FooAdjacentPayload$Wire; } + throw new Error('Unknown variant'); } -export function isFooInlineText(value: FooAdjacentPayload): value is { - type: 'foo_inline_text'; - payloadValue: string; -} { - return value.type === 'foo_inline_text'; +export function isFooInlineText( + value: FooAdjacentPayload, +): value is Record<'type', 'foo_inline_text'> & Record<'payloadValue', string> { + return value['type'] === 'foo_inline_text'; } -export function isFooInlineObject(value: FooAdjacentPayload): value is { - type: 'foo_inline_object'; - payloadValue: FooAdjacentPayloadFooInlineObjectPayloadValue; -} { - return value.type === 'foo_inline_object'; +export function isFooInlineObject( + value: FooAdjacentPayload, +): value is Record<'type', 'foo_inline_object'> & + Record<'payloadValue', FooAdjacentPayloadFooInlineObjectPayloadValue> { + return value['type'] === 'foo_inline_object'; } -export function isFooInlineList(value: FooAdjacentPayload): value is { - type: 'foo_inline_list'; - payloadValue: readonly FooArrayPart[]; -} { - return value.type === 'foo_inline_list'; +export function isFooInlineList( + value: FooAdjacentPayload, +): value is Record<'type', 'foo_inline_list'> & Record<'payloadValue', readonly FooArrayPart[]> { + return value['type'] === 'foo_inline_list'; } diff --git a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-camel-case-tagged-nullable-unions/models/FooAnyValue.ts.golden b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-camel-case-tagged-nullable-unions/models/FooAnyValue.ts.golden index 2fe4c9c2e..cb583c44c 100644 --- a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-camel-case-tagged-nullable-unions/models/FooAnyValue.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-camel-case-tagged-nullable-unions/models/FooAnyValue.ts.golden @@ -4,4 +4,16 @@ * TypeScript Fetch VP CamelCase Masked Regression Test — 1.0.0 * Masked fixture for TypeScript Fetch camelCase regression coverage. */ +import { convert$1FromJSON, convert$1ToJSON } from '../runtime/valueConversion$1'; + +export type FooAnyValue$Wire = unknown; + export type FooAnyValue = unknown; + +export function fooAnyValueFromJSON(json: FooAnyValue$Wire): FooAnyValue { + return convert$1FromJSON(json); +} + +export function fooAnyValueToJSON(value: FooAnyValue): FooAnyValue$Wire { + return convert$1ToJSON(value); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-camel-case-tagged-nullable-unions/models/FooEnvelope.ts.golden b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-camel-case-tagged-nullable-unions/models/FooEnvelope.ts.golden index 5b4446cdf..3db909eae 100644 --- a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-camel-case-tagged-nullable-unions/models/FooEnvelope.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-camel-case-tagged-nullable-unions/models/FooEnvelope.ts.golden @@ -6,7 +6,8 @@ */ import type { FooAdjacentPayload, FooAdjacentPayload$Wire } from './FooAdjacentPayload'; import { fooAdjacentPayloadFromJSON, fooAdjacentPayloadToJSON } from './FooAdjacentPayload'; -import type { FooAnyValue } from './FooAnyValue'; +import type { FooAnyValue, FooAnyValue$Wire } from './FooAnyValue'; +import { fooAnyValueFromJSON, fooAnyValueToJSON } from './FooAnyValue'; import type { FooMappedTagUnion, FooMappedTagUnion$Wire } from './FooMappedTagUnion'; import { fooMappedTagUnionFromJSON, fooMappedTagUnionToJSON } from './FooMappedTagUnion'; import type { FooNullableRefHolder, FooNullableRefHolder$Wire } from './FooNullableRefHolder'; @@ -20,7 +21,7 @@ import { fooUnionContentFromJSON, fooUnionContentToJSON } from './FooUnionConten export interface FooEnvelope$Wire { readonly adjacent_payload: FooAdjacentPayload$Wire; - readonly any_value: FooAnyValue; + readonly any_value: FooAnyValue$Wire; readonly mapped_tag_union: FooMappedTagUnion$Wire; readonly nullable_ref_holder: FooNullableRefHolder$Wire; readonly tag_only_union: FooTagOnlyUnion$Wire; @@ -41,7 +42,7 @@ export interface FooEnvelope { export function fooEnvelopeFromJSON(json: FooEnvelope$Wire): FooEnvelope { return { adjacentPayload: fooAdjacentPayloadFromJSON(json.adjacent_payload), - anyValue: json.any_value, + anyValue: fooAnyValueFromJSON(json.any_value), mappedTagUnion: fooMappedTagUnionFromJSON(json.mapped_tag_union), nullableRefHolder: fooNullableRefHolderFromJSON(json.nullable_ref_holder), tagOnlyUnion: fooTagOnlyUnionFromJSON(json.tag_only_union), @@ -53,7 +54,7 @@ export function fooEnvelopeFromJSON(json: FooEnvelope$Wire): FooEnvelope { export function fooEnvelopeToJSON(value: FooEnvelope): FooEnvelope$Wire { return { adjacent_payload: fooAdjacentPayloadToJSON(value.adjacentPayload), - any_value: value.anyValue, + any_value: fooAnyValueToJSON(value.anyValue), mapped_tag_union: fooMappedTagUnionToJSON(value.mappedTagUnion), nullable_ref_holder: fooNullableRefHolderToJSON(value.nullableRefHolder), tag_only_union: fooTagOnlyUnionToJSON(value.tagOnlyUnion), diff --git a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-camel-case-tagged-nullable-unions/models/FooMappedTagUnion.ts.golden b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-camel-case-tagged-nullable-unions/models/FooMappedTagUnion.ts.golden index 4a1e9d909..a74e6455a 100644 --- a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-camel-case-tagged-nullable-unions/models/FooMappedTagUnion.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-camel-case-tagged-nullable-unions/models/FooMappedTagUnion.ts.golden @@ -8,33 +8,41 @@ import type { FooOpenVariant, FooOpenVariant$Wire } from './FooOpenVariant'; import { fooOpenVariantFromJSON, fooOpenVariantToJSON } from './FooOpenVariant'; export type FooMappedTagUnion$Wire = - | { type: 'foo_closed_variant' } - | ({ type: 'foo_open_variant' } & FooOpenVariant$Wire); + | Record<'type', 'foo_closed_variant'> + | (Record<'type', 'foo_open_variant'> & FooOpenVariant$Wire); export type FooMappedTagUnion = - | { type: 'foo_closed_variant' } - | ({ type: 'foo_open_variant' } & FooOpenVariant); + | Record<'type', 'foo_closed_variant'> + | (Record<'type', 'foo_open_variant'> & FooOpenVariant); export function fooMappedTagUnionFromJSON(json: FooMappedTagUnion$Wire): FooMappedTagUnion { - switch (json.type) { - case 'foo_closed_variant': - return { type: 'foo_closed_variant' }; - case 'foo_open_variant': - return { ...fooOpenVariantFromJSON(json), type: 'foo_open_variant' }; + if (json['type'] === 'foo_closed_variant') { + return { ['type']: 'foo_closed_variant' } as FooMappedTagUnion; } + if (json['type'] === 'foo_open_variant') { + return { + ...fooOpenVariantFromJSON(json as unknown as FooOpenVariant$Wire), + ['type']: 'foo_open_variant', + } as FooMappedTagUnion; + } + throw new Error('Unknown variant'); } export function fooMappedTagUnionToJSON(value: FooMappedTagUnion): FooMappedTagUnion$Wire { - switch (value.type) { - case 'foo_closed_variant': - return { type: 'foo_closed_variant' } as FooMappedTagUnion$Wire; - case 'foo_open_variant': - return { ...fooOpenVariantToJSON(value), type: 'foo_open_variant' } as FooMappedTagUnion$Wire; + if (value['type'] === 'foo_closed_variant') { + return { ['type']: 'foo_closed_variant' } as FooMappedTagUnion$Wire; + } + if (value['type'] === 'foo_open_variant') { + return { + ...fooOpenVariantToJSON(value as unknown as FooOpenVariant), + ['type']: 'foo_open_variant', + } as FooMappedTagUnion$Wire; } + throw new Error('Unknown variant'); } -export function isFooOpenVariant(value: FooMappedTagUnion): value is { - type: 'foo_open_variant'; -} & FooOpenVariant { - return value.type === 'foo_open_variant'; +export function isFooOpenVariant( + value: FooMappedTagUnion, +): value is Record<'type', 'foo_open_variant'> & FooOpenVariant { + return value['type'] === 'foo_open_variant'; } diff --git a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-camel-case-tagged-nullable-unions/models/FooNullableItems.ts.golden b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-camel-case-tagged-nullable-unions/models/FooNullableItems.ts.golden index 667268bd5..901a396df 100644 --- a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-camel-case-tagged-nullable-unions/models/FooNullableItems.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-camel-case-tagged-nullable-unions/models/FooNullableItems.ts.golden @@ -4,8 +4,8 @@ * TypeScript Fetch VP CamelCase Masked Regression Test — 1.0.0 * Masked fixture for TypeScript Fetch camelCase regression coverage. */ +import { convert$3FromJSON, convert$3ToJSON } from '../runtime/valueConversion$3'; import type { FooNullableItem, FooNullableItem$Wire } from './FooNullableItem'; -import { fooNullableItemFromJSON, fooNullableItemToJSON } from './FooNullableItem'; export interface FooNullableItems$Wire { readonly content: readonly FooNullableItem$Wire[] | null; @@ -17,14 +17,12 @@ export interface FooNullableItems { export function fooNullableItemsFromJSON(json: FooNullableItems$Wire): FooNullableItems { return { - content: - json.content === null ? null : json.content.map((item) => fooNullableItemFromJSON(item)), + content: json.content === null ? null : convert$3FromJSON(json.content), }; } export function fooNullableItemsToJSON(value: FooNullableItems): FooNullableItems$Wire { return { - content: - value.content === null ? null : value.content.map((item) => fooNullableItemToJSON(item)), + content: value.content === null ? null : convert$3ToJSON(value.content), }; } diff --git a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-camel-case-tagged-nullable-unions/models/FooTagOnlyUnion.ts.golden b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-camel-case-tagged-nullable-unions/models/FooTagOnlyUnion.ts.golden index f47cbd138..848daee21 100644 --- a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-camel-case-tagged-nullable-unions/models/FooTagOnlyUnion.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-camel-case-tagged-nullable-unions/models/FooTagOnlyUnion.ts.golden @@ -8,46 +8,53 @@ import type { FooObjectPayload, FooObjectPayload$Wire } from './FooObjectPayload import { fooObjectPayloadFromJSON, fooObjectPayloadToJSON } from './FooObjectPayload'; export type FooTagOnlyUnion$Wire = - | { type: 'foo_unit_alpha' } - | { type: 'foo_unit_beta' } - | { type: 'foo_object_payload'; object_payload: FooObjectPayload$Wire }; + | Record<'type', 'foo_unit_alpha'> + | Record<'type', 'foo_unit_beta'> + | (Record<'type', 'foo_object_payload'> & Record<'object_payload', FooObjectPayload$Wire>); export type FooTagOnlyUnion = - | { type: 'foo_unit_alpha' } - | { type: 'foo_unit_beta' } - | { type: 'foo_object_payload'; objectPayload: FooObjectPayload }; + | Record<'type', 'foo_unit_alpha'> + | Record<'type', 'foo_unit_beta'> + | (Record<'type', 'foo_object_payload'> & Record<'objectPayload', FooObjectPayload>); export function fooTagOnlyUnionFromJSON(json: FooTagOnlyUnion$Wire): FooTagOnlyUnion { - switch (json.type) { - case 'foo_unit_alpha': - return { type: 'foo_unit_alpha' }; - case 'foo_unit_beta': - return { type: 'foo_unit_beta' }; - case 'foo_object_payload': - return { - type: 'foo_object_payload', - objectPayload: fooObjectPayloadFromJSON(json.object_payload), - }; + if (json['type'] === 'foo_unit_alpha') { + return { ['type']: 'foo_unit_alpha' } as FooTagOnlyUnion; } + if (json['type'] === 'foo_unit_beta') { + return { ['type']: 'foo_unit_beta' } as FooTagOnlyUnion; + } + if (json['type'] === 'foo_object_payload') { + return { + ['type']: 'foo_object_payload', + ['objectPayload']: fooObjectPayloadFromJSON( + (json as Record<'object_payload', FooObjectPayload$Wire>)['object_payload'], + ), + } as FooTagOnlyUnion; + } + throw new Error('Unknown variant'); } export function fooTagOnlyUnionToJSON(value: FooTagOnlyUnion): FooTagOnlyUnion$Wire { - switch (value.type) { - case 'foo_unit_alpha': - return { type: 'foo_unit_alpha' }; - case 'foo_unit_beta': - return { type: 'foo_unit_beta' }; - case 'foo_object_payload': - return { - type: 'foo_object_payload', - object_payload: fooObjectPayloadToJSON(value.objectPayload), - }; + if (value['type'] === 'foo_unit_alpha') { + return { ['type']: 'foo_unit_alpha' } as FooTagOnlyUnion$Wire; + } + if (value['type'] === 'foo_unit_beta') { + return { ['type']: 'foo_unit_beta' } as FooTagOnlyUnion$Wire; + } + if (value['type'] === 'foo_object_payload') { + return { + ['type']: 'foo_object_payload', + ['object_payload']: fooObjectPayloadToJSON( + (value as Record<'objectPayload', FooObjectPayload>)['objectPayload'], + ), + } as FooTagOnlyUnion$Wire; } + throw new Error('Unknown variant'); } -export function isFooObjectPayload(value: FooTagOnlyUnion): value is { - type: 'foo_object_payload'; - objectPayload: FooObjectPayload; -} { - return value.type === 'foo_object_payload'; +export function isFooObjectPayload( + value: FooTagOnlyUnion, +): value is Record<'type', 'foo_object_payload'> & Record<'objectPayload', FooObjectPayload> { + return value['type'] === 'foo_object_payload'; } diff --git a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-camel-case-tagged-nullable-unions/models/FooTaggedRef.ts.golden b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-camel-case-tagged-nullable-unions/models/FooTaggedRef.ts.golden index ebd0ad992..049c1f0e7 100644 --- a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-camel-case-tagged-nullable-unions/models/FooTaggedRef.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-camel-case-tagged-nullable-unions/models/FooTaggedRef.ts.golden @@ -7,27 +7,37 @@ import type { FooPayloadBody, FooPayloadBody$Wire } from './FooPayloadBody'; import { fooPayloadBodyFromJSON, fooPayloadBodyToJSON } from './FooPayloadBody'; -export type FooTaggedRef$Wire = { type: 'foo_ref_payload'; bar_payload: FooPayloadBody$Wire }; +export type FooTaggedRef$Wire = Record<'type', 'foo_ref_payload'> & + Record<'bar_payload', FooPayloadBody$Wire>; -export type FooTaggedRef = { type: 'foo_ref_payload'; barPayload: FooPayloadBody }; +export type FooTaggedRef = Record<'type', 'foo_ref_payload'> & Record<'barPayload', FooPayloadBody>; export function fooTaggedRefFromJSON(json: FooTaggedRef$Wire): FooTaggedRef { - switch (json.type) { - case 'foo_ref_payload': - return { type: 'foo_ref_payload', barPayload: fooPayloadBodyFromJSON(json.bar_payload) }; + if (json['type'] === 'foo_ref_payload') { + return { + ['type']: 'foo_ref_payload', + ['barPayload']: fooPayloadBodyFromJSON( + (json as Record<'bar_payload', FooPayloadBody$Wire>)['bar_payload'], + ), + } as FooTaggedRef; } + throw new Error('Unknown variant'); } export function fooTaggedRefToJSON(value: FooTaggedRef): FooTaggedRef$Wire { - switch (value.type) { - case 'foo_ref_payload': - return { type: 'foo_ref_payload', bar_payload: fooPayloadBodyToJSON(value.barPayload) }; + if (value['type'] === 'foo_ref_payload') { + return { + ['type']: 'foo_ref_payload', + ['bar_payload']: fooPayloadBodyToJSON( + (value as Record<'barPayload', FooPayloadBody>)['barPayload'], + ), + } as FooTaggedRef$Wire; } + throw new Error('Unknown variant'); } -export function isFooRefPayload(value: FooTaggedRef): value is { - type: 'foo_ref_payload'; - barPayload: FooPayloadBody; -} { - return value.type === 'foo_ref_payload'; +export function isFooRefPayload( + value: FooTaggedRef, +): value is Record<'type', 'foo_ref_payload'> & Record<'barPayload', FooPayloadBody> { + return value['type'] === 'foo_ref_payload'; } diff --git a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-camel-case-tagged-nullable-unions/models/FooUnionContent.ts.golden b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-camel-case-tagged-nullable-unions/models/FooUnionContent.ts.golden index bc0f1fe52..0f74cc51c 100644 --- a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-camel-case-tagged-nullable-unions/models/FooUnionContent.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-camel-case-tagged-nullable-unions/models/FooUnionContent.ts.golden @@ -4,6 +4,14 @@ * TypeScript Fetch VP CamelCase Masked Regression Test — 1.0.0 * Masked fixture for TypeScript Fetch camelCase regression coverage. */ +import { + matches$2FromJSON, + matches$2ToJSON, + matches$6FromJSON, + matches$6ToJSON, +} from '../runtime/unionMatch'; +import type { Value$0, Value$0Wire } from '../runtime/valueConversion$0'; +import { convert$0FromJSON, convert$0ToJSON } from '../runtime/valueConversion$0'; import type { FooArrayPart, FooArrayPart$Wire } from './FooArrayPart'; export type FooUnionContent$Wire = string | readonly FooArrayPart$Wire[]; @@ -11,9 +19,23 @@ export type FooUnionContent$Wire = string | readonly FooArrayPart$Wire[]; export type FooUnionContent = string | readonly FooArrayPart[]; export function fooUnionContentFromJSON(json: FooUnionContent$Wire): FooUnionContent { + const candidate: unknown = json; + if (matches$2FromJSON(candidate)) { + return json as unknown as string; + } + if (matches$6FromJSON(candidate)) { + return convert$0FromJSON(json as unknown as Value$0Wire); + } return json as unknown as FooUnionContent; } export function fooUnionContentToJSON(value: FooUnionContent): FooUnionContent$Wire { + const candidate: unknown = value; + if (matches$2ToJSON(candidate)) { + return value as unknown as string; + } + if (matches$6ToJSON(candidate)) { + return convert$0ToJSON(value as unknown as Value$0); + } return value as unknown as FooUnionContent$Wire; } diff --git a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-camel-case-tagged-nullable-unions/models/index.ts.golden b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-camel-case-tagged-nullable-unions/models/index.ts.golden index c48c1239d..57455feba 100644 --- a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-camel-case-tagged-nullable-unions/models/index.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-camel-case-tagged-nullable-unions/models/index.ts.golden @@ -28,7 +28,8 @@ export { fooAdjacentPayloadFooInlineObjectPayloadValueFromJSON, fooAdjacentPayloadFooInlineObjectPayloadValueToJSON, } from './FooAdjacentPayloadFooInlineObjectPayloadValue'; -export type { FooAnyValue } from './FooAnyValue'; +export type { FooAnyValue, FooAnyValue$Wire } from './FooAnyValue'; +export { fooAnyValueFromJSON, fooAnyValueToJSON } from './FooAnyValue'; export type { FooArrayPart, FooArrayPart$Wire } from './FooArrayPart'; export { fooArrayPartFromJSON, fooArrayPartToJSON } from './FooArrayPart'; export type { FooClosedVariant, FooClosedVariant$Wire } from './FooClosedVariant'; diff --git a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-camel-case-tagged-nullable-unions/runtime/unionMatch.ts.golden b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-camel-case-tagged-nullable-unions/runtime/unionMatch.ts.golden new file mode 100644 index 000000000..807484d2d --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-camel-case-tagged-nullable-unions/runtime/unionMatch.ts.golden @@ -0,0 +1,87 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * TypeScript Fetch VP CamelCase Masked Regression Test — 1.0.0 + * Masked fixture for TypeScript Fetch camelCase regression coverage. + */ +export function matches$0FromJSON(candidate: unknown): boolean { + return Array.isArray(candidate); +} + +export function matches$0ToJSON(candidate: unknown): boolean { + return Array.isArray(candidate); +} + +export function matches$2FromJSON(candidate: unknown): boolean { + return typeof candidate === 'string'; +} + +export function matches$2ToJSON(candidate: unknown): boolean { + return typeof candidate === 'string'; +} + +export function matches$3FromJSON(candidate: unknown): boolean { + return typeof candidate === 'number' && Number.isFinite(candidate); +} + +export function matches$3ToJSON(candidate: unknown): boolean { + return typeof candidate === 'number' && Number.isFinite(candidate); +} + +export function matches$4FromJSON(candidate: unknown): boolean { + return typeof candidate === 'number' && Number.isInteger(candidate); +} + +export function matches$4ToJSON(candidate: unknown): boolean { + return typeof candidate === 'number' && Number.isInteger(candidate); +} + +export function matches$5FromJSON(candidate: unknown): boolean { + return typeof candidate === 'boolean'; +} + +export function matches$5ToJSON(candidate: unknown): boolean { + return typeof candidate === 'boolean'; +} + +export function matches$6FromJSON(candidate: unknown): boolean { + if (!Array.isArray(candidate)) { + return false; + } + return candidate.every(matches$7FromJSON); +} + +export function matches$6ToJSON(candidate: unknown): boolean { + if (!Array.isArray(candidate)) { + return false; + } + return candidate.every(matches$7ToJSON); +} + +export function matches$7FromJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as Record; + if (!Object.prototype.hasOwnProperty.call(candidate, 'bar_text')) { + return false; + } + if (!matches$2FromJSON(object['bar_text'])) { + return false; + } + return true; +} + +export function matches$7ToJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as Record; + if (!Object.prototype.hasOwnProperty.call(candidate, 'barText')) { + return false; + } + if (!matches$2ToJSON(object['barText'])) { + return false; + } + return true; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-camel-case-tagged-nullable-unions/runtime/valueConversion$0.ts.golden b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-camel-case-tagged-nullable-unions/runtime/valueConversion$0.ts.golden new file mode 100644 index 000000000..c136f7d2d --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-camel-case-tagged-nullable-unions/runtime/valueConversion$0.ts.golden @@ -0,0 +1,20 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * TypeScript Fetch VP CamelCase Masked Regression Test — 1.0.0 + * Masked fixture for TypeScript Fetch camelCase regression coverage. + */ +import type { FooArrayPart, FooArrayPart$Wire } from '../models/FooArrayPart'; +import { fooArrayPartFromJSON, fooArrayPartToJSON } from '../models/FooArrayPart'; + +export type Value$0Wire = readonly FooArrayPart$Wire[]; + +export type Value$0 = readonly FooArrayPart[]; + +export function convert$0FromJSON(json: ReadonlyArray): Value$0 { + return json.map(fooArrayPartFromJSON); +} + +export function convert$0ToJSON(value: ReadonlyArray): Value$0Wire { + return value.map(fooArrayPartToJSON); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-camel-case-tagged-nullable-unions/runtime/valueConversion$1.ts.golden b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-camel-case-tagged-nullable-unions/runtime/valueConversion$1.ts.golden new file mode 100644 index 000000000..597647764 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-camel-case-tagged-nullable-unions/runtime/valueConversion$1.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * TypeScript Fetch VP CamelCase Masked Regression Test — 1.0.0 + * Masked fixture for TypeScript Fetch camelCase regression coverage. + */ +import { convert$2FromJSON, convert$2ToJSON } from './valueConversion$2'; + +export type Value$1Wire = unknown; + +export type Value$1 = unknown; + +export function convert$1FromJSON(json: unknown): Value$1 { + return convert$2FromJSON(json); +} + +export function convert$1ToJSON(value: unknown): Value$1Wire { + return convert$2ToJSON(value); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-camel-case-tagged-nullable-unions/runtime/valueConversion$2.ts.golden b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-camel-case-tagged-nullable-unions/runtime/valueConversion$2.ts.golden new file mode 100644 index 000000000..d5a866e4d --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-camel-case-tagged-nullable-unions/runtime/valueConversion$2.ts.golden @@ -0,0 +1,62 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * TypeScript Fetch VP CamelCase Masked Regression Test — 1.0.0 + * Masked fixture for TypeScript Fetch camelCase regression coverage. + */ +import { + matches$0FromJSON, + matches$0ToJSON, + matches$2FromJSON, + matches$2ToJSON, + matches$3FromJSON, + matches$3ToJSON, + matches$4FromJSON, + matches$4ToJSON, + matches$5FromJSON, + matches$5ToJSON, +} from '../runtime/unionMatch'; + +export type Value$2Wire = unknown; + +export type Value$2 = unknown; + +export function convert$2FromJSON(json: unknown): Value$2 { + const candidate: unknown = json; + if (matches$0FromJSON(candidate)) { + return json as unknown as readonly unknown[]; + } + if (matches$2FromJSON(candidate)) { + return json as unknown as string; + } + if (matches$3FromJSON(candidate)) { + return json as unknown as number; + } + if (matches$4FromJSON(candidate)) { + return json as unknown as number; + } + if (matches$5FromJSON(candidate)) { + return json as unknown as boolean; + } + return json as unknown as unknown; +} + +export function convert$2ToJSON(value: unknown): Value$2Wire { + const candidate: unknown = value; + if (matches$0ToJSON(candidate)) { + return value as unknown as readonly unknown[]; + } + if (matches$2ToJSON(candidate)) { + return value as unknown as string; + } + if (matches$3ToJSON(candidate)) { + return value as unknown as number; + } + if (matches$4ToJSON(candidate)) { + return value as unknown as number; + } + if (matches$5ToJSON(candidate)) { + return value as unknown as boolean; + } + return value as unknown as unknown; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-camel-case-tagged-nullable-unions/runtime/valueConversion$3.ts.golden b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-camel-case-tagged-nullable-unions/runtime/valueConversion$3.ts.golden new file mode 100644 index 000000000..b9a9bbde4 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-camel-case-tagged-nullable-unions/runtime/valueConversion$3.ts.golden @@ -0,0 +1,20 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * TypeScript Fetch VP CamelCase Masked Regression Test — 1.0.0 + * Masked fixture for TypeScript Fetch camelCase regression coverage. + */ +import type { FooNullableItem, FooNullableItem$Wire } from '../models/FooNullableItem'; +import { fooNullableItemFromJSON, fooNullableItemToJSON } from '../models/FooNullableItem'; + +export type Value$3Wire = readonly FooNullableItem$Wire[]; + +export type Value$3 = readonly FooNullableItem[]; + +export function convert$3FromJSON(json: ReadonlyArray): Value$3 { + return json.map(fooNullableItemFromJSON); +} + +export function convert$3ToJSON(value: ReadonlyArray): Value$3Wire { + return value.map(fooNullableItemToJSON); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/AllOfObjects.ts.golden b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/AllOfObjects.ts.golden index 9ad0a2df1..ec2a4dbd5 100644 --- a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/AllOfObjects.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/AllOfObjects.ts.golden @@ -7,6 +7,7 @@ import type { AllOfObjectsAllOf2 } from './AllOfObjectsAllOf2'; import type { BasicObject } from './BasicObject'; -/** Intersection of object types (merge) */ - +/** + * Intersection of object types (merge) + */ export type AllOfObjects = BasicObject & AllOfObjectsAllOf2; diff --git a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/AnyOfTypes.ts.golden b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/AnyOfTypes.ts.golden index 0b8307aee..46425673a 100644 --- a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/AnyOfTypes.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/AnyOfTypes.ts.golden @@ -4,6 +4,7 @@ * Comprehensive Schema Types Example — 1.0.0 * Comprehensive test for all OpenAPI v3.1.2 schema types */ -/** Any of multiple types */ - +/** + * Any of multiple types + */ export type AnyOfTypes = string | number; diff --git a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/ArrayWithMaxItems.ts.golden b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/ArrayWithMaxItems.ts.golden index 1c3b71c6b..31733b355 100644 --- a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/ArrayWithMaxItems.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/ArrayWithMaxItems.ts.golden @@ -4,6 +4,7 @@ * Comprehensive Schema Types Example — 1.0.0 * Comprehensive test for all OpenAPI v3.1.2 schema types */ -/** Array with maximum items constraint */ - +/** + * Array with maximum items constraint + */ export type ArrayWithMaxItems = readonly string[]; diff --git a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/ArrayWithMinItems.ts.golden b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/ArrayWithMinItems.ts.golden index 0d0c18a37..349236fbb 100644 --- a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/ArrayWithMinItems.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/ArrayWithMinItems.ts.golden @@ -4,6 +4,7 @@ * Comprehensive Schema Types Example — 1.0.0 * Comprehensive test for all OpenAPI v3.1.2 schema types */ -/** Array with minimum items constraint */ - +/** + * Array with minimum items constraint + */ export type ArrayWithMinItems = readonly string[]; diff --git a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/BinaryString.ts.golden b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/BinaryString.ts.golden index 458ee098e..685e67c24 100644 --- a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/BinaryString.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/BinaryString.ts.golden @@ -4,6 +4,7 @@ * Comprehensive Schema Types Example — 1.0.0 * Comprehensive test for all OpenAPI v3.1.2 schema types */ -/** String with binary format */ - +/** + * String with binary format + */ export type BinaryString = Blob | File; diff --git a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/BooleanArray.ts.golden b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/BooleanArray.ts.golden index 717eb2440..530a550ed 100644 --- a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/BooleanArray.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/BooleanArray.ts.golden @@ -4,6 +4,7 @@ * Comprehensive Schema Types Example — 1.0.0 * Comprehensive test for all OpenAPI v3.1.2 schema types */ -/** Array of booleans */ - +/** + * Array of booleans + */ export type BooleanArray = readonly boolean[]; diff --git a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/BooleanType.ts.golden b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/BooleanType.ts.golden index 2c397635b..a67dd27dc 100644 --- a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/BooleanType.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/BooleanType.ts.golden @@ -4,6 +4,7 @@ * Comprehensive Schema Types Example — 1.0.0 * Comprehensive test for all OpenAPI v3.1.2 schema types */ -/** Boolean type */ - +/** + * Boolean type + */ export type BooleanType = boolean; diff --git a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/ByteString.ts.golden b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/ByteString.ts.golden index 5127db307..ef7f545cf 100644 --- a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/ByteString.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/ByteString.ts.golden @@ -4,6 +4,7 @@ * Comprehensive Schema Types Example — 1.0.0 * Comprehensive test for all OpenAPI v3.1.2 schema types */ -/** String with byte format (base64) */ - +/** + * String with byte format (base64) + */ export type ByteString = string; diff --git a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/DateTimeString.ts.golden b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/DateTimeString.ts.golden index 89a847530..2d581caa9 100644 --- a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/DateTimeString.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/DateTimeString.ts.golden @@ -4,6 +4,7 @@ * Comprehensive Schema Types Example — 1.0.0 * Comprehensive test for all OpenAPI v3.1.2 schema types */ -/** String with date-time format */ - +/** + * String with date-time format + */ export type DateTimeString = string; diff --git a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/DoubleType.ts.golden b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/DoubleType.ts.golden index 85df54e8c..9397af818 100644 --- a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/DoubleType.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/DoubleType.ts.golden @@ -4,6 +4,7 @@ * Comprehensive Schema Types Example — 1.0.0 * Comprehensive test for all OpenAPI v3.1.2 schema types */ -/** Double precision floating point */ - +/** + * Double precision floating point + */ export type DoubleType = number; diff --git a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/EmailString.ts.golden b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/EmailString.ts.golden index fdc47e7b6..1187a607e 100644 --- a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/EmailString.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/EmailString.ts.golden @@ -4,6 +4,7 @@ * Comprehensive Schema Types Example — 1.0.0 * Comprehensive test for all OpenAPI v3.1.2 schema types */ -/** String with email format */ - +/** + * String with email format + */ export type EmailString = string; diff --git a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/EmptyObject.ts.golden b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/EmptyObject.ts.golden index 9a93ff401..03b4acc34 100644 --- a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/EmptyObject.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/EmptyObject.ts.golden @@ -4,6 +4,7 @@ * Comprehensive Schema Types Example — 1.0.0 * Comprehensive test for all OpenAPI v3.1.2 schema types */ -/** Object without properties */ - +/** + * Object without properties + */ export type EmptyObject = unknown; diff --git a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/FloatType.ts.golden b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/FloatType.ts.golden index b7abf054d..b31dbb72b 100644 --- a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/FloatType.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/FloatType.ts.golden @@ -4,6 +4,7 @@ * Comprehensive Schema Types Example — 1.0.0 * Comprehensive test for all OpenAPI v3.1.2 schema types */ -/** Floating point number */ - +/** + * Floating point number + */ export type FloatType = number; diff --git a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/Integer32.ts.golden b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/Integer32.ts.golden index f93ca7e03..44aaf6f35 100644 --- a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/Integer32.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/Integer32.ts.golden @@ -4,6 +4,7 @@ * Comprehensive Schema Types Example — 1.0.0 * Comprehensive test for all OpenAPI v3.1.2 schema types */ -/** 32-bit integer */ - +/** + * 32-bit integer + */ export type Integer32 = number; diff --git a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/Integer64.ts.golden b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/Integer64.ts.golden index d4f7bde2f..f6b73892c 100644 --- a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/Integer64.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/Integer64.ts.golden @@ -4,6 +4,7 @@ * Comprehensive Schema Types Example — 1.0.0 * Comprehensive test for all OpenAPI v3.1.2 schema types */ -/** 64-bit integer */ - +/** + * 64-bit integer + */ export type Integer64 = number; diff --git a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/IntegerType.ts.golden b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/IntegerType.ts.golden index 01e1f3b2c..c120bcb6b 100644 --- a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/IntegerType.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/IntegerType.ts.golden @@ -4,6 +4,7 @@ * Comprehensive Schema Types Example — 1.0.0 * Comprehensive test for all OpenAPI v3.1.2 schema types */ -/** Basic integer type */ - +/** + * Basic integer type + */ export type IntegerType = number; diff --git a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/NestedArray.ts.golden b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/NestedArray.ts.golden index 18bb7b11d..2a6fbc2fe 100644 --- a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/NestedArray.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/NestedArray.ts.golden @@ -4,6 +4,7 @@ * Comprehensive Schema Types Example — 1.0.0 * Comprehensive test for all OpenAPI v3.1.2 schema types */ -/** Array of arrays (nested) */ - +/** + * Array of arrays (nested) + */ export type NestedArray = readonly string[][]; diff --git a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/NullableString.ts.golden b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/NullableString.ts.golden index e5e82d2a6..ca5a999c3 100644 --- a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/NullableString.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/NullableString.ts.golden @@ -4,6 +4,7 @@ * Comprehensive Schema Types Example — 1.0.0 * Comprehensive test for all OpenAPI v3.1.2 schema types */ -/** Nullable string type (OpenAPI 3.1.2 style) */ - +/** + * Nullable string type (OpenAPI 3.1.2 style) + */ export type NullableString = string | null; diff --git a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/NumberArray.ts.golden b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/NumberArray.ts.golden index aaa993aa2..c2c4b7d76 100644 --- a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/NumberArray.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/NumberArray.ts.golden @@ -4,6 +4,7 @@ * Comprehensive Schema Types Example — 1.0.0 * Comprehensive test for all OpenAPI v3.1.2 schema types */ -/** Array of numbers */ - +/** + * Array of numbers + */ export type NumberArray = readonly number[]; diff --git a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/NumberType.ts.golden b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/NumberType.ts.golden index 00aa3d34e..7ed3e4f2d 100644 --- a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/NumberType.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/NumberType.ts.golden @@ -4,6 +4,7 @@ * Comprehensive Schema Types Example — 1.0.0 * Comprehensive test for all OpenAPI v3.1.2 schema types */ -/** Basic number type */ - +/** + * Basic number type + */ export type NumberType = number; diff --git a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/NumberWithMaximum.ts.golden b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/NumberWithMaximum.ts.golden index 87ae22b8b..0b763dfd3 100644 --- a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/NumberWithMaximum.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/NumberWithMaximum.ts.golden @@ -4,6 +4,7 @@ * Comprehensive Schema Types Example — 1.0.0 * Comprehensive test for all OpenAPI v3.1.2 schema types */ -/** Number with maximum value */ - +/** + * Number with maximum value + */ export type NumberWithMaximum = number; diff --git a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/NumberWithMinimum.ts.golden b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/NumberWithMinimum.ts.golden index 70427c5d3..dc5653df5 100644 --- a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/NumberWithMinimum.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/NumberWithMinimum.ts.golden @@ -4,6 +4,7 @@ * Comprehensive Schema Types Example — 1.0.0 * Comprehensive test for all OpenAPI v3.1.2 schema types */ -/** Number with minimum value */ - +/** + * Number with minimum value + */ export type NumberWithMinimum = number; diff --git a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/NumberWithMultipleOf.ts.golden b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/NumberWithMultipleOf.ts.golden index 6066650ad..d6359c710 100644 --- a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/NumberWithMultipleOf.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/NumberWithMultipleOf.ts.golden @@ -4,6 +4,7 @@ * Comprehensive Schema Types Example — 1.0.0 * Comprehensive test for all OpenAPI v3.1.2 schema types */ -/** Number divisible by multipleOf */ - +/** + * Number divisible by multipleOf + */ export type NumberWithMultipleOf = number; diff --git a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/ObjectArray.ts.golden b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/ObjectArray.ts.golden index fab136418..ad480898e 100644 --- a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/ObjectArray.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/ObjectArray.ts.golden @@ -6,6 +6,7 @@ */ import type { ObjectArrayItems } from './ObjectArrayItems'; -/** Array of objects */ - +/** + * Array of objects + */ export type ObjectArray = readonly ObjectArrayItems[]; diff --git a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/OneOfObjects.ts.golden b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/OneOfObjects.ts.golden index 7701b84d1..c9dcc338d 100644 --- a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/OneOfObjects.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/OneOfObjects.ts.golden @@ -7,6 +7,7 @@ import type { BasicObject } from './BasicObject'; import type { NestedObject } from './NestedObject'; -/** Union of object types */ - +/** + * Union of object types + */ export type OneOfObjects = BasicObject | NestedObject; diff --git a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/OneOfTypes.ts.golden b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/OneOfTypes.ts.golden index 1424d9043..8440bb2bf 100644 --- a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/OneOfTypes.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/OneOfTypes.ts.golden @@ -4,6 +4,7 @@ * Comprehensive Schema Types Example — 1.0.0 * Comprehensive test for all OpenAPI v3.1.2 schema types */ -/** Union of multiple primitive types */ - +/** + * Union of multiple primitive types + */ export type OneOfTypes = string | number | boolean; diff --git a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/OneOfWithNull.ts.golden b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/OneOfWithNull.ts.golden index a92d0ad58..74cc1dfbe 100644 --- a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/OneOfWithNull.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/OneOfWithNull.ts.golden @@ -6,6 +6,7 @@ */ import type { BasicObject } from './BasicObject'; -/** Union of string, null, and schema reference */ - +/** + * Union of string, null, and schema reference + */ export type OneOfWithNull = string | BasicObject | null; diff --git a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/PasswordString.ts.golden b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/PasswordString.ts.golden index 1cdbbcee3..fd6144e8f 100644 --- a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/PasswordString.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/PasswordString.ts.golden @@ -4,6 +4,7 @@ * Comprehensive Schema Types Example — 1.0.0 * Comprehensive test for all OpenAPI v3.1.2 schema types */ -/** String with password format */ - +/** + * String with password format + */ export type PasswordString = string; diff --git a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/ReferenceArray.ts.golden b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/ReferenceArray.ts.golden index e0ec43dc1..61877296b 100644 --- a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/ReferenceArray.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/ReferenceArray.ts.golden @@ -6,6 +6,7 @@ */ import type { StringType } from './StringType'; -/** Array of references */ - +/** + * Array of references + */ export type ReferenceArray = readonly StringType[]; diff --git a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/StringArray.ts.golden b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/StringArray.ts.golden index 4d6f71a88..d7d991777 100644 --- a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/StringArray.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/StringArray.ts.golden @@ -4,6 +4,7 @@ * Comprehensive Schema Types Example — 1.0.0 * Comprehensive test for all OpenAPI v3.1.2 schema types */ -/** Array of strings */ - +/** + * Array of strings + */ export type StringArray = readonly string[]; diff --git a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/StringType.ts.golden b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/StringType.ts.golden index 64a45ab04..ccf15b5fa 100644 --- a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/StringType.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/StringType.ts.golden @@ -4,6 +4,7 @@ * Comprehensive Schema Types Example — 1.0.0 * Comprehensive test for all OpenAPI v3.1.2 schema types */ -/** Basic string type */ - +/** + * Basic string type + */ export type StringType = string; diff --git a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/StringWithMaxLength.ts.golden b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/StringWithMaxLength.ts.golden index e5baf1264..8bfeb6c48 100644 --- a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/StringWithMaxLength.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/StringWithMaxLength.ts.golden @@ -4,6 +4,7 @@ * Comprehensive Schema Types Example — 1.0.0 * Comprehensive test for all OpenAPI v3.1.2 schema types */ -/** String with maximum length */ - +/** + * String with maximum length + */ export type StringWithMaxLength = string; diff --git a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/StringWithMinLength.ts.golden b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/StringWithMinLength.ts.golden index 2e283cb6b..686dd5a75 100644 --- a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/StringWithMinLength.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/StringWithMinLength.ts.golden @@ -4,6 +4,7 @@ * Comprehensive Schema Types Example — 1.0.0 * Comprehensive test for all OpenAPI v3.1.2 schema types */ -/** String with minimum length */ - +/** + * String with minimum length + */ export type StringWithMinLength = string; diff --git a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/StringWithPattern.ts.golden b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/StringWithPattern.ts.golden index 2875cc681..eb10dad83 100644 --- a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/StringWithPattern.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/StringWithPattern.ts.golden @@ -4,6 +4,7 @@ * Comprehensive Schema Types Example — 1.0.0 * Comprehensive test for all OpenAPI v3.1.2 schema types */ -/** String with regex pattern */ - +/** + * String with regex pattern + */ export type StringWithPattern = string; diff --git a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/UniqueItemsArray.ts.golden b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/UniqueItemsArray.ts.golden index 01090fa8f..0693273f5 100644 --- a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/UniqueItemsArray.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/UniqueItemsArray.ts.golden @@ -4,6 +4,7 @@ * Comprehensive Schema Types Example — 1.0.0 * Comprehensive test for all OpenAPI v3.1.2 schema types */ -/** Array with unique items constraint */ - +/** + * Array with unique items constraint + */ export type UniqueItemsArray = readonly string[]; diff --git a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/UriString.ts.golden b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/UriString.ts.golden index 2cfa7b11b..b357be2f4 100644 --- a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/UriString.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/UriString.ts.golden @@ -4,6 +4,7 @@ * Comprehensive Schema Types Example — 1.0.0 * Comprehensive test for all OpenAPI v3.1.2 schema types */ -/** String with URI format */ - +/** + * String with URI format + */ export type UriString = string; diff --git a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/UuidString.ts.golden b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/UuidString.ts.golden index a6db94f48..3125d2270 100644 --- a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/UuidString.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-comprehensive/models/UuidString.ts.golden @@ -4,6 +4,7 @@ * Comprehensive Schema Types Example — 1.0.0 * Comprehensive test for all OpenAPI v3.1.2 schema types */ -/** String with UUID format */ - +/** + * String with UUID format + */ export type UuidString = string; diff --git a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-delete-response/apis/TestResourceApi.ts.golden b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-delete-response/apis/TestResourceApi.ts.golden index 549b754ed..04083059a 100644 --- a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-delete-response/apis/TestResourceApi.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-delete-response/apis/TestResourceApi.ts.golden @@ -133,10 +133,10 @@ export class TestResourceApi extends BaseAPI implements TestResourceApiInterface requestParameters: ApiCreateTestResourceRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined) { throw new RequiredError( 'body', - 'Required parameter "body" was null or undefined when calling createTestResourceRaw().', + 'Required parameter "body" was undefined when calling createTestResourceRaw().', ); } // Build path with path parameters @@ -149,7 +149,7 @@ export class TestResourceApi extends BaseAPI implements TestResourceApiInterface }; // Prepare request body - const requestBody = requestParameters.body; + const requestBody = requestParameters['body']; // Make request const response = await this.request( { @@ -199,7 +199,7 @@ export class TestResourceApi extends BaseAPI implements TestResourceApiInterface requestParameters: ApiDeleteTestResourceRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.resourceId === undefined || requestParameters.resourceId === null) { + if (requestParameters['resourceId'] === undefined || requestParameters['resourceId'] === null) { throw new RequiredError( 'resourceId', 'Required parameter "resourceId" was null or undefined when calling deleteTestResourceRaw().', @@ -238,7 +238,7 @@ export class TestResourceApi extends BaseAPI implements TestResourceApiInterface status: response.status, raw: response, value: () => { - errorValue ??= new JSONApiResponse(errorRaw.clone(), (json) => { + errorValue ??= new JSONApiResponse(errorRaw.clone(), (json) => { return httpErrorResponseFromJSON(json as HttpErrorResponse$Wire); }).value(); return errorValue; @@ -251,7 +251,7 @@ export class TestResourceApi extends BaseAPI implements TestResourceApiInterface status: response.status, raw: response, value: () => { - errorValue ??= new JSONApiResponse(errorRaw.clone(), (json) => { + errorValue ??= new JSONApiResponse(errorRaw.clone(), (json) => { return httpErrorResponseFromJSON(json as HttpErrorResponse$Wire); }).value(); return errorValue; diff --git a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-delete-response/models/CreateTestResourceRequest.ts.golden b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-delete-response/models/CreateTestResourceRequest.ts.golden index 34cfa87bf..1aed88336 100644 --- a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-delete-response/models/CreateTestResourceRequest.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-delete-response/models/CreateTestResourceRequest.ts.golden @@ -4,6 +4,7 @@ * Test API with DELETE Response Schema — 1.0.0 * Test fixture for DELETE operations with JSON response schemas and type alias request bodies */ -/** Request to create a new test resource (type alias - no properties) */ - +/** + * Request to create a new test resource (type alias - no properties) + */ export type CreateTestResourceRequest = unknown; diff --git a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-enum-repr/apis/EnumReprApi.ts.golden b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-enum-repr/apis/EnumReprApi.ts.golden index 5cdd1da80..ab849ede1 100644 --- a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-enum-repr/apis/EnumReprApi.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-enum-repr/apis/EnumReprApi.ts.golden @@ -250,7 +250,7 @@ export class EnumReprApi extends BaseAPI implements EnumReprApiInterface { requestParameters: ApiHandleAdjacentlyTaggedRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling handleAdjacentlyTaggedRaw().', @@ -327,7 +327,7 @@ export class EnumReprApi extends BaseAPI implements EnumReprApiInterface { requestParameters: ApiHandleExternallyTaggedRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling handleExternallyTaggedRaw().', @@ -404,7 +404,7 @@ export class EnumReprApi extends BaseAPI implements EnumReprApiInterface { requestParameters: ApiHandleInternallyTaggedRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling handleInternallyTaggedRaw().', @@ -481,7 +481,7 @@ export class EnumReprApi extends BaseAPI implements EnumReprApiInterface { requestParameters: ApiHandleMixedRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling handleMixedRaw().', @@ -556,7 +556,7 @@ export class EnumReprApi extends BaseAPI implements EnumReprApiInterface { requestParameters: ApiHandleUntaggedRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling handleUntaggedRaw().', diff --git a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-enum-repr/models/AdjacentlyTaggedEnum.ts.golden b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-enum-repr/models/AdjacentlyTaggedEnum.ts.golden index af385123d..cd6784c07 100644 --- a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-enum-repr/models/AdjacentlyTaggedEnum.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-enum-repr/models/AdjacentlyTaggedEnum.ts.golden @@ -7,11 +7,12 @@ import type { VariantA } from './VariantA'; import type { VariantB } from './VariantB'; -/** Adjacently tagged enum +/** + * Adjacently tagged enum The tag and content are separate fields: -`{"ty": "VariantA", "data": {"field1": "...", "field2": 123}}` */ - +`{"ty": "VariantA", "data": {"field1": "...", "field2": 123}}` + */ export type AdjacentlyTaggedEnum = - | { ty: 'ADJACENTLY_TAGGED_ENUM_VARIANT_A'; data: VariantA } - | { ty: 'ADJACENTLY_TAGGED_ENUM_VARIANT_B'; data: VariantB }; + | (Record<'ty', 'ADJACENTLY_TAGGED_ENUM_VARIANT_A'> & Record<'data', VariantA>) + | (Record<'ty', 'ADJACENTLY_TAGGED_ENUM_VARIANT_B'> & Record<'data', VariantB>); diff --git a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-enum-repr/models/ExternallyTaggedEnum.ts.golden b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-enum-repr/models/ExternallyTaggedEnum.ts.golden index 09dc7debe..308da598f 100644 --- a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-enum-repr/models/ExternallyTaggedEnum.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-enum-repr/models/ExternallyTaggedEnum.ts.golden @@ -7,11 +7,12 @@ import type { VariantA } from './VariantA'; import type { VariantB } from './VariantB'; -/** Externally tagged enum (default Serde representation) +/** + * Externally tagged enum (default Serde representation) This is the default representation where the variant name is the key -and the content is the value: `{"VariantA": {"field1": "...", "field2": 123}}` */ - +and the content is the value: `{"VariantA": {"field1": "...", "field2": 123}}` + */ export type ExternallyTaggedEnum = - | { EXTERNALLY_TAGGED_ENUM_VARIANT_A: VariantA } - | { EXTERNALLY_TAGGED_ENUM_VARIANT_B: VariantB }; + | Record<'EXTERNALLY_TAGGED_ENUM_VARIANT_A', VariantA> + | Record<'EXTERNALLY_TAGGED_ENUM_VARIANT_B', VariantB>; diff --git a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-enum-repr/models/InternallyTaggedEnum.ts.golden b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-enum-repr/models/InternallyTaggedEnum.ts.golden index 75b637a62..e6c71a7bf 100644 --- a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-enum-repr/models/InternallyTaggedEnum.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-enum-repr/models/InternallyTaggedEnum.ts.golden @@ -7,11 +7,12 @@ import type { VariantA } from './VariantA'; import type { VariantB } from './VariantB'; -/** Internally tagged enum +/** + * Internally tagged enum The tag is a field inside the object alongside other fields: -`{"ty": "VariantA", "field1": "...", "field2": 123}` */ - +`{"ty": "VariantA", "field1": "...", "field2": 123}` + */ export type InternallyTaggedEnum = - | ({ ty: 'INTERNALLY_TAGGED_ENUM_VARIANT_A' } & VariantA) - | ({ ty: 'INTERNALLY_TAGGED_ENUM_VARIANT_B' } & VariantB); + | (Record<'ty', 'INTERNALLY_TAGGED_ENUM_VARIANT_A'> & VariantA) + | (Record<'ty', 'INTERNALLY_TAGGED_ENUM_VARIANT_B'> & VariantB); diff --git a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-enum-repr/models/MixedEnum.ts.golden b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-enum-repr/models/MixedEnum.ts.golden index 3c88b5414..c92e23f25 100644 --- a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-enum-repr/models/MixedEnum.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-enum-repr/models/MixedEnum.ts.golden @@ -7,9 +7,10 @@ import type { MixedEnumMember2 } from './MixedEnumMember2'; import type { MixedEnumMember4 } from './MixedEnumMember4'; -/** Mixed enum with unit variants (SimpleA, SimpleB) and tuple variants (VariantA(VariantA), VariantB(VariantB)) +/** + * Mixed enum with unit variants (SimpleA, SimpleB) and tuple variants (VariantA(VariantA), VariantB(VariantB)) Serializes as externally tagged: `{"SimpleA": null}`, `{"VariantA": {"field1": "...", "field2": 123}}`, -`{"SimpleB": null}`, `{"VariantB": {"field3": true, "field4": 1.23}}` */ - +`{"SimpleB": null}`, `{"VariantB": {"field3": true, "field4": 1.23}}` + */ export type MixedEnum = 'SimpleA' | MixedEnumMember2 | 'SimpleB' | MixedEnumMember4; diff --git a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-enum-repr/models/UntaggedEnum.ts.golden b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-enum-repr/models/UntaggedEnum.ts.golden index 2de828935..738a3a672 100644 --- a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-enum-repr/models/UntaggedEnum.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-enum-repr/models/UntaggedEnum.ts.golden @@ -7,9 +7,10 @@ import type { VariantA } from './VariantA'; import type { VariantB } from './VariantB'; -/** Untagged enum +/** + * Untagged enum No tag is used, variants are distinguished by their structure: -`{"field1": "...", "field2": 123}` or `{"field3": true, "field4": 1.23}` */ - +`{"field1": "...", "field2": 123}` or `{"field3": true, "field4": 1.23}` + */ export type UntaggedEnum = VariantA | VariantB; diff --git a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-petstore/apis/PetApi.ts.golden b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-petstore/apis/PetApi.ts.golden index b16c06363..123173b09 100644 --- a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-petstore/apis/PetApi.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-petstore/apis/PetApi.ts.golden @@ -420,7 +420,7 @@ export class PetApi extends BaseAPI implements PetApiInterface { requestParameters: ApiUpdatePetRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling updatePetRaw().', @@ -517,7 +517,7 @@ export class PetApi extends BaseAPI implements PetApiInterface { requestParameters: ApiAddPetRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling addPetRaw().', @@ -603,7 +603,7 @@ export class PetApi extends BaseAPI implements PetApiInterface { requestParameters: ApiFindPetsByStatusRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.status === undefined || requestParameters.status === null) { + if (requestParameters['status'] === undefined || requestParameters['status'] === null) { throw new RequiredError( 'status', 'Required parameter "status" was null or undefined when calling findPetsByStatusRaw().', @@ -677,7 +677,7 @@ export class PetApi extends BaseAPI implements PetApiInterface { requestParameters: ApiFindPetsByTagsRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.tags === undefined || requestParameters.tags === null) { + if (requestParameters['tags'] === undefined || requestParameters['tags'] === null) { throw new RequiredError( 'tags', 'Required parameter "tags" was null or undefined when calling findPetsByTagsRaw().', @@ -751,7 +751,7 @@ export class PetApi extends BaseAPI implements PetApiInterface { requestParameters: ApiGetPetByIdRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.petId === undefined || requestParameters.petId === null) { + if (requestParameters['petId'] === undefined || requestParameters['petId'] === null) { throw new RequiredError( 'petId', 'Required parameter "petId" was null or undefined when calling getPetByIdRaw().', @@ -834,7 +834,7 @@ export class PetApi extends BaseAPI implements PetApiInterface { requestParameters: ApiUpdatePetWithFormRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.petId === undefined || requestParameters.petId === null) { + if (requestParameters['petId'] === undefined || requestParameters['petId'] === null) { throw new RequiredError( 'petId', 'Required parameter "petId" was null or undefined when calling updatePetWithFormRaw().', @@ -912,7 +912,7 @@ export class PetApi extends BaseAPI implements PetApiInterface { requestParameters: ApiDeletePetRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.petId === undefined || requestParameters.petId === null) { + if (requestParameters['petId'] === undefined || requestParameters['petId'] === null) { throw new RequiredError( 'petId', 'Required parameter "petId" was null or undefined when calling deletePetRaw().', @@ -984,7 +984,7 @@ export class PetApi extends BaseAPI implements PetApiInterface { requestParameters: ApiUploadFileRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.petId === undefined || requestParameters.petId === null) { + if (requestParameters['petId'] === undefined || requestParameters['petId'] === null) { throw new RequiredError( 'petId', 'Required parameter "petId" was null or undefined when calling uploadFileRaw().', diff --git a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-petstore/apis/StoreApi.ts.golden b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-petstore/apis/StoreApi.ts.golden index 0b89a495c..5a58ee953 100644 --- a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-petstore/apis/StoreApi.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-petstore/apis/StoreApi.ts.golden @@ -263,7 +263,7 @@ export class StoreApi extends BaseAPI implements StoreApiInterface { requestParameters: ApiPlaceOrderRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling placeOrderRaw().', @@ -338,7 +338,7 @@ export class StoreApi extends BaseAPI implements StoreApiInterface { requestParameters: ApiGetOrderByIdRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.orderId === undefined || requestParameters.orderId === null) { + if (requestParameters['orderId'] === undefined || requestParameters['orderId'] === null) { throw new RequiredError( 'orderId', 'Required parameter "orderId" was null or undefined when calling getOrderByIdRaw().', @@ -421,7 +421,7 @@ export class StoreApi extends BaseAPI implements StoreApiInterface { requestParameters: ApiDeleteOrderRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.orderId === undefined || requestParameters.orderId === null) { + if (requestParameters['orderId'] === undefined || requestParameters['orderId'] === null) { throw new RequiredError( 'orderId', 'Required parameter "orderId" was null or undefined when calling deleteOrderRaw().', diff --git a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-petstore/apis/UserApi.ts.golden b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-petstore/apis/UserApi.ts.golden index dbbbcc76b..2b6739318 100644 --- a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-petstore/apis/UserApi.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-petstore/apis/UserApi.ts.golden @@ -336,7 +336,7 @@ export class UserApi extends BaseAPI implements UserApiInterface { requestParameters: ApiCreateUserRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling createUserRaw().', @@ -400,7 +400,7 @@ export class UserApi extends BaseAPI implements UserApiInterface { requestParameters: ApiCreateUsersWithListInputRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling createUsersWithListInputRaw().', @@ -585,7 +585,7 @@ export class UserApi extends BaseAPI implements UserApiInterface { requestParameters: ApiGetUserByNameRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.username === undefined || requestParameters.username === null) { + if (requestParameters['username'] === undefined || requestParameters['username'] === null) { throw new RequiredError( 'username', 'Required parameter "username" was null or undefined when calling getUserByNameRaw().', @@ -668,13 +668,13 @@ export class UserApi extends BaseAPI implements UserApiInterface { requestParameters: ApiUpdateUserRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.username === undefined || requestParameters.username === null) { + if (requestParameters['username'] === undefined || requestParameters['username'] === null) { throw new RequiredError( 'username', 'Required parameter "username" was null or undefined when calling updateUserRaw().', ); } - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling updateUserRaw().', @@ -761,7 +761,7 @@ export class UserApi extends BaseAPI implements UserApiInterface { requestParameters: ApiDeleteUserRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.username === undefined || requestParameters.username === null) { + if (requestParameters['username'] === undefined || requestParameters['username'] === null) { throw new RequiredError( 'username', 'Required parameter "username" was null or undefined when calling deleteUserRaw().', diff --git a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-self-referential-object/models/Foo.ts.golden b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-self-referential-object/models/Foo.ts.golden index 8dfe8a6bf..22188c11a 100644 --- a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-self-referential-object/models/Foo.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-self-referential-object/models/Foo.ts.golden @@ -3,6 +3,7 @@ * * Self-Referential Object Test — 1.0.0 */ +import { convert$0FromJSON, convert$0ToJSON } from '../runtime/valueConversion$0'; import type { Bar, Bar$Wire } from './Bar'; import { barFromJSON, barToJSON } from './Bar'; @@ -18,16 +19,14 @@ export interface Foo { export function fooFromJSON(json: Foo$Wire): Foo { return { - children: - json.children !== undefined ? json.children.map((item) => fooFromJSON(item)) : undefined, + children: json.children !== undefined ? convert$0FromJSON(json.children) : undefined, relatedBar: barFromJSON(json.related_bar), }; } export function fooToJSON(value: Foo): Foo$Wire { return { - children: - value.children !== undefined ? value.children.map((item) => fooToJSON(item)) : undefined, + children: value.children !== undefined ? convert$0ToJSON(value.children) : undefined, related_bar: barToJSON(value.relatedBar), }; } diff --git a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-self-referential-object/runtime/valueConversion$0.ts.golden b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-self-referential-object/runtime/valueConversion$0.ts.golden new file mode 100644 index 000000000..8fc2a6946 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp-self-referential-object/runtime/valueConversion$0.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Self-Referential Object Test — 1.0.0 + */ +import type { Foo, Foo$Wire } from '../models/Foo'; +import { fooFromJSON, fooToJSON } from '../models/Foo'; + +export type Value$0Wire = readonly Foo$Wire[]; + +export type Value$0 = readonly Foo[]; + +export function convert$0FromJSON(json: ReadonlyArray): Value$0 { + return json.map(fooFromJSON); +} + +export function convert$0ToJSON(value: ReadonlyArray): Value$0Wire { + return value.map(fooToJSON); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp/apis/DefaultApi.ts.golden b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp/apis/DefaultApi.ts.golden index 2663e85c5..b3416c877 100644 --- a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp/apis/DefaultApi.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp/apis/DefaultApi.ts.golden @@ -72,7 +72,7 @@ export class DefaultApi extends BaseAPI implements DefaultApiInterface { requestParameters: ApiCreateContainerRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling createContainerRaw().', @@ -88,7 +88,7 @@ export class DefaultApi extends BaseAPI implements DefaultApiInterface { }; // Prepare request body - const requestBody = containerImageToJSON(requestParameters.body); + const requestBody = containerImageToJSON(requestParameters['body']); // Make request const response = await this.request( { diff --git a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp/models/ContainerImage.ts.golden b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp/models/ContainerImage.ts.golden index 4a3fb4001..e263d3d4c 100644 --- a/tests/golden/typescript/typescript-fetch/ts-toolchain-vp/models/ContainerImage.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-toolchain-vp/models/ContainerImage.ts.golden @@ -14,33 +14,41 @@ import { } from './ContainerImageManaged'; export type ContainerImage$Wire = - | ({ kind: 'CONTAINER_IMAGE_MANAGED' } & ContainerImageManaged$Wire) - | ({ kind: 'CONTAINER_IMAGE_CUSTOM' } & ContainerImageCustom$Wire); + | (Record<'kind', 'CONTAINER_IMAGE_MANAGED'> & ContainerImageManaged$Wire) + | (Record<'kind', 'CONTAINER_IMAGE_CUSTOM'> & ContainerImageCustom$Wire); export type ContainerImage = - | ({ kind: 'CONTAINER_IMAGE_MANAGED' } & ContainerImageManaged) - | ({ kind: 'CONTAINER_IMAGE_CUSTOM' } & ContainerImageCustom); + | (Record<'kind', 'CONTAINER_IMAGE_MANAGED'> & ContainerImageManaged) + | (Record<'kind', 'CONTAINER_IMAGE_CUSTOM'> & ContainerImageCustom); export function containerImageFromJSON(json: ContainerImage$Wire): ContainerImage { - switch (json.kind) { - case 'CONTAINER_IMAGE_MANAGED': - return { ...containerImageManagedFromJSON(json), kind: 'CONTAINER_IMAGE_MANAGED' }; - case 'CONTAINER_IMAGE_CUSTOM': - return { ...containerImageCustomFromJSON(json), kind: 'CONTAINER_IMAGE_CUSTOM' }; + if (json['kind'] === 'CONTAINER_IMAGE_MANAGED') { + return { + ...containerImageManagedFromJSON(json as unknown as ContainerImageManaged$Wire), + ['kind']: 'CONTAINER_IMAGE_MANAGED', + } as ContainerImage; } + if (json['kind'] === 'CONTAINER_IMAGE_CUSTOM') { + return { + ...containerImageCustomFromJSON(json as unknown as ContainerImageCustom$Wire), + ['kind']: 'CONTAINER_IMAGE_CUSTOM', + } as ContainerImage; + } + throw new Error('Unknown variant'); } export function containerImageToJSON(value: ContainerImage): ContainerImage$Wire { - switch (value.kind) { - case 'CONTAINER_IMAGE_MANAGED': - return { - ...containerImageManagedToJSON(value), - kind: 'CONTAINER_IMAGE_MANAGED', - } as ContainerImage$Wire; - case 'CONTAINER_IMAGE_CUSTOM': - return { - ...containerImageCustomToJSON(value), - kind: 'CONTAINER_IMAGE_CUSTOM', - } as ContainerImage$Wire; + if (value['kind'] === 'CONTAINER_IMAGE_MANAGED') { + return { + ...containerImageManagedToJSON(value as unknown as ContainerImageManaged), + ['kind']: 'CONTAINER_IMAGE_MANAGED', + } as ContainerImage$Wire; + } + if (value['kind'] === 'CONTAINER_IMAGE_CUSTOM') { + return { + ...containerImageCustomToJSON(value as unknown as ContainerImageCustom), + ['kind']: 'CONTAINER_IMAGE_CUSTOM', + } as ContainerImage$Wire; } + throw new Error('Unknown variant'); } diff --git a/tests/golden/typescript/typescript-fetch/ts-type-guards/apis/DefaultApi.ts.golden b/tests/golden/typescript/typescript-fetch/ts-type-guards/apis/DefaultApi.ts.golden index 42d66ddd5..222b3b76a 100644 --- a/tests/golden/typescript/typescript-fetch/ts-type-guards/apis/DefaultApi.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-type-guards/apis/DefaultApi.ts.golden @@ -69,7 +69,7 @@ export class DefaultApi extends BaseAPI implements DefaultApiInterface { requestParameters: ApiHandleEventRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling handleEventRaw().', diff --git a/tests/golden/typescript/typescript-fetch/ts-type-guards/models/AppEvent.ts.golden b/tests/golden/typescript/typescript-fetch/ts-type-guards/models/AppEvent.ts.golden index a225cbedc..19954be2f 100644 --- a/tests/golden/typescript/typescript-fetch/ts-type-guards/models/AppEvent.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-type-guards/models/AppEvent.ts.golden @@ -7,27 +7,26 @@ import type { ClickEvent } from './ClickEvent'; import type { KeyEvent } from './KeyEvent'; import type { ScrollEvent } from './ScrollEvent'; -/** An application event */ - +/** + * An application event + */ export type AppEvent = - | ({ eventType: 'ClickEvent' } & ClickEvent) - | ({ eventType: 'ScrollEvent' } & ScrollEvent) - | ({ eventType: 'KeyEvent' } & KeyEvent); + | (Record<'eventType', 'ClickEvent'> & ClickEvent) + | (Record<'eventType', 'ScrollEvent'> & ScrollEvent) + | (Record<'eventType', 'KeyEvent'> & KeyEvent); -export function isClickEvent(value: AppEvent): value is { - eventType: 'ClickEvent'; -} & ClickEvent { - return value.eventType === 'ClickEvent'; +export function isClickEvent( + value: AppEvent, +): value is Record<'eventType', 'ClickEvent'> & ClickEvent { + return value['eventType'] === 'ClickEvent'; } -export function isScrollEvent(value: AppEvent): value is { - eventType: 'ScrollEvent'; -} & ScrollEvent { - return value.eventType === 'ScrollEvent'; +export function isScrollEvent( + value: AppEvent, +): value is Record<'eventType', 'ScrollEvent'> & ScrollEvent { + return value['eventType'] === 'ScrollEvent'; } -export function isKeyEvent(value: AppEvent): value is { - eventType: 'KeyEvent'; -} & KeyEvent { - return value.eventType === 'KeyEvent'; +export function isKeyEvent(value: AppEvent): value is Record<'eventType', 'KeyEvent'> & KeyEvent { + return value['eventType'] === 'KeyEvent'; } diff --git a/tests/golden/typescript/typescript-fetch/ts-type-guards/models/ServerResponse.ts.golden b/tests/golden/typescript/typescript-fetch/ts-type-guards/models/ServerResponse.ts.golden index 80f1d2eba..525335b09 100644 --- a/tests/golden/typescript/typescript-fetch/ts-type-guards/models/ServerResponse.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-type-guards/models/ServerResponse.ts.golden @@ -7,6 +7,7 @@ import type { ErrorResponse } from './ErrorResponse'; import type { SuccessResponse } from './SuccessResponse'; import type { UnspecifiedResponse } from './UnspecifiedResponse'; -/** An API response */ - +/** + * An API response + */ export type ServerResponse = SuccessResponse | ErrorResponse | UnspecifiedResponse; diff --git a/tests/golden/typescript/typescript-fetch/ts-type-guards/models/Shape.ts.golden b/tests/golden/typescript/typescript-fetch/ts-type-guards/models/Shape.ts.golden index c4e55e785..61c81c924 100644 --- a/tests/golden/typescript/typescript-fetch/ts-type-guards/models/Shape.ts.golden +++ b/tests/golden/typescript/typescript-fetch/ts-type-guards/models/Shape.ts.golden @@ -6,18 +6,15 @@ import type { Circle } from './Circle'; import type { Rectangle } from './Rectangle'; -/** A geometric shape */ - -export type Shape = ({ kind: 'circle' } & Circle) | ({ kind: 'rectangle' } & Rectangle); +/** + * A geometric shape + */ +export type Shape = Record<'kind', 'circle'> & Circle | Record<'kind', 'rectangle'> & Rectangle; -export function isCircle(value: Shape): value is { - kind: 'circle'; -} & Circle { - return value.kind === 'circle'; +export function isCircle(value: Shape): value is Record<'kind', 'circle'> & Circle { + return value['kind'] === 'circle'; } -export function isRectangle(value: Shape): value is { - kind: 'rectangle'; -} & Rectangle { - return value.kind === 'rectangle'; +export function isRectangle(value: Shape): value is Record<'kind', 'rectangle'> & Rectangle { + return value['kind'] === 'rectangle'; } diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/README.md.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/README.md.golden new file mode 100644 index 000000000..9a0608684 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/README.md.golden @@ -0,0 +1,264 @@ +# untagged-union-conversion + +Generated API client + +**Version:** 1.0.0 + +## Overview + +This package provides a TypeScript/JavaScript client for the Untagged union conversion API. It uses the native [Fetch API](https://fetch.spec.whatwg.org/) for HTTP requests and works in both Node.js and browser environments. + +## Features + +- ✨ **Type-safe** - Full TypeScript support with generated types +- 🚀 **Modern** - Uses native Fetch API, no external HTTP dependencies +- 🔧 **Configurable** - Flexible configuration options +- 🎯 **Middleware** - Support for request/response interceptors +- 📦 **Tree-shakeable** - Import only what you need +- 🌐 **Universal** - Works in Node.js and browsers + +## Installation + +### From npm (published package) + +```bash +npm install untagged-union-conversion +``` + +### From local path (development) + +Add the package to your `package.json` using the `file:` protocol: + +```json +{ + "dependencies": { + "untagged-union-conversion": "file:../../path/to/generated/package" + } +} +``` + +Then run: + +```bash +npm install +``` + +## Quick Start + +```typescript +import { Configuration, ConversionApi } from 'untagged-union-conversion'; + +// Create a configuration +const config = new Configuration({ + basePath: 'https://api.example.com', + headers: { + Authorization: 'Bearer YOUR_TOKEN', + }, +}); + +// Initialize the API client +const api = new ConversionApi(config); + +// Make API calls +try { + const result = await api.someMethod(); + console.log(result); +} catch (error) { + console.error('API Error:', error); +} +``` + +## Configuration + +The `Configuration` class accepts the following options: + +```typescript +interface ConfigurationParameters { + /** Base URL for API requests */ + basePath?: string; + + /** Custom fetch implementation */ + fetchApi?: typeof fetch; + + /** Request/response middleware */ + middleware?: Middleware[]; + + /** Custom query string serializer */ + queryParamsStringify?: (params: HTTPQuery) => string; + + /** Default headers for all requests */ + headers?: Record; + + /** Credentials mode for requests */ + credentials?: RequestCredentials; +} +``` + +### Example with custom configuration + +```typescript +const config = new Configuration({ + basePath: 'https://api.example.com', + headers: { + 'X-API-Key': 'your-api-key', + 'Content-Type': 'application/json', + }, + credentials: 'include', +}); +``` + +## Middleware + +Add custom middleware to intercept requests and responses: + +```typescript +import { Configuration, Middleware } from 'untagged-union-conversion'; + +const loggingMiddleware: Middleware = { + pre: async (context) => { + console.log('Request:', context.url); + return context; + }, + post: async (context) => { + console.log('Response:', context.response.status); + return context.response; + }, + onError: async (context) => { + console.error('Error:', context.error); + return undefined; + }, +}; + +const config = new Configuration({ + basePath: 'https://api.example.com', + middleware: [loggingMiddleware], +}); +``` + +## Error Handling + +The client throws typed errors for different failure scenarios: + +```typescript +import { ResponseError, FetchError, RequiredError } from 'untagged-union-conversion'; + +try { + const result = await api.someMethod(); +} catch (error) { + if (error instanceof ResponseError) { + // HTTP error response (4xx, 5xx) + console.error('HTTP Error:', error.response.status); + } else if (error instanceof FetchError) { + // Network or fetch error + console.error('Network Error:', error.cause); + } else if (error instanceof RequiredError) { + // Missing required parameter + console.error('Missing field:', error.field); + } +} +``` + +## API Reference + +This package exports the following: + +- **Configuration** - Client configuration class +- **BaseAPI** - Base class for all API clients +- **API Classes** - Generated API client classes (e.g., `UserApi`, `PostApi`) +- **Models** - Generated TypeScript interfaces for request/response types +- **Errors** - `ResponseError`, `FetchError`, `RequiredError` +- **Types** - TypeScript type definitions + +## Development + +### Building + +To build the package: + +```bash +npm install +npm run build +``` + +This will compile TypeScript to JavaScript in the `dist/` directory. + +### Building for ESM + +To build ES modules: + +```bash +npm run build +``` + +### Formatting and validation + +After generating or regenerating this client, install its development dependencies, +run all checks on the generated files and build the library: + +```bash +vp install +vp check +vp pack +``` + +The `check` and `build` npm scripts run the same Vite+ commands. +Use `vp fmt` (or the `fmt` script) to format any subsequent manual edits. +`vp check` validates formatting, lint rules, and types without modifying files. +Build output in `dist/` is excluded from formatting. + +## TypeScript Support + +This package includes TypeScript type definitions. No additional `@types` package is needed. + +### TypeScript Configuration + +This package works with standard TypeScript configurations. If you're using a bundler-based setup, you may want to configure: + +```json +{ + "compilerOptions": { + "moduleResolution": "bundler" + } +} +``` + +### Type Imports + +```typescript +import type { User, CreateUserRequest } from 'untagged-union-conversion'; + +const user: User = { + id: 1, + name: 'John Doe', + email: 'john@example.com', +}; +``` + +## Browser Support + +This package uses the native Fetch API, which is supported in: + +- Chrome 42+ +- Firefox 39+ +- Safari 10.1+ +- Edge 14+ +- Node.js 18+ (native fetch) +- Node.js <18 (with `node-fetch` polyfill) + +For older browsers, you may need to include a fetch polyfill. + +## License + +This is an auto-generated API client. Please refer to your API documentation for license information. + +## Support + +For issues related to the API itself, please contact the API provider. + +For issues with this generated client, please check the OpenAPI specification used to generate it. + +--- + +**Generated by OpenAPI Generator** + +API Version: 1.0.0 diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/apis/ConversionApi.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/apis/ConversionApi.ts.golden new file mode 100644 index 000000000..5d1b77c71 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/apis/ConversionApi.ts.golden @@ -0,0 +1,787 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { ExchangeMultipartMultipartRequestBody } from '../models/ExchangeMultipartMultipartRequestBody'; +import type { NullableDetail } from '../models/NullableDetail'; +import { nullableDetailToJSON } from '../models/NullableDetail'; +import type { NullableScalarInner } from '../models/NullableScalarInner'; +import { nullableScalarInnerToJSON } from '../models/NullableScalarInner'; +import type { SendOptionalInlineScalarRequest } from '../models/SendOptionalInlineScalarRequest'; +import { sendOptionalInlineScalarRequestToJSON } from '../models/SendOptionalInlineScalarRequest'; +import type { Tool, Tool$Wire } from '../models/Tool'; +import type { + Configuration, + HTTPQuery, + HttpSuccessResponse, + InitOverrideFunction, +} from '../runtime/runtime'; +import { + BaseAPI, + DefaultConfig, + JSONApiResponse, + RequiredError, + ResponseError, + VoidApiResponse, + multipartHeaderValue, + uploadFileData, + uploadFileFilename, +} from '../runtime/runtime'; +import { convert$35FromJSON, convert$35ToJSON } from '../runtime/valueConversion$35'; +import { convert$37FromJSON, convert$37ToJSON } from '../runtime/valueConversion$37'; +import { convert$39ToJSON } from '../runtime/valueConversion$39'; + +export interface ApiExchangeMatrixRequest { + body: Tool[][]; +} + +export interface ApiExchangeMultipartRequest { + body: ExchangeMultipartMultipartRequestBody; +} + +export interface ApiExchangeNullableRequest { + body: Tool[] | null; +} + +export interface ApiSendOptionalDetailRequest { + body?: NullableDetail; +} + +export interface ApiSendOptionalInlineScalarRequest { + body?: SendOptionalInlineScalarRequest; +} + +export interface ApiSendOptionalScalarRequest { + body?: NullableScalarInner; +} + +export interface ApiSendRequiredScalarRequest { + body: NullableScalarInner; +} + +export type ExchangeMatrixRawResponse = + | (JSONApiResponse & { status: 200 }) + | (JSONApiResponse & HttpSuccessResponse); +export type ExchangeMultipartRawResponse = + | (VoidApiResponse & { status: 204 }) + | (VoidApiResponse & HttpSuccessResponse); +export type ExchangeNullableRawResponse = + | (JSONApiResponse & { status: 200 }) + | (JSONApiResponse & HttpSuccessResponse); +export type SendOptionalDetailRawResponse = + | (VoidApiResponse & { status: 204 }) + | (VoidApiResponse & HttpSuccessResponse); +export type SendOptionalInlineScalarRawResponse = + | (VoidApiResponse & { status: 204 }) + | (VoidApiResponse & HttpSuccessResponse); +export type SendOptionalScalarRawResponse = + | (VoidApiResponse & { status: 204 }) + | (VoidApiResponse & HttpSuccessResponse); +export type SendRequiredScalarRawResponse = + | (VoidApiResponse & { status: 204 }) + | (VoidApiResponse & HttpSuccessResponse); + +export type ExchangeMatrixErrorDetail = + | { + kind: 'badRequest'; + status: 400; + raw: Response; + value(): Promise; + } + | { + kind: 'unexpected'; + status: number; + raw: Response; + value(): Promise; + }; + +export class ExchangeMatrixError extends ResponseError { + readonly detail: ExchangeMatrixErrorDetail; + + constructor( + response: Response, + detail: ExchangeMatrixErrorDetail, + msg = 'Response returned an error code', + ) { + super(response, msg); + this.detail = detail; + } +} + +export type ExchangeMultipartErrorDetail = { + kind: 'unexpected'; + status: number; + raw: Response; + value(): Promise; +}; + +export class ExchangeMultipartError extends ResponseError { + readonly detail: ExchangeMultipartErrorDetail; + + constructor( + response: Response, + detail: ExchangeMultipartErrorDetail, + msg = 'Response returned an error code', + ) { + super(response, msg); + this.detail = detail; + } +} + +export type ExchangeNullableErrorDetail = { + kind: 'unexpected'; + status: number; + raw: Response; + value(): Promise; +}; + +export class ExchangeNullableError extends ResponseError { + readonly detail: ExchangeNullableErrorDetail; + + constructor( + response: Response, + detail: ExchangeNullableErrorDetail, + msg = 'Response returned an error code', + ) { + super(response, msg); + this.detail = detail; + } +} + +export type SendOptionalDetailErrorDetail = { + kind: 'unexpected'; + status: number; + raw: Response; + value(): Promise; +}; + +export class SendOptionalDetailError extends ResponseError { + readonly detail: SendOptionalDetailErrorDetail; + + constructor( + response: Response, + detail: SendOptionalDetailErrorDetail, + msg = 'Response returned an error code', + ) { + super(response, msg); + this.detail = detail; + } +} + +export type SendOptionalInlineScalarErrorDetail = { + kind: 'unexpected'; + status: number; + raw: Response; + value(): Promise; +}; + +export class SendOptionalInlineScalarError extends ResponseError { + readonly detail: SendOptionalInlineScalarErrorDetail; + + constructor( + response: Response, + detail: SendOptionalInlineScalarErrorDetail, + msg = 'Response returned an error code', + ) { + super(response, msg); + this.detail = detail; + } +} + +export type SendOptionalScalarErrorDetail = { + kind: 'unexpected'; + status: number; + raw: Response; + value(): Promise; +}; + +export class SendOptionalScalarError extends ResponseError { + readonly detail: SendOptionalScalarErrorDetail; + + constructor( + response: Response, + detail: SendOptionalScalarErrorDetail, + msg = 'Response returned an error code', + ) { + super(response, msg); + this.detail = detail; + } +} + +export type SendRequiredScalarErrorDetail = { + kind: 'unexpected'; + status: number; + raw: Response; + value(): Promise; +}; + +export class SendRequiredScalarError extends ResponseError { + readonly detail: SendRequiredScalarErrorDetail; + + constructor( + response: Response, + detail: SendRequiredScalarErrorDetail, + msg = 'Response returned an error code', + ) { + super(response, msg); + this.detail = detail; + } +} + +export interface ConversionApiInterface { + exchangeMatrixRaw: ( + requestParameters: ApiExchangeMatrixRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ) => Promise; + exchangeMatrix: ( + requestParameters: ApiExchangeMatrixRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ) => Promise; + exchangeMultipartRaw: ( + requestParameters: ApiExchangeMultipartRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ) => Promise; + exchangeMultipart: ( + requestParameters: ApiExchangeMultipartRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ) => Promise; + exchangeNullableRaw: ( + requestParameters: ApiExchangeNullableRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ) => Promise; + exchangeNullable: ( + requestParameters: ApiExchangeNullableRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ) => Promise; + sendOptionalDetailRaw: ( + requestParameters: ApiSendOptionalDetailRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ) => Promise; + sendOptionalDetail: ( + requestParameters: ApiSendOptionalDetailRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ) => Promise; + sendOptionalInlineScalarRaw: ( + requestParameters: ApiSendOptionalInlineScalarRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ) => Promise; + sendOptionalInlineScalar: ( + requestParameters: ApiSendOptionalInlineScalarRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ) => Promise; + sendOptionalScalarRaw: ( + requestParameters: ApiSendOptionalScalarRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ) => Promise; + sendOptionalScalar: ( + requestParameters: ApiSendOptionalScalarRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ) => Promise; + sendRequiredScalarRaw: ( + requestParameters: ApiSendRequiredScalarRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ) => Promise; + sendRequiredScalar: ( + requestParameters: ApiSendRequiredScalarRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ) => Promise; +} + +export class ConversionApi extends BaseAPI implements ConversionApiInterface { + /** + * Initialize the API client + */ + constructor(configuration?: Configuration) { + super(configuration ?? DefaultConfig); + } + + async exchangeMatrixRaw( + requestParameters: ApiExchangeMatrixRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ): Promise { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { + throw new RequiredError( + 'body', + 'Required parameter "body" was null or undefined when calling exchangeMatrixRaw().', + ); + } + // Build path with path parameters + const urlPath = `/matrix`; + // Build query parameters + const queryParameters: HTTPQuery = {}; + // Build headers + const headerParameters: Record = { + 'Content-Type': 'application/json', + }; + + // Prepare request body + const requestBody = convert$35ToJSON(requestParameters['body']); + // Make request + const response = await this.request( + { + path: urlPath, + method: 'POST', + headers: headerParameters, + query: queryParameters, + body: requestBody, + }, + initOverrides, + ); + + // Handle responses + if (!(response.status >= 200 && response.status < 300)) { + const errorRaw = response.clone(); + if (response.status === 400) { + let errorValue: Promise | undefined; + throw new ExchangeMatrixError(response, { + kind: 'badRequest', + status: response.status as 400, + raw: response, + value: () => { + errorValue ??= new JSONApiResponse(errorRaw.clone(), (json) => { + return convert$35FromJSON(json as Tool$Wire[][]); + }).value(); + return errorValue; + }, + }); + } else { + let errorValue: Promise | undefined; + throw new ExchangeMatrixError(response, { + kind: 'unexpected', + status: response.status, + raw: response, + value: () => { + errorValue ??= errorRaw.clone().blob(); + return errorValue; + }, + }); + } + } + if (response.status === 200) { + return new JSONApiResponse(response, (json) => { + return convert$35FromJSON(json as Tool$Wire[][]); + }) as JSONApiResponse & { status: 200 }; + } else { + return new JSONApiResponse(response) as JSONApiResponse & HttpSuccessResponse; + } + } + + async exchangeMatrix( + requestParameters: ApiExchangeMatrixRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ): Promise { + const response = await this.exchangeMatrixRaw(requestParameters, initOverrides); + return (await response.value()) as Tool[][]; + } + + async exchangeMultipartRaw( + requestParameters: ApiExchangeMultipartRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ): Promise { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { + throw new RequiredError( + 'body', + 'Required parameter "body" was null or undefined when calling exchangeMultipartRaw().', + ); + } + // Build path with path parameters + const urlPath = `/multipart`; + // Build query parameters + const queryParameters: HTTPQuery = {}; + // Build headers + const headerParameters: Record = {}; + + // Prepare request body + const multipartBody = requestParameters['body']; + const multipartBoundary = '----openapi-nexus-' + Math.random().toString(16).slice(2); + const multipartChunks: Array = []; + headerParameters['Content-Type'] = 'multipart/form-data; boundary=' + multipartBoundary; + const multipartValue$0 = multipartBody['payloadValues']; + multipartChunks.push( + '--', + multipartBoundary, + '\r\nContent-Disposition: form-data; name="payload_values"\r\nContent-Type: application/json\r\n\r\n', + ); + multipartChunks.push(JSON.stringify(convert$39ToJSON(multipartValue$0))); + multipartChunks.push('\r\n'); + const multipartValue$1 = multipartBody['uploadFile']; + if (multipartValue$1 !== undefined && multipartValue$1 !== null) { + const multipartFilename$1 = uploadFileFilename(multipartValue$1, 'upload_file'); + multipartChunks.push( + '--', + multipartBoundary, + '\r\nContent-Disposition: form-data; name="upload_file"; filename="', + multipartHeaderValue(multipartFilename$1), + '"\r\nContent-Type: application/octet-stream\r\n\r\n', + ); + multipartChunks.push(uploadFileData(multipartValue$1)); + multipartChunks.push('\r\n'); + } + multipartChunks.push('--' + multipartBoundary + '--\r\n'); + const requestBody = new Blob(multipartChunks); + // Make request + const response = await this.request( + { + path: urlPath, + method: 'POST', + headers: headerParameters, + query: queryParameters, + body: requestBody, + }, + initOverrides, + ); + + // Handle responses + if (!(response.status >= 200 && response.status < 300)) { + const errorRaw = response.clone(); + { + let errorValue: Promise | undefined; + throw new ExchangeMultipartError(response, { + kind: 'unexpected', + status: response.status, + raw: response, + value: () => { + errorValue ??= errorRaw.clone().blob(); + return errorValue; + }, + }); + } + } + if (response.status === 204) { + return new VoidApiResponse(response) as VoidApiResponse & { status: 204 }; + } else { + return new VoidApiResponse(response) as VoidApiResponse & HttpSuccessResponse; + } + } + + async exchangeMultipart( + requestParameters: ApiExchangeMultipartRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ): Promise { + const response = await this.exchangeMultipartRaw(requestParameters, initOverrides); + return await response.value(); + } + + async exchangeNullableRaw( + requestParameters: ApiExchangeNullableRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ): Promise { + if (requestParameters['body'] === undefined) { + throw new RequiredError( + 'body', + 'Required parameter "body" was undefined when calling exchangeNullableRaw().', + ); + } + // Build path with path parameters + const urlPath = `/nullable`; + // Build query parameters + const queryParameters: HTTPQuery = {}; + // Build headers + const headerParameters: Record = { + 'Content-Type': 'application/json', + }; + + // Prepare request body + const requestBody = convert$37ToJSON(requestParameters['body']); + // Make request + const response = await this.request( + { + path: urlPath, + method: 'POST', + headers: headerParameters, + query: queryParameters, + body: requestBody, + }, + initOverrides, + ); + + // Handle responses + if (!(response.status >= 200 && response.status < 300)) { + const errorRaw = response.clone(); + { + let errorValue: Promise | undefined; + throw new ExchangeNullableError(response, { + kind: 'unexpected', + status: response.status, + raw: response, + value: () => { + errorValue ??= errorRaw.clone().blob(); + return errorValue; + }, + }); + } + } + if (response.status === 200) { + return new JSONApiResponse(response, (json) => { + return convert$37FromJSON(json as Tool$Wire[] | null); + }) as JSONApiResponse & { status: 200 }; + } else { + return new JSONApiResponse(response) as JSONApiResponse & HttpSuccessResponse; + } + } + + async exchangeNullable( + requestParameters: ApiExchangeNullableRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ): Promise { + const response = await this.exchangeNullableRaw(requestParameters, initOverrides); + return (await response.value()) as Tool[] | null; + } + + async sendOptionalDetailRaw( + requestParameters: ApiSendOptionalDetailRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ): Promise { + // Build path with path parameters + const urlPath = `/optional-detail`; + // Build query parameters + const queryParameters: HTTPQuery = {}; + // Build headers + const headerParameters: Record = { + 'Content-Type': 'application/json', + }; + + // Prepare request body + const requestBody = (() => { + const bodyValue = requestParameters['body']; + if (bodyValue === undefined) { + return undefined; + } + return nullableDetailToJSON(bodyValue); + })(); + // Make request + const response = await this.request( + { + path: urlPath, + method: 'POST', + headers: headerParameters, + query: queryParameters, + body: requestBody, + }, + initOverrides, + ); + + // Handle responses + if (!(response.status >= 200 && response.status < 300)) { + const errorRaw = response.clone(); + { + let errorValue: Promise | undefined; + throw new SendOptionalDetailError(response, { + kind: 'unexpected', + status: response.status, + raw: response, + value: () => { + errorValue ??= errorRaw.clone().blob(); + return errorValue; + }, + }); + } + } + if (response.status === 204) { + return new VoidApiResponse(response) as VoidApiResponse & { status: 204 }; + } else { + return new VoidApiResponse(response) as VoidApiResponse & HttpSuccessResponse; + } + } + + async sendOptionalDetail( + requestParameters: ApiSendOptionalDetailRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ): Promise { + const response = await this.sendOptionalDetailRaw(requestParameters, initOverrides); + return await response.value(); + } + + async sendOptionalInlineScalarRaw( + requestParameters: ApiSendOptionalInlineScalarRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ): Promise { + // Build path with path parameters + const urlPath = `/optional-inline-scalar`; + // Build query parameters + const queryParameters: HTTPQuery = {}; + // Build headers + const headerParameters: Record = { + 'Content-Type': 'application/json', + }; + + // Prepare request body + const requestBody = (() => { + const bodyValue = requestParameters['body']; + if (bodyValue === undefined) { + return undefined; + } + return sendOptionalInlineScalarRequestToJSON(bodyValue); + })(); + // Make request + const response = await this.request( + { + path: urlPath, + method: 'POST', + headers: headerParameters, + query: queryParameters, + body: requestBody, + }, + initOverrides, + ); + + // Handle responses + if (!(response.status >= 200 && response.status < 300)) { + const errorRaw = response.clone(); + { + let errorValue: Promise | undefined; + throw new SendOptionalInlineScalarError(response, { + kind: 'unexpected', + status: response.status, + raw: response, + value: () => { + errorValue ??= errorRaw.clone().blob(); + return errorValue; + }, + }); + } + } + if (response.status === 204) { + return new VoidApiResponse(response) as VoidApiResponse & { status: 204 }; + } else { + return new VoidApiResponse(response) as VoidApiResponse & HttpSuccessResponse; + } + } + + async sendOptionalInlineScalar( + requestParameters: ApiSendOptionalInlineScalarRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ): Promise { + const response = await this.sendOptionalInlineScalarRaw(requestParameters, initOverrides); + return await response.value(); + } + + async sendOptionalScalarRaw( + requestParameters: ApiSendOptionalScalarRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ): Promise { + // Build path with path parameters + const urlPath = `/optional-scalar`; + // Build query parameters + const queryParameters: HTTPQuery = {}; + // Build headers + const headerParameters: Record = { + 'Content-Type': 'application/json', + }; + + // Prepare request body + const requestBody = (() => { + const bodyValue = requestParameters['body']; + if (bodyValue === undefined) { + return undefined; + } + return nullableScalarInnerToJSON(bodyValue); + })(); + // Make request + const response = await this.request( + { + path: urlPath, + method: 'POST', + headers: headerParameters, + query: queryParameters, + body: requestBody, + }, + initOverrides, + ); + + // Handle responses + if (!(response.status >= 200 && response.status < 300)) { + const errorRaw = response.clone(); + { + let errorValue: Promise | undefined; + throw new SendOptionalScalarError(response, { + kind: 'unexpected', + status: response.status, + raw: response, + value: () => { + errorValue ??= errorRaw.clone().blob(); + return errorValue; + }, + }); + } + } + if (response.status === 204) { + return new VoidApiResponse(response) as VoidApiResponse & { status: 204 }; + } else { + return new VoidApiResponse(response) as VoidApiResponse & HttpSuccessResponse; + } + } + + async sendOptionalScalar( + requestParameters: ApiSendOptionalScalarRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ): Promise { + const response = await this.sendOptionalScalarRaw(requestParameters, initOverrides); + return await response.value(); + } + + async sendRequiredScalarRaw( + requestParameters: ApiSendRequiredScalarRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ): Promise { + if (requestParameters['body'] === undefined) { + throw new RequiredError( + 'body', + 'Required parameter "body" was undefined when calling sendRequiredScalarRaw().', + ); + } + // Build path with path parameters + const urlPath = `/required-scalar`; + // Build query parameters + const queryParameters: HTTPQuery = {}; + // Build headers + const headerParameters: Record = { + 'Content-Type': 'application/json', + }; + + // Prepare request body + const requestBody = nullableScalarInnerToJSON(requestParameters['body']); + // Make request + const response = await this.request( + { + path: urlPath, + method: 'POST', + headers: headerParameters, + query: queryParameters, + body: requestBody, + }, + initOverrides, + ); + + // Handle responses + if (!(response.status >= 200 && response.status < 300)) { + const errorRaw = response.clone(); + { + let errorValue: Promise | undefined; + throw new SendRequiredScalarError(response, { + kind: 'unexpected', + status: response.status, + raw: response, + value: () => { + errorValue ??= errorRaw.clone().blob(); + return errorValue; + }, + }); + } + } + if (response.status === 204) { + return new VoidApiResponse(response) as VoidApiResponse & { status: 204 }; + } else { + return new VoidApiResponse(response) as VoidApiResponse & HttpSuccessResponse; + } + } + + async sendRequiredScalar( + requestParameters: ApiSendRequiredScalarRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ): Promise { + const response = await this.sendRequiredScalarRaw(requestParameters, initOverrides); + return await response.value(); + } +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/apis/index.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/apis/index.ts.golden new file mode 100644 index 000000000..24b35e481 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/apis/index.ts.golden @@ -0,0 +1,39 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export type { + ApiExchangeMatrixRequest, + ExchangeMatrixRawResponse, + ExchangeMatrixErrorDetail, + ApiExchangeMultipartRequest, + ExchangeMultipartRawResponse, + ExchangeMultipartErrorDetail, + ApiExchangeNullableRequest, + ExchangeNullableRawResponse, + ExchangeNullableErrorDetail, + ApiSendOptionalDetailRequest, + SendOptionalDetailRawResponse, + SendOptionalDetailErrorDetail, + ApiSendOptionalInlineScalarRequest, + SendOptionalInlineScalarRawResponse, + SendOptionalInlineScalarErrorDetail, + ApiSendOptionalScalarRequest, + SendOptionalScalarRawResponse, + SendOptionalScalarErrorDetail, + ApiSendRequiredScalarRequest, + SendRequiredScalarRawResponse, + SendRequiredScalarErrorDetail, + ConversionApiInterface, +} from './ConversionApi'; +export { + ConversionApi, + ExchangeMatrixError, + ExchangeMultipartError, + ExchangeNullableError, + SendOptionalDetailError, + SendOptionalInlineScalarError, + SendOptionalScalarError, + SendRequiredScalarError, +} from './ConversionApi'; diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/index.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/index.ts.golden new file mode 100644 index 000000000..8ba5e6eb9 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/index.ts.golden @@ -0,0 +1,8 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export * from './runtime/runtime'; +export * from './apis'; +export * from './models'; diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/Adjacent.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/Adjacent.ts.golden new file mode 100644 index 000000000..a3580a4b6 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/Adjacent.ts.golden @@ -0,0 +1,43 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail, Detail$Wire } from './Detail'; +import { detailFromJSON, detailToJSON } from './Detail'; + +export type Adjacent$Wire = + | globalThis.Record<'event_type', 'empty'> + | (globalThis.Record<'event_type', 'data'> & globalThis.Record<'event_data', Detail$Wire>); + +export type Adjacent = + | globalThis.Record<'eventType', 'empty'> + | (globalThis.Record<'eventType', 'data'> & globalThis.Record<'eventData', Detail>); + +export function adjacentFromJSON(json: Adjacent$Wire): Adjacent { + if (json['event_type'] === 'empty') { + return { ['eventType']: 'empty' } as Adjacent; + } + if (json['event_type'] === 'data') { + return { + ['eventType']: 'data', + ['eventData']: detailFromJSON( + (json as globalThis.Record<'event_data', Detail$Wire>)['event_data'], + ), + } as Adjacent; + } + throw new Error('Unknown variant'); +} + +export function adjacentToJSON(value: Adjacent): Adjacent$Wire { + if (value['eventType'] === 'empty') { + return { ['event_type']: 'empty' } as Adjacent$Wire; + } + if (value['eventType'] === 'data') { + return { + ['event_type']: 'data', + ['event_data']: detailToJSON((value as globalThis.Record<'eventData', Detail>)['eventData']), + } as Adjacent$Wire; + } + throw new Error('Unknown variant'); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/AdjacentContainer.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/AdjacentContainer.ts.golden new file mode 100644 index 000000000..6f4872ec2 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/AdjacentContainer.ts.golden @@ -0,0 +1,60 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { convert$0FromJSON, convert$0ToJSON } from '../runtime/valueConversion$0'; +import type { Detail, Detail$Wire } from './Detail'; + +export type AdjacentContainer$Wire = + | globalThis.Record<'event_type', 'empty'> + | (globalThis.Record<'event_type', 'data'> & + globalThis.Record< + 'event_data', + readonly (globalThis.Record | string)[] + >); + +export type AdjacentContainer = + | globalThis.Record<'eventType', 'empty'> + | (globalThis.Record<'eventType', 'data'> & + globalThis.Record<'eventData', readonly (globalThis.Record | string)[]>); + +export function adjacentContainerFromJSON(json: AdjacentContainer$Wire): AdjacentContainer { + if (json['event_type'] === 'empty') { + return { ['eventType']: 'empty' } as AdjacentContainer; + } + if (json['event_type'] === 'data') { + return { + ['eventType']: 'data', + ['eventData']: convert$0FromJSON( + ( + json as globalThis.Record< + 'event_data', + readonly (globalThis.Record | string)[] + > + )['event_data'], + ), + } as AdjacentContainer; + } + throw new Error('Unknown variant'); +} + +export function adjacentContainerToJSON(value: AdjacentContainer): AdjacentContainer$Wire { + if (value['eventType'] === 'empty') { + return { ['event_type']: 'empty' } as AdjacentContainer$Wire; + } + if (value['eventType'] === 'data') { + return { + ['event_type']: 'data', + ['event_data']: convert$0ToJSON( + ( + value as globalThis.Record< + 'eventData', + readonly (globalThis.Record | string)[] + > + )['eventData'], + ), + } as AdjacentContainer$Wire; + } + throw new Error('Unknown variant'); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/AdjacentContainerEmpty.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/AdjacentContainerEmpty.ts.golden new file mode 100644 index 000000000..b59330795 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/AdjacentContainerEmpty.ts.golden @@ -0,0 +1,28 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface AdjacentContainerEmpty$Wire { + readonly event_type: 'empty'; +} + +export interface AdjacentContainerEmpty { + readonly eventType: 'empty'; +} + +export function adjacentContainerEmptyFromJSON( + json: AdjacentContainerEmpty$Wire, +): AdjacentContainerEmpty { + return { + eventType: json.event_type, + }; +} + +export function adjacentContainerEmptyToJSON( + value: AdjacentContainerEmpty, +): AdjacentContainerEmpty$Wire { + return { + event_type: value.eventType, + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/AdjacentEmpty.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/AdjacentEmpty.ts.golden new file mode 100644 index 000000000..665908893 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/AdjacentEmpty.ts.golden @@ -0,0 +1,24 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface AdjacentEmpty$Wire { + readonly event_type: 'empty'; +} + +export interface AdjacentEmpty { + readonly eventType: 'empty'; +} + +export function adjacentEmptyFromJSON(json: AdjacentEmpty$Wire): AdjacentEmpty { + return { + eventType: json.event_type, + }; +} + +export function adjacentEmptyToJSON(value: AdjacentEmpty): AdjacentEmpty$Wire { + return { + event_type: value.eventType, + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/AdjacentOrNumber.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/AdjacentOrNumber.ts.golden new file mode 100644 index 000000000..2cec6e028 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/AdjacentOrNumber.ts.golden @@ -0,0 +1,39 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$4FromJSON, + matches$4ToJSON, + matches$6FromJSON, + matches$6ToJSON, +} from '../runtime/unionMatch'; +import type { Adjacent, Adjacent$Wire } from './Adjacent'; +import { adjacentFromJSON, adjacentToJSON } from './Adjacent'; + +export type AdjacentOrNumber$Wire = Adjacent$Wire | number; + +export type AdjacentOrNumber = Adjacent | number; + +export function adjacentOrNumberFromJSON(json: AdjacentOrNumber$Wire): AdjacentOrNumber { + const candidate: unknown = json; + if (matches$6FromJSON(candidate)) { + return adjacentFromJSON(json as unknown as Adjacent$Wire); + } + if (matches$4FromJSON(candidate)) { + return json as unknown as number; + } + return json as unknown as AdjacentOrNumber; +} + +export function adjacentOrNumberToJSON(value: AdjacentOrNumber): AdjacentOrNumber$Wire { + const candidate: unknown = value; + if (matches$6ToJSON(candidate)) { + return adjacentToJSON(value as unknown as Adjacent); + } + if (matches$4ToJSON(candidate)) { + return value as unknown as number; + } + return value as unknown as AdjacentOrNumber$Wire; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/AdjacentPrimitive.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/AdjacentPrimitive.ts.golden new file mode 100644 index 000000000..55b59f406 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/AdjacentPrimitive.ts.golden @@ -0,0 +1,44 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { convert$3FromJSON, convert$3ToJSON } from '../runtime/valueConversion$3'; + +export type AdjacentPrimitive$Wire = + | globalThis.Record<'event_type', 'empty'> + | (globalThis.Record<'event_type', 'data'> & globalThis.Record<'event_data', readonly number[]>); + +export type AdjacentPrimitive = + | globalThis.Record<'eventType', 'empty'> + | (globalThis.Record<'eventType', 'data'> & globalThis.Record<'eventData', readonly number[]>); + +export function adjacentPrimitiveFromJSON(json: AdjacentPrimitive$Wire): AdjacentPrimitive { + if (json['event_type'] === 'empty') { + return { ['eventType']: 'empty' } as AdjacentPrimitive; + } + if (json['event_type'] === 'data') { + return { + ['eventType']: 'data', + ['eventData']: convert$3FromJSON( + (json as globalThis.Record<'event_data', readonly number[]>)['event_data'], + ), + } as AdjacentPrimitive; + } + throw new Error('Unknown variant'); +} + +export function adjacentPrimitiveToJSON(value: AdjacentPrimitive): AdjacentPrimitive$Wire { + if (value['eventType'] === 'empty') { + return { ['event_type']: 'empty' } as AdjacentPrimitive$Wire; + } + if (value['eventType'] === 'data') { + return { + ['event_type']: 'data', + ['event_data']: convert$3ToJSON( + (value as globalThis.Record<'eventData', readonly number[]>)['eventData'], + ), + } as AdjacentPrimitive$Wire; + } + throw new Error('Unknown variant'); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/AdjacentPrimitiveEmpty.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/AdjacentPrimitiveEmpty.ts.golden new file mode 100644 index 000000000..bcc6ac635 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/AdjacentPrimitiveEmpty.ts.golden @@ -0,0 +1,28 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface AdjacentPrimitiveEmpty$Wire { + readonly event_type: 'empty'; +} + +export interface AdjacentPrimitiveEmpty { + readonly eventType: 'empty'; +} + +export function adjacentPrimitiveEmptyFromJSON( + json: AdjacentPrimitiveEmpty$Wire, +): AdjacentPrimitiveEmpty { + return { + eventType: json.event_type, + }; +} + +export function adjacentPrimitiveEmptyToJSON( + value: AdjacentPrimitiveEmpty, +): AdjacentPrimitiveEmpty$Wire { + return { + event_type: value.eventType, + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/AliasChoice.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/AliasChoice.ts.golden new file mode 100644 index 000000000..ce96cc9a9 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/AliasChoice.ts.golden @@ -0,0 +1,41 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$11FromJSON, + matches$11ToJSON, + matches$1FromJSON, + matches$1ToJSON, +} from '../runtime/unionMatch'; +import type { Detail, Detail$Wire } from './Detail'; +import { detailFromJSON, detailToJSON } from './Detail'; +import type { ValueAlias, ValueAlias$Wire } from './ValueAlias'; +import { valueAliasFromJSON, valueAliasToJSON } from './ValueAlias'; + +export type AliasChoice$Wire = ValueAlias$Wire | Detail$Wire; + +export type AliasChoice = ValueAlias | Detail; + +export function aliasChoiceFromJSON(json: AliasChoice$Wire): AliasChoice { + const candidate: unknown = json; + if (matches$11FromJSON(candidate)) { + return valueAliasFromJSON(json as unknown as ValueAlias$Wire); + } + if (matches$1FromJSON(candidate)) { + return detailFromJSON(json as unknown as Detail$Wire); + } + return json as unknown as AliasChoice; +} + +export function aliasChoiceToJSON(value: AliasChoice): AliasChoice$Wire { + const candidate: unknown = value; + if (matches$11ToJSON(candidate)) { + return valueAliasToJSON(value as unknown as ValueAlias); + } + if (matches$1ToJSON(candidate)) { + return detailToJSON(value as unknown as Detail); + } + return value as unknown as AliasChoice$Wire; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/AliasOuter.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/AliasOuter.ts.golden new file mode 100644 index 000000000..4f146d040 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/AliasOuter.ts.golden @@ -0,0 +1,41 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$13FromJSON, + matches$13ToJSON, + matches$1FromJSON, + matches$1ToJSON, +} from '../runtime/unionMatch'; +import type { OpenAlias, OpenAlias$Wire } from './OpenAlias'; +import { openAliasFromJSON, openAliasToJSON } from './OpenAlias'; +import type { SearchTool, SearchTool$Wire } from './SearchTool'; +import { searchToolFromJSON, searchToolToJSON } from './SearchTool'; + +export type AliasOuter$Wire = unknown; + +export type AliasOuter = unknown; + +export function aliasOuterFromJSON(json: AliasOuter$Wire): AliasOuter { + const candidate: unknown = json; + if (matches$1FromJSON(candidate)) { + return openAliasFromJSON(json as unknown as OpenAlias$Wire); + } + if (matches$13FromJSON(candidate)) { + return searchToolFromJSON(json as unknown as SearchTool$Wire); + } + return openAliasFromJSON(json as unknown as OpenAlias$Wire); +} + +export function aliasOuterToJSON(value: AliasOuter): AliasOuter$Wire { + const candidate: unknown = value; + if (matches$1ToJSON(candidate)) { + return openAliasToJSON(value as unknown as OpenAlias); + } + if (matches$13ToJSON(candidate)) { + return searchToolToJSON(value as unknown as SearchTool); + } + return openAliasToJSON(value as unknown as OpenAlias); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/AlreadyNullableEnumParent.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/AlreadyNullableEnumParent.ts.golden new file mode 100644 index 000000000..3ade40c02 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/AlreadyNullableEnumParent.ts.golden @@ -0,0 +1,31 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { convert$5FromJSON, convert$5ToJSON } from '../runtime/valueConversion$5'; +import type { NullableScalarInner, NullableScalarInner$Wire } from './NullableScalarInner'; + +export interface AlreadyNullableEnumParent$Wire { + readonly scalar_value: NullableScalarInner$Wire | null; +} + +export interface AlreadyNullableEnumParent { + readonly scalarValue: NullableScalarInner | null; +} + +export function alreadyNullableEnumParentFromJSON( + json: AlreadyNullableEnumParent$Wire, +): AlreadyNullableEnumParent { + return { + scalarValue: convert$5FromJSON(json.scalar_value), + }; +} + +export function alreadyNullableEnumParentToJSON( + value: AlreadyNullableEnumParent, +): AlreadyNullableEnumParent$Wire { + return { + scalar_value: convert$5ToJSON(value.scalarValue), + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/AlreadyNullableOuterEnumParent.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/AlreadyNullableOuterEnumParent.ts.golden new file mode 100644 index 000000000..ba67c7849 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/AlreadyNullableOuterEnumParent.ts.golden @@ -0,0 +1,31 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { convert$6FromJSON, convert$6ToJSON } from '../runtime/valueConversion$6'; +import type { NullableScalarInner, NullableScalarInner$Wire } from './NullableScalarInner'; + +export interface AlreadyNullableOuterEnumParent$Wire { + readonly scalar_value: NullableScalarInner$Wire | null; +} + +export interface AlreadyNullableOuterEnumParent { + readonly scalarValue: NullableScalarInner | null; +} + +export function alreadyNullableOuterEnumParentFromJSON( + json: AlreadyNullableOuterEnumParent$Wire, +): AlreadyNullableOuterEnumParent { + return { + scalarValue: convert$6FromJSON(json.scalar_value), + }; +} + +export function alreadyNullableOuterEnumParentToJSON( + value: AlreadyNullableOuterEnumParent, +): AlreadyNullableOuterEnumParent$Wire { + return { + scalar_value: convert$6ToJSON(value.scalarValue), + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/AlternativeTool.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/AlternativeTool.ts.golden new file mode 100644 index 000000000..ea69f7eec --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/AlternativeTool.ts.golden @@ -0,0 +1,32 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface AlternativeTool$Wire { + readonly input_schema: globalThis.Record; + readonly name: string; + readonly second_option?: boolean; +} + +export interface AlternativeTool { + readonly inputSchema: globalThis.Record; + readonly name: string; + readonly secondOption?: boolean; +} + +export function alternativeToolFromJSON(json: AlternativeTool$Wire): AlternativeTool { + return { + inputSchema: json.input_schema, + name: json.name, + secondOption: json.second_option, + }; +} + +export function alternativeToolToJSON(value: AlternativeTool): AlternativeTool$Wire { + return { + input_schema: value.inputSchema, + name: value.name, + second_option: value.secondOption, + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/AmbiguousInner.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/AmbiguousInner.ts.golden new file mode 100644 index 000000000..a51c0088a --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/AmbiguousInner.ts.golden @@ -0,0 +1,41 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$17FromJSON, + matches$17ToJSON, + matches$1FromJSON, + matches$1ToJSON, +} from '../runtime/unionMatch'; +import type { Detail, Detail$Wire } from './Detail'; +import { detailFromJSON, detailToJSON } from './Detail'; +import type { OtherDetail, OtherDetail$Wire } from './OtherDetail'; +import { otherDetailFromJSON, otherDetailToJSON } from './OtherDetail'; + +export type AmbiguousInner$Wire = unknown; + +export type AmbiguousInner = unknown; + +export function ambiguousInnerFromJSON(json: AmbiguousInner$Wire): AmbiguousInner { + const candidate: unknown = json; + if (matches$1FromJSON(candidate)) { + return detailFromJSON(json as unknown as Detail$Wire); + } + if (matches$17FromJSON(candidate)) { + return otherDetailFromJSON(json as unknown as OtherDetail$Wire); + } + return json as unknown as unknown; +} + +export function ambiguousInnerToJSON(value: AmbiguousInner): AmbiguousInner$Wire { + const candidate: unknown = value; + if (matches$1ToJSON(candidate)) { + return detailToJSON(value as unknown as Detail); + } + if (matches$17ToJSON(candidate)) { + return otherDetailToJSON(value as unknown as OtherDetail); + } + return value as unknown as unknown; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/AmbiguousMap.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/AmbiguousMap.ts.golden new file mode 100644 index 000000000..88def1499 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/AmbiguousMap.ts.golden @@ -0,0 +1,40 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { ambiguousInnerFromJSON, ambiguousInnerToJSON } from './AmbiguousInner'; + +export interface AmbiguousMap$Wire extends Readonly> {} + +export interface AmbiguousMap extends Readonly> {} + +export function ambiguousMapFromJSON(json: AmbiguousMap$Wire): AmbiguousMap { + const sourceEntries = Object.entries(json); + const additionalEntries = sourceEntries; + const additionalProperties = Object.fromEntries( + additionalEntries.map(([key, value]) => { + const additionalValue = value; + return [key, ambiguousInnerFromJSON(additionalValue)] as const; + }), + ); + const result = { + ...additionalProperties, + }; + return result; +} + +export function ambiguousMapToJSON(value: AmbiguousMap): AmbiguousMap$Wire { + const sourceEntries = Object.entries(value); + const additionalEntries = sourceEntries; + const additionalProperties = Object.fromEntries( + additionalEntries.map(([key, value]) => { + const additionalValue = value; + return [key, ambiguousInnerToJSON(additionalValue)] as const; + }), + ); + const result = { + ...additionalProperties, + }; + return result; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/AmbiguousMapOuter.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/AmbiguousMapOuter.ts.golden new file mode 100644 index 000000000..6de2d8aab --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/AmbiguousMapOuter.ts.golden @@ -0,0 +1,28 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { matches$18FromJSON, matches$18ToJSON } from '../runtime/unionMatch'; +import type { AmbiguousMap, AmbiguousMap$Wire } from './AmbiguousMap'; +import { ambiguousMapFromJSON, ambiguousMapToJSON } from './AmbiguousMap'; + +export type AmbiguousMapOuter$Wire = unknown; + +export type AmbiguousMapOuter = unknown; + +export function ambiguousMapOuterFromJSON(json: AmbiguousMapOuter$Wire): AmbiguousMapOuter { + const candidate: unknown = json; + if (matches$18FromJSON(candidate)) { + return ambiguousMapFromJSON(json as unknown as AmbiguousMap$Wire); + } + return json as unknown as unknown; +} + +export function ambiguousMapOuterToJSON(value: AmbiguousMapOuter): AmbiguousMapOuter$Wire { + const candidate: unknown = value; + if (matches$18ToJSON(candidate)) { + return ambiguousMapToJSON(value as unknown as AmbiguousMap); + } + return value as unknown as unknown; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/AmbiguousOuter.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/AmbiguousOuter.ts.golden new file mode 100644 index 000000000..3978565f5 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/AmbiguousOuter.ts.golden @@ -0,0 +1,39 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$21FromJSON, + matches$21ToJSON, + matches$2FromJSON, + matches$2ToJSON, +} from '../runtime/unionMatch'; +import type { AmbiguousInner, AmbiguousInner$Wire } from './AmbiguousInner'; +import { ambiguousInnerFromJSON, ambiguousInnerToJSON } from './AmbiguousInner'; + +export type AmbiguousOuter$Wire = unknown; + +export type AmbiguousOuter = unknown; + +export function ambiguousOuterFromJSON(json: AmbiguousOuter$Wire): AmbiguousOuter { + const candidate: unknown = json; + if (matches$21FromJSON(candidate)) { + return ambiguousInnerFromJSON(json as unknown as AmbiguousInner$Wire); + } + if (matches$2FromJSON(candidate)) { + return json as unknown as string; + } + return ambiguousInnerFromJSON(json as unknown as AmbiguousInner$Wire); +} + +export function ambiguousOuterToJSON(value: AmbiguousOuter): AmbiguousOuter$Wire { + const candidate: unknown = value; + if (matches$21ToJSON(candidate)) { + return ambiguousInnerToJSON(value as unknown as AmbiguousInner); + } + if (matches$2ToJSON(candidate)) { + return value as unknown as string; + } + return ambiguousInnerToJSON(value as unknown as AmbiguousInner); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/AmbiguousTool.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/AmbiguousTool.ts.golden new file mode 100644 index 000000000..6c572ed0d --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/AmbiguousTool.ts.golden @@ -0,0 +1,41 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$23FromJSON, + matches$23ToJSON, + matches$24FromJSON, + matches$24ToJSON, +} from '../runtime/unionMatch'; +import type { AlternativeTool, AlternativeTool$Wire } from './AlternativeTool'; +import { alternativeToolFromJSON, alternativeToolToJSON } from './AlternativeTool'; +import type { ClientTool, ClientTool$Wire } from './ClientTool'; +import { clientToolFromJSON, clientToolToJSON } from './ClientTool'; + +export type AmbiguousTool$Wire = ClientTool$Wire | AlternativeTool$Wire; + +export type AmbiguousTool = ClientTool | AlternativeTool; + +export function ambiguousToolFromJSON(json: AmbiguousTool$Wire): AmbiguousTool { + const candidate: unknown = json; + if (matches$23FromJSON(candidate)) { + return clientToolFromJSON(json as unknown as ClientTool$Wire); + } + if (matches$24FromJSON(candidate)) { + return alternativeToolFromJSON(json as unknown as AlternativeTool$Wire); + } + return json as unknown as AmbiguousTool; +} + +export function ambiguousToolToJSON(value: AmbiguousTool): AmbiguousTool$Wire { + const candidate: unknown = value; + if (matches$23ToJSON(candidate)) { + return clientToolToJSON(value as unknown as ClientTool); + } + if (matches$24ToJSON(candidate)) { + return alternativeToolToJSON(value as unknown as AlternativeTool); + } + return value as unknown as AmbiguousTool$Wire; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/ArrayPayload.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/ArrayPayload.ts.golden new file mode 100644 index 000000000..cbde8a749 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/ArrayPayload.ts.golden @@ -0,0 +1,24 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface ArrayPayload$Wire { + readonly wire_items: readonly string[]; +} + +export interface ArrayPayload { + readonly wireItems: readonly string[]; +} + +export function arrayPayloadFromJSON(json: ArrayPayload$Wire): ArrayPayload { + return { + wireItems: json.wire_items, + }; +} + +export function arrayPayloadToJSON(value: ArrayPayload): ArrayPayload$Wire { + return { + wire_items: value.wireItems, + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/ArrayPayloadOrBoolean.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/ArrayPayloadOrBoolean.ts.golden new file mode 100644 index 000000000..9d13d9826 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/ArrayPayloadOrBoolean.ts.golden @@ -0,0 +1,43 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$25FromJSON, + matches$25ToJSON, + matches$5FromJSON, + matches$5ToJSON, +} from '../runtime/unionMatch'; +import type { ArrayPayload, ArrayPayload$Wire } from './ArrayPayload'; +import { arrayPayloadFromJSON, arrayPayloadToJSON } from './ArrayPayload'; + +export type ArrayPayloadOrBoolean$Wire = ArrayPayload$Wire | boolean; + +export type ArrayPayloadOrBoolean = ArrayPayload | boolean; + +export function arrayPayloadOrBooleanFromJSON( + json: ArrayPayloadOrBoolean$Wire, +): ArrayPayloadOrBoolean { + const candidate: unknown = json; + if (matches$25FromJSON(candidate)) { + return arrayPayloadFromJSON(json as unknown as ArrayPayload$Wire); + } + if (matches$5FromJSON(candidate)) { + return json as unknown as boolean; + } + return json as unknown as ArrayPayloadOrBoolean; +} + +export function arrayPayloadOrBooleanToJSON( + value: ArrayPayloadOrBoolean, +): ArrayPayloadOrBoolean$Wire { + const candidate: unknown = value; + if (matches$25ToJSON(candidate)) { + return arrayPayloadToJSON(value as unknown as ArrayPayload); + } + if (matches$5ToJSON(candidate)) { + return value as unknown as boolean; + } + return value as unknown as ArrayPayloadOrBoolean$Wire; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/ArrayWrapped.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/ArrayWrapped.ts.golden new file mode 100644 index 000000000..d71b89dfa --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/ArrayWrapped.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { DetailArrayOrObject, DetailArrayOrObject$Wire } from './DetailArrayOrObject'; +import { detailArrayOrObjectFromJSON, detailArrayOrObjectToJSON } from './DetailArrayOrObject'; + +export type ArrayWrapped$Wire = DetailArrayOrObject$Wire; + +export type ArrayWrapped = DetailArrayOrObject; + +export function arrayWrappedFromJSON(json: ArrayWrapped$Wire): ArrayWrapped { + return detailArrayOrObjectFromJSON(json); +} + +export function arrayWrappedToJSON(value: ArrayWrapped): ArrayWrapped$Wire { + return detailArrayOrObjectToJSON(value); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/BinaryModel.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/BinaryModel.ts.golden new file mode 100644 index 000000000..4febefc7f --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/BinaryModel.ts.golden @@ -0,0 +1,28 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface BinaryModel$Wire { + readonly binary_data: Blob | File; + readonly field_name: string; +} + +export interface BinaryModel { + readonly binaryData: Blob | File; + readonly fieldName: string; +} + +export function binaryModelFromJSON(json: BinaryModel$Wire): BinaryModel { + return { + binaryData: json.binary_data, + fieldName: json.field_name, + }; +} + +export function binaryModelToJSON(value: BinaryModel): BinaryModel$Wire { + return { + binary_data: value.binaryData, + field_name: value.fieldName, + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/BinaryUnion.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/BinaryUnion.ts.golden new file mode 100644 index 000000000..06768cdc8 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/BinaryUnion.ts.golden @@ -0,0 +1,39 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$27FromJSON, + matches$27ToJSON, + matches$5FromJSON, + matches$5ToJSON, +} from '../runtime/unionMatch'; +import type { BinaryModel, BinaryModel$Wire } from './BinaryModel'; +import { binaryModelFromJSON, binaryModelToJSON } from './BinaryModel'; + +export type BinaryUnion$Wire = BinaryModel$Wire | boolean; + +export type BinaryUnion = BinaryModel | boolean; + +export function binaryUnionFromJSON(json: BinaryUnion$Wire): BinaryUnion { + const candidate: unknown = json; + if (matches$27FromJSON(candidate)) { + return binaryModelFromJSON(json as unknown as BinaryModel$Wire); + } + if (matches$5FromJSON(candidate)) { + return json as unknown as boolean; + } + return json as unknown as BinaryUnion; +} + +export function binaryUnionToJSON(value: BinaryUnion): BinaryUnion$Wire { + const candidate: unknown = value; + if (matches$27ToJSON(candidate)) { + return binaryModelToJSON(value as unknown as BinaryModel); + } + if (matches$5ToJSON(candidate)) { + return value as unknown as boolean; + } + return value as unknown as BinaryUnion$Wire; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/ClientTool.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/ClientTool.ts.golden new file mode 100644 index 000000000..8f4d94ac9 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/ClientTool.ts.golden @@ -0,0 +1,28 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface ClientTool$Wire { + readonly input_schema: globalThis.Record; + readonly name: string; +} + +export interface ClientTool { + readonly inputSchema: globalThis.Record; + readonly name: string; +} + +export function clientToolFromJSON(json: ClientTool$Wire): ClientTool { + return { + inputSchema: json.input_schema, + name: json.name, + }; +} + +export function clientToolToJSON(value: ClientTool): ClientTool$Wire { + return { + input_schema: value.inputSchema, + name: value.name, + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/ConstrainedOuterEnumParent.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/ConstrainedOuterEnumParent.ts.golden new file mode 100644 index 000000000..bfb4340d5 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/ConstrainedOuterEnumParent.ts.golden @@ -0,0 +1,31 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { convert$7FromJSON, convert$7ToJSON } from '../runtime/valueConversion$7'; +import type { NullableScalarInner, NullableScalarInner$Wire } from './NullableScalarInner'; + +export interface ConstrainedOuterEnumParent$Wire { + readonly scalar_value: NullableScalarInner$Wire | null; +} + +export interface ConstrainedOuterEnumParent { + readonly scalarValue: NullableScalarInner | null; +} + +export function constrainedOuterEnumParentFromJSON( + json: ConstrainedOuterEnumParent$Wire, +): ConstrainedOuterEnumParent { + return { + scalarValue: convert$7FromJSON(json.scalar_value), + }; +} + +export function constrainedOuterEnumParentToJSON( + value: ConstrainedOuterEnumParent, +): ConstrainedOuterEnumParent$Wire { + return { + scalar_value: convert$7ToJSON(value.scalarValue), + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/ContainerOuterEnum.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/ContainerOuterEnum.ts.golden new file mode 100644 index 000000000..d800e4094 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/ContainerOuterEnum.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { convert$8FromJSON, convert$8ToJSON } from '../runtime/valueConversion$8'; +import type { NullableScalarInner, NullableScalarInner$Wire } from './NullableScalarInner'; + +export type ContainerOuterEnum$Wire = readonly (NullableScalarInner$Wire | null)[]; + +export type ContainerOuterEnum = readonly (NullableScalarInner | null)[]; + +export function containerOuterEnumFromJSON(json: ContainerOuterEnum$Wire): ContainerOuterEnum { + return convert$8FromJSON(json); +} + +export function containerOuterEnumToJSON(value: ContainerOuterEnum): ContainerOuterEnum$Wire { + return convert$8ToJSON(value); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/Detail.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/Detail.ts.golden new file mode 100644 index 000000000..f9714af2f --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/Detail.ts.golden @@ -0,0 +1,24 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface Detail$Wire { + readonly wire_name: string; +} + +export interface Detail { + readonly wireName: string; +} + +export function detailFromJSON(json: Detail$Wire): Detail { + return { + wireName: json.wire_name, + }; +} + +export function detailToJSON(value: Detail): Detail$Wire { + return { + wire_name: value.wireName, + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/DetailArrayOrObject.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/DetailArrayOrObject.ts.golden new file mode 100644 index 000000000..995bdebf0 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/DetailArrayOrObject.ts.golden @@ -0,0 +1,41 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$1FromJSON, + matches$1ToJSON, + matches$29FromJSON, + matches$29ToJSON, +} from '../runtime/unionMatch'; +import type { Value$11, Value$11Wire } from '../runtime/valueConversion$11'; +import { convert$11FromJSON, convert$11ToJSON } from '../runtime/valueConversion$11'; +import type { Detail, Detail$Wire } from './Detail'; +import { detailFromJSON, detailToJSON } from './Detail'; + +export type DetailArrayOrObject$Wire = readonly Detail$Wire[] | Detail$Wire; + +export type DetailArrayOrObject = readonly Detail[] | Detail; + +export function detailArrayOrObjectFromJSON(json: DetailArrayOrObject$Wire): DetailArrayOrObject { + const candidate: unknown = json; + if (matches$29FromJSON(candidate)) { + return convert$11FromJSON(json as unknown as Value$11Wire); + } + if (matches$1FromJSON(candidate)) { + return detailFromJSON(json as unknown as Detail$Wire); + } + return json as unknown as DetailArrayOrObject; +} + +export function detailArrayOrObjectToJSON(value: DetailArrayOrObject): DetailArrayOrObject$Wire { + const candidate: unknown = value; + if (matches$29ToJSON(candidate)) { + return convert$11ToJSON(value as unknown as Value$11); + } + if (matches$1ToJSON(candidate)) { + return detailToJSON(value as unknown as Detail); + } + return value as unknown as DetailArrayOrObject$Wire; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/DictionaryAlias.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/DictionaryAlias.ts.golden new file mode 100644 index 000000000..2c26c5575 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/DictionaryAlias.ts.golden @@ -0,0 +1,41 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail, Detail$Wire } from './Detail'; +import { detailFromJSON, detailToJSON } from './Detail'; + +export type DictionaryAlias$Wire = Readonly>; + +export type DictionaryAlias = Readonly>; + +export function dictionaryAliasFromJSON(json: DictionaryAlias$Wire): DictionaryAlias { + const sourceEntries = Object.entries(json); + const additionalEntries = sourceEntries; + const additionalProperties = Object.fromEntries( + additionalEntries.map(([key, value]) => { + const additionalValue = value; + return [key, detailFromJSON(additionalValue)] as const; + }), + ); + const result = { + ...additionalProperties, + }; + return result; +} + +export function dictionaryAliasToJSON(value: DictionaryAlias): DictionaryAlias$Wire { + const sourceEntries = Object.entries(value); + const additionalEntries = sourceEntries; + const additionalProperties = Object.fromEntries( + additionalEntries.map(([key, value]) => { + const additionalValue = value; + return [key, detailToJSON(additionalValue)] as const; + }), + ); + const result = { + ...additionalProperties, + }; + return result; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/EnumFreeOuterEnumParent.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/EnumFreeOuterEnumParent.ts.golden new file mode 100644 index 000000000..7a468e9e5 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/EnumFreeOuterEnumParent.ts.golden @@ -0,0 +1,31 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { convert$5FromJSON, convert$5ToJSON } from '../runtime/valueConversion$5'; +import type { NullableScalarInner, NullableScalarInner$Wire } from './NullableScalarInner'; + +export interface EnumFreeOuterEnumParent$Wire { + readonly scalar_value: NullableScalarInner$Wire | null; +} + +export interface EnumFreeOuterEnumParent { + readonly scalarValue: NullableScalarInner | null; +} + +export function enumFreeOuterEnumParentFromJSON( + json: EnumFreeOuterEnumParent$Wire, +): EnumFreeOuterEnumParent { + return { + scalarValue: json.scalar_value === null ? null : convert$5FromJSON(json.scalar_value), + }; +} + +export function enumFreeOuterEnumParentToJSON( + value: EnumFreeOuterEnumParent, +): EnumFreeOuterEnumParent$Wire { + return { + scalar_value: value.scalarValue === null ? null : convert$5ToJSON(value.scalarValue), + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/ExchangeMultipartMultipartRequestBody.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/ExchangeMultipartMultipartRequestBody.ts.golden new file mode 100644 index 000000000..67e02464e --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/ExchangeMultipartMultipartRequestBody.ts.golden @@ -0,0 +1,12 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { UploadFileInput } from '../runtime/runtime'; +import type { Detail } from './Detail'; + +export interface ExchangeMultipartMultipartRequestBody { + payloadValues: readonly (globalThis.Record | string)[]; + uploadFile?: UploadFileInput; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/ExchangeMultipartRequest.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/ExchangeMultipartRequest.ts.golden new file mode 100644 index 000000000..5c0478b0d --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/ExchangeMultipartRequest.ts.golden @@ -0,0 +1,35 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { convert$0FromJSON, convert$0ToJSON } from '../runtime/valueConversion$0'; +import type { Detail, Detail$Wire } from './Detail'; + +export interface ExchangeMultipartRequest$Wire { + readonly payload_values: readonly (globalThis.Record | string)[]; + readonly upload_file?: Blob | File; +} + +export interface ExchangeMultipartRequest { + readonly payloadValues: readonly (globalThis.Record | string)[]; + readonly uploadFile?: Blob | File; +} + +export function exchangeMultipartRequestFromJSON( + json: ExchangeMultipartRequest$Wire, +): ExchangeMultipartRequest { + return { + payloadValues: convert$0FromJSON(json.payload_values), + uploadFile: json.upload_file, + }; +} + +export function exchangeMultipartRequestToJSON( + value: ExchangeMultipartRequest, +): ExchangeMultipartRequest$Wire { + return { + payload_values: convert$0ToJSON(value.payloadValues), + upload_file: value.uploadFile, + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/ExplicitNullOverlap.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/ExplicitNullOverlap.ts.golden new file mode 100644 index 000000000..9dd79b7b1 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/ExplicitNullOverlap.ts.golden @@ -0,0 +1,47 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$30FromJSON, + matches$30ToJSON, + matches$32FromJSON, + matches$32ToJSON, +} from '../runtime/unionMatch'; +import type { NullableDetail, NullableDetail$Wire } from './NullableDetail'; +import { nullableDetailFromJSON, nullableDetailToJSON } from './NullableDetail'; +import type { NullableTool, NullableTool$Wire } from './NullableTool'; +import { nullableToolFromJSON, nullableToolToJSON } from './NullableTool'; + +export type ExplicitNullOverlap$Wire = NullableDetail$Wire | NullableTool$Wire | null; + +export type ExplicitNullOverlap = NullableDetail | NullableTool | null; + +export function explicitNullOverlapFromJSON(json: ExplicitNullOverlap$Wire): ExplicitNullOverlap { + const candidate: unknown = json; + if (matches$30FromJSON(candidate)) { + return nullableDetailFromJSON(json as unknown as NullableDetail$Wire); + } + if (matches$32FromJSON(candidate)) { + return nullableToolFromJSON(json as unknown as NullableTool$Wire); + } + if (candidate === null) { + return null; + } + return json as unknown as ExplicitNullOverlap; +} + +export function explicitNullOverlapToJSON(value: ExplicitNullOverlap): ExplicitNullOverlap$Wire { + const candidate: unknown = value; + if (matches$30ToJSON(candidate)) { + return nullableDetailToJSON(value as unknown as NullableDetail); + } + if (matches$32ToJSON(candidate)) { + return nullableToolToJSON(value as unknown as NullableTool); + } + if (candidate === null) { + return null; + } + return value as unknown as ExplicitNullOverlap$Wire; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/ExplicitNullableEnumChoice.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/ExplicitNullableEnumChoice.ts.golden new file mode 100644 index 000000000..7f7aa277e --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/ExplicitNullableEnumChoice.ts.golden @@ -0,0 +1,43 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$1FromJSON, + matches$1ToJSON, + matches$33FromJSON, + matches$33ToJSON, +} from '../runtime/unionMatch'; +import type { Detail, Detail$Wire } from './Detail'; +import { detailFromJSON, detailToJSON } from './Detail'; + +export type ExplicitNullableEnumChoice$Wire = readonly (number | null)[] | Detail$Wire; + +export type ExplicitNullableEnumChoice = readonly (number | null)[] | Detail; + +export function explicitNullableEnumChoiceFromJSON( + json: ExplicitNullableEnumChoice$Wire, +): ExplicitNullableEnumChoice { + const candidate: unknown = json; + if (matches$33FromJSON(candidate)) { + return json as unknown as readonly (number | null)[]; + } + if (matches$1FromJSON(candidate)) { + return detailFromJSON(json as unknown as Detail$Wire); + } + return json as unknown as ExplicitNullableEnumChoice; +} + +export function explicitNullableEnumChoiceToJSON( + value: ExplicitNullableEnumChoice, +): ExplicitNullableEnumChoice$Wire { + const candidate: unknown = value; + if (matches$33ToJSON(candidate)) { + return value as unknown as readonly (number | null)[]; + } + if (matches$1ToJSON(candidate)) { + return detailToJSON(value as unknown as Detail); + } + return value as unknown as ExplicitNullableEnumChoice$Wire; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/InlineDictionaryValues.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/InlineDictionaryValues.ts.golden new file mode 100644 index 000000000..a70b45a6c --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/InlineDictionaryValues.ts.golden @@ -0,0 +1,48 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$37FromJSON, + matches$37ToJSON, + matches$5FromJSON, + matches$5ToJSON, +} from '../runtime/unionMatch'; +import type { Value$13, Value$13Wire } from '../runtime/valueConversion$13'; +import { convert$13FromJSON, convert$13ToJSON } from '../runtime/valueConversion$13'; +import type { Detail, Detail$Wire } from './Detail'; + +export type InlineDictionaryValues$Wire = + | globalThis.Record | string> + | boolean; + +export type InlineDictionaryValues = + | globalThis.Record | string> + | boolean; + +export function inlineDictionaryValuesFromJSON( + json: InlineDictionaryValues$Wire, +): InlineDictionaryValues { + const candidate: unknown = json; + if (matches$37FromJSON(candidate)) { + return convert$13FromJSON(json as unknown as Value$13Wire); + } + if (matches$5FromJSON(candidate)) { + return json as unknown as boolean; + } + return json as unknown as InlineDictionaryValues; +} + +export function inlineDictionaryValuesToJSON( + value: InlineDictionaryValues, +): InlineDictionaryValues$Wire { + const candidate: unknown = value; + if (matches$37ToJSON(candidate)) { + return convert$13ToJSON(value as unknown as Value$13); + } + if (matches$5ToJSON(candidate)) { + return value as unknown as boolean; + } + return value as unknown as InlineDictionaryValues$Wire; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/InlineMixedValues.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/InlineMixedValues.ts.golden new file mode 100644 index 000000000..3980e2194 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/InlineMixedValues.ts.golden @@ -0,0 +1,43 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$11FromJSON, + matches$11ToJSON, + matches$1FromJSON, + matches$1ToJSON, +} from '../runtime/unionMatch'; +import type { Value$0, Value$0Wire } from '../runtime/valueConversion$0'; +import { convert$0FromJSON, convert$0ToJSON } from '../runtime/valueConversion$0'; +import type { Detail, Detail$Wire } from './Detail'; +import { detailFromJSON, detailToJSON } from './Detail'; + +export type InlineMixedValues$Wire = + | readonly (globalThis.Record | string)[] + | Detail$Wire; + +export type InlineMixedValues = readonly (globalThis.Record | string)[] | Detail; + +export function inlineMixedValuesFromJSON(json: InlineMixedValues$Wire): InlineMixedValues { + const candidate: unknown = json; + if (matches$11FromJSON(candidate)) { + return convert$0FromJSON(json as unknown as Value$0Wire); + } + if (matches$1FromJSON(candidate)) { + return detailFromJSON(json as unknown as Detail$Wire); + } + return json as unknown as InlineMixedValues; +} + +export function inlineMixedValuesToJSON(value: InlineMixedValues): InlineMixedValues$Wire { + const candidate: unknown = value; + if (matches$11ToJSON(candidate)) { + return convert$0ToJSON(value as unknown as Value$0); + } + if (matches$1ToJSON(candidate)) { + return detailToJSON(value as unknown as Detail); + } + return value as unknown as InlineMixedValues$Wire; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/InlineNumericValues.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/InlineNumericValues.ts.golden new file mode 100644 index 000000000..656bc0afa --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/InlineNumericValues.ts.golden @@ -0,0 +1,29 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { matches$38FromJSON, matches$38ToJSON } from '../runtime/unionMatch'; +import type { Value$15, Value$15Wire } from '../runtime/valueConversion$15'; +import { convert$15FromJSON, convert$15ToJSON } from '../runtime/valueConversion$15'; +import type { Detail, Detail$Wire } from './Detail'; + +export type InlineNumericValues$Wire = readonly (globalThis.Record | number)[]; + +export type InlineNumericValues = readonly (globalThis.Record | number)[]; + +export function inlineNumericValuesFromJSON(json: InlineNumericValues$Wire): InlineNumericValues { + const candidate: unknown = json; + if (matches$38FromJSON(candidate)) { + return convert$15FromJSON(json as unknown as Value$15Wire); + } + return json as unknown as InlineNumericValues; +} + +export function inlineNumericValuesToJSON(value: InlineNumericValues): InlineNumericValues$Wire { + const candidate: unknown = value; + if (matches$38ToJSON(candidate)) { + return convert$15ToJSON(value as unknown as Value$15); + } + return value as unknown as InlineNumericValues$Wire; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/InlineProperty.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/InlineProperty.ts.golden new file mode 100644 index 000000000..8a61a3876 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/InlineProperty.ts.golden @@ -0,0 +1,27 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { convert$0FromJSON, convert$0ToJSON } from '../runtime/valueConversion$0'; +import type { Detail, Detail$Wire } from './Detail'; + +export interface InlineProperty$Wire { + readonly entries: readonly (globalThis.Record | string)[]; +} + +export interface InlineProperty { + readonly entries: readonly (globalThis.Record | string)[]; +} + +export function inlinePropertyFromJSON(json: InlineProperty$Wire): InlineProperty { + return { + entries: convert$0FromJSON(json.entries), + }; +} + +export function inlinePropertyToJSON(value: InlineProperty): InlineProperty$Wire { + return { + entries: convert$0ToJSON(value.entries), + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/MapOuter.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/MapOuter.ts.golden new file mode 100644 index 000000000..e7728a8b7 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/MapOuter.ts.golden @@ -0,0 +1,28 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { matches$40FromJSON, matches$40ToJSON } from '../runtime/unionMatch'; +import type { OpenMap, OpenMap$Wire } from './OpenMap'; +import { openMapFromJSON, openMapToJSON } from './OpenMap'; + +export type MapOuter$Wire = unknown; + +export type MapOuter = unknown; + +export function mapOuterFromJSON(json: MapOuter$Wire): MapOuter { + const candidate: unknown = json; + if (matches$40FromJSON(candidate)) { + return openMapFromJSON(json as unknown as OpenMap$Wire); + } + return json as unknown as unknown; +} + +export function mapOuterToJSON(value: MapOuter): MapOuter$Wire { + const candidate: unknown = value; + if (matches$40ToJSON(candidate)) { + return openMapToJSON(value as unknown as OpenMap); + } + return value as unknown as unknown; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/MapWrapped.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/MapWrapped.ts.golden new file mode 100644 index 000000000..e60b0a99e --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/MapWrapped.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { NullableMapFallback, NullableMapFallback$Wire } from './NullableMapFallback'; +import { nullableMapFallbackFromJSON, nullableMapFallbackToJSON } from './NullableMapFallback'; + +export type MapWrapped$Wire = NullableMapFallback$Wire; + +export type MapWrapped = NullableMapFallback; + +export function mapWrappedFromJSON(json: MapWrapped$Wire): MapWrapped { + return nullableMapFallbackFromJSON(json); +} + +export function mapWrappedToJSON(value: MapWrapped): MapWrapped$Wire { + return nullableMapFallbackToJSON(value); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/MixedMapA.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/MixedMapA.ts.golden new file mode 100644 index 000000000..48f3d112a --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/MixedMapA.ts.golden @@ -0,0 +1,51 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail, Detail$Wire } from './Detail'; +import { detailFromJSON, detailToJSON } from './Detail'; + +export interface MixedMapA$Wire extends Readonly> { + readonly marker: string; +} + +export interface MixedMapA extends Readonly> { + readonly marker: string; +} + +export function mixedMapAFromJSON(json: MixedMapA$Wire): MixedMapA { + const sourceEntries = Object.entries(json).filter(([key]) => { + return !['marker'].includes(key); + }); + const additionalEntries = sourceEntries; + const additionalProperties = Object.fromEntries( + additionalEntries.map(([key, value]) => { + const additionalValue = value as Detail$Wire; + return [key, detailFromJSON(additionalValue)] as const; + }), + ); + const result = { + ...additionalProperties, + marker: json.marker, + }; + return result; +} + +export function mixedMapAToJSON(value: MixedMapA): MixedMapA$Wire { + const sourceEntries = Object.entries(value).filter(([key]) => { + return !['marker'].includes(key); + }); + const additionalEntries = sourceEntries; + const additionalProperties = Object.fromEntries( + additionalEntries.map(([key, value]) => { + const additionalValue = value as Detail; + return [key, detailToJSON(additionalValue)] as const; + }), + ); + const result = { + ...additionalProperties, + marker: value.marker, + }; + return result; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/MixedMapB.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/MixedMapB.ts.golden new file mode 100644 index 000000000..c907b6cc2 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/MixedMapB.ts.golden @@ -0,0 +1,51 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { convert$11FromJSON, convert$11ToJSON } from '../runtime/valueConversion$11'; +import type { Detail, Detail$Wire } from './Detail'; + +export interface MixedMapB$Wire extends Readonly> { + readonly marker: string; +} + +export interface MixedMapB extends Readonly> { + readonly marker: string; +} + +export function mixedMapBFromJSON(json: MixedMapB$Wire): MixedMapB { + const sourceEntries = Object.entries(json).filter(([key]) => { + return !['marker'].includes(key); + }); + const additionalEntries = sourceEntries; + const additionalProperties = Object.fromEntries( + additionalEntries.map(([key, value]) => { + const additionalValue = value as readonly Detail$Wire[]; + return [key, convert$11FromJSON(additionalValue)] as const; + }), + ); + const result = { + ...additionalProperties, + marker: json.marker, + }; + return result; +} + +export function mixedMapBToJSON(value: MixedMapB): MixedMapB$Wire { + const sourceEntries = Object.entries(value).filter(([key]) => { + return !['marker'].includes(key); + }); + const additionalEntries = sourceEntries; + const additionalProperties = Object.fromEntries( + additionalEntries.map(([key, value]) => { + const additionalValue = value as readonly Detail[]; + return [key, convert$11ToJSON(additionalValue)] as const; + }), + ); + const result = { + ...additionalProperties, + marker: value.marker, + }; + return result; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/MixedMapChoice.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/MixedMapChoice.ts.golden new file mode 100644 index 000000000..0598bb941 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/MixedMapChoice.ts.golden @@ -0,0 +1,41 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$43FromJSON, + matches$43ToJSON, + matches$45FromJSON, + matches$45ToJSON, +} from '../runtime/unionMatch'; +import type { MixedMapA, MixedMapA$Wire } from './MixedMapA'; +import { mixedMapAFromJSON, mixedMapAToJSON } from './MixedMapA'; +import type { MixedMapB, MixedMapB$Wire } from './MixedMapB'; +import { mixedMapBFromJSON, mixedMapBToJSON } from './MixedMapB'; + +export type MixedMapChoice$Wire = MixedMapA$Wire | MixedMapB$Wire; + +export type MixedMapChoice = MixedMapA | MixedMapB; + +export function mixedMapChoiceFromJSON(json: MixedMapChoice$Wire): MixedMapChoice { + const candidate: unknown = json; + if (matches$43FromJSON(candidate)) { + return mixedMapAFromJSON(json as unknown as MixedMapA$Wire); + } + if (matches$45FromJSON(candidate)) { + return mixedMapBFromJSON(json as unknown as MixedMapB$Wire); + } + return json as unknown as MixedMapChoice; +} + +export function mixedMapChoiceToJSON(value: MixedMapChoice): MixedMapChoice$Wire { + const candidate: unknown = value; + if (matches$43ToJSON(candidate)) { + return mixedMapAToJSON(value as unknown as MixedMapA); + } + if (matches$45ToJSON(candidate)) { + return mixedMapBToJSON(value as unknown as MixedMapB); + } + return value as unknown as MixedMapChoice$Wire; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NamedContainer.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NamedContainer.ts.golden new file mode 100644 index 000000000..e84180a65 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NamedContainer.ts.golden @@ -0,0 +1,42 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$11FromJSON, + matches$11ToJSON, + matches$1FromJSON, + matches$1ToJSON, +} from '../runtime/unionMatch'; +import type { Value$17, Value$17Wire } from '../runtime/valueConversion$17'; +import { convert$17FromJSON, convert$17ToJSON } from '../runtime/valueConversion$17'; +import type { Detail, Detail$Wire } from './Detail'; +import { detailFromJSON, detailToJSON } from './Detail'; +import type { NamedValue, NamedValue$Wire } from './NamedValue'; + +export type NamedContainer$Wire = readonly NamedValue$Wire[] | Detail$Wire; + +export type NamedContainer = readonly NamedValue[] | Detail; + +export function namedContainerFromJSON(json: NamedContainer$Wire): NamedContainer { + const candidate: unknown = json; + if (matches$11FromJSON(candidate)) { + return convert$17FromJSON(json as unknown as Value$17Wire); + } + if (matches$1FromJSON(candidate)) { + return detailFromJSON(json as unknown as Detail$Wire); + } + return json as unknown as NamedContainer; +} + +export function namedContainerToJSON(value: NamedContainer): NamedContainer$Wire { + const candidate: unknown = value; + if (matches$11ToJSON(candidate)) { + return convert$17ToJSON(value as unknown as Value$17); + } + if (matches$1ToJSON(candidate)) { + return detailToJSON(value as unknown as Detail); + } + return value as unknown as NamedContainer$Wire; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NamedValue.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NamedValue.ts.golden new file mode 100644 index 000000000..f8a7f00ec --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NamedValue.ts.golden @@ -0,0 +1,40 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$0FromJSON, + matches$0ToJSON, + matches$2FromJSON, + matches$2ToJSON, +} from '../runtime/unionMatch'; +import type { Value$12, Value$12Wire } from '../runtime/valueConversion$12'; +import { convert$12FromJSON, convert$12ToJSON } from '../runtime/valueConversion$12'; +import type { Detail, Detail$Wire } from './Detail'; + +export type NamedValue$Wire = globalThis.Record | string; + +export type NamedValue = globalThis.Record | string; + +export function namedValueFromJSON(json: NamedValue$Wire): NamedValue { + const candidate: unknown = json; + if (matches$0FromJSON(candidate)) { + return convert$12FromJSON(json as unknown as Value$12Wire); + } + if (matches$2FromJSON(candidate)) { + return json as unknown as string; + } + return json as unknown as NamedValue; +} + +export function namedValueToJSON(value: NamedValue): NamedValue$Wire { + const candidate: unknown = value; + if (matches$0ToJSON(candidate)) { + return convert$12ToJSON(value as unknown as Value$12); + } + if (matches$2ToJSON(candidate)) { + return value as unknown as string; + } + return value as unknown as NamedValue$Wire; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NestedDetailLists.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NestedDetailLists.ts.golden new file mode 100644 index 000000000..4a7d6a2a1 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NestedDetailLists.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { convert$18FromJSON, convert$18ToJSON } from '../runtime/valueConversion$18'; +import type { Detail, Detail$Wire } from './Detail'; + +export type NestedDetailLists$Wire = readonly Detail$Wire[][]; + +export type NestedDetailLists = readonly Detail[][]; + +export function nestedDetailListsFromJSON(json: NestedDetailLists$Wire): NestedDetailLists { + return convert$18FromJSON(json); +} + +export function nestedDetailListsToJSON(value: NestedDetailLists): NestedDetailLists$Wire { + return convert$18ToJSON(value); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NestedEnumChoice.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NestedEnumChoice.ts.golden new file mode 100644 index 000000000..a3519b308 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NestedEnumChoice.ts.golden @@ -0,0 +1,41 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$46FromJSON, + matches$46ToJSON, + matches$50FromJSON, + matches$50ToJSON, +} from '../runtime/unionMatch'; +import type { NestedEnumNarrow, NestedEnumNarrow$Wire } from './NestedEnumNarrow'; +import { nestedEnumNarrowFromJSON, nestedEnumNarrowToJSON } from './NestedEnumNarrow'; +import type { NumericTwo, NumericTwo$Wire } from './NumericTwo'; +import { numericTwoFromJSON, numericTwoToJSON } from './NumericTwo'; + +export type NestedEnumChoice$Wire = NestedEnumNarrow$Wire | NumericTwo$Wire; + +export type NestedEnumChoice = NestedEnumNarrow | NumericTwo; + +export function nestedEnumChoiceFromJSON(json: NestedEnumChoice$Wire): NestedEnumChoice { + const candidate: unknown = json; + if (matches$46FromJSON(candidate)) { + return nestedEnumNarrowFromJSON(json as unknown as NestedEnumNarrow$Wire); + } + if (matches$50FromJSON(candidate)) { + return numericTwoFromJSON(json as unknown as NumericTwo$Wire); + } + return json as unknown as NestedEnumChoice; +} + +export function nestedEnumChoiceToJSON(value: NestedEnumChoice): NestedEnumChoice$Wire { + const candidate: unknown = value; + if (matches$46ToJSON(candidate)) { + return nestedEnumNarrowToJSON(value as unknown as NestedEnumNarrow); + } + if (matches$50ToJSON(candidate)) { + return numericTwoToJSON(value as unknown as NumericTwo); + } + return value as unknown as NestedEnumChoice$Wire; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NestedEnumNarrow.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NestedEnumNarrow.ts.golden new file mode 100644 index 000000000..8249e388e --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NestedEnumNarrow.ts.golden @@ -0,0 +1,28 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface NestedEnumNarrow$Wire { + readonly first_value?: string; + readonly revision: number | null; +} + +export interface NestedEnumNarrow { + readonly firstValue?: string; + readonly revision: number | null; +} + +export function nestedEnumNarrowFromJSON(json: NestedEnumNarrow$Wire): NestedEnumNarrow { + return { + firstValue: json.first_value, + revision: json.revision, + }; +} + +export function nestedEnumNarrowToJSON(value: NestedEnumNarrow): NestedEnumNarrow$Wire { + return { + first_value: value.firstValue, + revision: value.revision, + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NestedExplicitNullOverlap.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NestedExplicitNullOverlap.ts.golden new file mode 100644 index 000000000..51530ccd9 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NestedExplicitNullOverlap.ts.golden @@ -0,0 +1,49 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$52FromJSON, + matches$52ToJSON, + matches$5FromJSON, + matches$5ToJSON, +} from '../runtime/unionMatch'; +import type { NullableOverlap, NullableOverlap$Wire } from './NullableOverlap'; +import { nullableOverlapFromJSON, nullableOverlapToJSON } from './NullableOverlap'; + +export type NestedExplicitNullOverlap$Wire = NullableOverlap$Wire | boolean | null; + +export type NestedExplicitNullOverlap = NullableOverlap | boolean | null; + +export function nestedExplicitNullOverlapFromJSON( + json: NestedExplicitNullOverlap$Wire, +): NestedExplicitNullOverlap { + const candidate: unknown = json; + if (matches$52FromJSON(candidate)) { + return nullableOverlapFromJSON(json as unknown as NullableOverlap$Wire); + } + if (matches$5FromJSON(candidate)) { + return json as unknown as boolean; + } + if (candidate === null) { + return null; + } + return json as unknown as NestedExplicitNullOverlap; +} + +export function nestedExplicitNullOverlapToJSON( + value: NestedExplicitNullOverlap, +): NestedExplicitNullOverlap$Wire { + const candidate: unknown = value; + if (matches$52ToJSON(candidate)) { + return nullableOverlapToJSON(value as unknown as NullableOverlap); + } + if (matches$5ToJSON(candidate)) { + return value as unknown as boolean; + } + if (candidate === null) { + return null; + } + return value as unknown as NestedExplicitNullOverlap$Wire; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NestedNullableEnumChoice.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NestedNullableEnumChoice.ts.golden new file mode 100644 index 000000000..4ccdde1ed --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NestedNullableEnumChoice.ts.golden @@ -0,0 +1,51 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$50FromJSON, + matches$50ToJSON, + matches$53FromJSON, + matches$53ToJSON, +} from '../runtime/unionMatch'; +import type { + NestedNullableEnumNarrow, + NestedNullableEnumNarrow$Wire, +} from './NestedNullableEnumNarrow'; +import { + nestedNullableEnumNarrowFromJSON, + nestedNullableEnumNarrowToJSON, +} from './NestedNullableEnumNarrow'; +import type { NumericTwo, NumericTwo$Wire } from './NumericTwo'; +import { numericTwoFromJSON, numericTwoToJSON } from './NumericTwo'; + +export type NestedNullableEnumChoice$Wire = NestedNullableEnumNarrow$Wire | NumericTwo$Wire; + +export type NestedNullableEnumChoice = NestedNullableEnumNarrow | NumericTwo; + +export function nestedNullableEnumChoiceFromJSON( + json: NestedNullableEnumChoice$Wire, +): NestedNullableEnumChoice { + const candidate: unknown = json; + if (matches$53FromJSON(candidate)) { + return nestedNullableEnumNarrowFromJSON(json as unknown as NestedNullableEnumNarrow$Wire); + } + if (matches$50FromJSON(candidate)) { + return numericTwoFromJSON(json as unknown as NumericTwo$Wire); + } + return json as unknown as NestedNullableEnumChoice; +} + +export function nestedNullableEnumChoiceToJSON( + value: NestedNullableEnumChoice, +): NestedNullableEnumChoice$Wire { + const candidate: unknown = value; + if (matches$53ToJSON(candidate)) { + return nestedNullableEnumNarrowToJSON(value as unknown as NestedNullableEnumNarrow); + } + if (matches$50ToJSON(candidate)) { + return numericTwoToJSON(value as unknown as NumericTwo); + } + return value as unknown as NestedNullableEnumChoice$Wire; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NestedNullableEnumNarrow.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NestedNullableEnumNarrow.ts.golden new file mode 100644 index 000000000..5d361f019 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NestedNullableEnumNarrow.ts.golden @@ -0,0 +1,32 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface NestedNullableEnumNarrow$Wire { + readonly first_value?: string; + readonly revision: number | null; +} + +export interface NestedNullableEnumNarrow { + readonly firstValue?: string; + readonly revision: number | null; +} + +export function nestedNullableEnumNarrowFromJSON( + json: NestedNullableEnumNarrow$Wire, +): NestedNullableEnumNarrow { + return { + firstValue: json.first_value, + revision: json.revision, + }; +} + +export function nestedNullableEnumNarrowToJSON( + value: NestedNullableEnumNarrow, +): NestedNullableEnumNarrow$Wire { + return { + first_value: value.firstValue, + revision: value.revision, + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NullableDetail.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NullableDetail.ts.golden new file mode 100644 index 000000000..63e4a8756 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NullableDetail.ts.golden @@ -0,0 +1,34 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { matches$1FromJSON, matches$1ToJSON } from '../runtime/unionMatch'; +import type { Detail, Detail$Wire } from './Detail'; +import { detailFromJSON, detailToJSON } from './Detail'; + +export type NullableDetail$Wire = Detail$Wire | null; + +export type NullableDetail = Detail | null; + +export function nullableDetailFromJSON(json: NullableDetail$Wire): NullableDetail { + const candidate: unknown = json; + if (matches$1FromJSON(candidate)) { + return detailFromJSON(json as unknown as Detail$Wire); + } + if (candidate === null) { + return null; + } + return json as unknown as NullableDetail; +} + +export function nullableDetailToJSON(value: NullableDetail): NullableDetail$Wire { + const candidate: unknown = value; + if (matches$1ToJSON(candidate)) { + return detailToJSON(value as unknown as Detail); + } + if (candidate === null) { + return null; + } + return value as unknown as NullableDetail$Wire; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NullableEnumParent.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NullableEnumParent.ts.golden new file mode 100644 index 000000000..1317fcdfe --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NullableEnumParent.ts.golden @@ -0,0 +1,27 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { convert$20FromJSON, convert$20ToJSON } from '../runtime/valueConversion$20'; +import type { ScalarEnumChoice, ScalarEnumChoice$Wire } from './ScalarEnumChoice'; + +export interface NullableEnumParent$Wire { + readonly scalar_value: ScalarEnumChoice$Wire | null; +} + +export interface NullableEnumParent { + readonly scalarValue: ScalarEnumChoice | null; +} + +export function nullableEnumParentFromJSON(json: NullableEnumParent$Wire): NullableEnumParent { + return { + scalarValue: convert$20FromJSON(json.scalar_value), + }; +} + +export function nullableEnumParentToJSON(value: NullableEnumParent): NullableEnumParent$Wire { + return { + scalar_value: convert$20ToJSON(value.scalarValue), + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NullableInlineEnumParent.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NullableInlineEnumParent.ts.golden new file mode 100644 index 000000000..bac7157dd --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NullableInlineEnumParent.ts.golden @@ -0,0 +1,30 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { convert$21FromJSON, convert$21ToJSON } from '../runtime/valueConversion$21'; + +export interface NullableInlineEnumParent$Wire { + readonly scalar_value: number | boolean | null; +} + +export interface NullableInlineEnumParent { + readonly scalarValue: number | boolean | null; +} + +export function nullableInlineEnumParentFromJSON( + json: NullableInlineEnumParent$Wire, +): NullableInlineEnumParent { + return { + scalarValue: convert$21FromJSON(json.scalar_value), + }; +} + +export function nullableInlineEnumParentToJSON( + value: NullableInlineEnumParent, +): NullableInlineEnumParent$Wire { + return { + scalar_value: convert$21ToJSON(value.scalarValue), + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NullableMapFallback.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NullableMapFallback.ts.golden new file mode 100644 index 000000000..8a1eff34c --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NullableMapFallback.ts.golden @@ -0,0 +1,39 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$1FromJSON, + matches$1ToJSON, + matches$57FromJSON, + matches$57ToJSON, +} from '../runtime/unionMatch'; +import type { Detail, Detail$Wire } from './Detail'; +import { detailFromJSON, detailToJSON } from './Detail'; + +export type NullableMapFallback$Wire = globalThis.Record | null | Detail$Wire; + +export type NullableMapFallback = globalThis.Record | null | Detail; + +export function nullableMapFallbackFromJSON(json: NullableMapFallback$Wire): NullableMapFallback { + const candidate: unknown = json; + if (matches$1FromJSON(candidate)) { + return detailFromJSON(json as unknown as Detail$Wire); + } + if (matches$57FromJSON(candidate)) { + return json as unknown as globalThis.Record | null; + } + return json as unknown as NullableMapFallback; +} + +export function nullableMapFallbackToJSON(value: NullableMapFallback): NullableMapFallback$Wire { + const candidate: unknown = value; + if (matches$1ToJSON(candidate)) { + return detailToJSON(value as unknown as Detail); + } + if (matches$57ToJSON(candidate)) { + return value as unknown as globalThis.Record | null; + } + return value as unknown as NullableMapFallback$Wire; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NullableNonNullScalars.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NullableNonNullScalars.ts.golden new file mode 100644 index 000000000..36b6af103 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NullableNonNullScalars.ts.golden @@ -0,0 +1,26 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { convert$23FromJSON, convert$23ToJSON } from '../runtime/valueConversion$23'; +import type { + NullableNonNullScalarsItems, + NullableNonNullScalarsItems$Wire, +} from './NullableNonNullScalarsItems'; + +export type NullableNonNullScalars$Wire = readonly (NullableNonNullScalarsItems$Wire | null)[]; + +export type NullableNonNullScalars = readonly (NullableNonNullScalarsItems | null)[]; + +export function nullableNonNullScalarsFromJSON( + json: NullableNonNullScalars$Wire, +): NullableNonNullScalars { + return convert$23FromJSON(json); +} + +export function nullableNonNullScalarsToJSON( + value: NullableNonNullScalars, +): NullableNonNullScalars$Wire { + return convert$23ToJSON(value); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NullableNonNullScalarsItems.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NullableNonNullScalarsItems.ts.golden new file mode 100644 index 000000000..24655fb6a --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NullableNonNullScalarsItems.ts.golden @@ -0,0 +1,41 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$3FromJSON, + matches$3ToJSON, + matches$4FromJSON, + matches$4ToJSON, +} from '../runtime/unionMatch'; + +export type NullableNonNullScalarsItems$Wire = number; + +export type NullableNonNullScalarsItems = number; + +export function nullableNonNullScalarsItemsFromJSON( + json: NullableNonNullScalarsItems$Wire, +): NullableNonNullScalarsItems { + const candidate: unknown = json; + if (matches$3FromJSON(candidate)) { + return json as unknown as number; + } + if (matches$4FromJSON(candidate)) { + return json as unknown as number; + } + return json as unknown as NullableNonNullScalarsItems; +} + +export function nullableNonNullScalarsItemsToJSON( + value: NullableNonNullScalarsItems, +): NullableNonNullScalarsItems$Wire { + const candidate: unknown = value; + if (matches$3ToJSON(candidate)) { + return value as unknown as number; + } + if (matches$4ToJSON(candidate)) { + return value as unknown as number; + } + return value as unknown as NullableNonNullScalarsItems$Wire; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NullableNumericChoice.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NullableNumericChoice.ts.golden new file mode 100644 index 000000000..2b5edfe7d --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NullableNumericChoice.ts.golden @@ -0,0 +1,45 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$58FromJSON, + matches$58ToJSON, + matches$61FromJSON, + matches$61ToJSON, +} from '../runtime/unionMatch'; +import type { NullableNumericOne, NullableNumericOne$Wire } from './NullableNumericOne'; +import { nullableNumericOneFromJSON, nullableNumericOneToJSON } from './NullableNumericOne'; +import type { NullableNumericTwo, NullableNumericTwo$Wire } from './NullableNumericTwo'; +import { nullableNumericTwoFromJSON, nullableNumericTwoToJSON } from './NullableNumericTwo'; + +export type NullableNumericChoice$Wire = NullableNumericOne$Wire | NullableNumericTwo$Wire; + +export type NullableNumericChoice = NullableNumericOne | NullableNumericTwo; + +export function nullableNumericChoiceFromJSON( + json: NullableNumericChoice$Wire, +): NullableNumericChoice { + const candidate: unknown = json; + if (matches$58FromJSON(candidate)) { + return nullableNumericOneFromJSON(json as unknown as NullableNumericOne$Wire); + } + if (matches$61FromJSON(candidate)) { + return nullableNumericTwoFromJSON(json as unknown as NullableNumericTwo$Wire); + } + return json as unknown as NullableNumericChoice; +} + +export function nullableNumericChoiceToJSON( + value: NullableNumericChoice, +): NullableNumericChoice$Wire { + const candidate: unknown = value; + if (matches$58ToJSON(candidate)) { + return nullableNumericOneToJSON(value as unknown as NullableNumericOne); + } + if (matches$61ToJSON(candidate)) { + return nullableNumericTwoToJSON(value as unknown as NullableNumericTwo); + } + return value as unknown as NullableNumericChoice$Wire; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NullableNumericOne.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NullableNumericOne.ts.golden new file mode 100644 index 000000000..e7d632515 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NullableNumericOne.ts.golden @@ -0,0 +1,28 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface NullableNumericOne$Wire { + readonly first_value?: string; + readonly revision: number | null; +} + +export interface NullableNumericOne { + readonly firstValue?: string; + readonly revision: number | null; +} + +export function nullableNumericOneFromJSON(json: NullableNumericOne$Wire): NullableNumericOne { + return { + firstValue: json.first_value, + revision: json.revision, + }; +} + +export function nullableNumericOneToJSON(value: NullableNumericOne): NullableNumericOne$Wire { + return { + first_value: value.firstValue, + revision: value.revision, + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NullableNumericTwo.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NullableNumericTwo.ts.golden new file mode 100644 index 000000000..e681475f2 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NullableNumericTwo.ts.golden @@ -0,0 +1,28 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface NullableNumericTwo$Wire { + readonly revision: number | null; + readonly second_value?: string; +} + +export interface NullableNumericTwo { + readonly revision: number | null; + readonly secondValue?: string; +} + +export function nullableNumericTwoFromJSON(json: NullableNumericTwo$Wire): NullableNumericTwo { + return { + revision: json.revision, + secondValue: json.second_value, + }; +} + +export function nullableNumericTwoToJSON(value: NullableNumericTwo): NullableNumericTwo$Wire { + return { + revision: value.revision, + second_value: value.secondValue, + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NullableOpaque.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NullableOpaque.ts.golden new file mode 100644 index 000000000..d4858f227 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NullableOpaque.ts.golden @@ -0,0 +1,24 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export type NullableOpaque$Wire = unknown; + +export type NullableOpaque = unknown; + +export function nullableOpaqueFromJSON(json: NullableOpaque$Wire): NullableOpaque { + const candidate: unknown = json; + if (candidate === null) { + return null; + } + return json as unknown as unknown; +} + +export function nullableOpaqueToJSON(value: NullableOpaque): NullableOpaque$Wire { + const candidate: unknown = value; + if (candidate === null) { + return null; + } + return value as unknown as unknown; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NullableOpaqueSiblingOuter.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NullableOpaqueSiblingOuter.ts.golden new file mode 100644 index 000000000..546d0fc80 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NullableOpaqueSiblingOuter.ts.golden @@ -0,0 +1,32 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { matches$64FromJSON, matches$64ToJSON } from '../runtime/unionMatch'; +import type { NullableOpaque, NullableOpaque$Wire } from './NullableOpaque'; +import { nullableOpaqueFromJSON, nullableOpaqueToJSON } from './NullableOpaque'; + +export type NullableOpaqueSiblingOuter$Wire = unknown; + +export type NullableOpaqueSiblingOuter = unknown; + +export function nullableOpaqueSiblingOuterFromJSON( + json: NullableOpaqueSiblingOuter$Wire, +): NullableOpaqueSiblingOuter { + const candidate: unknown = json; + if (matches$64FromJSON(candidate)) { + return json as unknown as boolean | null; + } + return nullableOpaqueFromJSON(json as unknown as NullableOpaque$Wire); +} + +export function nullableOpaqueSiblingOuterToJSON( + value: NullableOpaqueSiblingOuter, +): NullableOpaqueSiblingOuter$Wire { + const candidate: unknown = value; + if (matches$64ToJSON(candidate)) { + return value as unknown as boolean | null; + } + return nullableOpaqueToJSON(value as unknown as NullableOpaque); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NullableOpenExplicitOuter.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NullableOpenExplicitOuter.ts.golden new file mode 100644 index 000000000..4901d91af --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NullableOpenExplicitOuter.ts.golden @@ -0,0 +1,38 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { matches$65FromJSON, matches$65ToJSON } from '../runtime/unionMatch'; +import type { NullableOpenInner, NullableOpenInner$Wire } from './NullableOpenInner'; +import { nullableOpenInnerFromJSON, nullableOpenInnerToJSON } from './NullableOpenInner'; + +export type NullableOpenExplicitOuter$Wire = unknown; + +export type NullableOpenExplicitOuter = unknown; + +export function nullableOpenExplicitOuterFromJSON( + json: NullableOpenExplicitOuter$Wire, +): NullableOpenExplicitOuter { + const candidate: unknown = json; + if (matches$65FromJSON(candidate)) { + return nullableOpenInnerFromJSON(json as unknown as NullableOpenInner$Wire); + } + if (candidate === null) { + return null; + } + return nullableOpenInnerFromJSON(json as unknown as NullableOpenInner$Wire); +} + +export function nullableOpenExplicitOuterToJSON( + value: NullableOpenExplicitOuter, +): NullableOpenExplicitOuter$Wire { + const candidate: unknown = value; + if (matches$65ToJSON(candidate)) { + return nullableOpenInnerToJSON(value as unknown as NullableOpenInner); + } + if (candidate === null) { + return null; + } + return nullableOpenInnerToJSON(value as unknown as NullableOpenInner); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NullableOpenInner.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NullableOpenInner.ts.golden new file mode 100644 index 000000000..d3891f36c --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NullableOpenInner.ts.golden @@ -0,0 +1,37 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$66FromJSON, + matches$66ToJSON, + matches$67FromJSON, + matches$67ToJSON, +} from '../runtime/unionMatch'; + +export type NullableOpenInner$Wire = unknown; + +export type NullableOpenInner = unknown; + +export function nullableOpenInnerFromJSON(json: NullableOpenInner$Wire): NullableOpenInner { + const candidate: unknown = json; + if (matches$66FromJSON(candidate)) { + return json as unknown as string | null; + } + if (matches$67FromJSON(candidate)) { + return json as unknown as number | null; + } + return json as unknown as unknown; +} + +export function nullableOpenInnerToJSON(value: NullableOpenInner): NullableOpenInner$Wire { + const candidate: unknown = value; + if (matches$66ToJSON(candidate)) { + return value as unknown as string | null; + } + if (matches$67ToJSON(candidate)) { + return value as unknown as number | null; + } + return value as unknown as unknown; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NullableOpenSiblingOuter.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NullableOpenSiblingOuter.ts.golden new file mode 100644 index 000000000..38fbf3780 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NullableOpenSiblingOuter.ts.golden @@ -0,0 +1,43 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$64FromJSON, + matches$64ToJSON, + matches$65FromJSON, + matches$65ToJSON, +} from '../runtime/unionMatch'; +import type { NullableOpenInner, NullableOpenInner$Wire } from './NullableOpenInner'; +import { nullableOpenInnerFromJSON, nullableOpenInnerToJSON } from './NullableOpenInner'; + +export type NullableOpenSiblingOuter$Wire = unknown; + +export type NullableOpenSiblingOuter = unknown; + +export function nullableOpenSiblingOuterFromJSON( + json: NullableOpenSiblingOuter$Wire, +): NullableOpenSiblingOuter { + const candidate: unknown = json; + if (matches$65FromJSON(candidate)) { + return nullableOpenInnerFromJSON(json as unknown as NullableOpenInner$Wire); + } + if (matches$64FromJSON(candidate)) { + return json as unknown as boolean | null; + } + return nullableOpenInnerFromJSON(json as unknown as NullableOpenInner$Wire); +} + +export function nullableOpenSiblingOuterToJSON( + value: NullableOpenSiblingOuter, +): NullableOpenSiblingOuter$Wire { + const candidate: unknown = value; + if (matches$65ToJSON(candidate)) { + return nullableOpenInnerToJSON(value as unknown as NullableOpenInner); + } + if (matches$64ToJSON(candidate)) { + return value as unknown as boolean | null; + } + return nullableOpenInnerToJSON(value as unknown as NullableOpenInner); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NullableOverlap.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NullableOverlap.ts.golden new file mode 100644 index 000000000..4517ae2e6 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NullableOverlap.ts.golden @@ -0,0 +1,41 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$30FromJSON, + matches$30ToJSON, + matches$32FromJSON, + matches$32ToJSON, +} from '../runtime/unionMatch'; +import type { NullableDetail, NullableDetail$Wire } from './NullableDetail'; +import { nullableDetailFromJSON, nullableDetailToJSON } from './NullableDetail'; +import type { NullableTool, NullableTool$Wire } from './NullableTool'; +import { nullableToolFromJSON, nullableToolToJSON } from './NullableTool'; + +export type NullableOverlap$Wire = NullableDetail$Wire | NullableTool$Wire; + +export type NullableOverlap = NullableDetail | NullableTool; + +export function nullableOverlapFromJSON(json: NullableOverlap$Wire): NullableOverlap { + const candidate: unknown = json; + if (matches$30FromJSON(candidate)) { + return nullableDetailFromJSON(json as unknown as NullableDetail$Wire); + } + if (matches$32FromJSON(candidate)) { + return nullableToolFromJSON(json as unknown as NullableTool$Wire); + } + return json as unknown as NullableOverlap; +} + +export function nullableOverlapToJSON(value: NullableOverlap): NullableOverlap$Wire { + const candidate: unknown = value; + if (matches$30ToJSON(candidate)) { + return nullableDetailToJSON(value as unknown as NullableDetail); + } + if (matches$32ToJSON(candidate)) { + return nullableToolToJSON(value as unknown as NullableTool); + } + return value as unknown as NullableOverlap$Wire; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NullablePlainEnumParent.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NullablePlainEnumParent.ts.golden new file mode 100644 index 000000000..6183c6642 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NullablePlainEnumParent.ts.golden @@ -0,0 +1,31 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { ScalarEnumChoice, ScalarEnumChoice$Wire } from './ScalarEnumChoice'; +import { scalarEnumChoiceFromJSON, scalarEnumChoiceToJSON } from './ScalarEnumChoice'; + +export interface NullablePlainEnumParent$Wire { + readonly scalar_value: ScalarEnumChoice$Wire | null; +} + +export interface NullablePlainEnumParent { + readonly scalarValue: ScalarEnumChoice | null; +} + +export function nullablePlainEnumParentFromJSON( + json: NullablePlainEnumParent$Wire, +): NullablePlainEnumParent { + return { + scalarValue: json.scalar_value === null ? null : scalarEnumChoiceFromJSON(json.scalar_value), + }; +} + +export function nullablePlainEnumParentToJSON( + value: NullablePlainEnumParent, +): NullablePlainEnumParent$Wire { + return { + scalar_value: value.scalarValue === null ? null : scalarEnumChoiceToJSON(value.scalarValue), + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NullableScalarArray.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NullableScalarArray.ts.golden new file mode 100644 index 000000000..3dd47ea7a --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NullableScalarArray.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { convert$25FromJSON, convert$25ToJSON } from '../runtime/valueConversion$25'; +import type { NullableScalarInner, NullableScalarInner$Wire } from './NullableScalarInner'; + +export type NullableScalarArray$Wire = readonly (NullableScalarInner$Wire | null)[]; + +export type NullableScalarArray = readonly (NullableScalarInner | null)[]; + +export function nullableScalarArrayFromJSON(json: NullableScalarArray$Wire): NullableScalarArray { + return convert$25FromJSON(json); +} + +export function nullableScalarArrayToJSON(value: NullableScalarArray): NullableScalarArray$Wire { + return convert$25ToJSON(value); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NullableScalarInner.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NullableScalarInner.ts.golden new file mode 100644 index 000000000..e0e8794c8 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NullableScalarInner.ts.golden @@ -0,0 +1,37 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$66FromJSON, + matches$66ToJSON, + matches$67FromJSON, + matches$67ToJSON, +} from '../runtime/unionMatch'; + +export type NullableScalarInner$Wire = string | null | number; + +export type NullableScalarInner = string | null | number; + +export function nullableScalarInnerFromJSON(json: NullableScalarInner$Wire): NullableScalarInner { + const candidate: unknown = json; + if (matches$66FromJSON(candidate)) { + return json as unknown as string | null; + } + if (matches$67FromJSON(candidate)) { + return json as unknown as number | null; + } + return json as unknown as NullableScalarInner; +} + +export function nullableScalarInnerToJSON(value: NullableScalarInner): NullableScalarInner$Wire { + const candidate: unknown = value; + if (matches$66ToJSON(candidate)) { + return value as unknown as string | null; + } + if (matches$67ToJSON(candidate)) { + return value as unknown as number | null; + } + return value as unknown as NullableScalarInner$Wire; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NullableScalarMap.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NullableScalarMap.ts.golden new file mode 100644 index 000000000..7a4d88098 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NullableScalarMap.ts.golden @@ -0,0 +1,43 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { convert$5FromJSON, convert$5ToJSON } from '../runtime/valueConversion$5'; +import type { NullableScalarInner, NullableScalarInner$Wire } from './NullableScalarInner'; + +export interface NullableScalarMap$Wire extends Readonly< + Record +> {} + +export interface NullableScalarMap extends Readonly> {} + +export function nullableScalarMapFromJSON(json: NullableScalarMap$Wire): NullableScalarMap { + const sourceEntries = Object.entries(json); + const additionalEntries = sourceEntries; + const additionalProperties = Object.fromEntries( + additionalEntries.map(([key, value]) => { + const additionalValue = value; + return [key, convert$5FromJSON(additionalValue)] as const; + }), + ); + const result = { + ...additionalProperties, + }; + return result; +} + +export function nullableScalarMapToJSON(value: NullableScalarMap): NullableScalarMap$Wire { + const sourceEntries = Object.entries(value); + const additionalEntries = sourceEntries; + const additionalProperties = Object.fromEntries( + additionalEntries.map(([key, value]) => { + const additionalValue = value; + return [key, convert$5ToJSON(additionalValue)] as const; + }), + ); + const result = { + ...additionalProperties, + }; + return result; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NullableScalarParent.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NullableScalarParent.ts.golden new file mode 100644 index 000000000..efe8fbe79 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NullableScalarParent.ts.golden @@ -0,0 +1,29 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { NullableScalarInner, NullableScalarInner$Wire } from './NullableScalarInner'; +import { nullableScalarInnerFromJSON, nullableScalarInnerToJSON } from './NullableScalarInner'; + +export interface NullableScalarParent$Wire { + readonly inner_value: NullableScalarInner$Wire | null; +} + +export interface NullableScalarParent { + readonly innerValue: NullableScalarInner | null; +} + +export function nullableScalarParentFromJSON( + json: NullableScalarParent$Wire, +): NullableScalarParent { + return { + innerValue: nullableScalarInnerFromJSON(json.inner_value), + }; +} + +export function nullableScalarParentToJSON(value: NullableScalarParent): NullableScalarParent$Wire { + return { + inner_value: nullableScalarInnerToJSON(value.innerValue), + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NullableTool.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NullableTool.ts.golden new file mode 100644 index 000000000..557ed6c4b --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NullableTool.ts.golden @@ -0,0 +1,34 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { matches$68FromJSON, matches$68ToJSON } from '../runtime/unionMatch'; +import type { Tool, Tool$Wire } from './Tool'; +import { toolFromJSON, toolToJSON } from './Tool'; + +export type NullableTool$Wire = Tool$Wire | null; + +export type NullableTool = Tool | null; + +export function nullableToolFromJSON(json: NullableTool$Wire): NullableTool { + const candidate: unknown = json; + if (matches$68FromJSON(candidate)) { + return toolFromJSON(json as unknown as Tool$Wire); + } + if (candidate === null) { + return null; + } + return json as unknown as NullableTool; +} + +export function nullableToolToJSON(value: NullableTool): NullableTool$Wire { + const candidate: unknown = value; + if (matches$68ToJSON(candidate)) { + return toolToJSON(value as unknown as Tool); + } + if (candidate === null) { + return null; + } + return value as unknown as NullableTool$Wire; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NumericEnumChoice.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NumericEnumChoice.ts.golden new file mode 100644 index 000000000..a9f6b3f8a --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NumericEnumChoice.ts.golden @@ -0,0 +1,41 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$50FromJSON, + matches$50ToJSON, + matches$69FromJSON, + matches$69ToJSON, +} from '../runtime/unionMatch'; +import type { NumericOne, NumericOne$Wire } from './NumericOne'; +import { numericOneFromJSON, numericOneToJSON } from './NumericOne'; +import type { NumericTwo, NumericTwo$Wire } from './NumericTwo'; +import { numericTwoFromJSON, numericTwoToJSON } from './NumericTwo'; + +export type NumericEnumChoice$Wire = NumericOne$Wire | NumericTwo$Wire; + +export type NumericEnumChoice = NumericOne | NumericTwo; + +export function numericEnumChoiceFromJSON(json: NumericEnumChoice$Wire): NumericEnumChoice { + const candidate: unknown = json; + if (matches$69FromJSON(candidate)) { + return numericOneFromJSON(json as unknown as NumericOne$Wire); + } + if (matches$50FromJSON(candidate)) { + return numericTwoFromJSON(json as unknown as NumericTwo$Wire); + } + return json as unknown as NumericEnumChoice; +} + +export function numericEnumChoiceToJSON(value: NumericEnumChoice): NumericEnumChoice$Wire { + const candidate: unknown = value; + if (matches$69ToJSON(candidate)) { + return numericOneToJSON(value as unknown as NumericOne); + } + if (matches$50ToJSON(candidate)) { + return numericTwoToJSON(value as unknown as NumericTwo); + } + return value as unknown as NumericEnumChoice$Wire; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NumericOne.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NumericOne.ts.golden new file mode 100644 index 000000000..fd62c06ef --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NumericOne.ts.golden @@ -0,0 +1,28 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface NumericOne$Wire { + readonly first_value?: string; + readonly revision: number; +} + +export interface NumericOne { + readonly firstValue?: string; + readonly revision: number; +} + +export function numericOneFromJSON(json: NumericOne$Wire): NumericOne { + return { + firstValue: json.first_value, + revision: json.revision, + }; +} + +export function numericOneToJSON(value: NumericOne): NumericOne$Wire { + return { + first_value: value.firstValue, + revision: value.revision, + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NumericTwo.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NumericTwo.ts.golden new file mode 100644 index 000000000..4750f2e0d --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/NumericTwo.ts.golden @@ -0,0 +1,28 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface NumericTwo$Wire { + readonly revision: number; + readonly second_value?: string; +} + +export interface NumericTwo { + readonly revision: number; + readonly secondValue?: string; +} + +export function numericTwoFromJSON(json: NumericTwo$Wire): NumericTwo { + return { + revision: json.revision, + secondValue: json.second_value, + }; +} + +export function numericTwoToJSON(value: NumericTwo): NumericTwo$Wire { + return { + revision: value.revision, + second_value: value.secondValue, + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/OnceNullableOuter.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/OnceNullableOuter.ts.golden new file mode 100644 index 000000000..fb5cf6fd8 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/OnceNullableOuter.ts.golden @@ -0,0 +1,30 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { matches$30FromJSON, matches$30ToJSON } from '../runtime/unionMatch'; +import type { NullableDetail, NullableDetail$Wire } from './NullableDetail'; +import { nullableDetailFromJSON, nullableDetailToJSON } from './NullableDetail'; +import type { NullableOpaque, NullableOpaque$Wire } from './NullableOpaque'; +import { nullableOpaqueFromJSON, nullableOpaqueToJSON } from './NullableOpaque'; + +export type OnceNullableOuter$Wire = unknown; + +export type OnceNullableOuter = unknown; + +export function onceNullableOuterFromJSON(json: OnceNullableOuter$Wire): OnceNullableOuter { + const candidate: unknown = json; + if (matches$30FromJSON(candidate)) { + return nullableDetailFromJSON(json as unknown as NullableDetail$Wire); + } + return nullableOpaqueFromJSON(json as unknown as NullableOpaque$Wire); +} + +export function onceNullableOuterToJSON(value: OnceNullableOuter): OnceNullableOuter$Wire { + const candidate: unknown = value; + if (matches$30ToJSON(candidate)) { + return nullableDetailToJSON(value as unknown as NullableDetail); + } + return nullableOpaqueToJSON(value as unknown as NullableOpaque); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/OpenAlias.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/OpenAlias.ts.golden new file mode 100644 index 000000000..8046608e8 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/OpenAlias.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { OpenInner, OpenInner$Wire } from './OpenInner'; +import { openInnerFromJSON, openInnerToJSON } from './OpenInner'; + +export type OpenAlias$Wire = OpenInner$Wire; + +export type OpenAlias = OpenInner; + +export function openAliasFromJSON(json: OpenAlias$Wire): OpenAlias { + return openInnerFromJSON(json); +} + +export function openAliasToJSON(value: OpenAlias): OpenAlias$Wire { + return openInnerToJSON(value); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/OpenInner.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/OpenInner.ts.golden new file mode 100644 index 000000000..24e6cc387 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/OpenInner.ts.golden @@ -0,0 +1,28 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { matches$1FromJSON, matches$1ToJSON } from '../runtime/unionMatch'; +import type { Detail, Detail$Wire } from './Detail'; +import { detailFromJSON, detailToJSON } from './Detail'; + +export type OpenInner$Wire = unknown; + +export type OpenInner = unknown; + +export function openInnerFromJSON(json: OpenInner$Wire): OpenInner { + const candidate: unknown = json; + if (matches$1FromJSON(candidate)) { + return detailFromJSON(json as unknown as Detail$Wire); + } + return json as unknown as unknown; +} + +export function openInnerToJSON(value: OpenInner): OpenInner$Wire { + const candidate: unknown = value; + if (matches$1ToJSON(candidate)) { + return detailToJSON(value as unknown as Detail); + } + return value as unknown as unknown; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/OpenMap.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/OpenMap.ts.golden new file mode 100644 index 000000000..2bec8ab19 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/OpenMap.ts.golden @@ -0,0 +1,40 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { openInnerFromJSON, openInnerToJSON } from './OpenInner'; + +export interface OpenMap$Wire extends Readonly> {} + +export interface OpenMap extends Readonly> {} + +export function openMapFromJSON(json: OpenMap$Wire): OpenMap { + const sourceEntries = Object.entries(json); + const additionalEntries = sourceEntries; + const additionalProperties = Object.fromEntries( + additionalEntries.map(([key, value]) => { + const additionalValue = value; + return [key, openInnerFromJSON(additionalValue)] as const; + }), + ); + const result = { + ...additionalProperties, + }; + return result; +} + +export function openMapToJSON(value: OpenMap): OpenMap$Wire { + const sourceEntries = Object.entries(value); + const additionalEntries = sourceEntries; + const additionalProperties = Object.fromEntries( + additionalEntries.map(([key, value]) => { + const additionalValue = value; + return [key, openInnerToJSON(additionalValue)] as const; + }), + ); + const result = { + ...additionalProperties, + }; + return result; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/OpenTool.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/OpenTool.ts.golden new file mode 100644 index 000000000..cb3bcadbe --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/OpenTool.ts.golden @@ -0,0 +1,28 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { matches$23FromJSON, matches$23ToJSON } from '../runtime/unionMatch'; +import type { ClientTool, ClientTool$Wire } from './ClientTool'; +import { clientToolFromJSON, clientToolToJSON } from './ClientTool'; + +export type OpenTool$Wire = unknown; + +export type OpenTool = unknown; + +export function openToolFromJSON(json: OpenTool$Wire): OpenTool { + const candidate: unknown = json; + if (matches$23FromJSON(candidate)) { + return clientToolFromJSON(json as unknown as ClientTool$Wire); + } + return json as unknown as unknown; +} + +export function openToolToJSON(value: OpenTool): OpenTool$Wire { + const candidate: unknown = value; + if (matches$23ToJSON(candidate)) { + return clientToolToJSON(value as unknown as ClientTool); + } + return value as unknown as unknown; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/OtherDetail.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/OtherDetail.ts.golden new file mode 100644 index 000000000..9bb6b93d7 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/OtherDetail.ts.golden @@ -0,0 +1,28 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface OtherDetail$Wire { + readonly optional_flag?: boolean; + readonly wire_name: string; +} + +export interface OtherDetail { + readonly optionalFlag?: boolean; + readonly wireName: string; +} + +export function otherDetailFromJSON(json: OtherDetail$Wire): OtherDetail { + return { + optionalFlag: json.optional_flag, + wireName: json.wire_name, + }; +} + +export function otherDetailToJSON(value: OtherDetail): OtherDetail$Wire { + return { + optional_flag: value.optionalFlag, + wire_name: value.wireName, + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/Outer.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/Outer.ts.golden new file mode 100644 index 000000000..306cdaecf --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/Outer.ts.golden @@ -0,0 +1,39 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$1FromJSON, + matches$1ToJSON, + matches$2FromJSON, + matches$2ToJSON, +} from '../runtime/unionMatch'; +import type { OpenInner, OpenInner$Wire } from './OpenInner'; +import { openInnerFromJSON, openInnerToJSON } from './OpenInner'; + +export type Outer$Wire = unknown; + +export type Outer = unknown; + +export function outerFromJSON(json: Outer$Wire): Outer { + const candidate: unknown = json; + if (matches$1FromJSON(candidate)) { + return openInnerFromJSON(json as unknown as OpenInner$Wire); + } + if (matches$2FromJSON(candidate)) { + return json as unknown as string; + } + return openInnerFromJSON(json as unknown as OpenInner$Wire); +} + +export function outerToJSON(value: Outer): Outer$Wire { + const candidate: unknown = value; + if (matches$1ToJSON(candidate)) { + return openInnerToJSON(value as unknown as OpenInner); + } + if (matches$2ToJSON(candidate)) { + return value as unknown as string; + } + return openInnerToJSON(value as unknown as OpenInner); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/PrimitiveArrayChoice.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/PrimitiveArrayChoice.ts.golden new file mode 100644 index 000000000..2239d044f --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/PrimitiveArrayChoice.ts.golden @@ -0,0 +1,43 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$1FromJSON, + matches$1ToJSON, + matches$70FromJSON, + matches$70ToJSON, +} from '../runtime/unionMatch'; +import type { Value$3, Value$3Wire } from '../runtime/valueConversion$3'; +import { convert$3FromJSON, convert$3ToJSON } from '../runtime/valueConversion$3'; +import type { Detail, Detail$Wire } from './Detail'; +import { detailFromJSON, detailToJSON } from './Detail'; + +export type PrimitiveArrayChoice$Wire = readonly number[] | Detail$Wire; + +export type PrimitiveArrayChoice = readonly number[] | Detail; + +export function primitiveArrayChoiceFromJSON( + json: PrimitiveArrayChoice$Wire, +): PrimitiveArrayChoice { + const candidate: unknown = json; + if (matches$70FromJSON(candidate)) { + return convert$3FromJSON(json as unknown as Value$3Wire); + } + if (matches$1FromJSON(candidate)) { + return detailFromJSON(json as unknown as Detail$Wire); + } + return json as unknown as PrimitiveArrayChoice; +} + +export function primitiveArrayChoiceToJSON(value: PrimitiveArrayChoice): PrimitiveArrayChoice$Wire { + const candidate: unknown = value; + if (matches$70ToJSON(candidate)) { + return convert$3ToJSON(value as unknown as Value$3); + } + if (matches$1ToJSON(candidate)) { + return detailToJSON(value as unknown as Detail); + } + return value as unknown as PrimitiveArrayChoice$Wire; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/PrimitiveMapChoice.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/PrimitiveMapChoice.ts.golden new file mode 100644 index 000000000..8747fe586 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/PrimitiveMapChoice.ts.golden @@ -0,0 +1,41 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$1FromJSON, + matches$1ToJSON, + matches$72FromJSON, + matches$72ToJSON, +} from '../runtime/unionMatch'; +import type { Value$26, Value$26Wire } from '../runtime/valueConversion$26'; +import { convert$26FromJSON, convert$26ToJSON } from '../runtime/valueConversion$26'; +import type { Detail, Detail$Wire } from './Detail'; +import { detailFromJSON, detailToJSON } from './Detail'; + +export type PrimitiveMapChoice$Wire = globalThis.Record | Detail$Wire; + +export type PrimitiveMapChoice = globalThis.Record | Detail; + +export function primitiveMapChoiceFromJSON(json: PrimitiveMapChoice$Wire): PrimitiveMapChoice { + const candidate: unknown = json; + if (matches$72FromJSON(candidate)) { + return convert$26FromJSON(json as unknown as Value$26Wire); + } + if (matches$1FromJSON(candidate)) { + return detailFromJSON(json as unknown as Detail$Wire); + } + return json as unknown as PrimitiveMapChoice; +} + +export function primitiveMapChoiceToJSON(value: PrimitiveMapChoice): PrimitiveMapChoice$Wire { + const candidate: unknown = value; + if (matches$72ToJSON(candidate)) { + return convert$26ToJSON(value as unknown as Value$26); + } + if (matches$1ToJSON(candidate)) { + return detailToJSON(value as unknown as Detail); + } + return value as unknown as PrimitiveMapChoice$Wire; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/RawEnvelope.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/RawEnvelope.ts.golden new file mode 100644 index 000000000..508fa4eb9 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/RawEnvelope.ts.golden @@ -0,0 +1,37 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { AliasChoice, AliasChoice$Wire } from './AliasChoice'; +import { aliasChoiceFromJSON, aliasChoiceToJSON } from './AliasChoice'; +import type { Detail, Detail$Wire } from './Detail'; +import { detailFromJSON, detailToJSON } from './Detail'; + +export interface RawEnvelope$Wire { + readonly plain_detail: Detail$Wire; + readonly raw_choice: AliasChoice$Wire; + readonly request_id: string; +} + +export interface RawEnvelope { + readonly plainDetail: Detail; + readonly rawChoice: AliasChoice; + readonly requestId: string; +} + +export function rawEnvelopeFromJSON(json: RawEnvelope$Wire): RawEnvelope { + return { + plainDetail: detailFromJSON(json.plain_detail), + rawChoice: aliasChoiceFromJSON(json.raw_choice), + requestId: json.request_id, + }; +} + +export function rawEnvelopeToJSON(value: RawEnvelope): RawEnvelope$Wire { + return { + plain_detail: detailToJSON(value.plainDetail), + raw_choice: aliasChoiceToJSON(value.rawChoice), + request_id: value.requestId, + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/Readonly.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/Readonly.ts.golden new file mode 100644 index 000000000..ffd814862 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/Readonly.ts.golden @@ -0,0 +1,44 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { convert$11FromJSON, convert$11ToJSON } from '../runtime/valueConversion$11'; +import type { Detail, Detail$Wire } from './Detail'; +import { detailFromJSON, detailToJSON } from './Detail'; + +export type Readonly$Wire = globalThis.Readonly> & + globalThis.Readonly>; + +export type Readonly = globalThis.Readonly> & + globalThis.Readonly>; + +export function readonlyFromJSON(json: Readonly$Wire): Readonly { + if (Object.prototype.hasOwnProperty.call(json, 'extension_key')) { + const contentValue = (json as globalThis.Record<'extension_key', Detail$Wire>)['extension_key']; + const entries = Object.entries(json).filter(([key]) => key !== 'extension_key'); + const additionalProperties = Object.fromEntries( + entries.map(([key, value]) => { + const additionalValue = value as readonly Detail$Wire[]; + return [key, convert$11FromJSON(additionalValue)] as const; + }), + ); + return { ...additionalProperties, ['extension_key']: detailFromJSON(contentValue) }; + } + throw new Error('Unknown variant'); +} + +export function readonlyToJSON(value: Readonly): Readonly$Wire { + if (Object.prototype.hasOwnProperty.call(value, 'extension_key')) { + const contentValue = (value as globalThis.Record<'extension_key', Detail>)['extension_key']; + const entries = Object.entries(value).filter(([key]) => key !== 'extension_key'); + const additionalProperties = Object.fromEntries( + entries.map(([key, value]) => { + const additionalValue = value as readonly Detail[]; + return [key, convert$11ToJSON(additionalValue)] as const; + }), + ); + return { ...additionalProperties, ['extension_key']: detailToJSON(contentValue) }; + } + throw new Error('Unknown variant'); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/Record.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/Record.ts.golden new file mode 100644 index 000000000..b221a9594 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/Record.ts.golden @@ -0,0 +1,43 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail, Detail$Wire } from './Detail'; +import { detailFromJSON, detailToJSON } from './Detail'; + +export type Record$Wire = + | globalThis.Record<'event_type', 'empty'> + | (globalThis.Record<'event_type', 'data'> & globalThis.Record<'event_data', Detail$Wire>); + +export type Record = + | globalThis.Record<'eventType', 'empty'> + | (globalThis.Record<'eventType', 'data'> & globalThis.Record<'eventData', Detail>); + +export function recordFromJSON(json: Record$Wire): Record { + if (json['event_type'] === 'empty') { + return { ['eventType']: 'empty' } as Record; + } + if (json['event_type'] === 'data') { + return { + ['eventType']: 'data', + ['eventData']: detailFromJSON( + (json as globalThis.Record<'event_data', Detail$Wire>)['event_data'], + ), + } as Record; + } + throw new Error('Unknown variant'); +} + +export function recordToJSON(value: Record): Record$Wire { + if (value['eventType'] === 'empty') { + return { ['event_type']: 'empty' } as Record$Wire; + } + if (value['eventType'] === 'data') { + return { + ['event_type']: 'data', + ['event_data']: detailToJSON((value as globalThis.Record<'eventData', Detail>)['eventData']), + } as Record$Wire; + } + throw new Error('Unknown variant'); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/RecordEmpty.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/RecordEmpty.ts.golden new file mode 100644 index 000000000..9fd01d883 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/RecordEmpty.ts.golden @@ -0,0 +1,24 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface RecordEmpty$Wire { + readonly event_type: 'empty'; +} + +export interface RecordEmpty { + readonly eventType: 'empty'; +} + +export function recordEmptyFromJSON(json: RecordEmpty$Wire): RecordEmpty { + return { + eventType: json.event_type, + }; +} + +export function recordEmptyToJSON(value: RecordEmpty): RecordEmpty$Wire { + return { + event_type: value.eventType, + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/RecordOrBoolean.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/RecordOrBoolean.ts.golden new file mode 100644 index 000000000..7e716b034 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/RecordOrBoolean.ts.golden @@ -0,0 +1,39 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$5FromJSON, + matches$5ToJSON, + matches$6FromJSON, + matches$6ToJSON, +} from '../runtime/unionMatch'; +import type { Record, Record$Wire } from './Record'; +import { recordFromJSON, recordToJSON } from './Record'; + +export type RecordOrBoolean$Wire = Record$Wire | boolean; + +export type RecordOrBoolean = Record | boolean; + +export function recordOrBooleanFromJSON(json: RecordOrBoolean$Wire): RecordOrBoolean { + const candidate: unknown = json; + if (matches$6FromJSON(candidate)) { + return recordFromJSON(json as unknown as Record$Wire); + } + if (matches$5FromJSON(candidate)) { + return json as unknown as boolean; + } + return json as unknown as RecordOrBoolean; +} + +export function recordOrBooleanToJSON(value: RecordOrBoolean): RecordOrBoolean$Wire { + const candidate: unknown = value; + if (matches$6ToJSON(candidate)) { + return recordToJSON(value as unknown as Record); + } + if (matches$5ToJSON(candidate)) { + return value as unknown as boolean; + } + return value as unknown as RecordOrBoolean$Wire; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/RecursiveDictionaryChoice.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/RecursiveDictionaryChoice.ts.golden new file mode 100644 index 000000000..7b1dca097 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/RecursiveDictionaryChoice.ts.golden @@ -0,0 +1,38 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { matches$73FromJSON, matches$73ToJSON } from '../runtime/unionMatch'; +import type { + RecursiveOpenDictionary, + RecursiveOpenDictionary$Wire, +} from './RecursiveOpenDictionary'; +import { + recursiveOpenDictionaryFromJSON, + recursiveOpenDictionaryToJSON, +} from './RecursiveOpenDictionary'; + +export type RecursiveDictionaryChoice$Wire = unknown; + +export type RecursiveDictionaryChoice = unknown; + +export function recursiveDictionaryChoiceFromJSON( + json: RecursiveDictionaryChoice$Wire, +): RecursiveDictionaryChoice { + const candidate: unknown = json; + if (matches$73FromJSON(candidate)) { + return recursiveOpenDictionaryFromJSON(json as unknown as RecursiveOpenDictionary$Wire); + } + return json as unknown as unknown; +} + +export function recursiveDictionaryChoiceToJSON( + value: RecursiveDictionaryChoice, +): RecursiveDictionaryChoice$Wire { + const candidate: unknown = value; + if (matches$73ToJSON(candidate)) { + return recursiveOpenDictionaryToJSON(value as unknown as RecursiveOpenDictionary); + } + return value as unknown as unknown; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/RecursiveList.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/RecursiveList.ts.golden new file mode 100644 index 000000000..3773d228f --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/RecursiveList.ts.golden @@ -0,0 +1,41 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$1FromJSON, + matches$1ToJSON, + matches$81FromJSON, + matches$81ToJSON, +} from '../runtime/unionMatch'; +import type { Value$28, Value$28Wire } from '../runtime/valueConversion$28'; +import { convert$28FromJSON, convert$28ToJSON } from '../runtime/valueConversion$28'; +import type { Detail, Detail$Wire } from './Detail'; +import { detailFromJSON, detailToJSON } from './Detail'; + +export type RecursiveList$Wire = readonly RecursiveList$Wire[] | Detail$Wire; + +export type RecursiveList = readonly RecursiveList[] | Detail; + +export function recursiveListFromJSON(json: RecursiveList$Wire): RecursiveList { + const candidate: unknown = json; + if (matches$81FromJSON(candidate)) { + return convert$28FromJSON(json as unknown as Value$28Wire); + } + if (matches$1FromJSON(candidate)) { + return detailFromJSON(json as unknown as Detail$Wire); + } + return json as unknown as RecursiveList; +} + +export function recursiveListToJSON(value: RecursiveList): RecursiveList$Wire { + const candidate: unknown = value; + if (matches$81ToJSON(candidate)) { + return convert$28ToJSON(value as unknown as Value$28); + } + if (matches$1ToJSON(candidate)) { + return detailToJSON(value as unknown as Detail); + } + return value as unknown as RecursiveList$Wire; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/RecursiveOpenDictionary.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/RecursiveOpenDictionary.ts.golden new file mode 100644 index 000000000..a8dcce268 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/RecursiveOpenDictionary.ts.golden @@ -0,0 +1,45 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$1FromJSON, + matches$1ToJSON, + matches$74FromJSON, + matches$74ToJSON, +} from '../runtime/unionMatch'; +import type { Value$29, Value$29Wire } from '../runtime/valueConversion$29'; +import { convert$29FromJSON, convert$29ToJSON } from '../runtime/valueConversion$29'; +import type { Detail, Detail$Wire } from './Detail'; +import { detailFromJSON, detailToJSON } from './Detail'; + +export type RecursiveOpenDictionary$Wire = unknown; + +export type RecursiveOpenDictionary = unknown; + +export function recursiveOpenDictionaryFromJSON( + json: RecursiveOpenDictionary$Wire, +): RecursiveOpenDictionary { + const candidate: unknown = json; + if (matches$1FromJSON(candidate)) { + return detailFromJSON(json as unknown as Detail$Wire); + } + if (matches$74FromJSON(candidate)) { + return convert$29FromJSON(json as unknown as Value$29Wire); + } + return json as unknown as unknown; +} + +export function recursiveOpenDictionaryToJSON( + value: RecursiveOpenDictionary, +): RecursiveOpenDictionary$Wire { + const candidate: unknown = value; + if (matches$1ToJSON(candidate)) { + return detailToJSON(value as unknown as Detail); + } + if (matches$74ToJSON(candidate)) { + return convert$29ToJSON(value as unknown as Value$29); + } + return value as unknown as unknown; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/ScalarEnumChoice.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/ScalarEnumChoice.ts.golden new file mode 100644 index 000000000..a8054194c --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/ScalarEnumChoice.ts.golden @@ -0,0 +1,37 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$3FromJSON, + matches$3ToJSON, + matches$5FromJSON, + matches$5ToJSON, +} from '../runtime/unionMatch'; + +export type ScalarEnumChoice$Wire = number | boolean; + +export type ScalarEnumChoice = number | boolean; + +export function scalarEnumChoiceFromJSON(json: ScalarEnumChoice$Wire): ScalarEnumChoice { + const candidate: unknown = json; + if (matches$3FromJSON(candidate)) { + return json as unknown as number; + } + if (matches$5FromJSON(candidate)) { + return json as unknown as boolean; + } + return json as unknown as ScalarEnumChoice; +} + +export function scalarEnumChoiceToJSON(value: ScalarEnumChoice): ScalarEnumChoice$Wire { + const candidate: unknown = value; + if (matches$3ToJSON(candidate)) { + return value as unknown as number; + } + if (matches$5ToJSON(candidate)) { + return value as unknown as boolean; + } + return value as unknown as ScalarEnumChoice$Wire; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/SearchTool.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/SearchTool.ts.golden new file mode 100644 index 000000000..9992b2695 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/SearchTool.ts.golden @@ -0,0 +1,32 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface SearchTool$Wire { + readonly max_uses?: number; + readonly name: 'web_search'; + readonly type: 'web_search_20250305'; +} + +export interface SearchTool { + readonly maxUses?: number; + readonly name: 'web_search'; + readonly type: 'web_search_20250305'; +} + +export function searchToolFromJSON(json: SearchTool$Wire): SearchTool { + return { + maxUses: json.max_uses, + name: json.name, + type: json['type'], + }; +} + +export function searchToolToJSON(value: SearchTool): SearchTool$Wire { + return { + max_uses: value.maxUses, + name: value.name, + type: value['type'], + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/SendOptionalInlineScalarRequest.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/SendOptionalInlineScalarRequest.ts.golden new file mode 100644 index 000000000..49c3eaa26 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/SendOptionalInlineScalarRequest.ts.golden @@ -0,0 +1,41 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$66FromJSON, + matches$66ToJSON, + matches$67FromJSON, + matches$67ToJSON, +} from '../runtime/unionMatch'; + +export type SendOptionalInlineScalarRequest$Wire = string | null | number; + +export type SendOptionalInlineScalarRequest = string | null | number; + +export function sendOptionalInlineScalarRequestFromJSON( + json: SendOptionalInlineScalarRequest$Wire, +): SendOptionalInlineScalarRequest { + const candidate: unknown = json; + if (matches$66FromJSON(candidate)) { + return json as unknown as string | null; + } + if (matches$67FromJSON(candidate)) { + return json as unknown as number | null; + } + return json as unknown as SendOptionalInlineScalarRequest; +} + +export function sendOptionalInlineScalarRequestToJSON( + value: SendOptionalInlineScalarRequest, +): SendOptionalInlineScalarRequest$Wire { + const candidate: unknown = value; + if (matches$66ToJSON(candidate)) { + return value as unknown as string | null; + } + if (matches$67ToJSON(candidate)) { + return value as unknown as number | null; + } + return value as unknown as SendOptionalInlineScalarRequest$Wire; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/TaggedMapA.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/TaggedMapA.ts.golden new file mode 100644 index 000000000..8aa8640e2 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/TaggedMapA.ts.golden @@ -0,0 +1,43 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail, Detail$Wire } from './Detail'; +import { detailFromJSON, detailToJSON } from './Detail'; + +export type TaggedMapA$Wire = globalThis.Readonly> & + globalThis.Readonly>; + +export type TaggedMapA = globalThis.Readonly> & + globalThis.Readonly>; + +export function taggedMapAFromJSON(json: TaggedMapA$Wire): TaggedMapA { + if (Object.prototype.hasOwnProperty.call(json, 'payload_key')) { + const contentValue = (json as globalThis.Record<'payload_key', Detail$Wire>)['payload_key']; + const entries = Object.entries(json).filter(([key]) => key !== 'payload_key'); + const additionalProperties = Object.fromEntries( + entries.map(([key, value]) => { + const additionalValue = value as Detail$Wire; + return [key, detailFromJSON(additionalValue)] as const; + }), + ); + return { ...additionalProperties, ['payload_key']: detailFromJSON(contentValue) }; + } + throw new Error('Unknown variant'); +} + +export function taggedMapAToJSON(value: TaggedMapA): TaggedMapA$Wire { + if (Object.prototype.hasOwnProperty.call(value, 'payload_key')) { + const contentValue = (value as globalThis.Record<'payload_key', Detail>)['payload_key']; + const entries = Object.entries(value).filter(([key]) => key !== 'payload_key'); + const additionalProperties = Object.fromEntries( + entries.map(([key, value]) => { + const additionalValue = value as Detail; + return [key, detailToJSON(additionalValue)] as const; + }), + ); + return { ...additionalProperties, ['payload_key']: detailToJSON(contentValue) }; + } + throw new Error('Unknown variant'); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/TaggedMapB.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/TaggedMapB.ts.golden new file mode 100644 index 000000000..4c59d515e --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/TaggedMapB.ts.golden @@ -0,0 +1,44 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { convert$11FromJSON, convert$11ToJSON } from '../runtime/valueConversion$11'; +import type { Detail, Detail$Wire } from './Detail'; +import { detailFromJSON, detailToJSON } from './Detail'; + +export type TaggedMapB$Wire = globalThis.Readonly> & + globalThis.Readonly>; + +export type TaggedMapB = globalThis.Readonly> & + globalThis.Readonly>; + +export function taggedMapBFromJSON(json: TaggedMapB$Wire): TaggedMapB { + if (Object.prototype.hasOwnProperty.call(json, 'payload_key')) { + const contentValue = (json as globalThis.Record<'payload_key', Detail$Wire>)['payload_key']; + const entries = Object.entries(json).filter(([key]) => key !== 'payload_key'); + const additionalProperties = Object.fromEntries( + entries.map(([key, value]) => { + const additionalValue = value as readonly Detail$Wire[]; + return [key, convert$11FromJSON(additionalValue)] as const; + }), + ); + return { ...additionalProperties, ['payload_key']: detailFromJSON(contentValue) }; + } + throw new Error('Unknown variant'); +} + +export function taggedMapBToJSON(value: TaggedMapB): TaggedMapB$Wire { + if (Object.prototype.hasOwnProperty.call(value, 'payload_key')) { + const contentValue = (value as globalThis.Record<'payload_key', Detail>)['payload_key']; + const entries = Object.entries(value).filter(([key]) => key !== 'payload_key'); + const additionalProperties = Object.fromEntries( + entries.map(([key, value]) => { + const additionalValue = value as readonly Detail[]; + return [key, convert$11ToJSON(additionalValue)] as const; + }), + ); + return { ...additionalProperties, ['payload_key']: detailToJSON(contentValue) }; + } + throw new Error('Unknown variant'); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/TaggedMapChoice.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/TaggedMapChoice.ts.golden new file mode 100644 index 000000000..eed424c69 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/TaggedMapChoice.ts.golden @@ -0,0 +1,41 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$83FromJSON, + matches$83ToJSON, + matches$85FromJSON, + matches$85ToJSON, +} from '../runtime/unionMatch'; +import type { TaggedMapA, TaggedMapA$Wire } from './TaggedMapA'; +import { taggedMapAFromJSON, taggedMapAToJSON } from './TaggedMapA'; +import type { TaggedMapB, TaggedMapB$Wire } from './TaggedMapB'; +import { taggedMapBFromJSON, taggedMapBToJSON } from './TaggedMapB'; + +export type TaggedMapChoice$Wire = TaggedMapA$Wire | TaggedMapB$Wire; + +export type TaggedMapChoice = TaggedMapA | TaggedMapB; + +export function taggedMapChoiceFromJSON(json: TaggedMapChoice$Wire): TaggedMapChoice { + const candidate: unknown = json; + if (matches$83FromJSON(candidate)) { + return taggedMapAFromJSON(json as unknown as TaggedMapA$Wire); + } + if (matches$85FromJSON(candidate)) { + return taggedMapBFromJSON(json as unknown as TaggedMapB$Wire); + } + return json as unknown as TaggedMapChoice; +} + +export function taggedMapChoiceToJSON(value: TaggedMapChoice): TaggedMapChoice$Wire { + const candidate: unknown = value; + if (matches$83ToJSON(candidate)) { + return taggedMapAToJSON(value as unknown as TaggedMapA); + } + if (matches$85ToJSON(candidate)) { + return taggedMapBToJSON(value as unknown as TaggedMapB); + } + return value as unknown as TaggedMapChoice$Wire; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/Tool.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/Tool.ts.golden new file mode 100644 index 000000000..96a3aa39b --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/Tool.ts.golden @@ -0,0 +1,41 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$13FromJSON, + matches$13ToJSON, + matches$23FromJSON, + matches$23ToJSON, +} from '../runtime/unionMatch'; +import type { ClientTool, ClientTool$Wire } from './ClientTool'; +import { clientToolFromJSON, clientToolToJSON } from './ClientTool'; +import type { SearchTool, SearchTool$Wire } from './SearchTool'; +import { searchToolFromJSON, searchToolToJSON } from './SearchTool'; + +export type Tool$Wire = ClientTool$Wire | SearchTool$Wire; + +export type Tool = ClientTool | SearchTool; + +export function toolFromJSON(json: Tool$Wire): Tool { + const candidate: unknown = json; + if (matches$23FromJSON(candidate)) { + return clientToolFromJSON(json as unknown as ClientTool$Wire); + } + if (matches$13FromJSON(candidate)) { + return searchToolFromJSON(json as unknown as SearchTool$Wire); + } + return json as unknown as Tool; +} + +export function toolToJSON(value: Tool): Tool$Wire { + const candidate: unknown = value; + if (matches$23ToJSON(candidate)) { + return clientToolToJSON(value as unknown as ClientTool); + } + if (matches$13ToJSON(candidate)) { + return searchToolToJSON(value as unknown as SearchTool); + } + return value as unknown as Tool$Wire; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/ToolAlias.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/ToolAlias.ts.golden new file mode 100644 index 000000000..19920bb13 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/ToolAlias.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Tool, Tool$Wire } from './Tool'; +import { toolFromJSON, toolToJSON } from './Tool'; + +export type ToolAlias$Wire = Tool$Wire; + +export type ToolAlias = Tool; + +export function toolAliasFromJSON(json: ToolAlias$Wire): ToolAlias { + return toolFromJSON(json); +} + +export function toolAliasToJSON(value: ToolAlias): ToolAlias$Wire { + return toolToJSON(value); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/ToolList.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/ToolList.ts.golden new file mode 100644 index 000000000..d28c0aea8 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/ToolList.ts.golden @@ -0,0 +1,43 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$86FromJSON, + matches$86ToJSON, + matches$87FromJSON, + matches$87ToJSON, +} from '../runtime/unionMatch'; +import type { Value$30, Value$30Wire } from '../runtime/valueConversion$30'; +import { convert$30FromJSON, convert$30ToJSON } from '../runtime/valueConversion$30'; +import type { Value$31, Value$31Wire } from '../runtime/valueConversion$31'; +import { convert$31FromJSON, convert$31ToJSON } from '../runtime/valueConversion$31'; +import type { ClientTool, ClientTool$Wire } from './ClientTool'; +import type { SearchTool, SearchTool$Wire } from './SearchTool'; + +export type ToolList$Wire = readonly ClientTool$Wire[] | readonly SearchTool$Wire[]; + +export type ToolList = readonly ClientTool[] | readonly SearchTool[]; + +export function toolListFromJSON(json: ToolList$Wire): ToolList { + const candidate: unknown = json; + if (matches$86FromJSON(candidate)) { + return convert$30FromJSON(json as unknown as Value$30Wire); + } + if (matches$87FromJSON(candidate)) { + return convert$31FromJSON(json as unknown as Value$31Wire); + } + return json as unknown as ToolList; +} + +export function toolListToJSON(value: ToolList): ToolList$Wire { + const candidate: unknown = value; + if (matches$86ToJSON(candidate)) { + return convert$30ToJSON(value as unknown as Value$30); + } + if (matches$87ToJSON(candidate)) { + return convert$31ToJSON(value as unknown as Value$31); + } + return value as unknown as ToolList$Wire; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/ToolMap.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/ToolMap.ts.golden new file mode 100644 index 000000000..64fc74a9a --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/ToolMap.ts.golden @@ -0,0 +1,45 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$88FromJSON, + matches$88ToJSON, + matches$89FromJSON, + matches$89ToJSON, +} from '../runtime/unionMatch'; +import type { Value$32, Value$32Wire } from '../runtime/valueConversion$32'; +import { convert$32FromJSON, convert$32ToJSON } from '../runtime/valueConversion$32'; +import type { Value$33, Value$33Wire } from '../runtime/valueConversion$33'; +import { convert$33FromJSON, convert$33ToJSON } from '../runtime/valueConversion$33'; +import type { ClientTool, ClientTool$Wire } from './ClientTool'; +import type { SearchTool, SearchTool$Wire } from './SearchTool'; + +export type ToolMap$Wire = + | globalThis.Record + | globalThis.Record; + +export type ToolMap = globalThis.Record | globalThis.Record; + +export function toolMapFromJSON(json: ToolMap$Wire): ToolMap { + const candidate: unknown = json; + if (matches$88FromJSON(candidate)) { + return convert$32FromJSON(json as unknown as Value$32Wire); + } + if (matches$89FromJSON(candidate)) { + return convert$33FromJSON(json as unknown as Value$33Wire); + } + return json as unknown as ToolMap; +} + +export function toolMapToJSON(value: ToolMap): ToolMap$Wire { + const candidate: unknown = value; + if (matches$88ToJSON(candidate)) { + return convert$32ToJSON(value as unknown as Value$32); + } + if (matches$89ToJSON(candidate)) { + return convert$33ToJSON(value as unknown as Value$33); + } + return value as unknown as ToolMap$Wire; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/ToolRequest.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/ToolRequest.ts.golden new file mode 100644 index 000000000..5ef47e068 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/ToolRequest.ts.golden @@ -0,0 +1,27 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { convert$34FromJSON, convert$34ToJSON } from '../runtime/valueConversion$34'; +import type { Tool, Tool$Wire } from './Tool'; + +export interface ToolRequest$Wire { + readonly tools: readonly Tool$Wire[]; +} + +export interface ToolRequest { + readonly tools: readonly Tool[]; +} + +export function toolRequestFromJSON(json: ToolRequest$Wire): ToolRequest { + return { + tools: convert$34FromJSON(json.tools), + }; +} + +export function toolRequestToJSON(value: ToolRequest): ToolRequest$Wire { + return { + tools: convert$34ToJSON(value.tools), + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/TwiceNullableOpaque.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/TwiceNullableOpaque.ts.golden new file mode 100644 index 000000000..c0107b457 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/TwiceNullableOpaque.ts.golden @@ -0,0 +1,27 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { NullableOpaque, NullableOpaque$Wire } from './NullableOpaque'; +import { nullableOpaqueFromJSON, nullableOpaqueToJSON } from './NullableOpaque'; + +export type TwiceNullableOpaque$Wire = unknown; + +export type TwiceNullableOpaque = unknown; + +export function twiceNullableOpaqueFromJSON(json: TwiceNullableOpaque$Wire): TwiceNullableOpaque { + const candidate: unknown = json; + if (candidate === null) { + return null; + } + return nullableOpaqueFromJSON(json as unknown as NullableOpaque$Wire); +} + +export function twiceNullableOpaqueToJSON(value: TwiceNullableOpaque): TwiceNullableOpaque$Wire { + const candidate: unknown = value; + if (candidate === null) { + return null; + } + return nullableOpaqueToJSON(value as unknown as NullableOpaque); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/TwiceNullableOuter.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/TwiceNullableOuter.ts.golden new file mode 100644 index 000000000..05ffdefbb --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/TwiceNullableOuter.ts.golden @@ -0,0 +1,30 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { matches$30FromJSON, matches$30ToJSON } from '../runtime/unionMatch'; +import type { NullableDetail, NullableDetail$Wire } from './NullableDetail'; +import { nullableDetailFromJSON, nullableDetailToJSON } from './NullableDetail'; +import type { TwiceNullableOpaque, TwiceNullableOpaque$Wire } from './TwiceNullableOpaque'; +import { twiceNullableOpaqueFromJSON, twiceNullableOpaqueToJSON } from './TwiceNullableOpaque'; + +export type TwiceNullableOuter$Wire = unknown; + +export type TwiceNullableOuter = unknown; + +export function twiceNullableOuterFromJSON(json: TwiceNullableOuter$Wire): TwiceNullableOuter { + const candidate: unknown = json; + if (matches$30FromJSON(candidate)) { + return nullableDetailFromJSON(json as unknown as NullableDetail$Wire); + } + return twiceNullableOpaqueFromJSON(json as unknown as TwiceNullableOpaque$Wire); +} + +export function twiceNullableOuterToJSON(value: TwiceNullableOuter): TwiceNullableOuter$Wire { + const candidate: unknown = value; + if (matches$30ToJSON(candidate)) { + return nullableDetailToJSON(value as unknown as NullableDetail); + } + return twiceNullableOpaqueToJSON(value as unknown as TwiceNullableOpaque); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/UntypedNumericChoice.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/UntypedNumericChoice.ts.golden new file mode 100644 index 000000000..8b582ac2e --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/UntypedNumericChoice.ts.golden @@ -0,0 +1,43 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$90FromJSON, + matches$90ToJSON, + matches$91FromJSON, + matches$91ToJSON, +} from '../runtime/unionMatch'; +import type { UntypedNumericOne, UntypedNumericOne$Wire } from './UntypedNumericOne'; +import { untypedNumericOneFromJSON, untypedNumericOneToJSON } from './UntypedNumericOne'; +import type { UntypedNumericTwo, UntypedNumericTwo$Wire } from './UntypedNumericTwo'; +import { untypedNumericTwoFromJSON, untypedNumericTwoToJSON } from './UntypedNumericTwo'; + +export type UntypedNumericChoice$Wire = UntypedNumericOne$Wire | UntypedNumericTwo$Wire; + +export type UntypedNumericChoice = UntypedNumericOne | UntypedNumericTwo; + +export function untypedNumericChoiceFromJSON( + json: UntypedNumericChoice$Wire, +): UntypedNumericChoice { + const candidate: unknown = json; + if (matches$90FromJSON(candidate)) { + return untypedNumericOneFromJSON(json as unknown as UntypedNumericOne$Wire); + } + if (matches$91FromJSON(candidate)) { + return untypedNumericTwoFromJSON(json as unknown as UntypedNumericTwo$Wire); + } + return json as unknown as UntypedNumericChoice; +} + +export function untypedNumericChoiceToJSON(value: UntypedNumericChoice): UntypedNumericChoice$Wire { + const candidate: unknown = value; + if (matches$90ToJSON(candidate)) { + return untypedNumericOneToJSON(value as unknown as UntypedNumericOne); + } + if (matches$91ToJSON(candidate)) { + return untypedNumericTwoToJSON(value as unknown as UntypedNumericTwo); + } + return value as unknown as UntypedNumericChoice$Wire; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/UntypedNumericOne.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/UntypedNumericOne.ts.golden new file mode 100644 index 000000000..1695e895f --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/UntypedNumericOne.ts.golden @@ -0,0 +1,28 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface UntypedNumericOne$Wire { + readonly first_value?: string; + readonly revision: string; +} + +export interface UntypedNumericOne { + readonly firstValue?: string; + readonly revision: string; +} + +export function untypedNumericOneFromJSON(json: UntypedNumericOne$Wire): UntypedNumericOne { + return { + firstValue: json.first_value, + revision: json.revision, + }; +} + +export function untypedNumericOneToJSON(value: UntypedNumericOne): UntypedNumericOne$Wire { + return { + first_value: value.firstValue, + revision: value.revision, + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/UntypedNumericTwo.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/UntypedNumericTwo.ts.golden new file mode 100644 index 000000000..0ebd0aa68 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/UntypedNumericTwo.ts.golden @@ -0,0 +1,28 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface UntypedNumericTwo$Wire { + readonly revision: string; + readonly second_value?: string; +} + +export interface UntypedNumericTwo { + readonly revision: string; + readonly secondValue?: string; +} + +export function untypedNumericTwoFromJSON(json: UntypedNumericTwo$Wire): UntypedNumericTwo { + return { + revision: json.revision, + secondValue: json.second_value, + }; +} + +export function untypedNumericTwoToJSON(value: UntypedNumericTwo): UntypedNumericTwo$Wire { + return { + revision: value.revision, + second_value: value.secondValue, + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/ValueAlias.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/ValueAlias.ts.golden new file mode 100644 index 000000000..eb42f2ce8 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/ValueAlias.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { convert$0FromJSON, convert$0ToJSON } from '../runtime/valueConversion$0'; +import type { Detail, Detail$Wire } from './Detail'; + +export type ValueAlias$Wire = readonly (globalThis.Record | string)[]; + +export type ValueAlias = readonly (globalThis.Record | string)[]; + +export function valueAliasFromJSON(json: ValueAlias$Wire): ValueAlias { + return convert$0FromJSON(json); +} + +export function valueAliasToJSON(value: ValueAlias): ValueAlias$Wire { + return convert$0ToJSON(value); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/WrappedArrayOrBoolean.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/WrappedArrayOrBoolean.ts.golden new file mode 100644 index 000000000..0fd6866f9 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/WrappedArrayOrBoolean.ts.golden @@ -0,0 +1,43 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$5FromJSON, + matches$5ToJSON, + matches$92FromJSON, + matches$92ToJSON, +} from '../runtime/unionMatch'; +import type { ArrayWrapped, ArrayWrapped$Wire } from './ArrayWrapped'; +import { arrayWrappedFromJSON, arrayWrappedToJSON } from './ArrayWrapped'; + +export type WrappedArrayOrBoolean$Wire = ArrayWrapped$Wire | boolean; + +export type WrappedArrayOrBoolean = ArrayWrapped | boolean; + +export function wrappedArrayOrBooleanFromJSON( + json: WrappedArrayOrBoolean$Wire, +): WrappedArrayOrBoolean { + const candidate: unknown = json; + if (matches$92FromJSON(candidate)) { + return arrayWrappedFromJSON(json as unknown as ArrayWrapped$Wire); + } + if (matches$5FromJSON(candidate)) { + return json as unknown as boolean; + } + return json as unknown as WrappedArrayOrBoolean; +} + +export function wrappedArrayOrBooleanToJSON( + value: WrappedArrayOrBoolean, +): WrappedArrayOrBoolean$Wire { + const candidate: unknown = value; + if (matches$92ToJSON(candidate)) { + return arrayWrappedToJSON(value as unknown as ArrayWrapped); + } + if (matches$5ToJSON(candidate)) { + return value as unknown as boolean; + } + return value as unknown as WrappedArrayOrBoolean$Wire; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/WrappedMapOrBoolean.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/WrappedMapOrBoolean.ts.golden new file mode 100644 index 000000000..55449d7f0 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/WrappedMapOrBoolean.ts.golden @@ -0,0 +1,47 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$1FromJSON, + matches$1ToJSON, + matches$57FromJSON, + matches$57ToJSON, + matches$5FromJSON, + matches$5ToJSON, +} from '../runtime/unionMatch'; +import type { MapWrapped, MapWrapped$Wire } from './MapWrapped'; +import { mapWrappedFromJSON, mapWrappedToJSON } from './MapWrapped'; + +export type WrappedMapOrBoolean$Wire = MapWrapped$Wire | boolean; + +export type WrappedMapOrBoolean = MapWrapped | boolean; + +export function wrappedMapOrBooleanFromJSON(json: WrappedMapOrBoolean$Wire): WrappedMapOrBoolean { + const candidate: unknown = json; + if (matches$1FromJSON(candidate)) { + return mapWrappedFromJSON(json as unknown as MapWrapped$Wire); + } + if (matches$5FromJSON(candidate)) { + return json as unknown as boolean; + } + if (matches$57FromJSON(candidate)) { + return mapWrappedFromJSON(json as unknown as MapWrapped$Wire); + } + return json as unknown as WrappedMapOrBoolean; +} + +export function wrappedMapOrBooleanToJSON(value: WrappedMapOrBoolean): WrappedMapOrBoolean$Wire { + const candidate: unknown = value; + if (matches$1ToJSON(candidate)) { + return mapWrappedToJSON(value as unknown as MapWrapped); + } + if (matches$5ToJSON(candidate)) { + return value as unknown as boolean; + } + if (matches$57ToJSON(candidate)) { + return mapWrappedToJSON(value as unknown as MapWrapped); + } + return value as unknown as WrappedMapOrBoolean$Wire; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/WrappedScalarInner.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/WrappedScalarInner.ts.golden new file mode 100644 index 000000000..f7f6acf35 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/WrappedScalarInner.ts.golden @@ -0,0 +1,34 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { matches$65FromJSON, matches$65ToJSON } from '../runtime/unionMatch'; +import type { NullableScalarInner, NullableScalarInner$Wire } from './NullableScalarInner'; +import { nullableScalarInnerFromJSON, nullableScalarInnerToJSON } from './NullableScalarInner'; + +export type WrappedScalarInner$Wire = NullableScalarInner$Wire | null; + +export type WrappedScalarInner = NullableScalarInner | null; + +export function wrappedScalarInnerFromJSON(json: WrappedScalarInner$Wire): WrappedScalarInner { + const candidate: unknown = json; + if (matches$65FromJSON(candidate)) { + return nullableScalarInnerFromJSON(json as unknown as NullableScalarInner$Wire); + } + if (candidate === null) { + return null; + } + return json as unknown as WrappedScalarInner; +} + +export function wrappedScalarInnerToJSON(value: WrappedScalarInner): WrappedScalarInner$Wire { + const candidate: unknown = value; + if (matches$65ToJSON(candidate)) { + return nullableScalarInnerToJSON(value as unknown as NullableScalarInner); + } + if (candidate === null) { + return null; + } + return value as unknown as WrappedScalarInner$Wire; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/WrapperChoice.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/WrapperChoice.ts.golden new file mode 100644 index 000000000..d41aebde4 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/WrapperChoice.ts.golden @@ -0,0 +1,41 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$58FromJSON, + matches$58ToJSON, + matches$61FromJSON, + matches$61ToJSON, +} from '../runtime/unionMatch'; +import type { WrapperOne, WrapperOne$Wire } from './WrapperOne'; +import { wrapperOneFromJSON, wrapperOneToJSON } from './WrapperOne'; +import type { WrapperTwo, WrapperTwo$Wire } from './WrapperTwo'; +import { wrapperTwoFromJSON, wrapperTwoToJSON } from './WrapperTwo'; + +export type WrapperChoice$Wire = WrapperOne$Wire | WrapperTwo$Wire; + +export type WrapperChoice = WrapperOne | WrapperTwo; + +export function wrapperChoiceFromJSON(json: WrapperChoice$Wire): WrapperChoice { + const candidate: unknown = json; + if (matches$58FromJSON(candidate)) { + return wrapperOneFromJSON(json as unknown as WrapperOne$Wire); + } + if (matches$61FromJSON(candidate)) { + return wrapperTwoFromJSON(json as unknown as WrapperTwo$Wire); + } + return json as unknown as WrapperChoice; +} + +export function wrapperChoiceToJSON(value: WrapperChoice): WrapperChoice$Wire { + const candidate: unknown = value; + if (matches$58ToJSON(candidate)) { + return wrapperOneToJSON(value as unknown as WrapperOne); + } + if (matches$61ToJSON(candidate)) { + return wrapperTwoToJSON(value as unknown as WrapperTwo); + } + return value as unknown as WrapperChoice$Wire; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/WrapperOne.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/WrapperOne.ts.golden new file mode 100644 index 000000000..5d9658c83 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/WrapperOne.ts.golden @@ -0,0 +1,28 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface WrapperOne$Wire { + readonly first_value?: string; + readonly revision: number | null; +} + +export interface WrapperOne { + readonly firstValue?: string; + readonly revision: number | null; +} + +export function wrapperOneFromJSON(json: WrapperOne$Wire): WrapperOne { + return { + firstValue: json.first_value, + revision: json.revision, + }; +} + +export function wrapperOneToJSON(value: WrapperOne): WrapperOne$Wire { + return { + first_value: value.firstValue, + revision: value.revision, + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/WrapperTwo.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/WrapperTwo.ts.golden new file mode 100644 index 000000000..dcca3ef4c --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/WrapperTwo.ts.golden @@ -0,0 +1,28 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface WrapperTwo$Wire { + readonly revision: number | null; + readonly second_value?: string; +} + +export interface WrapperTwo { + readonly revision: number | null; + readonly secondValue?: string; +} + +export function wrapperTwoFromJSON(json: WrapperTwo$Wire): WrapperTwo { + return { + revision: json.revision, + secondValue: json.second_value, + }; +} + +export function wrapperTwoToJSON(value: WrapperTwo): WrapperTwo$Wire { + return { + revision: value.revision, + second_value: value.secondValue, + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/index.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/index.ts.golden new file mode 100644 index 000000000..5e37ecbbc --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/models/index.ts.golden @@ -0,0 +1,361 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export type { Adjacent, Adjacent$Wire } from './Adjacent'; +export { adjacentFromJSON, adjacentToJSON } from './Adjacent'; +export type { AdjacentContainer, AdjacentContainer$Wire } from './AdjacentContainer'; +export { adjacentContainerFromJSON, adjacentContainerToJSON } from './AdjacentContainer'; +export type { AdjacentContainerEmpty, AdjacentContainerEmpty$Wire } from './AdjacentContainerEmpty'; +export { + adjacentContainerEmptyFromJSON, + adjacentContainerEmptyToJSON, +} from './AdjacentContainerEmpty'; +export type { AdjacentEmpty, AdjacentEmpty$Wire } from './AdjacentEmpty'; +export { adjacentEmptyFromJSON, adjacentEmptyToJSON } from './AdjacentEmpty'; +export type { AdjacentOrNumber, AdjacentOrNumber$Wire } from './AdjacentOrNumber'; +export { adjacentOrNumberFromJSON, adjacentOrNumberToJSON } from './AdjacentOrNumber'; +export type { AdjacentPrimitive, AdjacentPrimitive$Wire } from './AdjacentPrimitive'; +export { adjacentPrimitiveFromJSON, adjacentPrimitiveToJSON } from './AdjacentPrimitive'; +export type { AdjacentPrimitiveEmpty, AdjacentPrimitiveEmpty$Wire } from './AdjacentPrimitiveEmpty'; +export { + adjacentPrimitiveEmptyFromJSON, + adjacentPrimitiveEmptyToJSON, +} from './AdjacentPrimitiveEmpty'; +export type { AliasChoice, AliasChoice$Wire } from './AliasChoice'; +export { aliasChoiceFromJSON, aliasChoiceToJSON } from './AliasChoice'; +export type { AliasOuter, AliasOuter$Wire } from './AliasOuter'; +export { aliasOuterFromJSON, aliasOuterToJSON } from './AliasOuter'; +export type { + AlreadyNullableEnumParent, + AlreadyNullableEnumParent$Wire, +} from './AlreadyNullableEnumParent'; +export { + alreadyNullableEnumParentFromJSON, + alreadyNullableEnumParentToJSON, +} from './AlreadyNullableEnumParent'; +export type { + AlreadyNullableOuterEnumParent, + AlreadyNullableOuterEnumParent$Wire, +} from './AlreadyNullableOuterEnumParent'; +export { + alreadyNullableOuterEnumParentFromJSON, + alreadyNullableOuterEnumParentToJSON, +} from './AlreadyNullableOuterEnumParent'; +export type { AlternativeTool, AlternativeTool$Wire } from './AlternativeTool'; +export { alternativeToolFromJSON, alternativeToolToJSON } from './AlternativeTool'; +export type { AmbiguousInner, AmbiguousInner$Wire } from './AmbiguousInner'; +export { ambiguousInnerFromJSON, ambiguousInnerToJSON } from './AmbiguousInner'; +export type { AmbiguousMap, AmbiguousMap$Wire } from './AmbiguousMap'; +export { ambiguousMapFromJSON, ambiguousMapToJSON } from './AmbiguousMap'; +export type { AmbiguousMapOuter, AmbiguousMapOuter$Wire } from './AmbiguousMapOuter'; +export { ambiguousMapOuterFromJSON, ambiguousMapOuterToJSON } from './AmbiguousMapOuter'; +export type { AmbiguousOuter, AmbiguousOuter$Wire } from './AmbiguousOuter'; +export { ambiguousOuterFromJSON, ambiguousOuterToJSON } from './AmbiguousOuter'; +export type { AmbiguousTool, AmbiguousTool$Wire } from './AmbiguousTool'; +export { ambiguousToolFromJSON, ambiguousToolToJSON } from './AmbiguousTool'; +export type { ArrayPayload, ArrayPayload$Wire } from './ArrayPayload'; +export { arrayPayloadFromJSON, arrayPayloadToJSON } from './ArrayPayload'; +export type { ArrayPayloadOrBoolean, ArrayPayloadOrBoolean$Wire } from './ArrayPayloadOrBoolean'; +export { + arrayPayloadOrBooleanFromJSON, + arrayPayloadOrBooleanToJSON, +} from './ArrayPayloadOrBoolean'; +export type { ArrayWrapped, ArrayWrapped$Wire } from './ArrayWrapped'; +export { arrayWrappedFromJSON, arrayWrappedToJSON } from './ArrayWrapped'; +export type { BinaryModel, BinaryModel$Wire } from './BinaryModel'; +export { binaryModelFromJSON, binaryModelToJSON } from './BinaryModel'; +export type { BinaryUnion, BinaryUnion$Wire } from './BinaryUnion'; +export { binaryUnionFromJSON, binaryUnionToJSON } from './BinaryUnion'; +export type { ClientTool, ClientTool$Wire } from './ClientTool'; +export { clientToolFromJSON, clientToolToJSON } from './ClientTool'; +export type { + ConstrainedOuterEnumParent, + ConstrainedOuterEnumParent$Wire, +} from './ConstrainedOuterEnumParent'; +export { + constrainedOuterEnumParentFromJSON, + constrainedOuterEnumParentToJSON, +} from './ConstrainedOuterEnumParent'; +export type { ContainerOuterEnum, ContainerOuterEnum$Wire } from './ContainerOuterEnum'; +export { containerOuterEnumFromJSON, containerOuterEnumToJSON } from './ContainerOuterEnum'; +export type { Detail, Detail$Wire } from './Detail'; +export { detailFromJSON, detailToJSON } from './Detail'; +export type { DetailArrayOrObject, DetailArrayOrObject$Wire } from './DetailArrayOrObject'; +export { detailArrayOrObjectFromJSON, detailArrayOrObjectToJSON } from './DetailArrayOrObject'; +export type { DictionaryAlias, DictionaryAlias$Wire } from './DictionaryAlias'; +export { dictionaryAliasFromJSON, dictionaryAliasToJSON } from './DictionaryAlias'; +export type { + EnumFreeOuterEnumParent, + EnumFreeOuterEnumParent$Wire, +} from './EnumFreeOuterEnumParent'; +export { + enumFreeOuterEnumParentFromJSON, + enumFreeOuterEnumParentToJSON, +} from './EnumFreeOuterEnumParent'; +export type { ExplicitNullOverlap, ExplicitNullOverlap$Wire } from './ExplicitNullOverlap'; +export { explicitNullOverlapFromJSON, explicitNullOverlapToJSON } from './ExplicitNullOverlap'; +export type { + ExplicitNullableEnumChoice, + ExplicitNullableEnumChoice$Wire, +} from './ExplicitNullableEnumChoice'; +export { + explicitNullableEnumChoiceFromJSON, + explicitNullableEnumChoiceToJSON, +} from './ExplicitNullableEnumChoice'; +export type { InlineDictionaryValues, InlineDictionaryValues$Wire } from './InlineDictionaryValues'; +export { + inlineDictionaryValuesFromJSON, + inlineDictionaryValuesToJSON, +} from './InlineDictionaryValues'; +export type { InlineMixedValues, InlineMixedValues$Wire } from './InlineMixedValues'; +export { inlineMixedValuesFromJSON, inlineMixedValuesToJSON } from './InlineMixedValues'; +export type { InlineNumericValues, InlineNumericValues$Wire } from './InlineNumericValues'; +export { inlineNumericValuesFromJSON, inlineNumericValuesToJSON } from './InlineNumericValues'; +export type { InlineProperty, InlineProperty$Wire } from './InlineProperty'; +export { inlinePropertyFromJSON, inlinePropertyToJSON } from './InlineProperty'; +export type { MapOuter, MapOuter$Wire } from './MapOuter'; +export { mapOuterFromJSON, mapOuterToJSON } from './MapOuter'; +export type { MapWrapped, MapWrapped$Wire } from './MapWrapped'; +export { mapWrappedFromJSON, mapWrappedToJSON } from './MapWrapped'; +export type { MixedMapA, MixedMapA$Wire } from './MixedMapA'; +export { mixedMapAFromJSON, mixedMapAToJSON } from './MixedMapA'; +export type { MixedMapB, MixedMapB$Wire } from './MixedMapB'; +export { mixedMapBFromJSON, mixedMapBToJSON } from './MixedMapB'; +export type { MixedMapChoice, MixedMapChoice$Wire } from './MixedMapChoice'; +export { mixedMapChoiceFromJSON, mixedMapChoiceToJSON } from './MixedMapChoice'; +export type { NamedContainer, NamedContainer$Wire } from './NamedContainer'; +export { namedContainerFromJSON, namedContainerToJSON } from './NamedContainer'; +export type { NamedValue, NamedValue$Wire } from './NamedValue'; +export { namedValueFromJSON, namedValueToJSON } from './NamedValue'; +export type { NestedDetailLists, NestedDetailLists$Wire } from './NestedDetailLists'; +export { nestedDetailListsFromJSON, nestedDetailListsToJSON } from './NestedDetailLists'; +export type { NestedEnumChoice, NestedEnumChoice$Wire } from './NestedEnumChoice'; +export { nestedEnumChoiceFromJSON, nestedEnumChoiceToJSON } from './NestedEnumChoice'; +export type { NestedEnumNarrow, NestedEnumNarrow$Wire } from './NestedEnumNarrow'; +export { nestedEnumNarrowFromJSON, nestedEnumNarrowToJSON } from './NestedEnumNarrow'; +export type { + NestedExplicitNullOverlap, + NestedExplicitNullOverlap$Wire, +} from './NestedExplicitNullOverlap'; +export { + nestedExplicitNullOverlapFromJSON, + nestedExplicitNullOverlapToJSON, +} from './NestedExplicitNullOverlap'; +export type { + NestedNullableEnumChoice, + NestedNullableEnumChoice$Wire, +} from './NestedNullableEnumChoice'; +export { + nestedNullableEnumChoiceFromJSON, + nestedNullableEnumChoiceToJSON, +} from './NestedNullableEnumChoice'; +export type { + NestedNullableEnumNarrow, + NestedNullableEnumNarrow$Wire, +} from './NestedNullableEnumNarrow'; +export { + nestedNullableEnumNarrowFromJSON, + nestedNullableEnumNarrowToJSON, +} from './NestedNullableEnumNarrow'; +export type { NullableDetail, NullableDetail$Wire } from './NullableDetail'; +export { nullableDetailFromJSON, nullableDetailToJSON } from './NullableDetail'; +export type { NullableEnumParent, NullableEnumParent$Wire } from './NullableEnumParent'; +export { nullableEnumParentFromJSON, nullableEnumParentToJSON } from './NullableEnumParent'; +export type { + NullableInlineEnumParent, + NullableInlineEnumParent$Wire, +} from './NullableInlineEnumParent'; +export { + nullableInlineEnumParentFromJSON, + nullableInlineEnumParentToJSON, +} from './NullableInlineEnumParent'; +export type { NullableMapFallback, NullableMapFallback$Wire } from './NullableMapFallback'; +export { nullableMapFallbackFromJSON, nullableMapFallbackToJSON } from './NullableMapFallback'; +export type { NullableNonNullScalars, NullableNonNullScalars$Wire } from './NullableNonNullScalars'; +export { + nullableNonNullScalarsFromJSON, + nullableNonNullScalarsToJSON, +} from './NullableNonNullScalars'; +export type { + NullableNonNullScalarsItems, + NullableNonNullScalarsItems$Wire, +} from './NullableNonNullScalarsItems'; +export { + nullableNonNullScalarsItemsFromJSON, + nullableNonNullScalarsItemsToJSON, +} from './NullableNonNullScalarsItems'; +export type { NullableNumericChoice, NullableNumericChoice$Wire } from './NullableNumericChoice'; +export { + nullableNumericChoiceFromJSON, + nullableNumericChoiceToJSON, +} from './NullableNumericChoice'; +export type { NullableNumericOne, NullableNumericOne$Wire } from './NullableNumericOne'; +export { nullableNumericOneFromJSON, nullableNumericOneToJSON } from './NullableNumericOne'; +export type { NullableNumericTwo, NullableNumericTwo$Wire } from './NullableNumericTwo'; +export { nullableNumericTwoFromJSON, nullableNumericTwoToJSON } from './NullableNumericTwo'; +export type { NullableOpaque, NullableOpaque$Wire } from './NullableOpaque'; +export { nullableOpaqueFromJSON, nullableOpaqueToJSON } from './NullableOpaque'; +export type { + NullableOpaqueSiblingOuter, + NullableOpaqueSiblingOuter$Wire, +} from './NullableOpaqueSiblingOuter'; +export { + nullableOpaqueSiblingOuterFromJSON, + nullableOpaqueSiblingOuterToJSON, +} from './NullableOpaqueSiblingOuter'; +export type { + NullableOpenExplicitOuter, + NullableOpenExplicitOuter$Wire, +} from './NullableOpenExplicitOuter'; +export { + nullableOpenExplicitOuterFromJSON, + nullableOpenExplicitOuterToJSON, +} from './NullableOpenExplicitOuter'; +export type { NullableOpenInner, NullableOpenInner$Wire } from './NullableOpenInner'; +export { nullableOpenInnerFromJSON, nullableOpenInnerToJSON } from './NullableOpenInner'; +export type { + NullableOpenSiblingOuter, + NullableOpenSiblingOuter$Wire, +} from './NullableOpenSiblingOuter'; +export { + nullableOpenSiblingOuterFromJSON, + nullableOpenSiblingOuterToJSON, +} from './NullableOpenSiblingOuter'; +export type { NullableOverlap, NullableOverlap$Wire } from './NullableOverlap'; +export { nullableOverlapFromJSON, nullableOverlapToJSON } from './NullableOverlap'; +export type { + NullablePlainEnumParent, + NullablePlainEnumParent$Wire, +} from './NullablePlainEnumParent'; +export { + nullablePlainEnumParentFromJSON, + nullablePlainEnumParentToJSON, +} from './NullablePlainEnumParent'; +export type { NullableScalarArray, NullableScalarArray$Wire } from './NullableScalarArray'; +export { nullableScalarArrayFromJSON, nullableScalarArrayToJSON } from './NullableScalarArray'; +export type { NullableScalarInner, NullableScalarInner$Wire } from './NullableScalarInner'; +export { nullableScalarInnerFromJSON, nullableScalarInnerToJSON } from './NullableScalarInner'; +export type { NullableScalarMap, NullableScalarMap$Wire } from './NullableScalarMap'; +export { nullableScalarMapFromJSON, nullableScalarMapToJSON } from './NullableScalarMap'; +export type { NullableScalarParent, NullableScalarParent$Wire } from './NullableScalarParent'; +export { nullableScalarParentFromJSON, nullableScalarParentToJSON } from './NullableScalarParent'; +export type { NullableTool, NullableTool$Wire } from './NullableTool'; +export { nullableToolFromJSON, nullableToolToJSON } from './NullableTool'; +export type { NumericEnumChoice, NumericEnumChoice$Wire } from './NumericEnumChoice'; +export { numericEnumChoiceFromJSON, numericEnumChoiceToJSON } from './NumericEnumChoice'; +export type { NumericOne, NumericOne$Wire } from './NumericOne'; +export { numericOneFromJSON, numericOneToJSON } from './NumericOne'; +export type { NumericTwo, NumericTwo$Wire } from './NumericTwo'; +export { numericTwoFromJSON, numericTwoToJSON } from './NumericTwo'; +export type { OnceNullableOuter, OnceNullableOuter$Wire } from './OnceNullableOuter'; +export { onceNullableOuterFromJSON, onceNullableOuterToJSON } from './OnceNullableOuter'; +export type { OpenAlias, OpenAlias$Wire } from './OpenAlias'; +export { openAliasFromJSON, openAliasToJSON } from './OpenAlias'; +export type { OpenInner, OpenInner$Wire } from './OpenInner'; +export { openInnerFromJSON, openInnerToJSON } from './OpenInner'; +export type { OpenMap, OpenMap$Wire } from './OpenMap'; +export { openMapFromJSON, openMapToJSON } from './OpenMap'; +export type { OpenTool, OpenTool$Wire } from './OpenTool'; +export { openToolFromJSON, openToolToJSON } from './OpenTool'; +export type { OtherDetail, OtherDetail$Wire } from './OtherDetail'; +export { otherDetailFromJSON, otherDetailToJSON } from './OtherDetail'; +export type { Outer, Outer$Wire } from './Outer'; +export { outerFromJSON, outerToJSON } from './Outer'; +export type { PrimitiveArrayChoice, PrimitiveArrayChoice$Wire } from './PrimitiveArrayChoice'; +export { primitiveArrayChoiceFromJSON, primitiveArrayChoiceToJSON } from './PrimitiveArrayChoice'; +export type { PrimitiveMapChoice, PrimitiveMapChoice$Wire } from './PrimitiveMapChoice'; +export { primitiveMapChoiceFromJSON, primitiveMapChoiceToJSON } from './PrimitiveMapChoice'; +export type { RawEnvelope, RawEnvelope$Wire } from './RawEnvelope'; +export { rawEnvelopeFromJSON, rawEnvelopeToJSON } from './RawEnvelope'; +export type { Readonly, Readonly$Wire } from './Readonly'; +export { readonlyFromJSON, readonlyToJSON } from './Readonly'; +export type { Record, Record$Wire } from './Record'; +export { recordFromJSON, recordToJSON } from './Record'; +export type { RecordEmpty, RecordEmpty$Wire } from './RecordEmpty'; +export { recordEmptyFromJSON, recordEmptyToJSON } from './RecordEmpty'; +export type { RecordOrBoolean, RecordOrBoolean$Wire } from './RecordOrBoolean'; +export { recordOrBooleanFromJSON, recordOrBooleanToJSON } from './RecordOrBoolean'; +export type { + RecursiveDictionaryChoice, + RecursiveDictionaryChoice$Wire, +} from './RecursiveDictionaryChoice'; +export { + recursiveDictionaryChoiceFromJSON, + recursiveDictionaryChoiceToJSON, +} from './RecursiveDictionaryChoice'; +export type { RecursiveList, RecursiveList$Wire } from './RecursiveList'; +export { recursiveListFromJSON, recursiveListToJSON } from './RecursiveList'; +export type { + RecursiveOpenDictionary, + RecursiveOpenDictionary$Wire, +} from './RecursiveOpenDictionary'; +export { + recursiveOpenDictionaryFromJSON, + recursiveOpenDictionaryToJSON, +} from './RecursiveOpenDictionary'; +export type { ScalarEnumChoice, ScalarEnumChoice$Wire } from './ScalarEnumChoice'; +export { scalarEnumChoiceFromJSON, scalarEnumChoiceToJSON } from './ScalarEnumChoice'; +export type { SearchTool, SearchTool$Wire } from './SearchTool'; +export { searchToolFromJSON, searchToolToJSON } from './SearchTool'; +export type { TaggedMapA, TaggedMapA$Wire } from './TaggedMapA'; +export { taggedMapAFromJSON, taggedMapAToJSON } from './TaggedMapA'; +export type { TaggedMapB, TaggedMapB$Wire } from './TaggedMapB'; +export { taggedMapBFromJSON, taggedMapBToJSON } from './TaggedMapB'; +export type { TaggedMapChoice, TaggedMapChoice$Wire } from './TaggedMapChoice'; +export { taggedMapChoiceFromJSON, taggedMapChoiceToJSON } from './TaggedMapChoice'; +export type { Tool, Tool$Wire } from './Tool'; +export { toolFromJSON, toolToJSON } from './Tool'; +export type { ToolAlias, ToolAlias$Wire } from './ToolAlias'; +export { toolAliasFromJSON, toolAliasToJSON } from './ToolAlias'; +export type { ToolList, ToolList$Wire } from './ToolList'; +export { toolListFromJSON, toolListToJSON } from './ToolList'; +export type { ToolMap, ToolMap$Wire } from './ToolMap'; +export { toolMapFromJSON, toolMapToJSON } from './ToolMap'; +export type { ToolRequest, ToolRequest$Wire } from './ToolRequest'; +export { toolRequestFromJSON, toolRequestToJSON } from './ToolRequest'; +export type { TwiceNullableOpaque, TwiceNullableOpaque$Wire } from './TwiceNullableOpaque'; +export { twiceNullableOpaqueFromJSON, twiceNullableOpaqueToJSON } from './TwiceNullableOpaque'; +export type { TwiceNullableOuter, TwiceNullableOuter$Wire } from './TwiceNullableOuter'; +export { twiceNullableOuterFromJSON, twiceNullableOuterToJSON } from './TwiceNullableOuter'; +export type { UntypedNumericChoice, UntypedNumericChoice$Wire } from './UntypedNumericChoice'; +export { untypedNumericChoiceFromJSON, untypedNumericChoiceToJSON } from './UntypedNumericChoice'; +export type { UntypedNumericOne, UntypedNumericOne$Wire } from './UntypedNumericOne'; +export { untypedNumericOneFromJSON, untypedNumericOneToJSON } from './UntypedNumericOne'; +export type { UntypedNumericTwo, UntypedNumericTwo$Wire } from './UntypedNumericTwo'; +export { untypedNumericTwoFromJSON, untypedNumericTwoToJSON } from './UntypedNumericTwo'; +export type { ValueAlias, ValueAlias$Wire } from './ValueAlias'; +export { valueAliasFromJSON, valueAliasToJSON } from './ValueAlias'; +export type { WrappedArrayOrBoolean, WrappedArrayOrBoolean$Wire } from './WrappedArrayOrBoolean'; +export { + wrappedArrayOrBooleanFromJSON, + wrappedArrayOrBooleanToJSON, +} from './WrappedArrayOrBoolean'; +export type { WrappedMapOrBoolean, WrappedMapOrBoolean$Wire } from './WrappedMapOrBoolean'; +export { wrappedMapOrBooleanFromJSON, wrappedMapOrBooleanToJSON } from './WrappedMapOrBoolean'; +export type { WrappedScalarInner, WrappedScalarInner$Wire } from './WrappedScalarInner'; +export { wrappedScalarInnerFromJSON, wrappedScalarInnerToJSON } from './WrappedScalarInner'; +export type { WrapperChoice, WrapperChoice$Wire } from './WrapperChoice'; +export { wrapperChoiceFromJSON, wrapperChoiceToJSON } from './WrapperChoice'; +export type { WrapperOne, WrapperOne$Wire } from './WrapperOne'; +export { wrapperOneFromJSON, wrapperOneToJSON } from './WrapperOne'; +export type { WrapperTwo, WrapperTwo$Wire } from './WrapperTwo'; +export { wrapperTwoFromJSON, wrapperTwoToJSON } from './WrapperTwo'; +export type { + ExchangeMultipartRequest, + ExchangeMultipartRequest$Wire, +} from './ExchangeMultipartRequest'; +export { + exchangeMultipartRequestFromJSON, + exchangeMultipartRequestToJSON, +} from './ExchangeMultipartRequest'; +export type { + SendOptionalInlineScalarRequest, + SendOptionalInlineScalarRequest$Wire, +} from './SendOptionalInlineScalarRequest'; +export { + sendOptionalInlineScalarRequestFromJSON, + sendOptionalInlineScalarRequestToJSON, +} from './SendOptionalInlineScalarRequest'; +export type { ExchangeMultipartMultipartRequestBody } from './ExchangeMultipartMultipartRequestBody'; diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/package.json.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/package.json.golden new file mode 100644 index 000000000..cef8d7580 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/package.json.golden @@ -0,0 +1,32 @@ +{ + "name": "untagged-union-conversion", + "version": "1.0.0", + "description": "TypeScript client for Untagged union conversion", + "keywords": [ + "api-client", + "generated", + "openapi", + "typescript" + ], + "files": [ + "dist" + ], + "type": "module", + "main": "./dist/index.mjs", + "types": "./dist/index.d.mts", + "exports": { + ".": { + "default": "./dist/index.mjs", + "types": "./dist/index.d.mts" + } + }, + "scripts": { + "build": "vp pack", + "check": "vp check", + "fmt": "vp fmt" + }, + "devDependencies": { + "typescript": "^7.0.2", + "vite-plus": "^1.5.0" + } +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/runtime.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/runtime.ts.golden new file mode 100644 index 000000000..084a11e06 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/runtime.ts.golden @@ -0,0 +1,667 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export const BASE_PATH = 'http://localhost'.replace(/\/+$/, ''); + +export interface ConfigurationParameters { + basePath?: string; // override base path + fetchApi?: FetchAPI; // override for fetch implementation + middleware?: Middleware[]; // middleware to apply before/after fetch requests + queryParamsStringify?: (params: HTTPQuery) => string; // stringify function for query strings + username?: string | (() => string | Promise); // parameter for basic security + password?: string | (() => string | Promise); // parameter for basic security + apiKey?: string | Promise | ((name: string) => string | Promise); // parameter for apiKey security + accessToken?: + | string + | Promise + | ((name?: string, scopes?: string[]) => string | Promise); // parameter for oauth2 security + headers?: HTTPHeaders; //header params we want to use on every request + credentials?: RequestCredentials; //value for the credentials param we want to use on each request +} + +export class Configuration { + private configuration: ConfigurationParameters; + + constructor(configuration: ConfigurationParameters = {}) { + this.configuration = configuration; + } + + set config(configuration: ConfigurationParameters) { + this.configuration = configuration; + } + + get basePath(): string { + return this.configuration.basePath != null ? this.configuration.basePath : BASE_PATH; + } + + get fetchApi(): FetchAPI | undefined { + return this.configuration.fetchApi; + } + + get middleware(): Middleware[] { + return this.configuration.middleware || []; + } + + get queryParamsStringify(): (params: HTTPQuery) => string { + return this.configuration.queryParamsStringify || querystring; + } + + get username(): (() => string | Promise) | undefined { + const username = this.configuration.username; + if (username) { + return typeof username === 'function' ? username : async () => username; + } + return undefined; + } + + get password(): (() => string | Promise) | undefined { + const password = this.configuration.password; + if (password) { + return typeof password === 'function' ? password : async () => password; + } + return undefined; + } + + get apiKey(): ((name: string) => string | Promise) | undefined { + const apiKey = this.configuration.apiKey; + if (apiKey) { + return typeof apiKey === 'function' ? apiKey : () => apiKey; + } + return undefined; + } + + get accessToken(): ((name?: string, scopes?: string[]) => string | Promise) | undefined { + const accessToken = this.configuration.accessToken; + if (accessToken) { + return typeof accessToken === 'function' ? accessToken : async () => accessToken; + } + return undefined; + } + + get headers(): HTTPHeaders | undefined { + return this.configuration.headers; + } + + get credentials(): RequestCredentials | undefined { + return this.configuration.credentials; + } +} + +export const DefaultConfig = new Configuration(); + +export interface UploadFile { + data: Blob; + filename?: string; +} + +export type UploadFileInput = Blob | File | UploadFile; + +export function uploadFileData(value: UploadFileInput): Blob { + return isUploadFile(value) ? value.data : value; +} + +export function uploadFileFilename(value: UploadFileInput, fallback: string): string { + if (isFile(value) && value.name) { + return value.name; + } + if (isUploadFile(value) && value.filename) { + return value.filename; + } + return fallback; +} + +export function multipartHeaderValue(value: string): string { + return value + .replace(/[\r\n]/g, '') + .replace(/\\/g, '\\\\') + .replace(/"/g, '\\"'); +} + +function isUploadFile(value: UploadFileInput): value is UploadFile { + return typeof value === 'object' && value !== null && 'data' in value; +} + +function isFile(value: UploadFileInput): value is File { + return typeof File !== 'undefined' && value instanceof File; +} + +/** + * This is the base class for all generated API classes. + */ +export class BaseAPI { + private static readonly jsonRegex = new RegExp( + '^(:?application/json|[^;/ \t]+/[^;/ \t]+[+]json)[ \t]*(:?;.*)?$', + 'i', + ); + protected configuration: Configuration; + private middleware: Middleware[]; + + constructor(configuration: Configuration = DefaultConfig) { + this.configuration = configuration; + this.middleware = configuration.middleware; + } + + withMiddleware(this: T, ...middlewares: Middleware[]) { + const next = this.clone(); + next.middleware = next.middleware.concat(...middlewares); + return next; + } + + withPreMiddleware(this: T, ...preMiddlewares: Array) { + const middlewares = preMiddlewares.map((pre) => ({ pre })); + return this.withMiddleware(...middlewares); + } + + withPostMiddleware(this: T, ...postMiddlewares: Array) { + const middlewares = postMiddlewares.map((post) => ({ post })); + return this.withMiddleware(...middlewares); + } + + /** + * Check if the given MIME is a JSON MIME. + * JSON MIME examples: + * application/json + * application/json; charset=UTF8 + * APPLICATION/JSON + * application/vnd.company+json + * @param mime - MIME (Multipurpose Internet Mail Extensions) + * @return True if the given MIME is JSON, false otherwise. + */ + protected isJsonMime(mime: string | null | undefined): boolean { + if (!mime) { + return false; + } + return BaseAPI.jsonRegex.test(mime); + } + + protected async request( + context: RequestOpts, + initOverrides?: RequestInit | InitOverrideFunction, + ): Promise { + const { url, init } = await this.createFetchParams(context, initOverrides); + return await this.fetchApi(url, init); + } + + private async createFetchParams( + context: RequestOpts, + initOverrides?: RequestInit | InitOverrideFunction, + ) { + let url = this.configuration.basePath + context.path; + if (context.query !== undefined && Object.keys(context.query).length !== 0) { + // only add the querystring to the URL if there are query parameters. + // this is done to avoid urls ending with a "?" character which buggy webservers + // do not handle correctly sometimes. + url += '?' + this.configuration.queryParamsStringify(context.query); + } + + const headers = Object.assign({}, this.configuration.headers, context.headers); + Object.keys(headers).forEach((key) => (headers[key] === undefined ? delete headers[key] : {})); + + const token = await this.configuration.accessToken?.(); + if (token) { + headers['Authorization'] = `Bearer ${token}`; + } + + if (!headers['Authorization']) { + const username = await this.configuration.username?.(); + const password = await this.configuration.password?.(); + if (username && password) { + headers['Authorization'] = `Basic ${btoa(`${username}:${password}`)}`; + } + } + + const initOverrideFn = + typeof initOverrides === 'function' ? initOverrides : async () => initOverrides; + + const initParams: HTTPRequestInit = { + method: context.method, + headers, + body: context.body, + credentials: this.configuration.credentials, + signal: context.signal, + }; + + const overriddenInit = { + ...initParams, + ...(await initOverrideFn({ + init: initParams, + context, + })), + }; + + let body: BodyInit | null | undefined; + if ( + isFormData(overriddenInit.body) || + overriddenInit.body instanceof URLSearchParams || + isBlob(overriddenInit.body) + ) { + body = overriddenInit.body; + } else if (this.isJsonMime(headers['Content-Type'])) { + body = JSON.stringify(overriddenInit.body); + } else { + body = overriddenInit.body as BodyInit | null | undefined; + } + + const init: RequestInit = { + ...overriddenInit, + body, + }; + + return { url, init }; + } + + private fetchApi = async (url: string, init: RequestInit) => { + let fetchParams = { url, init }; + for (const middleware of this.middleware) { + if (middleware.pre) { + fetchParams = + (await middleware.pre({ + fetch: this.fetchApi, + ...fetchParams, + })) || fetchParams; + } + } + let response: Response | undefined = undefined; + try { + response = await (this.configuration.fetchApi || fetch)(fetchParams.url, fetchParams.init); + } catch (e) { + for (const middleware of this.middleware) { + if (middleware.onError) { + response = + (await middleware.onError({ + fetch: this.fetchApi, + url: fetchParams.url, + init: fetchParams.init, + error: e, + response: response ? response.clone() : undefined, + })) || response; + } + } + if (response === undefined) { + if (e instanceof Error) { + throw new FetchError( + e, + 'The request failed and the interceptors did not return an alternative response', + ); + } else { + throw e; + } + } + } + for (const middleware of this.middleware) { + if (middleware.post) { + response = + (await middleware.post({ + fetch: this.fetchApi, + url: fetchParams.url, + init: fetchParams.init, + response: response.clone(), + })) || response; + } + } + return response; + }; + + /** + * Create a shallow clone of `this` by constructing a new instance + * and then shallow cloning data members. + */ + private clone(this: T): T { + const constructor = this.constructor as new (configuration: Configuration) => T; + const next = new constructor(this.configuration); + next.middleware = this.middleware.slice(); + return next; + } +} + +function isBlob(value: unknown): value is Blob { + return typeof Blob !== 'undefined' && value instanceof Blob; +} + +function isFormData(value: unknown): value is FormData { + return typeof FormData !== 'undefined' && value instanceof FormData; +} + +export class ResponseError extends Error { + override name = 'ResponseError' as const; + public response: Response; + + constructor(response: Response, msg?: string) { + super(msg); + this.response = response; + } +} + +export class FetchError extends Error { + override name = 'FetchError' as const; + public cause: Error; + + constructor(cause: Error, msg?: string) { + super(msg); + this.cause = cause; + } +} + +export class RequiredError extends Error { + override name = 'RequiredError' as const; + public field: string; + + constructor(field: string, msg?: string) { + super(msg); + this.field = field; + } +} + +export type FetchAPI = WindowOrWorkerGlobalScope['fetch']; + +export type Json = unknown; +export type HTTPMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS' | 'HEAD'; +export type HTTPHeaders = { [key: string]: string }; +export type HTTPQuery = { + [key: string]: + | string + | number + | null + | boolean + | Array + | Set + | HTTPQuery; +}; +export type HTTPBody = Json | Blob | FormData | URLSearchParams; // eslint-disable-line @typescript-eslint/no-redundant-type-constituents +export type HTTPRequestInit = { + headers?: HTTPHeaders; + method: HTTPMethod; + credentials?: RequestCredentials; + body?: HTTPBody; + signal?: AbortSignal; +}; +export type ModelPropertyNaming = 'camelCase' | 'snake_case' | 'PascalCase' | 'original'; + +export type InitOverrideFunction = (requestContext: { + init: HTTPRequestInit; + context: RequestOpts; +}) => Promise; + +export interface FetchParams { + url: string; + init: RequestInit; +} + +export interface RequestOpts { + path: string; + method: HTTPMethod; + headers: HTTPHeaders; + query?: HTTPQuery; + body?: HTTPBody; + signal?: AbortSignal; +} + +export function querystring(params: HTTPQuery, prefix: string = ''): string { + return Object.keys(params) + .map((key) => querystringSingleKey(key, params[key], prefix)) + .filter((part) => part.length > 0) + .join('&'); +} + +function querystringSingleKey( + key: string, + value: + | string + | number + | null + | undefined + | boolean + | Array + | Set + | HTTPQuery, + keyPrefix: string = '', +): string { + const fullKey = keyPrefix + (keyPrefix.length ? `[${key}]` : key); + if (value instanceof Array) { + const multiValue = value + .map((singleValue) => encodeURIComponent(String(singleValue))) + .join(`&${encodeURIComponent(fullKey)}=`); + return `${encodeURIComponent(fullKey)}=${multiValue}`; + } + if (value instanceof Set) { + const valueAsArray = Array.from(value); + return querystringSingleKey(key, valueAsArray, keyPrefix); + } + if (value instanceof Date) { + return `${encodeURIComponent(fullKey)}=${encodeURIComponent(value.toISOString())}`; + } + if (value instanceof Object) { + return querystring(value as HTTPQuery, fullKey); + } + return `${encodeURIComponent(fullKey)}=${encodeURIComponent(String(value))}`; +} + +export function exists(json: Record, key: string): boolean { + const value = json[key]; + return value !== null && value !== undefined; +} + +export function mapValues(data: Record, fn: (item: T) => U): Record { + const result: Record = {}; + for (const key of Object.keys(data)) { + result[key] = fn(data[key]); + } + return result; +} + +export function canConsumeForm(consumes: Consume[]): boolean { + for (const consume of consumes) { + if ('multipart/form-data' === consume.contentType) { + return true; + } + } + return false; +} + +export interface Consume { + contentType: string; +} + +export interface RequestContext { + fetch: FetchAPI; + url: string; + init: RequestInit; +} + +export interface ResponseContext { + fetch: FetchAPI; + url: string; + init: RequestInit; + response: Response; +} + +export interface ErrorContext { + fetch: FetchAPI; + url: string; + init: RequestInit; + error: unknown; + response?: Response; +} + +export interface Middleware { + pre?(context: RequestContext): Promise; + post?(context: ResponseContext): Promise; + onError?(context: ErrorContext): Promise; +} + +export interface ApiResponse { + raw: Response; + value(): Promise; +} + +export type HttpSuccessStatus = + | 200 + | 201 + | 202 + | 203 + | 204 + | 205 + | 206 + | 207 + | 208 + | 209 + | 210 + | 211 + | 212 + | 213 + | 214 + | 215 + | 216 + | 217 + | 218 + | 219 + | 220 + | 221 + | 222 + | 223 + | 224 + | 225 + | 226 + | 227 + | 228 + | 229 + | 230 + | 231 + | 232 + | 233 + | 234 + | 235 + | 236 + | 237 + | 238 + | 239 + | 240 + | 241 + | 242 + | 243 + | 244 + | 245 + | 246 + | 247 + | 248 + | 249 + | 250 + | 251 + | 252 + | 253 + | 254 + | 255 + | 256 + | 257 + | 258 + | 259 + | 260 + | 261 + | 262 + | 263 + | 264 + | 265 + | 266 + | 267 + | 268 + | 269 + | 270 + | 271 + | 272 + | 273 + | 274 + | 275 + | 276 + | 277 + | 278 + | 279 + | 280 + | 281 + | 282 + | 283 + | 284 + | 285 + | 286 + | 287 + | 288 + | 289 + | 290 + | 291 + | 292 + | 293 + | 294 + | 295 + | 296 + | 297 + | 298 + | 299; + +export type HttpSuccessResponse = { status: HttpSuccessStatus }; + +export interface ResponseTransformer { + (json: unknown): T; +} + +class ApiResponseBase { + public readonly status: number; + public readonly ok: boolean; + public readonly statusText: string; + public readonly headers: Headers; + public readonly raw: Response; + + constructor(raw: Response) { + this.raw = raw; + this.status = raw.status; + this.ok = raw.ok; + this.statusText = raw.statusText; + this.headers = raw.headers; + } +} + +export class JSONApiResponse extends ApiResponseBase { + private transformer: ResponseTransformer; + + constructor( + raw: Response, + transformer: ResponseTransformer = (jsonValue: unknown) => jsonValue as T, + ) { + super(raw); + this.transformer = transformer; + } + + async value(): Promise { + return this.transformer(await this.raw.json()); + } +} + +export class VoidApiResponse extends ApiResponseBase { + constructor(raw: Response) { + super(raw); + } + + async value(): Promise { + return undefined; + } +} + +export class BlobApiResponse extends ApiResponseBase { + constructor(raw: Response) { + super(raw); + } + + async value(): Promise { + return await this.raw.blob(); + } +} + +export class TextApiResponse extends ApiResponseBase { + constructor(raw: Response) { + super(raw); + } + + async value(): Promise { + return await this.raw.text(); + } +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/unionMatch.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/unionMatch.ts.golden new file mode 100644 index 000000000..db847f09b --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/unionMatch.ts.golden @@ -0,0 +1,1884 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export function matches$0FromJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + return Object.values(candidate).every(matches$1FromJSON); +} + +export function matches$0ToJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + return Object.values(candidate).every(matches$1ToJSON); +} + +export function matches$1FromJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (!Object.prototype.hasOwnProperty.call(candidate, 'wire_name')) { + return false; + } + if (!matches$2FromJSON(object['wire_name'])) { + return false; + } + return true; +} + +export function matches$1ToJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (!Object.prototype.hasOwnProperty.call(candidate, 'wireName')) { + return false; + } + if (!matches$2ToJSON(object['wireName'])) { + return false; + } + return true; +} + +export function matches$2FromJSON(candidate: unknown): boolean { + return typeof candidate === 'string'; +} + +export function matches$2ToJSON(candidate: unknown): boolean { + return typeof candidate === 'string'; +} + +export function matches$3FromJSON(candidate: unknown): boolean { + return typeof candidate === 'number' && Number.isInteger(candidate); +} + +export function matches$3ToJSON(candidate: unknown): boolean { + return typeof candidate === 'number' && Number.isInteger(candidate); +} + +export function matches$4FromJSON(candidate: unknown): boolean { + return typeof candidate === 'number' && Number.isFinite(candidate); +} + +export function matches$4ToJSON(candidate: unknown): boolean { + return typeof candidate === 'number' && Number.isFinite(candidate); +} + +export function matches$5FromJSON(candidate: unknown): boolean { + return typeof candidate === 'boolean'; +} + +export function matches$5ToJSON(candidate: unknown): boolean { + return typeof candidate === 'boolean'; +} + +export function matches$6FromJSON(candidate: unknown): boolean { + if (matches$7FromJSON(candidate)) { + return true; + } + if (matches$9FromJSON(candidate)) { + return true; + } + return false; +} + +export function matches$6ToJSON(candidate: unknown): boolean { + if (matches$7ToJSON(candidate)) { + return true; + } + if (matches$9ToJSON(candidate)) { + return true; + } + return false; +} + +export function matches$7FromJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (!Object.prototype.hasOwnProperty.call(candidate, 'event_type')) { + return false; + } + if (!matches$8FromJSON(object['event_type'])) { + return false; + } + return true; +} + +export function matches$7ToJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (!Object.prototype.hasOwnProperty.call(candidate, 'eventType')) { + return false; + } + if (!matches$8ToJSON(object['eventType'])) { + return false; + } + return true; +} + +export function matches$8FromJSON(candidate: unknown): boolean { + return candidate === 'empty'; +} + +export function matches$8ToJSON(candidate: unknown): boolean { + return candidate === 'empty'; +} + +export function matches$9FromJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (!Object.prototype.hasOwnProperty.call(candidate, 'event_type')) { + return false; + } + if (!matches$10FromJSON(object['event_type'])) { + return false; + } + if (!Object.prototype.hasOwnProperty.call(candidate, 'event_data')) { + return false; + } + if (!matches$1FromJSON(object['event_data'])) { + return false; + } + return true; +} + +export function matches$9ToJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (!Object.prototype.hasOwnProperty.call(candidate, 'eventType')) { + return false; + } + if (!matches$10ToJSON(object['eventType'])) { + return false; + } + if (!Object.prototype.hasOwnProperty.call(candidate, 'eventData')) { + return false; + } + if (!matches$1ToJSON(object['eventData'])) { + return false; + } + return true; +} + +export function matches$10FromJSON(candidate: unknown): boolean { + return candidate === 'data'; +} + +export function matches$10ToJSON(candidate: unknown): boolean { + return candidate === 'data'; +} + +export function matches$11FromJSON(candidate: unknown): boolean { + if (!Array.isArray(candidate)) { + return false; + } + return candidate.every(matches$12FromJSON); +} + +export function matches$11ToJSON(candidate: unknown): boolean { + if (!Array.isArray(candidate)) { + return false; + } + return candidate.every(matches$12ToJSON); +} + +export function matches$12FromJSON(candidate: unknown): boolean { + if (matches$0FromJSON(candidate)) { + return true; + } + if (matches$2FromJSON(candidate)) { + return true; + } + return false; +} + +export function matches$12ToJSON(candidate: unknown): boolean { + if (matches$0ToJSON(candidate)) { + return true; + } + if (matches$2ToJSON(candidate)) { + return true; + } + return false; +} + +export function matches$13FromJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (Object.prototype.hasOwnProperty.call(candidate, 'max_uses')) { + const item = object['max_uses']; + if (item !== undefined && !matches$3FromJSON(item)) { + return false; + } + } + if (!Object.prototype.hasOwnProperty.call(candidate, 'name')) { + return false; + } + if (!matches$14FromJSON(object['name'])) { + return false; + } + if (!Object.prototype.hasOwnProperty.call(candidate, 'type')) { + return false; + } + if (!matches$15FromJSON(object['type'])) { + return false; + } + return true; +} + +export function matches$13ToJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (Object.prototype.hasOwnProperty.call(candidate, 'maxUses')) { + const item = object['maxUses']; + if (item !== undefined && !matches$3ToJSON(item)) { + return false; + } + } + if (!Object.prototype.hasOwnProperty.call(candidate, 'name')) { + return false; + } + if (!matches$14ToJSON(object['name'])) { + return false; + } + if (!Object.prototype.hasOwnProperty.call(candidate, 'type')) { + return false; + } + if (!matches$15ToJSON(object['type'])) { + return false; + } + return true; +} + +export function matches$14FromJSON(candidate: unknown): boolean { + return candidate === 'web_search'; +} + +export function matches$14ToJSON(candidate: unknown): boolean { + return candidate === 'web_search'; +} + +export function matches$15FromJSON(candidate: unknown): boolean { + return candidate === 'web_search_20250305'; +} + +export function matches$15ToJSON(candidate: unknown): boolean { + return candidate === 'web_search_20250305'; +} + +export function matches$17FromJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (Object.prototype.hasOwnProperty.call(candidate, 'optional_flag')) { + const item = object['optional_flag']; + if (item !== undefined && !matches$5FromJSON(item)) { + return false; + } + } + if (!Object.prototype.hasOwnProperty.call(candidate, 'wire_name')) { + return false; + } + if (!matches$2FromJSON(object['wire_name'])) { + return false; + } + return true; +} + +export function matches$17ToJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (Object.prototype.hasOwnProperty.call(candidate, 'optionalFlag')) { + const item = object['optionalFlag']; + if (item !== undefined && !matches$5ToJSON(item)) { + return false; + } + } + if (!Object.prototype.hasOwnProperty.call(candidate, 'wireName')) { + return false; + } + if (!matches$2ToJSON(object['wireName'])) { + return false; + } + return true; +} + +export function matches$18FromJSON(candidate: unknown): boolean { + if (!matches$19FromJSON(candidate)) { + return false; + } + return Object.values(candidate as globalThis.Record).some(matches$21FromJSON); +} + +export function matches$18ToJSON(candidate: unknown): boolean { + if (!matches$19ToJSON(candidate)) { + return false; + } + return Object.values(candidate as globalThis.Record).some(matches$21ToJSON); +} + +export function matches$19FromJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + return true; +} + +export function matches$19ToJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + return true; +} + +export function matches$21FromJSON(candidate: unknown): boolean { + if (matches$1FromJSON(candidate)) { + return true; + } + if (matches$17FromJSON(candidate)) { + return true; + } + return false; +} + +export function matches$21ToJSON(candidate: unknown): boolean { + if (matches$1ToJSON(candidate)) { + return true; + } + if (matches$17ToJSON(candidate)) { + return true; + } + return false; +} + +export function matches$22FromJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + return true; +} + +export function matches$22ToJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + return true; +} + +export function matches$23FromJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (!Object.prototype.hasOwnProperty.call(candidate, 'input_schema')) { + return false; + } + if (!matches$22FromJSON(object['input_schema'])) { + return false; + } + if (!Object.prototype.hasOwnProperty.call(candidate, 'name')) { + return false; + } + if (!matches$2FromJSON(object['name'])) { + return false; + } + return true; +} + +export function matches$23ToJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (!Object.prototype.hasOwnProperty.call(candidate, 'inputSchema')) { + return false; + } + if (!matches$22ToJSON(object['inputSchema'])) { + return false; + } + if (!Object.prototype.hasOwnProperty.call(candidate, 'name')) { + return false; + } + if (!matches$2ToJSON(object['name'])) { + return false; + } + return true; +} + +export function matches$24FromJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (!Object.prototype.hasOwnProperty.call(candidate, 'input_schema')) { + return false; + } + if (!matches$22FromJSON(object['input_schema'])) { + return false; + } + if (!Object.prototype.hasOwnProperty.call(candidate, 'name')) { + return false; + } + if (!matches$2FromJSON(object['name'])) { + return false; + } + if (Object.prototype.hasOwnProperty.call(candidate, 'second_option')) { + const item = object['second_option']; + if (item !== undefined && !matches$5FromJSON(item)) { + return false; + } + } + return true; +} + +export function matches$24ToJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (!Object.prototype.hasOwnProperty.call(candidate, 'inputSchema')) { + return false; + } + if (!matches$22ToJSON(object['inputSchema'])) { + return false; + } + if (!Object.prototype.hasOwnProperty.call(candidate, 'name')) { + return false; + } + if (!matches$2ToJSON(object['name'])) { + return false; + } + if (Object.prototype.hasOwnProperty.call(candidate, 'secondOption')) { + const item = object['secondOption']; + if (item !== undefined && !matches$5ToJSON(item)) { + return false; + } + } + return true; +} + +export function matches$25FromJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (!Object.prototype.hasOwnProperty.call(candidate, 'wire_items')) { + return false; + } + if (!matches$26FromJSON(object['wire_items'])) { + return false; + } + return true; +} + +export function matches$25ToJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (!Object.prototype.hasOwnProperty.call(candidate, 'wireItems')) { + return false; + } + if (!matches$26ToJSON(object['wireItems'])) { + return false; + } + return true; +} + +export function matches$26FromJSON(candidate: unknown): boolean { + if (!Array.isArray(candidate)) { + return false; + } + return candidate.every(matches$2FromJSON); +} + +export function matches$26ToJSON(candidate: unknown): boolean { + if (!Array.isArray(candidate)) { + return false; + } + return candidate.every(matches$2ToJSON); +} + +export function matches$27FromJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (!Object.prototype.hasOwnProperty.call(candidate, 'binary_data')) { + return false; + } + if (!matches$28FromJSON(object['binary_data'])) { + return false; + } + if (!Object.prototype.hasOwnProperty.call(candidate, 'field_name')) { + return false; + } + if (!matches$2FromJSON(object['field_name'])) { + return false; + } + return true; +} + +export function matches$27ToJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (!Object.prototype.hasOwnProperty.call(candidate, 'binaryData')) { + return false; + } + if (!matches$28ToJSON(object['binaryData'])) { + return false; + } + if (!Object.prototype.hasOwnProperty.call(candidate, 'fieldName')) { + return false; + } + if (!matches$2ToJSON(object['fieldName'])) { + return false; + } + return true; +} + +export function matches$28FromJSON(candidate: unknown): boolean { + if (typeof Blob !== 'undefined' && candidate instanceof Blob) { + return true; + } + return typeof File !== 'undefined' && candidate instanceof File; +} + +export function matches$28ToJSON(candidate: unknown): boolean { + if (typeof Blob !== 'undefined' && candidate instanceof Blob) { + return true; + } + return typeof File !== 'undefined' && candidate instanceof File; +} + +export function matches$29FromJSON(candidate: unknown): boolean { + if (!Array.isArray(candidate)) { + return false; + } + return candidate.every(matches$1FromJSON); +} + +export function matches$29ToJSON(candidate: unknown): boolean { + if (!Array.isArray(candidate)) { + return false; + } + return candidate.every(matches$1ToJSON); +} + +export function matches$30FromJSON(candidate: unknown): boolean { + if (matches$31FromJSON(candidate)) { + return true; + } + if (matches$1FromJSON(candidate)) { + return true; + } + return false; +} + +export function matches$30ToJSON(candidate: unknown): boolean { + if (matches$31ToJSON(candidate)) { + return true; + } + if (matches$1ToJSON(candidate)) { + return true; + } + return false; +} + +export function matches$31FromJSON(candidate: unknown): boolean { + return candidate === null; +} + +export function matches$31ToJSON(candidate: unknown): boolean { + return candidate === null; +} + +export function matches$32FromJSON(candidate: unknown): boolean { + if (matches$31FromJSON(candidate)) { + return true; + } + if (matches$23FromJSON(candidate)) { + return true; + } + if (matches$13FromJSON(candidate)) { + return true; + } + return false; +} + +export function matches$32ToJSON(candidate: unknown): boolean { + if (matches$31ToJSON(candidate)) { + return true; + } + if (matches$23ToJSON(candidate)) { + return true; + } + if (matches$13ToJSON(candidate)) { + return true; + } + return false; +} + +export function matches$33FromJSON(candidate: unknown): boolean { + if (!Array.isArray(candidate)) { + return false; + } + return candidate.every(matches$34FromJSON); +} + +export function matches$33ToJSON(candidate: unknown): boolean { + if (!Array.isArray(candidate)) { + return false; + } + return candidate.every(matches$34ToJSON); +} + +export function matches$34FromJSON(candidate: unknown): boolean { + return candidate === null || matches$35FromJSON(candidate); +} + +export function matches$34ToJSON(candidate: unknown): boolean { + return candidate === null || matches$35ToJSON(candidate); +} + +export function matches$35FromJSON(candidate: unknown): boolean { + if (!matches$3FromJSON(candidate)) { + return false; + } + if (!matches$36FromJSON(candidate)) { + return false; + } + return true; +} + +export function matches$35ToJSON(candidate: unknown): boolean { + if (!matches$3ToJSON(candidate)) { + return false; + } + if (!matches$36ToJSON(candidate)) { + return false; + } + return true; +} + +export function matches$36FromJSON(candidate: unknown): boolean { + return candidate === 1; +} + +export function matches$36ToJSON(candidate: unknown): boolean { + return candidate === 1; +} + +export function matches$37FromJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + return Object.values(candidate).every(matches$12FromJSON); +} + +export function matches$37ToJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + return Object.values(candidate).every(matches$12ToJSON); +} + +export function matches$38FromJSON(candidate: unknown): boolean { + if (!Array.isArray(candidate)) { + return false; + } + return candidate.every(matches$39FromJSON); +} + +export function matches$38ToJSON(candidate: unknown): boolean { + if (!Array.isArray(candidate)) { + return false; + } + return candidate.every(matches$39ToJSON); +} + +export function matches$39FromJSON(candidate: unknown): boolean { + if (matches$0FromJSON(candidate)) { + return true; + } + if (matches$3FromJSON(candidate)) { + return true; + } + if (matches$4FromJSON(candidate)) { + return true; + } + return false; +} + +export function matches$39ToJSON(candidate: unknown): boolean { + if (matches$0ToJSON(candidate)) { + return true; + } + if (matches$3ToJSON(candidate)) { + return true; + } + if (matches$4ToJSON(candidate)) { + return true; + } + return false; +} + +export function matches$40FromJSON(candidate: unknown): boolean { + if (!matches$41FromJSON(candidate)) { + return false; + } + return Object.values(candidate as globalThis.Record).some(matches$1FromJSON); +} + +export function matches$40ToJSON(candidate: unknown): boolean { + if (!matches$41ToJSON(candidate)) { + return false; + } + return Object.values(candidate as globalThis.Record).some(matches$1ToJSON); +} + +export function matches$41FromJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + return true; +} + +export function matches$41ToJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + return true; +} + +export function matches$43FromJSON(candidate: unknown): boolean { + if (!matches$44FromJSON(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + const declared = ['marker']; + for (const [key, item] of Object.entries(object)) { + if (!declared.includes(key) && !matches$1FromJSON(item)) { + return false; + } + } + return true; +} + +export function matches$43ToJSON(candidate: unknown): boolean { + if (!matches$44ToJSON(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + const declared = ['marker']; + for (const [key, item] of Object.entries(object)) { + if (!declared.includes(key) && !matches$1ToJSON(item)) { + return false; + } + } + return true; +} + +export function matches$44FromJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (!Object.prototype.hasOwnProperty.call(candidate, 'marker')) { + return false; + } + if (!matches$2FromJSON(object['marker'])) { + return false; + } + return true; +} + +export function matches$44ToJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (!Object.prototype.hasOwnProperty.call(candidate, 'marker')) { + return false; + } + if (!matches$2ToJSON(object['marker'])) { + return false; + } + return true; +} + +export function matches$45FromJSON(candidate: unknown): boolean { + if (!matches$44FromJSON(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + const declared = ['marker']; + for (const [key, item] of Object.entries(object)) { + if (!declared.includes(key) && !matches$29FromJSON(item)) { + return false; + } + } + return true; +} + +export function matches$45ToJSON(candidate: unknown): boolean { + if (!matches$44ToJSON(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + const declared = ['marker']; + for (const [key, item] of Object.entries(object)) { + if (!declared.includes(key) && !matches$29ToJSON(item)) { + return false; + } + } + return true; +} + +export function matches$46FromJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (Object.prototype.hasOwnProperty.call(candidate, 'first_value')) { + const item = object['first_value']; + if (item !== undefined && !matches$2FromJSON(item)) { + return false; + } + } + if (!Object.prototype.hasOwnProperty.call(candidate, 'revision')) { + return false; + } + if (!matches$47FromJSON(object['revision'])) { + return false; + } + return true; +} + +export function matches$46ToJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (Object.prototype.hasOwnProperty.call(candidate, 'firstValue')) { + const item = object['firstValue']; + if (item !== undefined && !matches$2ToJSON(item)) { + return false; + } + } + if (!Object.prototype.hasOwnProperty.call(candidate, 'revision')) { + return false; + } + if (!matches$47ToJSON(object['revision'])) { + return false; + } + return true; +} + +export function matches$47FromJSON(candidate: unknown): boolean { + if (!matches$34FromJSON(candidate)) { + return false; + } + if (!matches$48FromJSON(candidate)) { + return false; + } + return true; +} + +export function matches$47ToJSON(candidate: unknown): boolean { + if (!matches$34ToJSON(candidate)) { + return false; + } + if (!matches$48ToJSON(candidate)) { + return false; + } + return true; +} + +export function matches$48FromJSON(candidate: unknown): boolean { + if (matches$36FromJSON(candidate)) { + return true; + } + if (matches$49FromJSON(candidate)) { + return true; + } + return false; +} + +export function matches$48ToJSON(candidate: unknown): boolean { + if (matches$36ToJSON(candidate)) { + return true; + } + if (matches$49ToJSON(candidate)) { + return true; + } + return false; +} + +export function matches$49FromJSON(candidate: unknown): boolean { + return candidate === 2; +} + +export function matches$49ToJSON(candidate: unknown): boolean { + return candidate === 2; +} + +export function matches$50FromJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (!Object.prototype.hasOwnProperty.call(candidate, 'revision')) { + return false; + } + if (!matches$51FromJSON(object['revision'])) { + return false; + } + if (Object.prototype.hasOwnProperty.call(candidate, 'second_value')) { + const item = object['second_value']; + if (item !== undefined && !matches$2FromJSON(item)) { + return false; + } + } + return true; +} + +export function matches$50ToJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (!Object.prototype.hasOwnProperty.call(candidate, 'revision')) { + return false; + } + if (!matches$51ToJSON(object['revision'])) { + return false; + } + if (Object.prototype.hasOwnProperty.call(candidate, 'secondValue')) { + const item = object['secondValue']; + if (item !== undefined && !matches$2ToJSON(item)) { + return false; + } + } + return true; +} + +export function matches$51FromJSON(candidate: unknown): boolean { + if (!matches$3FromJSON(candidate)) { + return false; + } + if (!matches$49FromJSON(candidate)) { + return false; + } + return true; +} + +export function matches$51ToJSON(candidate: unknown): boolean { + if (!matches$3ToJSON(candidate)) { + return false; + } + if (!matches$49ToJSON(candidate)) { + return false; + } + return true; +} + +export function matches$52FromJSON(candidate: unknown): boolean { + if (matches$31FromJSON(candidate)) { + return true; + } + if (matches$1FromJSON(candidate)) { + return true; + } + if (matches$23FromJSON(candidate)) { + return true; + } + if (matches$13FromJSON(candidate)) { + return true; + } + return false; +} + +export function matches$52ToJSON(candidate: unknown): boolean { + if (matches$31ToJSON(candidate)) { + return true; + } + if (matches$1ToJSON(candidate)) { + return true; + } + if (matches$23ToJSON(candidate)) { + return true; + } + if (matches$13ToJSON(candidate)) { + return true; + } + return false; +} + +export function matches$53FromJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (Object.prototype.hasOwnProperty.call(candidate, 'first_value')) { + const item = object['first_value']; + if (item !== undefined && !matches$2FromJSON(item)) { + return false; + } + } + if (!Object.prototype.hasOwnProperty.call(candidate, 'revision')) { + return false; + } + if (!matches$54FromJSON(object['revision'])) { + return false; + } + return true; +} + +export function matches$53ToJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (Object.prototype.hasOwnProperty.call(candidate, 'firstValue')) { + const item = object['firstValue']; + if (item !== undefined && !matches$2ToJSON(item)) { + return false; + } + } + if (!Object.prototype.hasOwnProperty.call(candidate, 'revision')) { + return false; + } + if (!matches$54ToJSON(object['revision'])) { + return false; + } + return true; +} + +export function matches$54FromJSON(candidate: unknown): boolean { + if (!matches$34FromJSON(candidate)) { + return false; + } + if (!matches$55FromJSON(candidate)) { + return false; + } + return true; +} + +export function matches$54ToJSON(candidate: unknown): boolean { + if (!matches$34ToJSON(candidate)) { + return false; + } + if (!matches$55ToJSON(candidate)) { + return false; + } + return true; +} + +export function matches$55FromJSON(candidate: unknown): boolean { + if (matches$36FromJSON(candidate)) { + return true; + } + if (matches$56FromJSON(candidate)) { + return true; + } + return false; +} + +export function matches$55ToJSON(candidate: unknown): boolean { + if (matches$36ToJSON(candidate)) { + return true; + } + if (matches$56ToJSON(candidate)) { + return true; + } + return false; +} + +export function matches$56FromJSON(candidate: unknown): boolean { + return candidate === null; +} + +export function matches$56ToJSON(candidate: unknown): boolean { + return candidate === null; +} + +export function matches$57FromJSON(candidate: unknown): boolean { + if (matches$31FromJSON(candidate)) { + return true; + } + if (matches$22FromJSON(candidate)) { + return true; + } + return false; +} + +export function matches$57ToJSON(candidate: unknown): boolean { + if (matches$31ToJSON(candidate)) { + return true; + } + if (matches$22ToJSON(candidate)) { + return true; + } + return false; +} + +export function matches$58FromJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (Object.prototype.hasOwnProperty.call(candidate, 'first_value')) { + const item = object['first_value']; + if (item !== undefined && !matches$2FromJSON(item)) { + return false; + } + } + if (!Object.prototype.hasOwnProperty.call(candidate, 'revision')) { + return false; + } + if (!matches$59FromJSON(object['revision'])) { + return false; + } + return true; +} + +export function matches$58ToJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (Object.prototype.hasOwnProperty.call(candidate, 'firstValue')) { + const item = object['firstValue']; + if (item !== undefined && !matches$2ToJSON(item)) { + return false; + } + } + if (!Object.prototype.hasOwnProperty.call(candidate, 'revision')) { + return false; + } + if (!matches$59ToJSON(object['revision'])) { + return false; + } + return true; +} + +export function matches$59FromJSON(candidate: unknown): boolean { + if (!matches$60FromJSON(candidate)) { + return false; + } + if (!matches$36FromJSON(candidate)) { + return false; + } + return true; +} + +export function matches$59ToJSON(candidate: unknown): boolean { + if (!matches$60ToJSON(candidate)) { + return false; + } + if (!matches$36ToJSON(candidate)) { + return false; + } + return true; +} + +export function matches$60FromJSON(candidate: unknown): boolean { + return candidate === null || matches$3FromJSON(candidate); +} + +export function matches$60ToJSON(candidate: unknown): boolean { + return candidate === null || matches$3ToJSON(candidate); +} + +export function matches$61FromJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (!Object.prototype.hasOwnProperty.call(candidate, 'revision')) { + return false; + } + if (!matches$62FromJSON(object['revision'])) { + return false; + } + if (Object.prototype.hasOwnProperty.call(candidate, 'second_value')) { + const item = object['second_value']; + if (item !== undefined && !matches$2FromJSON(item)) { + return false; + } + } + return true; +} + +export function matches$61ToJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (!Object.prototype.hasOwnProperty.call(candidate, 'revision')) { + return false; + } + if (!matches$62ToJSON(object['revision'])) { + return false; + } + if (Object.prototype.hasOwnProperty.call(candidate, 'secondValue')) { + const item = object['secondValue']; + if (item !== undefined && !matches$2ToJSON(item)) { + return false; + } + } + return true; +} + +export function matches$62FromJSON(candidate: unknown): boolean { + if (!matches$60FromJSON(candidate)) { + return false; + } + if (!matches$63FromJSON(candidate)) { + return false; + } + return true; +} + +export function matches$62ToJSON(candidate: unknown): boolean { + if (!matches$60ToJSON(candidate)) { + return false; + } + if (!matches$63ToJSON(candidate)) { + return false; + } + return true; +} + +export function matches$63FromJSON(candidate: unknown): boolean { + if (matches$49FromJSON(candidate)) { + return true; + } + if (matches$56FromJSON(candidate)) { + return true; + } + return false; +} + +export function matches$63ToJSON(candidate: unknown): boolean { + if (matches$49ToJSON(candidate)) { + return true; + } + if (matches$56ToJSON(candidate)) { + return true; + } + return false; +} + +export function matches$64FromJSON(candidate: unknown): boolean { + if (matches$31FromJSON(candidate)) { + return true; + } + if (matches$5FromJSON(candidate)) { + return true; + } + return false; +} + +export function matches$64ToJSON(candidate: unknown): boolean { + if (matches$31ToJSON(candidate)) { + return true; + } + if (matches$5ToJSON(candidate)) { + return true; + } + return false; +} + +export function matches$65FromJSON(candidate: unknown): boolean { + if (matches$31FromJSON(candidate)) { + return true; + } + if (matches$2FromJSON(candidate)) { + return true; + } + if (matches$4FromJSON(candidate)) { + return true; + } + return false; +} + +export function matches$65ToJSON(candidate: unknown): boolean { + if (matches$31ToJSON(candidate)) { + return true; + } + if (matches$2ToJSON(candidate)) { + return true; + } + if (matches$4ToJSON(candidate)) { + return true; + } + return false; +} + +export function matches$66FromJSON(candidate: unknown): boolean { + if (matches$31FromJSON(candidate)) { + return true; + } + if (matches$2FromJSON(candidate)) { + return true; + } + return false; +} + +export function matches$66ToJSON(candidate: unknown): boolean { + if (matches$31ToJSON(candidate)) { + return true; + } + if (matches$2ToJSON(candidate)) { + return true; + } + return false; +} + +export function matches$67FromJSON(candidate: unknown): boolean { + if (matches$31FromJSON(candidate)) { + return true; + } + if (matches$4FromJSON(candidate)) { + return true; + } + return false; +} + +export function matches$67ToJSON(candidate: unknown): boolean { + if (matches$31ToJSON(candidate)) { + return true; + } + if (matches$4ToJSON(candidate)) { + return true; + } + return false; +} + +export function matches$68FromJSON(candidate: unknown): boolean { + if (matches$23FromJSON(candidate)) { + return true; + } + if (matches$13FromJSON(candidate)) { + return true; + } + return false; +} + +export function matches$68ToJSON(candidate: unknown): boolean { + if (matches$23ToJSON(candidate)) { + return true; + } + if (matches$13ToJSON(candidate)) { + return true; + } + return false; +} + +export function matches$69FromJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (Object.prototype.hasOwnProperty.call(candidate, 'first_value')) { + const item = object['first_value']; + if (item !== undefined && !matches$2FromJSON(item)) { + return false; + } + } + if (!Object.prototype.hasOwnProperty.call(candidate, 'revision')) { + return false; + } + if (!matches$35FromJSON(object['revision'])) { + return false; + } + return true; +} + +export function matches$69ToJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (Object.prototype.hasOwnProperty.call(candidate, 'firstValue')) { + const item = object['firstValue']; + if (item !== undefined && !matches$2ToJSON(item)) { + return false; + } + } + if (!Object.prototype.hasOwnProperty.call(candidate, 'revision')) { + return false; + } + if (!matches$35ToJSON(object['revision'])) { + return false; + } + return true; +} + +export function matches$70FromJSON(candidate: unknown): boolean { + if (!Array.isArray(candidate)) { + return false; + } + return candidate.every(matches$71FromJSON); +} + +export function matches$70ToJSON(candidate: unknown): boolean { + if (!Array.isArray(candidate)) { + return false; + } + return candidate.every(matches$71ToJSON); +} + +export function matches$71FromJSON(candidate: unknown): boolean { + if (matches$3FromJSON(candidate)) { + return true; + } + if (matches$4FromJSON(candidate)) { + return true; + } + return false; +} + +export function matches$71ToJSON(candidate: unknown): boolean { + if (matches$3ToJSON(candidate)) { + return true; + } + if (matches$4ToJSON(candidate)) { + return true; + } + return false; +} + +export function matches$72FromJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + return Object.values(candidate).every(matches$71FromJSON); +} + +export function matches$72ToJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + return Object.values(candidate).every(matches$71ToJSON); +} + +export function matches$73FromJSON(candidate: unknown): boolean { + if (matches$1FromJSON(candidate)) { + return true; + } + if (matches$74FromJSON(candidate)) { + return true; + } + return false; +} + +export function matches$73ToJSON(candidate: unknown): boolean { + if (matches$1ToJSON(candidate)) { + return true; + } + if (matches$74ToJSON(candidate)) { + return true; + } + return false; +} + +export function matches$74FromJSON(candidate: unknown): boolean { + if (!matches$75FromJSON(candidate)) { + return false; + } + return Object.values(candidate as globalThis.Record).some(matches$77FromJSON); +} + +export function matches$74ToJSON(candidate: unknown): boolean { + if (!matches$75ToJSON(candidate)) { + return false; + } + return Object.values(candidate as globalThis.Record).some(matches$77ToJSON); +} + +export function matches$75FromJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + return true; +} + +export function matches$75ToJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + return true; +} + +export function matches$76FromJSON(candidate: unknown): boolean { + return matchRecursiveOpenDictionaryFromJSON(candidate) > 0; +} + +export function matches$76ToJSON(candidate: unknown): boolean { + return matchRecursiveOpenDictionaryToJSON(candidate) > 0; +} + +export function matches$77FromJSON(candidate: unknown): boolean { + return matchRecursiveOpenDictionaryFromJSON(candidate) === 2; +} + +export function matches$77ToJSON(candidate: unknown): boolean { + return matchRecursiveOpenDictionaryToJSON(candidate) === 2; +} + +export function matches$79FromJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + return Object.values(candidate).every(matches$80FromJSON); +} + +export function matches$79ToJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + return Object.values(candidate).every(matches$80ToJSON); +} + +export function matches$80FromJSON(candidate: unknown): boolean { + return matchRecursiveOpenDictionaryFromJSON(candidate) === 1; +} + +export function matches$80ToJSON(candidate: unknown): boolean { + return matchRecursiveOpenDictionaryToJSON(candidate) === 1; +} + +export function matches$81FromJSON(candidate: unknown): boolean { + if (!Array.isArray(candidate)) { + return false; + } + return candidate.every(matches$82FromJSON); +} + +export function matches$81ToJSON(candidate: unknown): boolean { + if (!Array.isArray(candidate)) { + return false; + } + return candidate.every(matches$82ToJSON); +} + +export function matches$82FromJSON(candidate: unknown): boolean { + return matchRecursiveListFromJSON(candidate) > 0; +} + +export function matches$82ToJSON(candidate: unknown): boolean { + return matchRecursiveListToJSON(candidate) > 0; +} + +export function matches$83FromJSON(candidate: unknown): boolean { + if (!matches$84FromJSON(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + const declared = ['payload_key']; + for (const [key, item] of Object.entries(object)) { + if (!declared.includes(key) && !matches$1FromJSON(item)) { + return false; + } + } + return true; +} + +export function matches$83ToJSON(candidate: unknown): boolean { + if (!matches$84ToJSON(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + const declared = ['payload_key']; + for (const [key, item] of Object.entries(object)) { + if (!declared.includes(key) && !matches$1ToJSON(item)) { + return false; + } + } + return true; +} + +export function matches$84FromJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (!Object.prototype.hasOwnProperty.call(candidate, 'payload_key')) { + return false; + } + if (!matches$1FromJSON(object['payload_key'])) { + return false; + } + return true; +} + +export function matches$84ToJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (!Object.prototype.hasOwnProperty.call(candidate, 'payload_key')) { + return false; + } + if (!matches$1ToJSON(object['payload_key'])) { + return false; + } + return true; +} + +export function matches$85FromJSON(candidate: unknown): boolean { + if (!matches$84FromJSON(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + const declared = ['payload_key']; + for (const [key, item] of Object.entries(object)) { + if (!declared.includes(key) && !matches$29FromJSON(item)) { + return false; + } + } + return true; +} + +export function matches$85ToJSON(candidate: unknown): boolean { + if (!matches$84ToJSON(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + const declared = ['payload_key']; + for (const [key, item] of Object.entries(object)) { + if (!declared.includes(key) && !matches$29ToJSON(item)) { + return false; + } + } + return true; +} + +export function matches$86FromJSON(candidate: unknown): boolean { + if (!Array.isArray(candidate)) { + return false; + } + return candidate.every(matches$23FromJSON); +} + +export function matches$86ToJSON(candidate: unknown): boolean { + if (!Array.isArray(candidate)) { + return false; + } + return candidate.every(matches$23ToJSON); +} + +export function matches$87FromJSON(candidate: unknown): boolean { + if (!Array.isArray(candidate)) { + return false; + } + return candidate.every(matches$13FromJSON); +} + +export function matches$87ToJSON(candidate: unknown): boolean { + if (!Array.isArray(candidate)) { + return false; + } + return candidate.every(matches$13ToJSON); +} + +export function matches$88FromJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + return Object.values(candidate).every(matches$23FromJSON); +} + +export function matches$88ToJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + return Object.values(candidate).every(matches$23ToJSON); +} + +export function matches$89FromJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + return Object.values(candidate).every(matches$13FromJSON); +} + +export function matches$89ToJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + return Object.values(candidate).every(matches$13ToJSON); +} + +export function matches$90FromJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (Object.prototype.hasOwnProperty.call(candidate, 'first_value')) { + const item = object['first_value']; + if (item !== undefined && !matches$2FromJSON(item)) { + return false; + } + } + if (!Object.prototype.hasOwnProperty.call(candidate, 'revision')) { + return false; + } + if (!matches$36FromJSON(object['revision'])) { + return false; + } + return true; +} + +export function matches$90ToJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (Object.prototype.hasOwnProperty.call(candidate, 'firstValue')) { + const item = object['firstValue']; + if (item !== undefined && !matches$2ToJSON(item)) { + return false; + } + } + if (!Object.prototype.hasOwnProperty.call(candidate, 'revision')) { + return false; + } + if (!matches$36ToJSON(object['revision'])) { + return false; + } + return true; +} + +export function matches$91FromJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (!Object.prototype.hasOwnProperty.call(candidate, 'revision')) { + return false; + } + if (!matches$49FromJSON(object['revision'])) { + return false; + } + if (Object.prototype.hasOwnProperty.call(candidate, 'second_value')) { + const item = object['second_value']; + if (item !== undefined && !matches$2FromJSON(item)) { + return false; + } + } + return true; +} + +export function matches$91ToJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (!Object.prototype.hasOwnProperty.call(candidate, 'revision')) { + return false; + } + if (!matches$49ToJSON(object['revision'])) { + return false; + } + if (Object.prototype.hasOwnProperty.call(candidate, 'secondValue')) { + const item = object['secondValue']; + if (item !== undefined && !matches$2ToJSON(item)) { + return false; + } + } + return true; +} + +export function matches$92FromJSON(candidate: unknown): boolean { + if (matches$29FromJSON(candidate)) { + return true; + } + if (matches$1FromJSON(candidate)) { + return true; + } + return false; +} + +export function matches$92ToJSON(candidate: unknown): boolean { + if (matches$29ToJSON(candidate)) { + return true; + } + if (matches$1ToJSON(candidate)) { + return true; + } + return false; +} + +export function matches$93FromJSON(candidate: unknown): boolean { + if (matches$81FromJSON(candidate)) { + return true; + } + if (matches$1FromJSON(candidate)) { + return true; + } + return false; +} + +export function matches$93ToJSON(candidate: unknown): boolean { + if (matches$81ToJSON(candidate)) { + return true; + } + if (matches$1ToJSON(candidate)) { + return true; + } + return false; +} + +export function matchRecursiveListFromJSON(candidate: unknown): number { + if (matches$93FromJSON(candidate)) { + return 2; + } + return 0; +} + +export function matchRecursiveListToJSON(candidate: unknown): number { + if (matches$93ToJSON(candidate)) { + return 2; + } + return 0; +} + +export function matchRecursiveOpenDictionaryFromJSON(candidate: unknown): number { + if (matches$73FromJSON(candidate)) { + return 2; + } + return 1; +} + +export function matchRecursiveOpenDictionaryToJSON(candidate: unknown): number { + if (matches$73ToJSON(candidate)) { + return 2; + } + return 1; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$0.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$0.ts.golden new file mode 100644 index 000000000..749b67e7e --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$0.ts.golden @@ -0,0 +1,23 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail, Detail$Wire } from '../models/Detail'; +import { convert$1FromJSON, convert$1ToJSON } from './valueConversion$1'; + +export type Value$0Wire = readonly (globalThis.Record | string)[]; + +export type Value$0 = readonly (globalThis.Record | string)[]; + +export function convert$0FromJSON( + json: ReadonlyArray | string>, +): Value$0 { + return json.map(convert$1FromJSON); +} + +export function convert$0ToJSON( + value: ReadonlyArray | string>, +): Value$0Wire { + return value.map(convert$1ToJSON); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$1.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$1.ts.golden new file mode 100644 index 000000000..1b2b400d4 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$1.ts.golden @@ -0,0 +1,40 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail, Detail$Wire } from '../models/Detail'; +import { + matches$0FromJSON, + matches$0ToJSON, + matches$2FromJSON, + matches$2ToJSON, +} from '../runtime/unionMatch'; +import type { Value$2, Value$2Wire } from './valueConversion$2'; +import { convert$2FromJSON, convert$2ToJSON } from './valueConversion$2'; + +export type Value$1Wire = globalThis.Record | string; + +export type Value$1 = globalThis.Record | string; + +export function convert$1FromJSON(json: globalThis.Record | string): Value$1 { + const candidate: unknown = json; + if (matches$0FromJSON(candidate)) { + return convert$2FromJSON(json as unknown as Value$2Wire); + } + if (matches$2FromJSON(candidate)) { + return json as unknown as string; + } + return json as unknown as Value$1; +} + +export function convert$1ToJSON(value: globalThis.Record | string): Value$1Wire { + const candidate: unknown = value; + if (matches$0ToJSON(candidate)) { + return convert$2ToJSON(value as unknown as Value$2); + } + if (matches$2ToJSON(candidate)) { + return value as unknown as string; + } + return value as unknown as Value$1Wire; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$10.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$10.ts.golden new file mode 100644 index 000000000..dbdd4d749 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$10.ts.golden @@ -0,0 +1,22 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { NullableScalarInner, NullableScalarInner$Wire } from '../models/NullableScalarInner'; +import { + nullableScalarInnerFromJSON, + nullableScalarInnerToJSON, +} from '../models/NullableScalarInner'; + +export type Value$10Wire = NullableScalarInner$Wire | null; + +export type Value$10 = NullableScalarInner | null; + +export function convert$10FromJSON(json: NullableScalarInner$Wire | null): Value$10 { + return nullableScalarInnerFromJSON(json); +} + +export function convert$10ToJSON(value: NullableScalarInner | null): Value$10Wire { + return nullableScalarInnerToJSON(value); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$11.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$11.ts.golden new file mode 100644 index 000000000..21583b7af --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$11.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail, Detail$Wire } from '../models/Detail'; +import { detailFromJSON, detailToJSON } from '../models/Detail'; + +export type Value$11Wire = readonly Detail$Wire[]; + +export type Value$11 = readonly Detail[]; + +export function convert$11FromJSON(json: ReadonlyArray): Value$11 { + return json.map(detailFromJSON); +} + +export function convert$11ToJSON(value: ReadonlyArray): Value$11Wire { + return value.map(detailToJSON); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$12.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$12.ts.golden new file mode 100644 index 000000000..f60bf44a7 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$12.ts.golden @@ -0,0 +1,23 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail, Detail$Wire } from '../models/Detail'; +import { detailFromJSON, detailToJSON } from '../models/Detail'; + +export type Value$12Wire = globalThis.Record; + +export type Value$12 = globalThis.Record; + +export function convert$12FromJSON(json: globalThis.Record): Value$12 { + return Object.fromEntries( + Object.entries(json ?? {}).map(([key, item]) => [key, detailFromJSON(item)]), + ); +} + +export function convert$12ToJSON(value: globalThis.Record): Value$12Wire { + return Object.fromEntries( + Object.entries(value ?? {}).map(([key, item]) => [key, detailToJSON(item)]), + ); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$13.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$13.ts.golden new file mode 100644 index 000000000..a9cb240cd --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$13.ts.golden @@ -0,0 +1,30 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail, Detail$Wire } from '../models/Detail'; +import { convert$14FromJSON, convert$14ToJSON } from './valueConversion$14'; + +export type Value$13Wire = globalThis.Record< + string, + globalThis.Record | string +>; + +export type Value$13 = globalThis.Record | string>; + +export function convert$13FromJSON( + json: globalThis.Record | string>, +): Value$13 { + return Object.fromEntries( + Object.entries(json ?? {}).map(([key, item]) => [key, convert$14FromJSON(item)]), + ); +} + +export function convert$13ToJSON( + value: globalThis.Record | string>, +): Value$13Wire { + return Object.fromEntries( + Object.entries(value ?? {}).map(([key, item]) => [key, convert$14ToJSON(item)]), + ); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$14.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$14.ts.golden new file mode 100644 index 000000000..7e0fb80f1 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$14.ts.golden @@ -0,0 +1,42 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail, Detail$Wire } from '../models/Detail'; +import { + matches$0FromJSON, + matches$0ToJSON, + matches$2FromJSON, + matches$2ToJSON, +} from '../runtime/unionMatch'; +import type { Value$12, Value$12Wire } from './valueConversion$12'; +import { convert$12FromJSON, convert$12ToJSON } from './valueConversion$12'; + +export type Value$14Wire = globalThis.Record | string; + +export type Value$14 = globalThis.Record | string; + +export function convert$14FromJSON( + json: globalThis.Record | string, +): Value$14 { + const candidate: unknown = json; + if (matches$0FromJSON(candidate)) { + return convert$12FromJSON(json as unknown as Value$12Wire); + } + if (matches$2FromJSON(candidate)) { + return json as unknown as string; + } + return json as unknown as Value$14; +} + +export function convert$14ToJSON(value: globalThis.Record | string): Value$14Wire { + const candidate: unknown = value; + if (matches$0ToJSON(candidate)) { + return convert$12ToJSON(value as unknown as Value$12); + } + if (matches$2ToJSON(candidate)) { + return value as unknown as string; + } + return value as unknown as Value$14Wire; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$15.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$15.ts.golden new file mode 100644 index 000000000..4846290d7 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$15.ts.golden @@ -0,0 +1,23 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail, Detail$Wire } from '../models/Detail'; +import { convert$16FromJSON, convert$16ToJSON } from './valueConversion$16'; + +export type Value$15Wire = readonly (globalThis.Record | number)[]; + +export type Value$15 = readonly (globalThis.Record | number)[]; + +export function convert$15FromJSON( + json: ReadonlyArray | number>, +): Value$15 { + return json.map(convert$16FromJSON); +} + +export function convert$15ToJSON( + value: ReadonlyArray | number>, +): Value$15Wire { + return value.map(convert$16ToJSON); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$16.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$16.ts.golden new file mode 100644 index 000000000..cba365223 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$16.ts.golden @@ -0,0 +1,50 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail, Detail$Wire } from '../models/Detail'; +import { + matches$0FromJSON, + matches$0ToJSON, + matches$3FromJSON, + matches$3ToJSON, + matches$4FromJSON, + matches$4ToJSON, +} from '../runtime/unionMatch'; +import type { Value$2, Value$2Wire } from './valueConversion$2'; +import { convert$2FromJSON, convert$2ToJSON } from './valueConversion$2'; + +export type Value$16Wire = globalThis.Record | number; + +export type Value$16 = globalThis.Record | number; + +export function convert$16FromJSON( + json: globalThis.Record | number, +): Value$16 { + const candidate: unknown = json; + if (matches$0FromJSON(candidate)) { + return convert$2FromJSON(json as unknown as Value$2Wire); + } + if (matches$3FromJSON(candidate)) { + return json as unknown as number; + } + if (matches$4FromJSON(candidate)) { + return json as unknown as number; + } + return json as unknown as Value$16; +} + +export function convert$16ToJSON(value: globalThis.Record | number): Value$16Wire { + const candidate: unknown = value; + if (matches$0ToJSON(candidate)) { + return convert$2ToJSON(value as unknown as Value$2); + } + if (matches$3ToJSON(candidate)) { + return value as unknown as number; + } + if (matches$4ToJSON(candidate)) { + return value as unknown as number; + } + return value as unknown as Value$16Wire; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$17.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$17.ts.golden new file mode 100644 index 000000000..382d38b78 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$17.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { NamedValue, NamedValue$Wire } from '../models/NamedValue'; +import { namedValueFromJSON, namedValueToJSON } from '../models/NamedValue'; + +export type Value$17Wire = readonly NamedValue$Wire[]; + +export type Value$17 = readonly NamedValue[]; + +export function convert$17FromJSON(json: ReadonlyArray): Value$17 { + return json.map(namedValueFromJSON); +} + +export function convert$17ToJSON(value: ReadonlyArray): Value$17Wire { + return value.map(namedValueToJSON); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$18.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$18.ts.golden new file mode 100644 index 000000000..8750aeb73 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$18.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail, Detail$Wire } from '../models/Detail'; +import { convert$19FromJSON, convert$19ToJSON } from './valueConversion$19'; + +export type Value$18Wire = readonly Detail$Wire[][]; + +export type Value$18 = readonly Detail[][]; + +export function convert$18FromJSON(json: ReadonlyArray>): Value$18 { + return json.map(convert$19FromJSON); +} + +export function convert$18ToJSON(value: ReadonlyArray>): Value$18Wire { + return value.map(convert$19ToJSON); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$19.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$19.ts.golden new file mode 100644 index 000000000..f4ebb22dd --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$19.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail, Detail$Wire } from '../models/Detail'; +import { detailFromJSON, detailToJSON } from '../models/Detail'; + +export type Value$19Wire = Detail$Wire[]; + +export type Value$19 = Detail[]; + +export function convert$19FromJSON(json: ReadonlyArray): Value$19 { + return json.map(detailFromJSON); +} + +export function convert$19ToJSON(value: ReadonlyArray): Value$19Wire { + return value.map(detailToJSON); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$2.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$2.ts.golden new file mode 100644 index 000000000..7b464d5a8 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$2.ts.golden @@ -0,0 +1,23 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail, Detail$Wire } from '../models/Detail'; +import { detailFromJSON, detailToJSON } from '../models/Detail'; + +export type Value$2Wire = globalThis.Record; + +export type Value$2 = globalThis.Record; + +export function convert$2FromJSON(json: globalThis.Record): Value$2 { + return Object.fromEntries( + Object.entries(json ?? {}).map(([key, item]) => [key, detailFromJSON(item)]), + ); +} + +export function convert$2ToJSON(value: globalThis.Record): Value$2Wire { + return Object.fromEntries( + Object.entries(value ?? {}).map(([key, item]) => [key, detailToJSON(item)]), + ); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$20.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$20.ts.golden new file mode 100644 index 000000000..97fab7c63 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$20.ts.golden @@ -0,0 +1,25 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { ScalarEnumChoice, ScalarEnumChoice$Wire } from '../models/ScalarEnumChoice'; +import { scalarEnumChoiceFromJSON, scalarEnumChoiceToJSON } from '../models/ScalarEnumChoice'; + +export type Value$20Wire = ScalarEnumChoice$Wire | null; + +export type Value$20 = ScalarEnumChoice | null; + +export function convert$20FromJSON(json: ScalarEnumChoice$Wire | null): Value$20 { + if (json == null) { + return null; + } + return scalarEnumChoiceFromJSON(json); +} + +export function convert$20ToJSON(value: ScalarEnumChoice | null): Value$20Wire { + if (value == null) { + return null; + } + return scalarEnumChoiceToJSON(value); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$21.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$21.ts.golden new file mode 100644 index 000000000..2834fd572 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$21.ts.golden @@ -0,0 +1,24 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { convert$22FromJSON, convert$22ToJSON } from './valueConversion$22'; + +export type Value$21Wire = number | boolean | null; + +export type Value$21 = number | boolean | null; + +export function convert$21FromJSON(json: number | boolean | null): Value$21 { + if (json == null) { + return null; + } + return convert$22FromJSON(json); +} + +export function convert$21ToJSON(value: number | boolean | null): Value$21Wire { + if (value == null) { + return null; + } + return convert$22ToJSON(value); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$22.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$22.ts.golden new file mode 100644 index 000000000..ae8e9759e --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$22.ts.golden @@ -0,0 +1,37 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$3FromJSON, + matches$3ToJSON, + matches$5FromJSON, + matches$5ToJSON, +} from '../runtime/unionMatch'; + +export type Value$22Wire = number | boolean; + +export type Value$22 = number | boolean; + +export function convert$22FromJSON(json: number | boolean): Value$22 { + const candidate: unknown = json; + if (matches$3FromJSON(candidate)) { + return json as unknown as number; + } + if (matches$5FromJSON(candidate)) { + return json as unknown as boolean; + } + return json as unknown as Value$22; +} + +export function convert$22ToJSON(value: number | boolean): Value$22Wire { + const candidate: unknown = value; + if (matches$3ToJSON(candidate)) { + return value as unknown as number; + } + if (matches$5ToJSON(candidate)) { + return value as unknown as boolean; + } + return value as unknown as Value$22Wire; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$23.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$23.ts.golden new file mode 100644 index 000000000..59ab2c2e0 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$23.ts.golden @@ -0,0 +1,26 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { + NullableNonNullScalarsItems, + NullableNonNullScalarsItems$Wire, +} from '../models/NullableNonNullScalarsItems'; +import { convert$24FromJSON, convert$24ToJSON } from './valueConversion$24'; + +export type Value$23Wire = readonly (NullableNonNullScalarsItems$Wire | null)[]; + +export type Value$23 = readonly (NullableNonNullScalarsItems | null)[]; + +export function convert$23FromJSON( + json: ReadonlyArray, +): Value$23 { + return json.map(convert$24FromJSON); +} + +export function convert$23ToJSON( + value: ReadonlyArray, +): Value$23Wire { + return value.map(convert$24ToJSON); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$24.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$24.ts.golden new file mode 100644 index 000000000..4789ce70a --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$24.ts.golden @@ -0,0 +1,31 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { + NullableNonNullScalarsItems, + NullableNonNullScalarsItems$Wire, +} from '../models/NullableNonNullScalarsItems'; +import { + nullableNonNullScalarsItemsFromJSON, + nullableNonNullScalarsItemsToJSON, +} from '../models/NullableNonNullScalarsItems'; + +export type Value$24Wire = NullableNonNullScalarsItems$Wire | null; + +export type Value$24 = NullableNonNullScalarsItems | null; + +export function convert$24FromJSON(json: NullableNonNullScalarsItems$Wire | null): Value$24 { + if (json == null) { + return null; + } + return nullableNonNullScalarsItemsFromJSON(json); +} + +export function convert$24ToJSON(value: NullableNonNullScalarsItems | null): Value$24Wire { + if (value == null) { + return null; + } + return nullableNonNullScalarsItemsToJSON(value); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$25.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$25.ts.golden new file mode 100644 index 000000000..0145d6124 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$25.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { NullableScalarInner, NullableScalarInner$Wire } from '../models/NullableScalarInner'; +import { convert$10FromJSON, convert$10ToJSON } from './valueConversion$10'; + +export type Value$25Wire = readonly (NullableScalarInner$Wire | null)[]; + +export type Value$25 = readonly (NullableScalarInner | null)[]; + +export function convert$25FromJSON(json: ReadonlyArray): Value$25 { + return json.map(convert$10FromJSON); +} + +export function convert$25ToJSON(value: ReadonlyArray): Value$25Wire { + return value.map(convert$10ToJSON); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$26.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$26.ts.golden new file mode 100644 index 000000000..6940ba2db --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$26.ts.golden @@ -0,0 +1,22 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { convert$27FromJSON, convert$27ToJSON } from './valueConversion$27'; + +export type Value$26Wire = globalThis.Record; + +export type Value$26 = globalThis.Record; + +export function convert$26FromJSON(json: globalThis.Record): Value$26 { + return Object.fromEntries( + Object.entries(json ?? {}).map(([key, item]) => [key, convert$27FromJSON(item)]), + ); +} + +export function convert$26ToJSON(value: globalThis.Record): Value$26Wire { + return Object.fromEntries( + Object.entries(value ?? {}).map(([key, item]) => [key, convert$27ToJSON(item)]), + ); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$27.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$27.ts.golden new file mode 100644 index 000000000..b4288b004 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$27.ts.golden @@ -0,0 +1,37 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$3FromJSON, + matches$3ToJSON, + matches$4FromJSON, + matches$4ToJSON, +} from '../runtime/unionMatch'; + +export type Value$27Wire = number; + +export type Value$27 = number; + +export function convert$27FromJSON(json: number): Value$27 { + const candidate: unknown = json; + if (matches$3FromJSON(candidate)) { + return json as unknown as number; + } + if (matches$4FromJSON(candidate)) { + return json as unknown as number; + } + return json as unknown as Value$27; +} + +export function convert$27ToJSON(value: number): Value$27Wire { + const candidate: unknown = value; + if (matches$3ToJSON(candidate)) { + return value as unknown as number; + } + if (matches$4ToJSON(candidate)) { + return value as unknown as number; + } + return value as unknown as Value$27Wire; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$28.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$28.ts.golden new file mode 100644 index 000000000..095990381 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$28.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { RecursiveList, RecursiveList$Wire } from '../models/RecursiveList'; +import { recursiveListFromJSON, recursiveListToJSON } from '../models/RecursiveList'; + +export type Value$28Wire = readonly RecursiveList$Wire[]; + +export type Value$28 = readonly RecursiveList[]; + +export function convert$28FromJSON(json: ReadonlyArray): Value$28 { + return json.map(recursiveListFromJSON); +} + +export function convert$28ToJSON(value: ReadonlyArray): Value$28Wire { + return value.map(recursiveListToJSON); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$29.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$29.ts.golden new file mode 100644 index 000000000..06af8d4a2 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$29.ts.golden @@ -0,0 +1,33 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { + RecursiveOpenDictionary, + RecursiveOpenDictionary$Wire, +} from '../models/RecursiveOpenDictionary'; +import { + recursiveOpenDictionaryFromJSON, + recursiveOpenDictionaryToJSON, +} from '../models/RecursiveOpenDictionary'; + +export type Value$29Wire = globalThis.Record; + +export type Value$29 = globalThis.Record; + +export function convert$29FromJSON( + json: globalThis.Record, +): Value$29 { + return Object.fromEntries( + Object.entries(json ?? {}).map(([key, item]) => [key, recursiveOpenDictionaryFromJSON(item)]), + ); +} + +export function convert$29ToJSON( + value: globalThis.Record, +): Value$29Wire { + return Object.fromEntries( + Object.entries(value ?? {}).map(([key, item]) => [key, recursiveOpenDictionaryToJSON(item)]), + ); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$3.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$3.ts.golden new file mode 100644 index 000000000..5c8843f1f --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$3.ts.golden @@ -0,0 +1,18 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { convert$4FromJSON, convert$4ToJSON } from './valueConversion$4'; + +export type Value$3Wire = readonly number[]; + +export type Value$3 = readonly number[]; + +export function convert$3FromJSON(json: ReadonlyArray): Value$3 { + return json.map(convert$4FromJSON); +} + +export function convert$3ToJSON(value: ReadonlyArray): Value$3Wire { + return value.map(convert$4ToJSON); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$30.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$30.ts.golden new file mode 100644 index 000000000..141b85a29 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$30.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { ClientTool, ClientTool$Wire } from '../models/ClientTool'; +import { clientToolFromJSON, clientToolToJSON } from '../models/ClientTool'; + +export type Value$30Wire = readonly ClientTool$Wire[]; + +export type Value$30 = readonly ClientTool[]; + +export function convert$30FromJSON(json: ReadonlyArray): Value$30 { + return json.map(clientToolFromJSON); +} + +export function convert$30ToJSON(value: ReadonlyArray): Value$30Wire { + return value.map(clientToolToJSON); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$31.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$31.ts.golden new file mode 100644 index 000000000..c27ab86ef --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$31.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { SearchTool, SearchTool$Wire } from '../models/SearchTool'; +import { searchToolFromJSON, searchToolToJSON } from '../models/SearchTool'; + +export type Value$31Wire = readonly SearchTool$Wire[]; + +export type Value$31 = readonly SearchTool[]; + +export function convert$31FromJSON(json: ReadonlyArray): Value$31 { + return json.map(searchToolFromJSON); +} + +export function convert$31ToJSON(value: ReadonlyArray): Value$31Wire { + return value.map(searchToolToJSON); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$32.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$32.ts.golden new file mode 100644 index 000000000..75441a8d0 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$32.ts.golden @@ -0,0 +1,23 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { ClientTool, ClientTool$Wire } from '../models/ClientTool'; +import { clientToolFromJSON, clientToolToJSON } from '../models/ClientTool'; + +export type Value$32Wire = globalThis.Record; + +export type Value$32 = globalThis.Record; + +export function convert$32FromJSON(json: globalThis.Record): Value$32 { + return Object.fromEntries( + Object.entries(json ?? {}).map(([key, item]) => [key, clientToolFromJSON(item)]), + ); +} + +export function convert$32ToJSON(value: globalThis.Record): Value$32Wire { + return Object.fromEntries( + Object.entries(value ?? {}).map(([key, item]) => [key, clientToolToJSON(item)]), + ); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$33.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$33.ts.golden new file mode 100644 index 000000000..e586341e6 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$33.ts.golden @@ -0,0 +1,23 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { SearchTool, SearchTool$Wire } from '../models/SearchTool'; +import { searchToolFromJSON, searchToolToJSON } from '../models/SearchTool'; + +export type Value$33Wire = globalThis.Record; + +export type Value$33 = globalThis.Record; + +export function convert$33FromJSON(json: globalThis.Record): Value$33 { + return Object.fromEntries( + Object.entries(json ?? {}).map(([key, item]) => [key, searchToolFromJSON(item)]), + ); +} + +export function convert$33ToJSON(value: globalThis.Record): Value$33Wire { + return Object.fromEntries( + Object.entries(value ?? {}).map(([key, item]) => [key, searchToolToJSON(item)]), + ); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$34.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$34.ts.golden new file mode 100644 index 000000000..fd7476ff4 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$34.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Tool, Tool$Wire } from '../models/Tool'; +import { toolFromJSON, toolToJSON } from '../models/Tool'; + +export type Value$34Wire = readonly Tool$Wire[]; + +export type Value$34 = readonly Tool[]; + +export function convert$34FromJSON(json: ReadonlyArray): Value$34 { + return json.map(toolFromJSON); +} + +export function convert$34ToJSON(value: ReadonlyArray): Value$34Wire { + return value.map(toolToJSON); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$35.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$35.ts.golden new file mode 100644 index 000000000..6c053a5ea --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$35.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Tool, Tool$Wire } from '../models/Tool'; +import { convert$36FromJSON, convert$36ToJSON } from './valueConversion$36'; + +export type Value$35Wire = Tool$Wire[][]; + +export type Value$35 = Tool[][]; + +export function convert$35FromJSON(json: ReadonlyArray>): Value$35 { + return json.map(convert$36FromJSON); +} + +export function convert$35ToJSON(value: ReadonlyArray>): Value$35Wire { + return value.map(convert$36ToJSON); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$36.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$36.ts.golden new file mode 100644 index 000000000..ffd60171d --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$36.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Tool, Tool$Wire } from '../models/Tool'; +import { toolFromJSON, toolToJSON } from '../models/Tool'; + +export type Value$36Wire = Tool$Wire[]; + +export type Value$36 = Tool[]; + +export function convert$36FromJSON(json: ReadonlyArray): Value$36 { + return json.map(toolFromJSON); +} + +export function convert$36ToJSON(value: ReadonlyArray): Value$36Wire { + return value.map(toolToJSON); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$37.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$37.ts.golden new file mode 100644 index 000000000..f866475d0 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$37.ts.golden @@ -0,0 +1,25 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Tool, Tool$Wire } from '../models/Tool'; +import { convert$38FromJSON, convert$38ToJSON } from './valueConversion$38'; + +export type Value$37Wire = Tool$Wire[] | null; + +export type Value$37 = Tool[] | null; + +export function convert$37FromJSON(json: ReadonlyArray | null): Value$37 { + if (json == null) { + return null; + } + return convert$38FromJSON(json); +} + +export function convert$37ToJSON(value: ReadonlyArray | null): Value$37Wire { + if (value == null) { + return null; + } + return convert$38ToJSON(value); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$38.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$38.ts.golden new file mode 100644 index 000000000..1b078e45a --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$38.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Tool, Tool$Wire } from '../models/Tool'; +import { toolFromJSON, toolToJSON } from '../models/Tool'; + +export type Value$38Wire = Tool$Wire[]; + +export type Value$38 = Tool[]; + +export function convert$38FromJSON(json: ReadonlyArray): Value$38 { + return json.map(toolFromJSON); +} + +export function convert$38ToJSON(value: ReadonlyArray): Value$38Wire { + return value.map(toolToJSON); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$39.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$39.ts.golden new file mode 100644 index 000000000..c9867bbc4 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$39.ts.golden @@ -0,0 +1,23 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail, Detail$Wire } from '../models/Detail'; +import { convert$40FromJSON, convert$40ToJSON } from './valueConversion$40'; + +export type Value$39Wire = (globalThis.Record | string)[]; + +export type Value$39 = (globalThis.Record | string)[]; + +export function convert$39FromJSON( + json: ReadonlyArray | string>, +): Value$39 { + return json.map(convert$40FromJSON); +} + +export function convert$39ToJSON( + value: ReadonlyArray | string>, +): Value$39Wire { + return value.map(convert$40ToJSON); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$4.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$4.ts.golden new file mode 100644 index 000000000..5b105e839 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$4.ts.golden @@ -0,0 +1,37 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$3FromJSON, + matches$3ToJSON, + matches$4FromJSON, + matches$4ToJSON, +} from '../runtime/unionMatch'; + +export type Value$4Wire = number; + +export type Value$4 = number; + +export function convert$4FromJSON(json: number): Value$4 { + const candidate: unknown = json; + if (matches$3FromJSON(candidate)) { + return json as unknown as number; + } + if (matches$4FromJSON(candidate)) { + return json as unknown as number; + } + return json as unknown as Value$4; +} + +export function convert$4ToJSON(value: number): Value$4Wire { + const candidate: unknown = value; + if (matches$3ToJSON(candidate)) { + return value as unknown as number; + } + if (matches$4ToJSON(candidate)) { + return value as unknown as number; + } + return value as unknown as Value$4Wire; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$40.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$40.ts.golden new file mode 100644 index 000000000..1855802cf --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$40.ts.golden @@ -0,0 +1,42 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail, Detail$Wire } from '../models/Detail'; +import { + matches$0FromJSON, + matches$0ToJSON, + matches$2FromJSON, + matches$2ToJSON, +} from '../runtime/unionMatch'; +import type { Value$41, Value$41Wire } from './valueConversion$41'; +import { convert$41FromJSON, convert$41ToJSON } from './valueConversion$41'; + +export type Value$40Wire = globalThis.Record | string; + +export type Value$40 = globalThis.Record | string; + +export function convert$40FromJSON( + json: globalThis.Record | string, +): Value$40 { + const candidate: unknown = json; + if (matches$0FromJSON(candidate)) { + return convert$41FromJSON(json as unknown as Value$41Wire); + } + if (matches$2FromJSON(candidate)) { + return json as unknown as string; + } + return json as unknown as Value$40; +} + +export function convert$40ToJSON(value: globalThis.Record | string): Value$40Wire { + const candidate: unknown = value; + if (matches$0ToJSON(candidate)) { + return convert$41ToJSON(value as unknown as Value$41); + } + if (matches$2ToJSON(candidate)) { + return value as unknown as string; + } + return value as unknown as Value$40Wire; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$41.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$41.ts.golden new file mode 100644 index 000000000..1a910fc9d --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$41.ts.golden @@ -0,0 +1,23 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail, Detail$Wire } from '../models/Detail'; +import { detailFromJSON, detailToJSON } from '../models/Detail'; + +export type Value$41Wire = globalThis.Record; + +export type Value$41 = globalThis.Record; + +export function convert$41FromJSON(json: globalThis.Record): Value$41 { + return Object.fromEntries( + Object.entries(json ?? {}).map(([key, item]) => [key, detailFromJSON(item)]), + ); +} + +export function convert$41ToJSON(value: globalThis.Record): Value$41Wire { + return Object.fromEntries( + Object.entries(value ?? {}).map(([key, item]) => [key, detailToJSON(item)]), + ); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$5.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$5.ts.golden new file mode 100644 index 000000000..d9f62811e --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$5.ts.golden @@ -0,0 +1,22 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { NullableScalarInner, NullableScalarInner$Wire } from '../models/NullableScalarInner'; +import { + nullableScalarInnerFromJSON, + nullableScalarInnerToJSON, +} from '../models/NullableScalarInner'; + +export type Value$5Wire = NullableScalarInner$Wire | null; + +export type Value$5 = NullableScalarInner | null; + +export function convert$5FromJSON(json: NullableScalarInner$Wire | null): Value$5 { + return nullableScalarInnerFromJSON(json); +} + +export function convert$5ToJSON(value: NullableScalarInner | null): Value$5Wire { + return nullableScalarInnerToJSON(value); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$6.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$6.ts.golden new file mode 100644 index 000000000..39b326ff6 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$6.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { NullableScalarInner, NullableScalarInner$Wire } from '../models/NullableScalarInner'; +import { convert$5FromJSON, convert$5ToJSON } from './valueConversion$5'; + +export type Value$6Wire = NullableScalarInner$Wire | null; + +export type Value$6 = NullableScalarInner | null; + +export function convert$6FromJSON(json: NullableScalarInner$Wire | null): Value$6 { + return convert$5FromJSON(json); +} + +export function convert$6ToJSON(value: NullableScalarInner | null): Value$6Wire { + return convert$5ToJSON(value); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$7.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$7.ts.golden new file mode 100644 index 000000000..140a41e00 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$7.ts.golden @@ -0,0 +1,25 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { NullableScalarInner, NullableScalarInner$Wire } from '../models/NullableScalarInner'; +import { convert$5FromJSON, convert$5ToJSON } from './valueConversion$5'; + +export type Value$7Wire = NullableScalarInner$Wire | null; + +export type Value$7 = NullableScalarInner | null; + +export function convert$7FromJSON(json: NullableScalarInner$Wire | null): Value$7 { + if (json == null) { + return null; + } + return convert$5FromJSON(json); +} + +export function convert$7ToJSON(value: NullableScalarInner | null): Value$7Wire { + if (value == null) { + return null; + } + return convert$5ToJSON(value); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$8.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$8.ts.golden new file mode 100644 index 000000000..658b0543a --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$8.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { NullableScalarInner, NullableScalarInner$Wire } from '../models/NullableScalarInner'; +import { convert$9FromJSON, convert$9ToJSON } from './valueConversion$9'; + +export type Value$8Wire = readonly (NullableScalarInner$Wire | null)[]; + +export type Value$8 = readonly (NullableScalarInner | null)[]; + +export function convert$8FromJSON(json: ReadonlyArray): Value$8 { + return json.map(convert$9FromJSON); +} + +export function convert$8ToJSON(value: ReadonlyArray): Value$8Wire { + return value.map(convert$9ToJSON); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$9.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$9.ts.golden new file mode 100644 index 000000000..c10c565ea --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/runtime/valueConversion$9.ts.golden @@ -0,0 +1,25 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { NullableScalarInner, NullableScalarInner$Wire } from '../models/NullableScalarInner'; +import { convert$10FromJSON, convert$10ToJSON } from './valueConversion$10'; + +export type Value$9Wire = NullableScalarInner$Wire | null; + +export type Value$9 = NullableScalarInner | null; + +export function convert$9FromJSON(json: NullableScalarInner$Wire | null): Value$9 { + if (json == null) { + return null; + } + return convert$10FromJSON(json); +} + +export function convert$9ToJSON(value: NullableScalarInner | null): Value$9Wire { + if (value == null) { + return null; + } + return convert$10ToJSON(value); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/tsconfig.esm.json.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/tsconfig.esm.json.golden new file mode 100644 index 000000000..769c716fd --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/tsconfig.esm.json.golden @@ -0,0 +1,7 @@ +{ + "compilerOptions": { + "module": "ES2020", + "outDir": "dist/esm" + }, + "extends": "./tsconfig.json" +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/tsconfig.json.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/tsconfig.json.golden new file mode 100644 index 000000000..9f125842c --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/tsconfig.json.golden @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "declaration": true, + "declarationMap": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "lib": ["ES2020", "DOM"], + "module": "ES2020", + "moduleResolution": "bundler", + "outDir": "./dist", + "resolveJsonModule": true, + "rootDir": "./", + "skipLibCheck": true, + "sourceMap": true, + "strict": true, + "target": "ES2020", + "typeRoots": ["node_modules/@types"] + }, + "exclude": ["dist", "node_modules"], + "include": ["**/*.ts"] +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/vite.config.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/vite.config.ts.golden new file mode 100644 index 000000000..065569dd3 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-first-match/vite.config.ts.golden @@ -0,0 +1,20 @@ +import { defineConfig } from 'vite-plus'; + +export default defineConfig({ + fmt: { + singleQuote: true, + ignorePatterns: ['dist/**'], + }, + lint: { + options: { + typeAware: true, + typeCheck: true, + }, + ignorePatterns: ['node_modules', 'dist'], + }, + pack: { + entry: ['index.ts'], + dts: true, + format: ['esm'], + }, +}); diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/README.md.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/README.md.golden new file mode 100644 index 000000000..9a0608684 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/README.md.golden @@ -0,0 +1,264 @@ +# untagged-union-conversion + +Generated API client + +**Version:** 1.0.0 + +## Overview + +This package provides a TypeScript/JavaScript client for the Untagged union conversion API. It uses the native [Fetch API](https://fetch.spec.whatwg.org/) for HTTP requests and works in both Node.js and browser environments. + +## Features + +- ✨ **Type-safe** - Full TypeScript support with generated types +- 🚀 **Modern** - Uses native Fetch API, no external HTTP dependencies +- 🔧 **Configurable** - Flexible configuration options +- 🎯 **Middleware** - Support for request/response interceptors +- 📦 **Tree-shakeable** - Import only what you need +- 🌐 **Universal** - Works in Node.js and browsers + +## Installation + +### From npm (published package) + +```bash +npm install untagged-union-conversion +``` + +### From local path (development) + +Add the package to your `package.json` using the `file:` protocol: + +```json +{ + "dependencies": { + "untagged-union-conversion": "file:../../path/to/generated/package" + } +} +``` + +Then run: + +```bash +npm install +``` + +## Quick Start + +```typescript +import { Configuration, ConversionApi } from 'untagged-union-conversion'; + +// Create a configuration +const config = new Configuration({ + basePath: 'https://api.example.com', + headers: { + Authorization: 'Bearer YOUR_TOKEN', + }, +}); + +// Initialize the API client +const api = new ConversionApi(config); + +// Make API calls +try { + const result = await api.someMethod(); + console.log(result); +} catch (error) { + console.error('API Error:', error); +} +``` + +## Configuration + +The `Configuration` class accepts the following options: + +```typescript +interface ConfigurationParameters { + /** Base URL for API requests */ + basePath?: string; + + /** Custom fetch implementation */ + fetchApi?: typeof fetch; + + /** Request/response middleware */ + middleware?: Middleware[]; + + /** Custom query string serializer */ + queryParamsStringify?: (params: HTTPQuery) => string; + + /** Default headers for all requests */ + headers?: Record; + + /** Credentials mode for requests */ + credentials?: RequestCredentials; +} +``` + +### Example with custom configuration + +```typescript +const config = new Configuration({ + basePath: 'https://api.example.com', + headers: { + 'X-API-Key': 'your-api-key', + 'Content-Type': 'application/json', + }, + credentials: 'include', +}); +``` + +## Middleware + +Add custom middleware to intercept requests and responses: + +```typescript +import { Configuration, Middleware } from 'untagged-union-conversion'; + +const loggingMiddleware: Middleware = { + pre: async (context) => { + console.log('Request:', context.url); + return context; + }, + post: async (context) => { + console.log('Response:', context.response.status); + return context.response; + }, + onError: async (context) => { + console.error('Error:', context.error); + return undefined; + }, +}; + +const config = new Configuration({ + basePath: 'https://api.example.com', + middleware: [loggingMiddleware], +}); +``` + +## Error Handling + +The client throws typed errors for different failure scenarios: + +```typescript +import { ResponseError, FetchError, RequiredError } from 'untagged-union-conversion'; + +try { + const result = await api.someMethod(); +} catch (error) { + if (error instanceof ResponseError) { + // HTTP error response (4xx, 5xx) + console.error('HTTP Error:', error.response.status); + } else if (error instanceof FetchError) { + // Network or fetch error + console.error('Network Error:', error.cause); + } else if (error instanceof RequiredError) { + // Missing required parameter + console.error('Missing field:', error.field); + } +} +``` + +## API Reference + +This package exports the following: + +- **Configuration** - Client configuration class +- **BaseAPI** - Base class for all API clients +- **API Classes** - Generated API client classes (e.g., `UserApi`, `PostApi`) +- **Models** - Generated TypeScript interfaces for request/response types +- **Errors** - `ResponseError`, `FetchError`, `RequiredError` +- **Types** - TypeScript type definitions + +## Development + +### Building + +To build the package: + +```bash +npm install +npm run build +``` + +This will compile TypeScript to JavaScript in the `dist/` directory. + +### Building for ESM + +To build ES modules: + +```bash +npm run build +``` + +### Formatting and validation + +After generating or regenerating this client, install its development dependencies, +run all checks on the generated files and build the library: + +```bash +vp install +vp check +vp pack +``` + +The `check` and `build` npm scripts run the same Vite+ commands. +Use `vp fmt` (or the `fmt` script) to format any subsequent manual edits. +`vp check` validates formatting, lint rules, and types without modifying files. +Build output in `dist/` is excluded from formatting. + +## TypeScript Support + +This package includes TypeScript type definitions. No additional `@types` package is needed. + +### TypeScript Configuration + +This package works with standard TypeScript configurations. If you're using a bundler-based setup, you may want to configure: + +```json +{ + "compilerOptions": { + "moduleResolution": "bundler" + } +} +``` + +### Type Imports + +```typescript +import type { User, CreateUserRequest } from 'untagged-union-conversion'; + +const user: User = { + id: 1, + name: 'John Doe', + email: 'john@example.com', +}; +``` + +## Browser Support + +This package uses the native Fetch API, which is supported in: + +- Chrome 42+ +- Firefox 39+ +- Safari 10.1+ +- Edge 14+ +- Node.js 18+ (native fetch) +- Node.js <18 (with `node-fetch` polyfill) + +For older browsers, you may need to include a fetch polyfill. + +## License + +This is an auto-generated API client. Please refer to your API documentation for license information. + +## Support + +For issues related to the API itself, please contact the API provider. + +For issues with this generated client, please check the OpenAPI specification used to generate it. + +--- + +**Generated by OpenAPI Generator** + +API Version: 1.0.0 diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/apis/ConversionApi.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/apis/ConversionApi.ts.golden new file mode 100644 index 000000000..b909e89d2 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/apis/ConversionApi.ts.golden @@ -0,0 +1,786 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { ExchangeMultipartMultipartRequestBody } from '../models/ExchangeMultipartMultipartRequestBody'; +import type { NullableDetail } from '../models/NullableDetail'; +import { nullableDetailToJSON } from '../models/NullableDetail'; +import type { NullableScalarInner } from '../models/NullableScalarInner'; +import { nullableScalarInnerToJSON } from '../models/NullableScalarInner'; +import type { SendOptionalInlineScalarRequest } from '../models/SendOptionalInlineScalarRequest'; +import { sendOptionalInlineScalarRequestToJSON } from '../models/SendOptionalInlineScalarRequest'; +import type { Tool, Tool$Wire } from '../models/Tool'; +import type { + Configuration, + HTTPQuery, + HttpSuccessResponse, + InitOverrideFunction, +} from '../runtime/runtime'; +import { + BaseAPI, + DefaultConfig, + JSONApiResponse, + RequiredError, + ResponseError, + VoidApiResponse, + multipartHeaderValue, + uploadFileData, + uploadFileFilename, +} from '../runtime/runtime'; +import { convert$22FromJSON, convert$22ToJSON } from '../runtime/valueConversion$22'; +import { convert$24FromJSON, convert$24ToJSON } from '../runtime/valueConversion$24'; + +export interface ApiExchangeMatrixRequest { + body: Tool[][]; +} + +export interface ApiExchangeMultipartRequest { + body: ExchangeMultipartMultipartRequestBody; +} + +export interface ApiExchangeNullableRequest { + body: Tool[] | null; +} + +export interface ApiSendOptionalDetailRequest { + body?: NullableDetail; +} + +export interface ApiSendOptionalInlineScalarRequest { + body?: SendOptionalInlineScalarRequest; +} + +export interface ApiSendOptionalScalarRequest { + body?: NullableScalarInner; +} + +export interface ApiSendRequiredScalarRequest { + body: NullableScalarInner; +} + +export type ExchangeMatrixRawResponse = + | (JSONApiResponse & { status: 200 }) + | (JSONApiResponse & HttpSuccessResponse); +export type ExchangeMultipartRawResponse = + | (VoidApiResponse & { status: 204 }) + | (VoidApiResponse & HttpSuccessResponse); +export type ExchangeNullableRawResponse = + | (JSONApiResponse & { status: 200 }) + | (JSONApiResponse & HttpSuccessResponse); +export type SendOptionalDetailRawResponse = + | (VoidApiResponse & { status: 204 }) + | (VoidApiResponse & HttpSuccessResponse); +export type SendOptionalInlineScalarRawResponse = + | (VoidApiResponse & { status: 204 }) + | (VoidApiResponse & HttpSuccessResponse); +export type SendOptionalScalarRawResponse = + | (VoidApiResponse & { status: 204 }) + | (VoidApiResponse & HttpSuccessResponse); +export type SendRequiredScalarRawResponse = + | (VoidApiResponse & { status: 204 }) + | (VoidApiResponse & HttpSuccessResponse); + +export type ExchangeMatrixErrorDetail = + | { + kind: 'badRequest'; + status: 400; + raw: Response; + value(): Promise; + } + | { + kind: 'unexpected'; + status: number; + raw: Response; + value(): Promise; + }; + +export class ExchangeMatrixError extends ResponseError { + readonly detail: ExchangeMatrixErrorDetail; + + constructor( + response: Response, + detail: ExchangeMatrixErrorDetail, + msg = 'Response returned an error code', + ) { + super(response, msg); + this.detail = detail; + } +} + +export type ExchangeMultipartErrorDetail = { + kind: 'unexpected'; + status: number; + raw: Response; + value(): Promise; +}; + +export class ExchangeMultipartError extends ResponseError { + readonly detail: ExchangeMultipartErrorDetail; + + constructor( + response: Response, + detail: ExchangeMultipartErrorDetail, + msg = 'Response returned an error code', + ) { + super(response, msg); + this.detail = detail; + } +} + +export type ExchangeNullableErrorDetail = { + kind: 'unexpected'; + status: number; + raw: Response; + value(): Promise; +}; + +export class ExchangeNullableError extends ResponseError { + readonly detail: ExchangeNullableErrorDetail; + + constructor( + response: Response, + detail: ExchangeNullableErrorDetail, + msg = 'Response returned an error code', + ) { + super(response, msg); + this.detail = detail; + } +} + +export type SendOptionalDetailErrorDetail = { + kind: 'unexpected'; + status: number; + raw: Response; + value(): Promise; +}; + +export class SendOptionalDetailError extends ResponseError { + readonly detail: SendOptionalDetailErrorDetail; + + constructor( + response: Response, + detail: SendOptionalDetailErrorDetail, + msg = 'Response returned an error code', + ) { + super(response, msg); + this.detail = detail; + } +} + +export type SendOptionalInlineScalarErrorDetail = { + kind: 'unexpected'; + status: number; + raw: Response; + value(): Promise; +}; + +export class SendOptionalInlineScalarError extends ResponseError { + readonly detail: SendOptionalInlineScalarErrorDetail; + + constructor( + response: Response, + detail: SendOptionalInlineScalarErrorDetail, + msg = 'Response returned an error code', + ) { + super(response, msg); + this.detail = detail; + } +} + +export type SendOptionalScalarErrorDetail = { + kind: 'unexpected'; + status: number; + raw: Response; + value(): Promise; +}; + +export class SendOptionalScalarError extends ResponseError { + readonly detail: SendOptionalScalarErrorDetail; + + constructor( + response: Response, + detail: SendOptionalScalarErrorDetail, + msg = 'Response returned an error code', + ) { + super(response, msg); + this.detail = detail; + } +} + +export type SendRequiredScalarErrorDetail = { + kind: 'unexpected'; + status: number; + raw: Response; + value(): Promise; +}; + +export class SendRequiredScalarError extends ResponseError { + readonly detail: SendRequiredScalarErrorDetail; + + constructor( + response: Response, + detail: SendRequiredScalarErrorDetail, + msg = 'Response returned an error code', + ) { + super(response, msg); + this.detail = detail; + } +} + +export interface ConversionApiInterface { + exchangeMatrixRaw: ( + requestParameters: ApiExchangeMatrixRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ) => Promise; + exchangeMatrix: ( + requestParameters: ApiExchangeMatrixRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ) => Promise; + exchangeMultipartRaw: ( + requestParameters: ApiExchangeMultipartRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ) => Promise; + exchangeMultipart: ( + requestParameters: ApiExchangeMultipartRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ) => Promise; + exchangeNullableRaw: ( + requestParameters: ApiExchangeNullableRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ) => Promise; + exchangeNullable: ( + requestParameters: ApiExchangeNullableRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ) => Promise; + sendOptionalDetailRaw: ( + requestParameters: ApiSendOptionalDetailRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ) => Promise; + sendOptionalDetail: ( + requestParameters: ApiSendOptionalDetailRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ) => Promise; + sendOptionalInlineScalarRaw: ( + requestParameters: ApiSendOptionalInlineScalarRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ) => Promise; + sendOptionalInlineScalar: ( + requestParameters: ApiSendOptionalInlineScalarRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ) => Promise; + sendOptionalScalarRaw: ( + requestParameters: ApiSendOptionalScalarRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ) => Promise; + sendOptionalScalar: ( + requestParameters: ApiSendOptionalScalarRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ) => Promise; + sendRequiredScalarRaw: ( + requestParameters: ApiSendRequiredScalarRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ) => Promise; + sendRequiredScalar: ( + requestParameters: ApiSendRequiredScalarRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ) => Promise; +} + +export class ConversionApi extends BaseAPI implements ConversionApiInterface { + /** + * Initialize the API client + */ + constructor(configuration?: Configuration) { + super(configuration ?? DefaultConfig); + } + + async exchangeMatrixRaw( + requestParameters: ApiExchangeMatrixRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ): Promise { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { + throw new RequiredError( + 'body', + 'Required parameter "body" was null or undefined when calling exchangeMatrixRaw().', + ); + } + // Build path with path parameters + const urlPath = `/matrix`; + // Build query parameters + const queryParameters: HTTPQuery = {}; + // Build headers + const headerParameters: Record = { + 'Content-Type': 'application/json', + }; + + // Prepare request body + const requestBody = convert$22ToJSON(requestParameters['body']); + // Make request + const response = await this.request( + { + path: urlPath, + method: 'POST', + headers: headerParameters, + query: queryParameters, + body: requestBody, + }, + initOverrides, + ); + + // Handle responses + if (!(response.status >= 200 && response.status < 300)) { + const errorRaw = response.clone(); + if (response.status === 400) { + let errorValue: Promise | undefined; + throw new ExchangeMatrixError(response, { + kind: 'badRequest', + status: response.status as 400, + raw: response, + value: () => { + errorValue ??= new JSONApiResponse(errorRaw.clone(), (json) => { + return convert$22FromJSON(json as Tool$Wire[][]); + }).value(); + return errorValue; + }, + }); + } else { + let errorValue: Promise | undefined; + throw new ExchangeMatrixError(response, { + kind: 'unexpected', + status: response.status, + raw: response, + value: () => { + errorValue ??= errorRaw.clone().blob(); + return errorValue; + }, + }); + } + } + if (response.status === 200) { + return new JSONApiResponse(response, (json) => { + return convert$22FromJSON(json as Tool$Wire[][]); + }) as JSONApiResponse & { status: 200 }; + } else { + return new JSONApiResponse(response) as JSONApiResponse & HttpSuccessResponse; + } + } + + async exchangeMatrix( + requestParameters: ApiExchangeMatrixRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ): Promise { + const response = await this.exchangeMatrixRaw(requestParameters, initOverrides); + return (await response.value()) as Tool[][]; + } + + async exchangeMultipartRaw( + requestParameters: ApiExchangeMultipartRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ): Promise { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { + throw new RequiredError( + 'body', + 'Required parameter "body" was null or undefined when calling exchangeMultipartRaw().', + ); + } + // Build path with path parameters + const urlPath = `/multipart`; + // Build query parameters + const queryParameters: HTTPQuery = {}; + // Build headers + const headerParameters: Record = {}; + + // Prepare request body + const multipartBody = requestParameters['body']; + const multipartBoundary = '----openapi-nexus-' + Math.random().toString(16).slice(2); + const multipartChunks: Array = []; + headerParameters['Content-Type'] = 'multipart/form-data; boundary=' + multipartBoundary; + const multipartValue$0 = multipartBody['payloadValues']; + multipartChunks.push( + '--', + multipartBoundary, + '\r\nContent-Disposition: form-data; name="payload_values"\r\nContent-Type: application/json\r\n\r\n', + ); + multipartChunks.push(JSON.stringify(multipartValue$0)); + multipartChunks.push('\r\n'); + const multipartValue$1 = multipartBody['uploadFile']; + if (multipartValue$1 !== undefined && multipartValue$1 !== null) { + const multipartFilename$1 = uploadFileFilename(multipartValue$1, 'upload_file'); + multipartChunks.push( + '--', + multipartBoundary, + '\r\nContent-Disposition: form-data; name="upload_file"; filename="', + multipartHeaderValue(multipartFilename$1), + '"\r\nContent-Type: application/octet-stream\r\n\r\n', + ); + multipartChunks.push(uploadFileData(multipartValue$1)); + multipartChunks.push('\r\n'); + } + multipartChunks.push('--' + multipartBoundary + '--\r\n'); + const requestBody = new Blob(multipartChunks); + // Make request + const response = await this.request( + { + path: urlPath, + method: 'POST', + headers: headerParameters, + query: queryParameters, + body: requestBody, + }, + initOverrides, + ); + + // Handle responses + if (!(response.status >= 200 && response.status < 300)) { + const errorRaw = response.clone(); + { + let errorValue: Promise | undefined; + throw new ExchangeMultipartError(response, { + kind: 'unexpected', + status: response.status, + raw: response, + value: () => { + errorValue ??= errorRaw.clone().blob(); + return errorValue; + }, + }); + } + } + if (response.status === 204) { + return new VoidApiResponse(response) as VoidApiResponse & { status: 204 }; + } else { + return new VoidApiResponse(response) as VoidApiResponse & HttpSuccessResponse; + } + } + + async exchangeMultipart( + requestParameters: ApiExchangeMultipartRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ): Promise { + const response = await this.exchangeMultipartRaw(requestParameters, initOverrides); + return await response.value(); + } + + async exchangeNullableRaw( + requestParameters: ApiExchangeNullableRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ): Promise { + if (requestParameters['body'] === undefined) { + throw new RequiredError( + 'body', + 'Required parameter "body" was undefined when calling exchangeNullableRaw().', + ); + } + // Build path with path parameters + const urlPath = `/nullable`; + // Build query parameters + const queryParameters: HTTPQuery = {}; + // Build headers + const headerParameters: Record = { + 'Content-Type': 'application/json', + }; + + // Prepare request body + const requestBody = convert$24ToJSON(requestParameters['body']); + // Make request + const response = await this.request( + { + path: urlPath, + method: 'POST', + headers: headerParameters, + query: queryParameters, + body: requestBody, + }, + initOverrides, + ); + + // Handle responses + if (!(response.status >= 200 && response.status < 300)) { + const errorRaw = response.clone(); + { + let errorValue: Promise | undefined; + throw new ExchangeNullableError(response, { + kind: 'unexpected', + status: response.status, + raw: response, + value: () => { + errorValue ??= errorRaw.clone().blob(); + return errorValue; + }, + }); + } + } + if (response.status === 200) { + return new JSONApiResponse(response, (json) => { + return convert$24FromJSON(json as Tool$Wire[] | null); + }) as JSONApiResponse & { status: 200 }; + } else { + return new JSONApiResponse(response) as JSONApiResponse & HttpSuccessResponse; + } + } + + async exchangeNullable( + requestParameters: ApiExchangeNullableRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ): Promise { + const response = await this.exchangeNullableRaw(requestParameters, initOverrides); + return (await response.value()) as Tool[] | null; + } + + async sendOptionalDetailRaw( + requestParameters: ApiSendOptionalDetailRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ): Promise { + // Build path with path parameters + const urlPath = `/optional-detail`; + // Build query parameters + const queryParameters: HTTPQuery = {}; + // Build headers + const headerParameters: Record = { + 'Content-Type': 'application/json', + }; + + // Prepare request body + const requestBody = (() => { + const bodyValue = requestParameters['body']; + if (bodyValue === undefined) { + return undefined; + } + return nullableDetailToJSON(bodyValue); + })(); + // Make request + const response = await this.request( + { + path: urlPath, + method: 'POST', + headers: headerParameters, + query: queryParameters, + body: requestBody, + }, + initOverrides, + ); + + // Handle responses + if (!(response.status >= 200 && response.status < 300)) { + const errorRaw = response.clone(); + { + let errorValue: Promise | undefined; + throw new SendOptionalDetailError(response, { + kind: 'unexpected', + status: response.status, + raw: response, + value: () => { + errorValue ??= errorRaw.clone().blob(); + return errorValue; + }, + }); + } + } + if (response.status === 204) { + return new VoidApiResponse(response) as VoidApiResponse & { status: 204 }; + } else { + return new VoidApiResponse(response) as VoidApiResponse & HttpSuccessResponse; + } + } + + async sendOptionalDetail( + requestParameters: ApiSendOptionalDetailRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ): Promise { + const response = await this.sendOptionalDetailRaw(requestParameters, initOverrides); + return await response.value(); + } + + async sendOptionalInlineScalarRaw( + requestParameters: ApiSendOptionalInlineScalarRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ): Promise { + // Build path with path parameters + const urlPath = `/optional-inline-scalar`; + // Build query parameters + const queryParameters: HTTPQuery = {}; + // Build headers + const headerParameters: Record = { + 'Content-Type': 'application/json', + }; + + // Prepare request body + const requestBody = (() => { + const bodyValue = requestParameters['body']; + if (bodyValue === undefined) { + return undefined; + } + return sendOptionalInlineScalarRequestToJSON(bodyValue); + })(); + // Make request + const response = await this.request( + { + path: urlPath, + method: 'POST', + headers: headerParameters, + query: queryParameters, + body: requestBody, + }, + initOverrides, + ); + + // Handle responses + if (!(response.status >= 200 && response.status < 300)) { + const errorRaw = response.clone(); + { + let errorValue: Promise | undefined; + throw new SendOptionalInlineScalarError(response, { + kind: 'unexpected', + status: response.status, + raw: response, + value: () => { + errorValue ??= errorRaw.clone().blob(); + return errorValue; + }, + }); + } + } + if (response.status === 204) { + return new VoidApiResponse(response) as VoidApiResponse & { status: 204 }; + } else { + return new VoidApiResponse(response) as VoidApiResponse & HttpSuccessResponse; + } + } + + async sendOptionalInlineScalar( + requestParameters: ApiSendOptionalInlineScalarRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ): Promise { + const response = await this.sendOptionalInlineScalarRaw(requestParameters, initOverrides); + return await response.value(); + } + + async sendOptionalScalarRaw( + requestParameters: ApiSendOptionalScalarRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ): Promise { + // Build path with path parameters + const urlPath = `/optional-scalar`; + // Build query parameters + const queryParameters: HTTPQuery = {}; + // Build headers + const headerParameters: Record = { + 'Content-Type': 'application/json', + }; + + // Prepare request body + const requestBody = (() => { + const bodyValue = requestParameters['body']; + if (bodyValue === undefined) { + return undefined; + } + return nullableScalarInnerToJSON(bodyValue); + })(); + // Make request + const response = await this.request( + { + path: urlPath, + method: 'POST', + headers: headerParameters, + query: queryParameters, + body: requestBody, + }, + initOverrides, + ); + + // Handle responses + if (!(response.status >= 200 && response.status < 300)) { + const errorRaw = response.clone(); + { + let errorValue: Promise | undefined; + throw new SendOptionalScalarError(response, { + kind: 'unexpected', + status: response.status, + raw: response, + value: () => { + errorValue ??= errorRaw.clone().blob(); + return errorValue; + }, + }); + } + } + if (response.status === 204) { + return new VoidApiResponse(response) as VoidApiResponse & { status: 204 }; + } else { + return new VoidApiResponse(response) as VoidApiResponse & HttpSuccessResponse; + } + } + + async sendOptionalScalar( + requestParameters: ApiSendOptionalScalarRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ): Promise { + const response = await this.sendOptionalScalarRaw(requestParameters, initOverrides); + return await response.value(); + } + + async sendRequiredScalarRaw( + requestParameters: ApiSendRequiredScalarRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ): Promise { + if (requestParameters['body'] === undefined) { + throw new RequiredError( + 'body', + 'Required parameter "body" was undefined when calling sendRequiredScalarRaw().', + ); + } + // Build path with path parameters + const urlPath = `/required-scalar`; + // Build query parameters + const queryParameters: HTTPQuery = {}; + // Build headers + const headerParameters: Record = { + 'Content-Type': 'application/json', + }; + + // Prepare request body + const requestBody = nullableScalarInnerToJSON(requestParameters['body']); + // Make request + const response = await this.request( + { + path: urlPath, + method: 'POST', + headers: headerParameters, + query: queryParameters, + body: requestBody, + }, + initOverrides, + ); + + // Handle responses + if (!(response.status >= 200 && response.status < 300)) { + const errorRaw = response.clone(); + { + let errorValue: Promise | undefined; + throw new SendRequiredScalarError(response, { + kind: 'unexpected', + status: response.status, + raw: response, + value: () => { + errorValue ??= errorRaw.clone().blob(); + return errorValue; + }, + }); + } + } + if (response.status === 204) { + return new VoidApiResponse(response) as VoidApiResponse & { status: 204 }; + } else { + return new VoidApiResponse(response) as VoidApiResponse & HttpSuccessResponse; + } + } + + async sendRequiredScalar( + requestParameters: ApiSendRequiredScalarRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ): Promise { + const response = await this.sendRequiredScalarRaw(requestParameters, initOverrides); + return await response.value(); + } +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/apis/index.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/apis/index.ts.golden new file mode 100644 index 000000000..24b35e481 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/apis/index.ts.golden @@ -0,0 +1,39 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export type { + ApiExchangeMatrixRequest, + ExchangeMatrixRawResponse, + ExchangeMatrixErrorDetail, + ApiExchangeMultipartRequest, + ExchangeMultipartRawResponse, + ExchangeMultipartErrorDetail, + ApiExchangeNullableRequest, + ExchangeNullableRawResponse, + ExchangeNullableErrorDetail, + ApiSendOptionalDetailRequest, + SendOptionalDetailRawResponse, + SendOptionalDetailErrorDetail, + ApiSendOptionalInlineScalarRequest, + SendOptionalInlineScalarRawResponse, + SendOptionalInlineScalarErrorDetail, + ApiSendOptionalScalarRequest, + SendOptionalScalarRawResponse, + SendOptionalScalarErrorDetail, + ApiSendRequiredScalarRequest, + SendRequiredScalarRawResponse, + SendRequiredScalarErrorDetail, + ConversionApiInterface, +} from './ConversionApi'; +export { + ConversionApi, + ExchangeMatrixError, + ExchangeMultipartError, + ExchangeNullableError, + SendOptionalDetailError, + SendOptionalInlineScalarError, + SendOptionalScalarError, + SendRequiredScalarError, +} from './ConversionApi'; diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/index.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/index.ts.golden new file mode 100644 index 000000000..8ba5e6eb9 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/index.ts.golden @@ -0,0 +1,8 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export * from './runtime/runtime'; +export * from './apis'; +export * from './models'; diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/Adjacent.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/Adjacent.ts.golden new file mode 100644 index 000000000..a3580a4b6 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/Adjacent.ts.golden @@ -0,0 +1,43 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail, Detail$Wire } from './Detail'; +import { detailFromJSON, detailToJSON } from './Detail'; + +export type Adjacent$Wire = + | globalThis.Record<'event_type', 'empty'> + | (globalThis.Record<'event_type', 'data'> & globalThis.Record<'event_data', Detail$Wire>); + +export type Adjacent = + | globalThis.Record<'eventType', 'empty'> + | (globalThis.Record<'eventType', 'data'> & globalThis.Record<'eventData', Detail>); + +export function adjacentFromJSON(json: Adjacent$Wire): Adjacent { + if (json['event_type'] === 'empty') { + return { ['eventType']: 'empty' } as Adjacent; + } + if (json['event_type'] === 'data') { + return { + ['eventType']: 'data', + ['eventData']: detailFromJSON( + (json as globalThis.Record<'event_data', Detail$Wire>)['event_data'], + ), + } as Adjacent; + } + throw new Error('Unknown variant'); +} + +export function adjacentToJSON(value: Adjacent): Adjacent$Wire { + if (value['eventType'] === 'empty') { + return { ['event_type']: 'empty' } as Adjacent$Wire; + } + if (value['eventType'] === 'data') { + return { + ['event_type']: 'data', + ['event_data']: detailToJSON((value as globalThis.Record<'eventData', Detail>)['eventData']), + } as Adjacent$Wire; + } + throw new Error('Unknown variant'); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/AdjacentContainer.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/AdjacentContainer.ts.golden new file mode 100644 index 000000000..7ab0d2d56 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/AdjacentContainer.ts.golden @@ -0,0 +1,55 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail$Wire } from './Detail'; + +export type AdjacentContainer$Wire = + | globalThis.Record<'event_type', 'empty'> + | (globalThis.Record<'event_type', 'data'> & + globalThis.Record< + 'event_data', + readonly (globalThis.Record | string)[] + >); + +export type AdjacentContainer = + | globalThis.Record<'eventType', 'empty'> + | (globalThis.Record<'eventType', 'data'> & + globalThis.Record<'eventData', readonly (globalThis.Record | string)[]>); + +export function adjacentContainerFromJSON(json: AdjacentContainer$Wire): AdjacentContainer { + if (json['event_type'] === 'empty') { + return { ['eventType']: 'empty' } as AdjacentContainer; + } + if (json['event_type'] === 'data') { + return { + ['eventType']: 'data', + ['eventData']: ( + json as globalThis.Record< + 'event_data', + readonly (globalThis.Record | string)[] + > + )['event_data'], + } as AdjacentContainer; + } + throw new Error('Unknown variant'); +} + +export function adjacentContainerToJSON(value: AdjacentContainer): AdjacentContainer$Wire { + if (value['eventType'] === 'empty') { + return { ['event_type']: 'empty' } as AdjacentContainer$Wire; + } + if (value['eventType'] === 'data') { + return { + ['event_type']: 'data', + ['event_data']: ( + value as globalThis.Record< + 'eventData', + readonly (globalThis.Record | string)[] + > + )['eventData'], + } as AdjacentContainer$Wire; + } + throw new Error('Unknown variant'); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/AdjacentContainerEmpty.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/AdjacentContainerEmpty.ts.golden new file mode 100644 index 000000000..b59330795 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/AdjacentContainerEmpty.ts.golden @@ -0,0 +1,28 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface AdjacentContainerEmpty$Wire { + readonly event_type: 'empty'; +} + +export interface AdjacentContainerEmpty { + readonly eventType: 'empty'; +} + +export function adjacentContainerEmptyFromJSON( + json: AdjacentContainerEmpty$Wire, +): AdjacentContainerEmpty { + return { + eventType: json.event_type, + }; +} + +export function adjacentContainerEmptyToJSON( + value: AdjacentContainerEmpty, +): AdjacentContainerEmpty$Wire { + return { + event_type: value.eventType, + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/AdjacentEmpty.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/AdjacentEmpty.ts.golden new file mode 100644 index 000000000..665908893 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/AdjacentEmpty.ts.golden @@ -0,0 +1,24 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface AdjacentEmpty$Wire { + readonly event_type: 'empty'; +} + +export interface AdjacentEmpty { + readonly eventType: 'empty'; +} + +export function adjacentEmptyFromJSON(json: AdjacentEmpty$Wire): AdjacentEmpty { + return { + eventType: json.event_type, + }; +} + +export function adjacentEmptyToJSON(value: AdjacentEmpty): AdjacentEmpty$Wire { + return { + event_type: value.eventType, + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/AdjacentOrNumber.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/AdjacentOrNumber.ts.golden new file mode 100644 index 000000000..dddd9f8cf --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/AdjacentOrNumber.ts.golden @@ -0,0 +1,18 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Adjacent$Wire } from './Adjacent'; + +export type AdjacentOrNumber$Wire = Adjacent$Wire | number; + +export type AdjacentOrNumber = AdjacentOrNumber$Wire; + +export function adjacentOrNumberFromJSON(json: AdjacentOrNumber$Wire): AdjacentOrNumber { + return json; +} + +export function adjacentOrNumberToJSON(value: AdjacentOrNumber): AdjacentOrNumber$Wire { + return value; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/AdjacentPrimitive.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/AdjacentPrimitive.ts.golden new file mode 100644 index 000000000..11b72f575 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/AdjacentPrimitive.ts.golden @@ -0,0 +1,38 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export type AdjacentPrimitive$Wire = + | globalThis.Record<'event_type', 'empty'> + | (globalThis.Record<'event_type', 'data'> & globalThis.Record<'event_data', readonly number[]>); + +export type AdjacentPrimitive = + | globalThis.Record<'eventType', 'empty'> + | (globalThis.Record<'eventType', 'data'> & globalThis.Record<'eventData', readonly number[]>); + +export function adjacentPrimitiveFromJSON(json: AdjacentPrimitive$Wire): AdjacentPrimitive { + if (json['event_type'] === 'empty') { + return { ['eventType']: 'empty' } as AdjacentPrimitive; + } + if (json['event_type'] === 'data') { + return { + ['eventType']: 'data', + ['eventData']: (json as globalThis.Record<'event_data', readonly number[]>)['event_data'], + } as AdjacentPrimitive; + } + throw new Error('Unknown variant'); +} + +export function adjacentPrimitiveToJSON(value: AdjacentPrimitive): AdjacentPrimitive$Wire { + if (value['eventType'] === 'empty') { + return { ['event_type']: 'empty' } as AdjacentPrimitive$Wire; + } + if (value['eventType'] === 'data') { + return { + ['event_type']: 'data', + ['event_data']: (value as globalThis.Record<'eventData', readonly number[]>)['eventData'], + } as AdjacentPrimitive$Wire; + } + throw new Error('Unknown variant'); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/AdjacentPrimitiveEmpty.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/AdjacentPrimitiveEmpty.ts.golden new file mode 100644 index 000000000..bcc6ac635 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/AdjacentPrimitiveEmpty.ts.golden @@ -0,0 +1,28 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface AdjacentPrimitiveEmpty$Wire { + readonly event_type: 'empty'; +} + +export interface AdjacentPrimitiveEmpty { + readonly eventType: 'empty'; +} + +export function adjacentPrimitiveEmptyFromJSON( + json: AdjacentPrimitiveEmpty$Wire, +): AdjacentPrimitiveEmpty { + return { + eventType: json.event_type, + }; +} + +export function adjacentPrimitiveEmptyToJSON( + value: AdjacentPrimitiveEmpty, +): AdjacentPrimitiveEmpty$Wire { + return { + event_type: value.eventType, + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/AliasChoice.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/AliasChoice.ts.golden new file mode 100644 index 000000000..8f487ccf6 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/AliasChoice.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail$Wire } from './Detail'; +import type { ValueAlias$Wire } from './ValueAlias'; + +export type AliasChoice$Wire = ValueAlias$Wire | Detail$Wire; + +export type AliasChoice = AliasChoice$Wire; + +export function aliasChoiceFromJSON(json: AliasChoice$Wire): AliasChoice { + return json; +} + +export function aliasChoiceToJSON(value: AliasChoice): AliasChoice$Wire { + return value; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/AliasOuter.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/AliasOuter.ts.golden new file mode 100644 index 000000000..53da30c53 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/AliasOuter.ts.golden @@ -0,0 +1,16 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export type AliasOuter$Wire = unknown; + +export type AliasOuter = AliasOuter$Wire; + +export function aliasOuterFromJSON(json: AliasOuter$Wire): AliasOuter { + return json; +} + +export function aliasOuterToJSON(value: AliasOuter): AliasOuter$Wire { + return value; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/AlreadyNullableEnumParent.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/AlreadyNullableEnumParent.ts.golden new file mode 100644 index 000000000..6d80334b9 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/AlreadyNullableEnumParent.ts.golden @@ -0,0 +1,31 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { convert$0FromJSON, convert$0ToJSON } from '../runtime/valueConversion$0'; +import type { NullableScalarInner, NullableScalarInner$Wire } from './NullableScalarInner'; + +export interface AlreadyNullableEnumParent$Wire { + readonly scalar_value: NullableScalarInner$Wire | null; +} + +export interface AlreadyNullableEnumParent { + readonly scalarValue: NullableScalarInner | null; +} + +export function alreadyNullableEnumParentFromJSON( + json: AlreadyNullableEnumParent$Wire, +): AlreadyNullableEnumParent { + return { + scalarValue: convert$0FromJSON(json.scalar_value), + }; +} + +export function alreadyNullableEnumParentToJSON( + value: AlreadyNullableEnumParent, +): AlreadyNullableEnumParent$Wire { + return { + scalar_value: convert$0ToJSON(value.scalarValue), + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/AlreadyNullableOuterEnumParent.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/AlreadyNullableOuterEnumParent.ts.golden new file mode 100644 index 000000000..b2dee57f8 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/AlreadyNullableOuterEnumParent.ts.golden @@ -0,0 +1,31 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { convert$1FromJSON, convert$1ToJSON } from '../runtime/valueConversion$1'; +import type { NullableScalarInner, NullableScalarInner$Wire } from './NullableScalarInner'; + +export interface AlreadyNullableOuterEnumParent$Wire { + readonly scalar_value: NullableScalarInner$Wire | null; +} + +export interface AlreadyNullableOuterEnumParent { + readonly scalarValue: NullableScalarInner | null; +} + +export function alreadyNullableOuterEnumParentFromJSON( + json: AlreadyNullableOuterEnumParent$Wire, +): AlreadyNullableOuterEnumParent { + return { + scalarValue: convert$1FromJSON(json.scalar_value), + }; +} + +export function alreadyNullableOuterEnumParentToJSON( + value: AlreadyNullableOuterEnumParent, +): AlreadyNullableOuterEnumParent$Wire { + return { + scalar_value: convert$1ToJSON(value.scalarValue), + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/AlternativeTool.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/AlternativeTool.ts.golden new file mode 100644 index 000000000..ea69f7eec --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/AlternativeTool.ts.golden @@ -0,0 +1,32 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface AlternativeTool$Wire { + readonly input_schema: globalThis.Record; + readonly name: string; + readonly second_option?: boolean; +} + +export interface AlternativeTool { + readonly inputSchema: globalThis.Record; + readonly name: string; + readonly secondOption?: boolean; +} + +export function alternativeToolFromJSON(json: AlternativeTool$Wire): AlternativeTool { + return { + inputSchema: json.input_schema, + name: json.name, + secondOption: json.second_option, + }; +} + +export function alternativeToolToJSON(value: AlternativeTool): AlternativeTool$Wire { + return { + input_schema: value.inputSchema, + name: value.name, + second_option: value.secondOption, + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/AmbiguousInner.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/AmbiguousInner.ts.golden new file mode 100644 index 000000000..46328b236 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/AmbiguousInner.ts.golden @@ -0,0 +1,16 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export type AmbiguousInner$Wire = unknown; + +export type AmbiguousInner = AmbiguousInner$Wire; + +export function ambiguousInnerFromJSON(json: AmbiguousInner$Wire): AmbiguousInner { + return json; +} + +export function ambiguousInnerToJSON(value: AmbiguousInner): AmbiguousInner$Wire { + return value; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/AmbiguousMap.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/AmbiguousMap.ts.golden new file mode 100644 index 000000000..88def1499 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/AmbiguousMap.ts.golden @@ -0,0 +1,40 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { ambiguousInnerFromJSON, ambiguousInnerToJSON } from './AmbiguousInner'; + +export interface AmbiguousMap$Wire extends Readonly> {} + +export interface AmbiguousMap extends Readonly> {} + +export function ambiguousMapFromJSON(json: AmbiguousMap$Wire): AmbiguousMap { + const sourceEntries = Object.entries(json); + const additionalEntries = sourceEntries; + const additionalProperties = Object.fromEntries( + additionalEntries.map(([key, value]) => { + const additionalValue = value; + return [key, ambiguousInnerFromJSON(additionalValue)] as const; + }), + ); + const result = { + ...additionalProperties, + }; + return result; +} + +export function ambiguousMapToJSON(value: AmbiguousMap): AmbiguousMap$Wire { + const sourceEntries = Object.entries(value); + const additionalEntries = sourceEntries; + const additionalProperties = Object.fromEntries( + additionalEntries.map(([key, value]) => { + const additionalValue = value; + return [key, ambiguousInnerToJSON(additionalValue)] as const; + }), + ); + const result = { + ...additionalProperties, + }; + return result; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/AmbiguousMapOuter.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/AmbiguousMapOuter.ts.golden new file mode 100644 index 000000000..5ab585f5c --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/AmbiguousMapOuter.ts.golden @@ -0,0 +1,16 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export type AmbiguousMapOuter$Wire = unknown; + +export type AmbiguousMapOuter = AmbiguousMapOuter$Wire; + +export function ambiguousMapOuterFromJSON(json: AmbiguousMapOuter$Wire): AmbiguousMapOuter { + return json; +} + +export function ambiguousMapOuterToJSON(value: AmbiguousMapOuter): AmbiguousMapOuter$Wire { + return value; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/AmbiguousOuter.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/AmbiguousOuter.ts.golden new file mode 100644 index 000000000..4a4390363 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/AmbiguousOuter.ts.golden @@ -0,0 +1,16 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export type AmbiguousOuter$Wire = unknown; + +export type AmbiguousOuter = AmbiguousOuter$Wire; + +export function ambiguousOuterFromJSON(json: AmbiguousOuter$Wire): AmbiguousOuter { + return json; +} + +export function ambiguousOuterToJSON(value: AmbiguousOuter): AmbiguousOuter$Wire { + return value; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/AmbiguousTool.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/AmbiguousTool.ts.golden new file mode 100644 index 000000000..a88521498 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/AmbiguousTool.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { AlternativeTool$Wire } from './AlternativeTool'; +import type { ClientTool$Wire } from './ClientTool'; + +export type AmbiguousTool$Wire = ClientTool$Wire | AlternativeTool$Wire; + +export type AmbiguousTool = AmbiguousTool$Wire; + +export function ambiguousToolFromJSON(json: AmbiguousTool$Wire): AmbiguousTool { + return json; +} + +export function ambiguousToolToJSON(value: AmbiguousTool): AmbiguousTool$Wire { + return value; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/ArrayPayload.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/ArrayPayload.ts.golden new file mode 100644 index 000000000..cbde8a749 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/ArrayPayload.ts.golden @@ -0,0 +1,24 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface ArrayPayload$Wire { + readonly wire_items: readonly string[]; +} + +export interface ArrayPayload { + readonly wireItems: readonly string[]; +} + +export function arrayPayloadFromJSON(json: ArrayPayload$Wire): ArrayPayload { + return { + wireItems: json.wire_items, + }; +} + +export function arrayPayloadToJSON(value: ArrayPayload): ArrayPayload$Wire { + return { + wire_items: value.wireItems, + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/ArrayPayloadOrBoolean.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/ArrayPayloadOrBoolean.ts.golden new file mode 100644 index 000000000..92f87e863 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/ArrayPayloadOrBoolean.ts.golden @@ -0,0 +1,22 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { ArrayPayload$Wire } from './ArrayPayload'; + +export type ArrayPayloadOrBoolean$Wire = ArrayPayload$Wire | boolean; + +export type ArrayPayloadOrBoolean = ArrayPayloadOrBoolean$Wire; + +export function arrayPayloadOrBooleanFromJSON( + json: ArrayPayloadOrBoolean$Wire, +): ArrayPayloadOrBoolean { + return json; +} + +export function arrayPayloadOrBooleanToJSON( + value: ArrayPayloadOrBoolean, +): ArrayPayloadOrBoolean$Wire { + return value; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/ArrayWrapped.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/ArrayWrapped.ts.golden new file mode 100644 index 000000000..d71b89dfa --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/ArrayWrapped.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { DetailArrayOrObject, DetailArrayOrObject$Wire } from './DetailArrayOrObject'; +import { detailArrayOrObjectFromJSON, detailArrayOrObjectToJSON } from './DetailArrayOrObject'; + +export type ArrayWrapped$Wire = DetailArrayOrObject$Wire; + +export type ArrayWrapped = DetailArrayOrObject; + +export function arrayWrappedFromJSON(json: ArrayWrapped$Wire): ArrayWrapped { + return detailArrayOrObjectFromJSON(json); +} + +export function arrayWrappedToJSON(value: ArrayWrapped): ArrayWrapped$Wire { + return detailArrayOrObjectToJSON(value); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/BinaryModel.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/BinaryModel.ts.golden new file mode 100644 index 000000000..4febefc7f --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/BinaryModel.ts.golden @@ -0,0 +1,28 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface BinaryModel$Wire { + readonly binary_data: Blob | File; + readonly field_name: string; +} + +export interface BinaryModel { + readonly binaryData: Blob | File; + readonly fieldName: string; +} + +export function binaryModelFromJSON(json: BinaryModel$Wire): BinaryModel { + return { + binaryData: json.binary_data, + fieldName: json.field_name, + }; +} + +export function binaryModelToJSON(value: BinaryModel): BinaryModel$Wire { + return { + binary_data: value.binaryData, + field_name: value.fieldName, + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/BinaryUnion.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/BinaryUnion.ts.golden new file mode 100644 index 000000000..5d8ea4565 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/BinaryUnion.ts.golden @@ -0,0 +1,18 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { BinaryModel$Wire } from './BinaryModel'; + +export type BinaryUnion$Wire = BinaryModel$Wire | boolean; + +export type BinaryUnion = BinaryUnion$Wire; + +export function binaryUnionFromJSON(json: BinaryUnion$Wire): BinaryUnion { + return json; +} + +export function binaryUnionToJSON(value: BinaryUnion): BinaryUnion$Wire { + return value; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/ClientTool.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/ClientTool.ts.golden new file mode 100644 index 000000000..8f4d94ac9 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/ClientTool.ts.golden @@ -0,0 +1,28 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface ClientTool$Wire { + readonly input_schema: globalThis.Record; + readonly name: string; +} + +export interface ClientTool { + readonly inputSchema: globalThis.Record; + readonly name: string; +} + +export function clientToolFromJSON(json: ClientTool$Wire): ClientTool { + return { + inputSchema: json.input_schema, + name: json.name, + }; +} + +export function clientToolToJSON(value: ClientTool): ClientTool$Wire { + return { + input_schema: value.inputSchema, + name: value.name, + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/ConstrainedOuterEnumParent.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/ConstrainedOuterEnumParent.ts.golden new file mode 100644 index 000000000..62bec288d --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/ConstrainedOuterEnumParent.ts.golden @@ -0,0 +1,31 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { convert$2FromJSON, convert$2ToJSON } from '../runtime/valueConversion$2'; +import type { NullableScalarInner, NullableScalarInner$Wire } from './NullableScalarInner'; + +export interface ConstrainedOuterEnumParent$Wire { + readonly scalar_value: NullableScalarInner$Wire | null; +} + +export interface ConstrainedOuterEnumParent { + readonly scalarValue: NullableScalarInner | null; +} + +export function constrainedOuterEnumParentFromJSON( + json: ConstrainedOuterEnumParent$Wire, +): ConstrainedOuterEnumParent { + return { + scalarValue: convert$2FromJSON(json.scalar_value), + }; +} + +export function constrainedOuterEnumParentToJSON( + value: ConstrainedOuterEnumParent, +): ConstrainedOuterEnumParent$Wire { + return { + scalar_value: convert$2ToJSON(value.scalarValue), + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/ContainerOuterEnum.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/ContainerOuterEnum.ts.golden new file mode 100644 index 000000000..0d167e389 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/ContainerOuterEnum.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { convert$3FromJSON, convert$3ToJSON } from '../runtime/valueConversion$3'; +import type { NullableScalarInner, NullableScalarInner$Wire } from './NullableScalarInner'; + +export type ContainerOuterEnum$Wire = readonly (NullableScalarInner$Wire | null)[]; + +export type ContainerOuterEnum = readonly (NullableScalarInner | null)[]; + +export function containerOuterEnumFromJSON(json: ContainerOuterEnum$Wire): ContainerOuterEnum { + return convert$3FromJSON(json); +} + +export function containerOuterEnumToJSON(value: ContainerOuterEnum): ContainerOuterEnum$Wire { + return convert$3ToJSON(value); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/Detail.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/Detail.ts.golden new file mode 100644 index 000000000..f9714af2f --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/Detail.ts.golden @@ -0,0 +1,24 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface Detail$Wire { + readonly wire_name: string; +} + +export interface Detail { + readonly wireName: string; +} + +export function detailFromJSON(json: Detail$Wire): Detail { + return { + wireName: json.wire_name, + }; +} + +export function detailToJSON(value: Detail): Detail$Wire { + return { + wire_name: value.wireName, + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/DetailArrayOrObject.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/DetailArrayOrObject.ts.golden new file mode 100644 index 000000000..87e9cbed8 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/DetailArrayOrObject.ts.golden @@ -0,0 +1,18 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail$Wire } from './Detail'; + +export type DetailArrayOrObject$Wire = readonly Detail$Wire[] | Detail$Wire; + +export type DetailArrayOrObject = DetailArrayOrObject$Wire; + +export function detailArrayOrObjectFromJSON(json: DetailArrayOrObject$Wire): DetailArrayOrObject { + return json; +} + +export function detailArrayOrObjectToJSON(value: DetailArrayOrObject): DetailArrayOrObject$Wire { + return value; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/DictionaryAlias.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/DictionaryAlias.ts.golden new file mode 100644 index 000000000..2c26c5575 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/DictionaryAlias.ts.golden @@ -0,0 +1,41 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail, Detail$Wire } from './Detail'; +import { detailFromJSON, detailToJSON } from './Detail'; + +export type DictionaryAlias$Wire = Readonly>; + +export type DictionaryAlias = Readonly>; + +export function dictionaryAliasFromJSON(json: DictionaryAlias$Wire): DictionaryAlias { + const sourceEntries = Object.entries(json); + const additionalEntries = sourceEntries; + const additionalProperties = Object.fromEntries( + additionalEntries.map(([key, value]) => { + const additionalValue = value; + return [key, detailFromJSON(additionalValue)] as const; + }), + ); + const result = { + ...additionalProperties, + }; + return result; +} + +export function dictionaryAliasToJSON(value: DictionaryAlias): DictionaryAlias$Wire { + const sourceEntries = Object.entries(value); + const additionalEntries = sourceEntries; + const additionalProperties = Object.fromEntries( + additionalEntries.map(([key, value]) => { + const additionalValue = value; + return [key, detailToJSON(additionalValue)] as const; + }), + ); + const result = { + ...additionalProperties, + }; + return result; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/EnumFreeOuterEnumParent.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/EnumFreeOuterEnumParent.ts.golden new file mode 100644 index 000000000..3eeaa26ff --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/EnumFreeOuterEnumParent.ts.golden @@ -0,0 +1,31 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { convert$0FromJSON, convert$0ToJSON } from '../runtime/valueConversion$0'; +import type { NullableScalarInner, NullableScalarInner$Wire } from './NullableScalarInner'; + +export interface EnumFreeOuterEnumParent$Wire { + readonly scalar_value: NullableScalarInner$Wire | null; +} + +export interface EnumFreeOuterEnumParent { + readonly scalarValue: NullableScalarInner | null; +} + +export function enumFreeOuterEnumParentFromJSON( + json: EnumFreeOuterEnumParent$Wire, +): EnumFreeOuterEnumParent { + return { + scalarValue: json.scalar_value === null ? null : convert$0FromJSON(json.scalar_value), + }; +} + +export function enumFreeOuterEnumParentToJSON( + value: EnumFreeOuterEnumParent, +): EnumFreeOuterEnumParent$Wire { + return { + scalar_value: value.scalarValue === null ? null : convert$0ToJSON(value.scalarValue), + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/ExchangeMultipartMultipartRequestBody.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/ExchangeMultipartMultipartRequestBody.ts.golden new file mode 100644 index 000000000..1e84c1d5b --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/ExchangeMultipartMultipartRequestBody.ts.golden @@ -0,0 +1,12 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { UploadFileInput } from '../runtime/runtime'; +import type { Detail$Wire } from './Detail'; + +export interface ExchangeMultipartMultipartRequestBody { + payloadValues: readonly (globalThis.Record | string)[]; + uploadFile?: UploadFileInput; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/ExchangeMultipartRequest.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/ExchangeMultipartRequest.ts.golden new file mode 100644 index 000000000..168d0b306 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/ExchangeMultipartRequest.ts.golden @@ -0,0 +1,34 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail$Wire } from './Detail'; + +export interface ExchangeMultipartRequest$Wire { + readonly payload_values: readonly (globalThis.Record | string)[]; + readonly upload_file?: Blob | File; +} + +export interface ExchangeMultipartRequest { + readonly payloadValues: readonly (globalThis.Record | string)[]; + readonly uploadFile?: Blob | File; +} + +export function exchangeMultipartRequestFromJSON( + json: ExchangeMultipartRequest$Wire, +): ExchangeMultipartRequest { + return { + payloadValues: json.payload_values, + uploadFile: json.upload_file, + }; +} + +export function exchangeMultipartRequestToJSON( + value: ExchangeMultipartRequest, +): ExchangeMultipartRequest$Wire { + return { + payload_values: value.payloadValues, + upload_file: value.uploadFile, + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/ExplicitNullOverlap.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/ExplicitNullOverlap.ts.golden new file mode 100644 index 000000000..6e6995f2a --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/ExplicitNullOverlap.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { NullableDetail$Wire } from './NullableDetail'; +import type { NullableTool$Wire } from './NullableTool'; + +export type ExplicitNullOverlap$Wire = NullableDetail$Wire | NullableTool$Wire | null; + +export type ExplicitNullOverlap = ExplicitNullOverlap$Wire; + +export function explicitNullOverlapFromJSON(json: ExplicitNullOverlap$Wire): ExplicitNullOverlap { + return json; +} + +export function explicitNullOverlapToJSON(value: ExplicitNullOverlap): ExplicitNullOverlap$Wire { + return value; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/ExplicitNullableEnumChoice.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/ExplicitNullableEnumChoice.ts.golden new file mode 100644 index 000000000..73c774b91 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/ExplicitNullableEnumChoice.ts.golden @@ -0,0 +1,22 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail$Wire } from './Detail'; + +export type ExplicitNullableEnumChoice$Wire = readonly (number | null)[] | Detail$Wire; + +export type ExplicitNullableEnumChoice = ExplicitNullableEnumChoice$Wire; + +export function explicitNullableEnumChoiceFromJSON( + json: ExplicitNullableEnumChoice$Wire, +): ExplicitNullableEnumChoice { + return json; +} + +export function explicitNullableEnumChoiceToJSON( + value: ExplicitNullableEnumChoice, +): ExplicitNullableEnumChoice$Wire { + return value; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/InlineDictionaryValues.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/InlineDictionaryValues.ts.golden new file mode 100644 index 000000000..e66f30505 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/InlineDictionaryValues.ts.golden @@ -0,0 +1,24 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail$Wire } from './Detail'; + +export type InlineDictionaryValues$Wire = + | globalThis.Record | string> + | boolean; + +export type InlineDictionaryValues = InlineDictionaryValues$Wire; + +export function inlineDictionaryValuesFromJSON( + json: InlineDictionaryValues$Wire, +): InlineDictionaryValues { + return json; +} + +export function inlineDictionaryValuesToJSON( + value: InlineDictionaryValues, +): InlineDictionaryValues$Wire { + return value; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/InlineMixedValues.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/InlineMixedValues.ts.golden new file mode 100644 index 000000000..bc03c44e0 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/InlineMixedValues.ts.golden @@ -0,0 +1,20 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail$Wire } from './Detail'; + +export type InlineMixedValues$Wire = + | readonly (globalThis.Record | string)[] + | Detail$Wire; + +export type InlineMixedValues = InlineMixedValues$Wire; + +export function inlineMixedValuesFromJSON(json: InlineMixedValues$Wire): InlineMixedValues { + return json; +} + +export function inlineMixedValuesToJSON(value: InlineMixedValues): InlineMixedValues$Wire { + return value; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/InlineNumericValues.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/InlineNumericValues.ts.golden new file mode 100644 index 000000000..d4fc1ad5d --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/InlineNumericValues.ts.golden @@ -0,0 +1,18 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail$Wire } from './Detail'; + +export type InlineNumericValues$Wire = readonly (globalThis.Record | number)[]; + +export type InlineNumericValues = InlineNumericValues$Wire; + +export function inlineNumericValuesFromJSON(json: InlineNumericValues$Wire): InlineNumericValues { + return json; +} + +export function inlineNumericValuesToJSON(value: InlineNumericValues): InlineNumericValues$Wire { + return value; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/InlineProperty.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/InlineProperty.ts.golden new file mode 100644 index 000000000..a8aa0144e --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/InlineProperty.ts.golden @@ -0,0 +1,26 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail$Wire } from './Detail'; + +export interface InlineProperty$Wire { + readonly entries: readonly (globalThis.Record | string)[]; +} + +export interface InlineProperty { + readonly entries: readonly (globalThis.Record | string)[]; +} + +export function inlinePropertyFromJSON(json: InlineProperty$Wire): InlineProperty { + return { + entries: json.entries, + }; +} + +export function inlinePropertyToJSON(value: InlineProperty): InlineProperty$Wire { + return { + entries: value.entries, + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/MapOuter.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/MapOuter.ts.golden new file mode 100644 index 000000000..acb86eee1 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/MapOuter.ts.golden @@ -0,0 +1,16 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export type MapOuter$Wire = unknown; + +export type MapOuter = MapOuter$Wire; + +export function mapOuterFromJSON(json: MapOuter$Wire): MapOuter { + return json; +} + +export function mapOuterToJSON(value: MapOuter): MapOuter$Wire { + return value; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/MapWrapped.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/MapWrapped.ts.golden new file mode 100644 index 000000000..e60b0a99e --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/MapWrapped.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { NullableMapFallback, NullableMapFallback$Wire } from './NullableMapFallback'; +import { nullableMapFallbackFromJSON, nullableMapFallbackToJSON } from './NullableMapFallback'; + +export type MapWrapped$Wire = NullableMapFallback$Wire; + +export type MapWrapped = NullableMapFallback; + +export function mapWrappedFromJSON(json: MapWrapped$Wire): MapWrapped { + return nullableMapFallbackFromJSON(json); +} + +export function mapWrappedToJSON(value: MapWrapped): MapWrapped$Wire { + return nullableMapFallbackToJSON(value); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/MixedMapA.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/MixedMapA.ts.golden new file mode 100644 index 000000000..48f3d112a --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/MixedMapA.ts.golden @@ -0,0 +1,51 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail, Detail$Wire } from './Detail'; +import { detailFromJSON, detailToJSON } from './Detail'; + +export interface MixedMapA$Wire extends Readonly> { + readonly marker: string; +} + +export interface MixedMapA extends Readonly> { + readonly marker: string; +} + +export function mixedMapAFromJSON(json: MixedMapA$Wire): MixedMapA { + const sourceEntries = Object.entries(json).filter(([key]) => { + return !['marker'].includes(key); + }); + const additionalEntries = sourceEntries; + const additionalProperties = Object.fromEntries( + additionalEntries.map(([key, value]) => { + const additionalValue = value as Detail$Wire; + return [key, detailFromJSON(additionalValue)] as const; + }), + ); + const result = { + ...additionalProperties, + marker: json.marker, + }; + return result; +} + +export function mixedMapAToJSON(value: MixedMapA): MixedMapA$Wire { + const sourceEntries = Object.entries(value).filter(([key]) => { + return !['marker'].includes(key); + }); + const additionalEntries = sourceEntries; + const additionalProperties = Object.fromEntries( + additionalEntries.map(([key, value]) => { + const additionalValue = value as Detail; + return [key, detailToJSON(additionalValue)] as const; + }), + ); + const result = { + ...additionalProperties, + marker: value.marker, + }; + return result; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/MixedMapB.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/MixedMapB.ts.golden new file mode 100644 index 000000000..beb0a5371 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/MixedMapB.ts.golden @@ -0,0 +1,51 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { convert$6FromJSON, convert$6ToJSON } from '../runtime/valueConversion$6'; +import type { Detail, Detail$Wire } from './Detail'; + +export interface MixedMapB$Wire extends Readonly> { + readonly marker: string; +} + +export interface MixedMapB extends Readonly> { + readonly marker: string; +} + +export function mixedMapBFromJSON(json: MixedMapB$Wire): MixedMapB { + const sourceEntries = Object.entries(json).filter(([key]) => { + return !['marker'].includes(key); + }); + const additionalEntries = sourceEntries; + const additionalProperties = Object.fromEntries( + additionalEntries.map(([key, value]) => { + const additionalValue = value as readonly Detail$Wire[]; + return [key, convert$6FromJSON(additionalValue)] as const; + }), + ); + const result = { + ...additionalProperties, + marker: json.marker, + }; + return result; +} + +export function mixedMapBToJSON(value: MixedMapB): MixedMapB$Wire { + const sourceEntries = Object.entries(value).filter(([key]) => { + return !['marker'].includes(key); + }); + const additionalEntries = sourceEntries; + const additionalProperties = Object.fromEntries( + additionalEntries.map(([key, value]) => { + const additionalValue = value as readonly Detail[]; + return [key, convert$6ToJSON(additionalValue)] as const; + }), + ); + const result = { + ...additionalProperties, + marker: value.marker, + }; + return result; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/MixedMapChoice.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/MixedMapChoice.ts.golden new file mode 100644 index 000000000..e8a0bbc9a --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/MixedMapChoice.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { MixedMapA$Wire } from './MixedMapA'; +import type { MixedMapB$Wire } from './MixedMapB'; + +export type MixedMapChoice$Wire = MixedMapA$Wire | MixedMapB$Wire; + +export type MixedMapChoice = MixedMapChoice$Wire; + +export function mixedMapChoiceFromJSON(json: MixedMapChoice$Wire): MixedMapChoice { + return json; +} + +export function mixedMapChoiceToJSON(value: MixedMapChoice): MixedMapChoice$Wire { + return value; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NamedContainer.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NamedContainer.ts.golden new file mode 100644 index 000000000..8d055fd0e --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NamedContainer.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail$Wire } from './Detail'; +import type { NamedValue$Wire } from './NamedValue'; + +export type NamedContainer$Wire = readonly NamedValue$Wire[] | Detail$Wire; + +export type NamedContainer = NamedContainer$Wire; + +export function namedContainerFromJSON(json: NamedContainer$Wire): NamedContainer { + return json; +} + +export function namedContainerToJSON(value: NamedContainer): NamedContainer$Wire { + return value; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NamedValue.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NamedValue.ts.golden new file mode 100644 index 000000000..50c2171aa --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NamedValue.ts.golden @@ -0,0 +1,18 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail$Wire } from './Detail'; + +export type NamedValue$Wire = globalThis.Record | string; + +export type NamedValue = NamedValue$Wire; + +export function namedValueFromJSON(json: NamedValue$Wire): NamedValue { + return json; +} + +export function namedValueToJSON(value: NamedValue): NamedValue$Wire { + return value; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NestedDetailLists.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NestedDetailLists.ts.golden new file mode 100644 index 000000000..4d386a950 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NestedDetailLists.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { convert$9FromJSON, convert$9ToJSON } from '../runtime/valueConversion$9'; +import type { Detail, Detail$Wire } from './Detail'; + +export type NestedDetailLists$Wire = readonly Detail$Wire[][]; + +export type NestedDetailLists = readonly Detail[][]; + +export function nestedDetailListsFromJSON(json: NestedDetailLists$Wire): NestedDetailLists { + return convert$9FromJSON(json); +} + +export function nestedDetailListsToJSON(value: NestedDetailLists): NestedDetailLists$Wire { + return convert$9ToJSON(value); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NestedEnumChoice.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NestedEnumChoice.ts.golden new file mode 100644 index 000000000..2dd356c09 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NestedEnumChoice.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { NestedEnumNarrow$Wire } from './NestedEnumNarrow'; +import type { NumericTwo$Wire } from './NumericTwo'; + +export type NestedEnumChoice$Wire = NestedEnumNarrow$Wire | NumericTwo$Wire; + +export type NestedEnumChoice = NestedEnumChoice$Wire; + +export function nestedEnumChoiceFromJSON(json: NestedEnumChoice$Wire): NestedEnumChoice { + return json; +} + +export function nestedEnumChoiceToJSON(value: NestedEnumChoice): NestedEnumChoice$Wire { + return value; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NestedEnumNarrow.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NestedEnumNarrow.ts.golden new file mode 100644 index 000000000..8249e388e --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NestedEnumNarrow.ts.golden @@ -0,0 +1,28 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface NestedEnumNarrow$Wire { + readonly first_value?: string; + readonly revision: number | null; +} + +export interface NestedEnumNarrow { + readonly firstValue?: string; + readonly revision: number | null; +} + +export function nestedEnumNarrowFromJSON(json: NestedEnumNarrow$Wire): NestedEnumNarrow { + return { + firstValue: json.first_value, + revision: json.revision, + }; +} + +export function nestedEnumNarrowToJSON(value: NestedEnumNarrow): NestedEnumNarrow$Wire { + return { + first_value: value.firstValue, + revision: value.revision, + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NestedExplicitNullOverlap.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NestedExplicitNullOverlap.ts.golden new file mode 100644 index 000000000..58a31f342 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NestedExplicitNullOverlap.ts.golden @@ -0,0 +1,22 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { NullableOverlap$Wire } from './NullableOverlap'; + +export type NestedExplicitNullOverlap$Wire = NullableOverlap$Wire | boolean | null; + +export type NestedExplicitNullOverlap = NestedExplicitNullOverlap$Wire; + +export function nestedExplicitNullOverlapFromJSON( + json: NestedExplicitNullOverlap$Wire, +): NestedExplicitNullOverlap { + return json; +} + +export function nestedExplicitNullOverlapToJSON( + value: NestedExplicitNullOverlap, +): NestedExplicitNullOverlap$Wire { + return value; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NestedNullableEnumChoice.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NestedNullableEnumChoice.ts.golden new file mode 100644 index 000000000..cfcf07917 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NestedNullableEnumChoice.ts.golden @@ -0,0 +1,23 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { NestedNullableEnumNarrow$Wire } from './NestedNullableEnumNarrow'; +import type { NumericTwo$Wire } from './NumericTwo'; + +export type NestedNullableEnumChoice$Wire = NestedNullableEnumNarrow$Wire | NumericTwo$Wire; + +export type NestedNullableEnumChoice = NestedNullableEnumChoice$Wire; + +export function nestedNullableEnumChoiceFromJSON( + json: NestedNullableEnumChoice$Wire, +): NestedNullableEnumChoice { + return json; +} + +export function nestedNullableEnumChoiceToJSON( + value: NestedNullableEnumChoice, +): NestedNullableEnumChoice$Wire { + return value; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NestedNullableEnumNarrow.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NestedNullableEnumNarrow.ts.golden new file mode 100644 index 000000000..5d361f019 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NestedNullableEnumNarrow.ts.golden @@ -0,0 +1,32 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface NestedNullableEnumNarrow$Wire { + readonly first_value?: string; + readonly revision: number | null; +} + +export interface NestedNullableEnumNarrow { + readonly firstValue?: string; + readonly revision: number | null; +} + +export function nestedNullableEnumNarrowFromJSON( + json: NestedNullableEnumNarrow$Wire, +): NestedNullableEnumNarrow { + return { + firstValue: json.first_value, + revision: json.revision, + }; +} + +export function nestedNullableEnumNarrowToJSON( + value: NestedNullableEnumNarrow, +): NestedNullableEnumNarrow$Wire { + return { + first_value: value.firstValue, + revision: value.revision, + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NullableDetail.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NullableDetail.ts.golden new file mode 100644 index 000000000..be8a75fac --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NullableDetail.ts.golden @@ -0,0 +1,18 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail$Wire } from './Detail'; + +export type NullableDetail$Wire = Detail$Wire | null; + +export type NullableDetail = NullableDetail$Wire; + +export function nullableDetailFromJSON(json: NullableDetail$Wire): NullableDetail { + return json; +} + +export function nullableDetailToJSON(value: NullableDetail): NullableDetail$Wire { + return value; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NullableEnumParent.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NullableEnumParent.ts.golden new file mode 100644 index 000000000..584a83629 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NullableEnumParent.ts.golden @@ -0,0 +1,27 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { convert$11FromJSON, convert$11ToJSON } from '../runtime/valueConversion$11'; +import type { ScalarEnumChoice, ScalarEnumChoice$Wire } from './ScalarEnumChoice'; + +export interface NullableEnumParent$Wire { + readonly scalar_value: ScalarEnumChoice$Wire | null; +} + +export interface NullableEnumParent { + readonly scalarValue: ScalarEnumChoice | null; +} + +export function nullableEnumParentFromJSON(json: NullableEnumParent$Wire): NullableEnumParent { + return { + scalarValue: convert$11FromJSON(json.scalar_value), + }; +} + +export function nullableEnumParentToJSON(value: NullableEnumParent): NullableEnumParent$Wire { + return { + scalar_value: convert$11ToJSON(value.scalarValue), + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NullableInlineEnumParent.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NullableInlineEnumParent.ts.golden new file mode 100644 index 000000000..d9a9461b3 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NullableInlineEnumParent.ts.golden @@ -0,0 +1,28 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface NullableInlineEnumParent$Wire { + readonly scalar_value: number | boolean | null; +} + +export interface NullableInlineEnumParent { + readonly scalarValue: number | boolean | null; +} + +export function nullableInlineEnumParentFromJSON( + json: NullableInlineEnumParent$Wire, +): NullableInlineEnumParent { + return { + scalarValue: json.scalar_value, + }; +} + +export function nullableInlineEnumParentToJSON( + value: NullableInlineEnumParent, +): NullableInlineEnumParent$Wire { + return { + scalar_value: value.scalarValue, + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NullableMapFallback.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NullableMapFallback.ts.golden new file mode 100644 index 000000000..73831d493 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NullableMapFallback.ts.golden @@ -0,0 +1,18 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail$Wire } from './Detail'; + +export type NullableMapFallback$Wire = globalThis.Record | null | Detail$Wire; + +export type NullableMapFallback = NullableMapFallback$Wire; + +export function nullableMapFallbackFromJSON(json: NullableMapFallback$Wire): NullableMapFallback { + return json; +} + +export function nullableMapFallbackToJSON(value: NullableMapFallback): NullableMapFallback$Wire { + return value; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NullableNonNullScalars.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NullableNonNullScalars.ts.golden new file mode 100644 index 000000000..9137df186 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NullableNonNullScalars.ts.golden @@ -0,0 +1,26 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { convert$12FromJSON, convert$12ToJSON } from '../runtime/valueConversion$12'; +import type { + NullableNonNullScalarsItems, + NullableNonNullScalarsItems$Wire, +} from './NullableNonNullScalarsItems'; + +export type NullableNonNullScalars$Wire = readonly (NullableNonNullScalarsItems$Wire | null)[]; + +export type NullableNonNullScalars = readonly (NullableNonNullScalarsItems | null)[]; + +export function nullableNonNullScalarsFromJSON( + json: NullableNonNullScalars$Wire, +): NullableNonNullScalars { + return convert$12FromJSON(json); +} + +export function nullableNonNullScalarsToJSON( + value: NullableNonNullScalars, +): NullableNonNullScalars$Wire { + return convert$12ToJSON(value); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NullableNonNullScalarsItems.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NullableNonNullScalarsItems.ts.golden new file mode 100644 index 000000000..b7dd314e7 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NullableNonNullScalarsItems.ts.golden @@ -0,0 +1,20 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export type NullableNonNullScalarsItems$Wire = number; + +export type NullableNonNullScalarsItems = NullableNonNullScalarsItems$Wire; + +export function nullableNonNullScalarsItemsFromJSON( + json: NullableNonNullScalarsItems$Wire, +): NullableNonNullScalarsItems { + return json; +} + +export function nullableNonNullScalarsItemsToJSON( + value: NullableNonNullScalarsItems, +): NullableNonNullScalarsItems$Wire { + return value; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NullableNumericChoice.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NullableNumericChoice.ts.golden new file mode 100644 index 000000000..aad7561d4 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NullableNumericChoice.ts.golden @@ -0,0 +1,23 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { NullableNumericOne$Wire } from './NullableNumericOne'; +import type { NullableNumericTwo$Wire } from './NullableNumericTwo'; + +export type NullableNumericChoice$Wire = NullableNumericOne$Wire | NullableNumericTwo$Wire; + +export type NullableNumericChoice = NullableNumericChoice$Wire; + +export function nullableNumericChoiceFromJSON( + json: NullableNumericChoice$Wire, +): NullableNumericChoice { + return json; +} + +export function nullableNumericChoiceToJSON( + value: NullableNumericChoice, +): NullableNumericChoice$Wire { + return value; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NullableNumericOne.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NullableNumericOne.ts.golden new file mode 100644 index 000000000..e7d632515 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NullableNumericOne.ts.golden @@ -0,0 +1,28 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface NullableNumericOne$Wire { + readonly first_value?: string; + readonly revision: number | null; +} + +export interface NullableNumericOne { + readonly firstValue?: string; + readonly revision: number | null; +} + +export function nullableNumericOneFromJSON(json: NullableNumericOne$Wire): NullableNumericOne { + return { + firstValue: json.first_value, + revision: json.revision, + }; +} + +export function nullableNumericOneToJSON(value: NullableNumericOne): NullableNumericOne$Wire { + return { + first_value: value.firstValue, + revision: value.revision, + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NullableNumericTwo.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NullableNumericTwo.ts.golden new file mode 100644 index 000000000..e681475f2 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NullableNumericTwo.ts.golden @@ -0,0 +1,28 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface NullableNumericTwo$Wire { + readonly revision: number | null; + readonly second_value?: string; +} + +export interface NullableNumericTwo { + readonly revision: number | null; + readonly secondValue?: string; +} + +export function nullableNumericTwoFromJSON(json: NullableNumericTwo$Wire): NullableNumericTwo { + return { + revision: json.revision, + secondValue: json.second_value, + }; +} + +export function nullableNumericTwoToJSON(value: NullableNumericTwo): NullableNumericTwo$Wire { + return { + revision: value.revision, + second_value: value.secondValue, + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NullableOpaque.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NullableOpaque.ts.golden new file mode 100644 index 000000000..0ab755b64 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NullableOpaque.ts.golden @@ -0,0 +1,16 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export type NullableOpaque$Wire = unknown; + +export type NullableOpaque = NullableOpaque$Wire; + +export function nullableOpaqueFromJSON(json: NullableOpaque$Wire): NullableOpaque { + return json; +} + +export function nullableOpaqueToJSON(value: NullableOpaque): NullableOpaque$Wire { + return value; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NullableOpaqueSiblingOuter.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NullableOpaqueSiblingOuter.ts.golden new file mode 100644 index 000000000..7827564b5 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NullableOpaqueSiblingOuter.ts.golden @@ -0,0 +1,20 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export type NullableOpaqueSiblingOuter$Wire = unknown; + +export type NullableOpaqueSiblingOuter = NullableOpaqueSiblingOuter$Wire; + +export function nullableOpaqueSiblingOuterFromJSON( + json: NullableOpaqueSiblingOuter$Wire, +): NullableOpaqueSiblingOuter { + return json; +} + +export function nullableOpaqueSiblingOuterToJSON( + value: NullableOpaqueSiblingOuter, +): NullableOpaqueSiblingOuter$Wire { + return value; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NullableOpenExplicitOuter.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NullableOpenExplicitOuter.ts.golden new file mode 100644 index 000000000..fe494e787 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NullableOpenExplicitOuter.ts.golden @@ -0,0 +1,20 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export type NullableOpenExplicitOuter$Wire = unknown; + +export type NullableOpenExplicitOuter = NullableOpenExplicitOuter$Wire; + +export function nullableOpenExplicitOuterFromJSON( + json: NullableOpenExplicitOuter$Wire, +): NullableOpenExplicitOuter { + return json; +} + +export function nullableOpenExplicitOuterToJSON( + value: NullableOpenExplicitOuter, +): NullableOpenExplicitOuter$Wire { + return value; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NullableOpenInner.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NullableOpenInner.ts.golden new file mode 100644 index 000000000..3a646a5c5 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NullableOpenInner.ts.golden @@ -0,0 +1,16 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export type NullableOpenInner$Wire = unknown; + +export type NullableOpenInner = NullableOpenInner$Wire; + +export function nullableOpenInnerFromJSON(json: NullableOpenInner$Wire): NullableOpenInner { + return json; +} + +export function nullableOpenInnerToJSON(value: NullableOpenInner): NullableOpenInner$Wire { + return value; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NullableOpenSiblingOuter.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NullableOpenSiblingOuter.ts.golden new file mode 100644 index 000000000..12670b532 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NullableOpenSiblingOuter.ts.golden @@ -0,0 +1,20 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export type NullableOpenSiblingOuter$Wire = unknown; + +export type NullableOpenSiblingOuter = NullableOpenSiblingOuter$Wire; + +export function nullableOpenSiblingOuterFromJSON( + json: NullableOpenSiblingOuter$Wire, +): NullableOpenSiblingOuter { + return json; +} + +export function nullableOpenSiblingOuterToJSON( + value: NullableOpenSiblingOuter, +): NullableOpenSiblingOuter$Wire { + return value; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NullableOverlap.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NullableOverlap.ts.golden new file mode 100644 index 000000000..f75e31e2a --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NullableOverlap.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { NullableDetail$Wire } from './NullableDetail'; +import type { NullableTool$Wire } from './NullableTool'; + +export type NullableOverlap$Wire = NullableDetail$Wire | NullableTool$Wire; + +export type NullableOverlap = NullableOverlap$Wire; + +export function nullableOverlapFromJSON(json: NullableOverlap$Wire): NullableOverlap { + return json; +} + +export function nullableOverlapToJSON(value: NullableOverlap): NullableOverlap$Wire { + return value; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NullablePlainEnumParent.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NullablePlainEnumParent.ts.golden new file mode 100644 index 000000000..6183c6642 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NullablePlainEnumParent.ts.golden @@ -0,0 +1,31 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { ScalarEnumChoice, ScalarEnumChoice$Wire } from './ScalarEnumChoice'; +import { scalarEnumChoiceFromJSON, scalarEnumChoiceToJSON } from './ScalarEnumChoice'; + +export interface NullablePlainEnumParent$Wire { + readonly scalar_value: ScalarEnumChoice$Wire | null; +} + +export interface NullablePlainEnumParent { + readonly scalarValue: ScalarEnumChoice | null; +} + +export function nullablePlainEnumParentFromJSON( + json: NullablePlainEnumParent$Wire, +): NullablePlainEnumParent { + return { + scalarValue: json.scalar_value === null ? null : scalarEnumChoiceFromJSON(json.scalar_value), + }; +} + +export function nullablePlainEnumParentToJSON( + value: NullablePlainEnumParent, +): NullablePlainEnumParent$Wire { + return { + scalar_value: value.scalarValue === null ? null : scalarEnumChoiceToJSON(value.scalarValue), + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NullableScalarArray.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NullableScalarArray.ts.golden new file mode 100644 index 000000000..1f3975e52 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NullableScalarArray.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { convert$14FromJSON, convert$14ToJSON } from '../runtime/valueConversion$14'; +import type { NullableScalarInner, NullableScalarInner$Wire } from './NullableScalarInner'; + +export type NullableScalarArray$Wire = readonly (NullableScalarInner$Wire | null)[]; + +export type NullableScalarArray = readonly (NullableScalarInner | null)[]; + +export function nullableScalarArrayFromJSON(json: NullableScalarArray$Wire): NullableScalarArray { + return convert$14FromJSON(json); +} + +export function nullableScalarArrayToJSON(value: NullableScalarArray): NullableScalarArray$Wire { + return convert$14ToJSON(value); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NullableScalarInner.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NullableScalarInner.ts.golden new file mode 100644 index 000000000..273aa09c0 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NullableScalarInner.ts.golden @@ -0,0 +1,16 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export type NullableScalarInner$Wire = string | null | number; + +export type NullableScalarInner = NullableScalarInner$Wire; + +export function nullableScalarInnerFromJSON(json: NullableScalarInner$Wire): NullableScalarInner { + return json; +} + +export function nullableScalarInnerToJSON(value: NullableScalarInner): NullableScalarInner$Wire { + return value; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NullableScalarMap.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NullableScalarMap.ts.golden new file mode 100644 index 000000000..a9f327bf9 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NullableScalarMap.ts.golden @@ -0,0 +1,43 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { convert$0FromJSON, convert$0ToJSON } from '../runtime/valueConversion$0'; +import type { NullableScalarInner, NullableScalarInner$Wire } from './NullableScalarInner'; + +export interface NullableScalarMap$Wire extends Readonly< + Record +> {} + +export interface NullableScalarMap extends Readonly> {} + +export function nullableScalarMapFromJSON(json: NullableScalarMap$Wire): NullableScalarMap { + const sourceEntries = Object.entries(json); + const additionalEntries = sourceEntries; + const additionalProperties = Object.fromEntries( + additionalEntries.map(([key, value]) => { + const additionalValue = value; + return [key, convert$0FromJSON(additionalValue)] as const; + }), + ); + const result = { + ...additionalProperties, + }; + return result; +} + +export function nullableScalarMapToJSON(value: NullableScalarMap): NullableScalarMap$Wire { + const sourceEntries = Object.entries(value); + const additionalEntries = sourceEntries; + const additionalProperties = Object.fromEntries( + additionalEntries.map(([key, value]) => { + const additionalValue = value; + return [key, convert$0ToJSON(additionalValue)] as const; + }), + ); + const result = { + ...additionalProperties, + }; + return result; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NullableScalarParent.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NullableScalarParent.ts.golden new file mode 100644 index 000000000..efe8fbe79 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NullableScalarParent.ts.golden @@ -0,0 +1,29 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { NullableScalarInner, NullableScalarInner$Wire } from './NullableScalarInner'; +import { nullableScalarInnerFromJSON, nullableScalarInnerToJSON } from './NullableScalarInner'; + +export interface NullableScalarParent$Wire { + readonly inner_value: NullableScalarInner$Wire | null; +} + +export interface NullableScalarParent { + readonly innerValue: NullableScalarInner | null; +} + +export function nullableScalarParentFromJSON( + json: NullableScalarParent$Wire, +): NullableScalarParent { + return { + innerValue: nullableScalarInnerFromJSON(json.inner_value), + }; +} + +export function nullableScalarParentToJSON(value: NullableScalarParent): NullableScalarParent$Wire { + return { + inner_value: nullableScalarInnerToJSON(value.innerValue), + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NullableTool.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NullableTool.ts.golden new file mode 100644 index 000000000..19c61d501 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NullableTool.ts.golden @@ -0,0 +1,18 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Tool$Wire } from './Tool'; + +export type NullableTool$Wire = Tool$Wire | null; + +export type NullableTool = NullableTool$Wire; + +export function nullableToolFromJSON(json: NullableTool$Wire): NullableTool { + return json; +} + +export function nullableToolToJSON(value: NullableTool): NullableTool$Wire { + return value; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NumericEnumChoice.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NumericEnumChoice.ts.golden new file mode 100644 index 000000000..30956dc68 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NumericEnumChoice.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { NumericOne$Wire } from './NumericOne'; +import type { NumericTwo$Wire } from './NumericTwo'; + +export type NumericEnumChoice$Wire = NumericOne$Wire | NumericTwo$Wire; + +export type NumericEnumChoice = NumericEnumChoice$Wire; + +export function numericEnumChoiceFromJSON(json: NumericEnumChoice$Wire): NumericEnumChoice { + return json; +} + +export function numericEnumChoiceToJSON(value: NumericEnumChoice): NumericEnumChoice$Wire { + return value; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NumericOne.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NumericOne.ts.golden new file mode 100644 index 000000000..fd62c06ef --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NumericOne.ts.golden @@ -0,0 +1,28 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface NumericOne$Wire { + readonly first_value?: string; + readonly revision: number; +} + +export interface NumericOne { + readonly firstValue?: string; + readonly revision: number; +} + +export function numericOneFromJSON(json: NumericOne$Wire): NumericOne { + return { + firstValue: json.first_value, + revision: json.revision, + }; +} + +export function numericOneToJSON(value: NumericOne): NumericOne$Wire { + return { + first_value: value.firstValue, + revision: value.revision, + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NumericTwo.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NumericTwo.ts.golden new file mode 100644 index 000000000..4750f2e0d --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/NumericTwo.ts.golden @@ -0,0 +1,28 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface NumericTwo$Wire { + readonly revision: number; + readonly second_value?: string; +} + +export interface NumericTwo { + readonly revision: number; + readonly secondValue?: string; +} + +export function numericTwoFromJSON(json: NumericTwo$Wire): NumericTwo { + return { + revision: json.revision, + secondValue: json.second_value, + }; +} + +export function numericTwoToJSON(value: NumericTwo): NumericTwo$Wire { + return { + revision: value.revision, + second_value: value.secondValue, + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/OnceNullableOuter.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/OnceNullableOuter.ts.golden new file mode 100644 index 000000000..91ce49507 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/OnceNullableOuter.ts.golden @@ -0,0 +1,16 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export type OnceNullableOuter$Wire = unknown; + +export type OnceNullableOuter = OnceNullableOuter$Wire; + +export function onceNullableOuterFromJSON(json: OnceNullableOuter$Wire): OnceNullableOuter { + return json; +} + +export function onceNullableOuterToJSON(value: OnceNullableOuter): OnceNullableOuter$Wire { + return value; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/OpenAlias.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/OpenAlias.ts.golden new file mode 100644 index 000000000..8046608e8 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/OpenAlias.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { OpenInner, OpenInner$Wire } from './OpenInner'; +import { openInnerFromJSON, openInnerToJSON } from './OpenInner'; + +export type OpenAlias$Wire = OpenInner$Wire; + +export type OpenAlias = OpenInner; + +export function openAliasFromJSON(json: OpenAlias$Wire): OpenAlias { + return openInnerFromJSON(json); +} + +export function openAliasToJSON(value: OpenAlias): OpenAlias$Wire { + return openInnerToJSON(value); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/OpenInner.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/OpenInner.ts.golden new file mode 100644 index 000000000..5ca61e080 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/OpenInner.ts.golden @@ -0,0 +1,16 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export type OpenInner$Wire = unknown; + +export type OpenInner = OpenInner$Wire; + +export function openInnerFromJSON(json: OpenInner$Wire): OpenInner { + return json; +} + +export function openInnerToJSON(value: OpenInner): OpenInner$Wire { + return value; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/OpenMap.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/OpenMap.ts.golden new file mode 100644 index 000000000..2bec8ab19 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/OpenMap.ts.golden @@ -0,0 +1,40 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { openInnerFromJSON, openInnerToJSON } from './OpenInner'; + +export interface OpenMap$Wire extends Readonly> {} + +export interface OpenMap extends Readonly> {} + +export function openMapFromJSON(json: OpenMap$Wire): OpenMap { + const sourceEntries = Object.entries(json); + const additionalEntries = sourceEntries; + const additionalProperties = Object.fromEntries( + additionalEntries.map(([key, value]) => { + const additionalValue = value; + return [key, openInnerFromJSON(additionalValue)] as const; + }), + ); + const result = { + ...additionalProperties, + }; + return result; +} + +export function openMapToJSON(value: OpenMap): OpenMap$Wire { + const sourceEntries = Object.entries(value); + const additionalEntries = sourceEntries; + const additionalProperties = Object.fromEntries( + additionalEntries.map(([key, value]) => { + const additionalValue = value; + return [key, openInnerToJSON(additionalValue)] as const; + }), + ); + const result = { + ...additionalProperties, + }; + return result; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/OpenTool.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/OpenTool.ts.golden new file mode 100644 index 000000000..dbe7bf1a7 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/OpenTool.ts.golden @@ -0,0 +1,16 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export type OpenTool$Wire = unknown; + +export type OpenTool = OpenTool$Wire; + +export function openToolFromJSON(json: OpenTool$Wire): OpenTool { + return json; +} + +export function openToolToJSON(value: OpenTool): OpenTool$Wire { + return value; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/OtherDetail.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/OtherDetail.ts.golden new file mode 100644 index 000000000..9bb6b93d7 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/OtherDetail.ts.golden @@ -0,0 +1,28 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface OtherDetail$Wire { + readonly optional_flag?: boolean; + readonly wire_name: string; +} + +export interface OtherDetail { + readonly optionalFlag?: boolean; + readonly wireName: string; +} + +export function otherDetailFromJSON(json: OtherDetail$Wire): OtherDetail { + return { + optionalFlag: json.optional_flag, + wireName: json.wire_name, + }; +} + +export function otherDetailToJSON(value: OtherDetail): OtherDetail$Wire { + return { + optional_flag: value.optionalFlag, + wire_name: value.wireName, + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/Outer.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/Outer.ts.golden new file mode 100644 index 000000000..af84c397e --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/Outer.ts.golden @@ -0,0 +1,16 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export type Outer$Wire = unknown; + +export type Outer = Outer$Wire; + +export function outerFromJSON(json: Outer$Wire): Outer { + return json; +} + +export function outerToJSON(value: Outer): Outer$Wire { + return value; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/PrimitiveArrayChoice.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/PrimitiveArrayChoice.ts.golden new file mode 100644 index 000000000..562a6762e --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/PrimitiveArrayChoice.ts.golden @@ -0,0 +1,20 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail$Wire } from './Detail'; + +export type PrimitiveArrayChoice$Wire = readonly number[] | Detail$Wire; + +export type PrimitiveArrayChoice = PrimitiveArrayChoice$Wire; + +export function primitiveArrayChoiceFromJSON( + json: PrimitiveArrayChoice$Wire, +): PrimitiveArrayChoice { + return json; +} + +export function primitiveArrayChoiceToJSON(value: PrimitiveArrayChoice): PrimitiveArrayChoice$Wire { + return value; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/PrimitiveMapChoice.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/PrimitiveMapChoice.ts.golden new file mode 100644 index 000000000..12f989dfb --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/PrimitiveMapChoice.ts.golden @@ -0,0 +1,18 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail$Wire } from './Detail'; + +export type PrimitiveMapChoice$Wire = globalThis.Record | Detail$Wire; + +export type PrimitiveMapChoice = PrimitiveMapChoice$Wire; + +export function primitiveMapChoiceFromJSON(json: PrimitiveMapChoice$Wire): PrimitiveMapChoice { + return json; +} + +export function primitiveMapChoiceToJSON(value: PrimitiveMapChoice): PrimitiveMapChoice$Wire { + return value; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/RawEnvelope.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/RawEnvelope.ts.golden new file mode 100644 index 000000000..508fa4eb9 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/RawEnvelope.ts.golden @@ -0,0 +1,37 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { AliasChoice, AliasChoice$Wire } from './AliasChoice'; +import { aliasChoiceFromJSON, aliasChoiceToJSON } from './AliasChoice'; +import type { Detail, Detail$Wire } from './Detail'; +import { detailFromJSON, detailToJSON } from './Detail'; + +export interface RawEnvelope$Wire { + readonly plain_detail: Detail$Wire; + readonly raw_choice: AliasChoice$Wire; + readonly request_id: string; +} + +export interface RawEnvelope { + readonly plainDetail: Detail; + readonly rawChoice: AliasChoice; + readonly requestId: string; +} + +export function rawEnvelopeFromJSON(json: RawEnvelope$Wire): RawEnvelope { + return { + plainDetail: detailFromJSON(json.plain_detail), + rawChoice: aliasChoiceFromJSON(json.raw_choice), + requestId: json.request_id, + }; +} + +export function rawEnvelopeToJSON(value: RawEnvelope): RawEnvelope$Wire { + return { + plain_detail: detailToJSON(value.plainDetail), + raw_choice: aliasChoiceToJSON(value.rawChoice), + request_id: value.requestId, + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/Readonly.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/Readonly.ts.golden new file mode 100644 index 000000000..5e4f45908 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/Readonly.ts.golden @@ -0,0 +1,44 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { convert$6FromJSON, convert$6ToJSON } from '../runtime/valueConversion$6'; +import type { Detail, Detail$Wire } from './Detail'; +import { detailFromJSON, detailToJSON } from './Detail'; + +export type Readonly$Wire = globalThis.Readonly> & + globalThis.Readonly>; + +export type Readonly = globalThis.Readonly> & + globalThis.Readonly>; + +export function readonlyFromJSON(json: Readonly$Wire): Readonly { + if (Object.prototype.hasOwnProperty.call(json, 'extension_key')) { + const contentValue = (json as globalThis.Record<'extension_key', Detail$Wire>)['extension_key']; + const entries = Object.entries(json).filter(([key]) => key !== 'extension_key'); + const additionalProperties = Object.fromEntries( + entries.map(([key, value]) => { + const additionalValue = value as readonly Detail$Wire[]; + return [key, convert$6FromJSON(additionalValue)] as const; + }), + ); + return { ...additionalProperties, ['extension_key']: detailFromJSON(contentValue) }; + } + throw new Error('Unknown variant'); +} + +export function readonlyToJSON(value: Readonly): Readonly$Wire { + if (Object.prototype.hasOwnProperty.call(value, 'extension_key')) { + const contentValue = (value as globalThis.Record<'extension_key', Detail>)['extension_key']; + const entries = Object.entries(value).filter(([key]) => key !== 'extension_key'); + const additionalProperties = Object.fromEntries( + entries.map(([key, value]) => { + const additionalValue = value as readonly Detail[]; + return [key, convert$6ToJSON(additionalValue)] as const; + }), + ); + return { ...additionalProperties, ['extension_key']: detailToJSON(contentValue) }; + } + throw new Error('Unknown variant'); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/Record.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/Record.ts.golden new file mode 100644 index 000000000..b221a9594 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/Record.ts.golden @@ -0,0 +1,43 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail, Detail$Wire } from './Detail'; +import { detailFromJSON, detailToJSON } from './Detail'; + +export type Record$Wire = + | globalThis.Record<'event_type', 'empty'> + | (globalThis.Record<'event_type', 'data'> & globalThis.Record<'event_data', Detail$Wire>); + +export type Record = + | globalThis.Record<'eventType', 'empty'> + | (globalThis.Record<'eventType', 'data'> & globalThis.Record<'eventData', Detail>); + +export function recordFromJSON(json: Record$Wire): Record { + if (json['event_type'] === 'empty') { + return { ['eventType']: 'empty' } as Record; + } + if (json['event_type'] === 'data') { + return { + ['eventType']: 'data', + ['eventData']: detailFromJSON( + (json as globalThis.Record<'event_data', Detail$Wire>)['event_data'], + ), + } as Record; + } + throw new Error('Unknown variant'); +} + +export function recordToJSON(value: Record): Record$Wire { + if (value['eventType'] === 'empty') { + return { ['event_type']: 'empty' } as Record$Wire; + } + if (value['eventType'] === 'data') { + return { + ['event_type']: 'data', + ['event_data']: detailToJSON((value as globalThis.Record<'eventData', Detail>)['eventData']), + } as Record$Wire; + } + throw new Error('Unknown variant'); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/RecordEmpty.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/RecordEmpty.ts.golden new file mode 100644 index 000000000..9fd01d883 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/RecordEmpty.ts.golden @@ -0,0 +1,24 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface RecordEmpty$Wire { + readonly event_type: 'empty'; +} + +export interface RecordEmpty { + readonly eventType: 'empty'; +} + +export function recordEmptyFromJSON(json: RecordEmpty$Wire): RecordEmpty { + return { + eventType: json.event_type, + }; +} + +export function recordEmptyToJSON(value: RecordEmpty): RecordEmpty$Wire { + return { + event_type: value.eventType, + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/RecordOrBoolean.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/RecordOrBoolean.ts.golden new file mode 100644 index 000000000..6d5839e1f --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/RecordOrBoolean.ts.golden @@ -0,0 +1,18 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Record$Wire } from './Record'; + +export type RecordOrBoolean$Wire = Record$Wire | boolean; + +export type RecordOrBoolean = RecordOrBoolean$Wire; + +export function recordOrBooleanFromJSON(json: RecordOrBoolean$Wire): RecordOrBoolean { + return json; +} + +export function recordOrBooleanToJSON(value: RecordOrBoolean): RecordOrBoolean$Wire { + return value; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/RecursiveDictionaryChoice.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/RecursiveDictionaryChoice.ts.golden new file mode 100644 index 000000000..c31447e18 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/RecursiveDictionaryChoice.ts.golden @@ -0,0 +1,20 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export type RecursiveDictionaryChoice$Wire = unknown; + +export type RecursiveDictionaryChoice = RecursiveDictionaryChoice$Wire; + +export function recursiveDictionaryChoiceFromJSON( + json: RecursiveDictionaryChoice$Wire, +): RecursiveDictionaryChoice { + return json; +} + +export function recursiveDictionaryChoiceToJSON( + value: RecursiveDictionaryChoice, +): RecursiveDictionaryChoice$Wire { + return value; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/RecursiveList.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/RecursiveList.ts.golden new file mode 100644 index 000000000..20b970b9a --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/RecursiveList.ts.golden @@ -0,0 +1,18 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail$Wire } from './Detail'; + +export type RecursiveList$Wire = readonly RecursiveList$Wire[] | Detail$Wire; + +export type RecursiveList = RecursiveList$Wire; + +export function recursiveListFromJSON(json: RecursiveList$Wire): RecursiveList { + return json; +} + +export function recursiveListToJSON(value: RecursiveList): RecursiveList$Wire { + return value; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/RecursiveOpenDictionary.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/RecursiveOpenDictionary.ts.golden new file mode 100644 index 000000000..0eb9370d9 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/RecursiveOpenDictionary.ts.golden @@ -0,0 +1,20 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export type RecursiveOpenDictionary$Wire = unknown; + +export type RecursiveOpenDictionary = RecursiveOpenDictionary$Wire; + +export function recursiveOpenDictionaryFromJSON( + json: RecursiveOpenDictionary$Wire, +): RecursiveOpenDictionary { + return json; +} + +export function recursiveOpenDictionaryToJSON( + value: RecursiveOpenDictionary, +): RecursiveOpenDictionary$Wire { + return value; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/ScalarEnumChoice.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/ScalarEnumChoice.ts.golden new file mode 100644 index 000000000..bee2fa43b --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/ScalarEnumChoice.ts.golden @@ -0,0 +1,16 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export type ScalarEnumChoice$Wire = number | boolean; + +export type ScalarEnumChoice = ScalarEnumChoice$Wire; + +export function scalarEnumChoiceFromJSON(json: ScalarEnumChoice$Wire): ScalarEnumChoice { + return json; +} + +export function scalarEnumChoiceToJSON(value: ScalarEnumChoice): ScalarEnumChoice$Wire { + return value; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/SearchTool.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/SearchTool.ts.golden new file mode 100644 index 000000000..9992b2695 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/SearchTool.ts.golden @@ -0,0 +1,32 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface SearchTool$Wire { + readonly max_uses?: number; + readonly name: 'web_search'; + readonly type: 'web_search_20250305'; +} + +export interface SearchTool { + readonly maxUses?: number; + readonly name: 'web_search'; + readonly type: 'web_search_20250305'; +} + +export function searchToolFromJSON(json: SearchTool$Wire): SearchTool { + return { + maxUses: json.max_uses, + name: json.name, + type: json['type'], + }; +} + +export function searchToolToJSON(value: SearchTool): SearchTool$Wire { + return { + max_uses: value.maxUses, + name: value.name, + type: value['type'], + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/SendOptionalInlineScalarRequest.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/SendOptionalInlineScalarRequest.ts.golden new file mode 100644 index 000000000..7e2d49e03 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/SendOptionalInlineScalarRequest.ts.golden @@ -0,0 +1,20 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export type SendOptionalInlineScalarRequest$Wire = string | null | number; + +export type SendOptionalInlineScalarRequest = SendOptionalInlineScalarRequest$Wire; + +export function sendOptionalInlineScalarRequestFromJSON( + json: SendOptionalInlineScalarRequest$Wire, +): SendOptionalInlineScalarRequest { + return json; +} + +export function sendOptionalInlineScalarRequestToJSON( + value: SendOptionalInlineScalarRequest, +): SendOptionalInlineScalarRequest$Wire { + return value; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/TaggedMapA.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/TaggedMapA.ts.golden new file mode 100644 index 000000000..8aa8640e2 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/TaggedMapA.ts.golden @@ -0,0 +1,43 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail, Detail$Wire } from './Detail'; +import { detailFromJSON, detailToJSON } from './Detail'; + +export type TaggedMapA$Wire = globalThis.Readonly> & + globalThis.Readonly>; + +export type TaggedMapA = globalThis.Readonly> & + globalThis.Readonly>; + +export function taggedMapAFromJSON(json: TaggedMapA$Wire): TaggedMapA { + if (Object.prototype.hasOwnProperty.call(json, 'payload_key')) { + const contentValue = (json as globalThis.Record<'payload_key', Detail$Wire>)['payload_key']; + const entries = Object.entries(json).filter(([key]) => key !== 'payload_key'); + const additionalProperties = Object.fromEntries( + entries.map(([key, value]) => { + const additionalValue = value as Detail$Wire; + return [key, detailFromJSON(additionalValue)] as const; + }), + ); + return { ...additionalProperties, ['payload_key']: detailFromJSON(contentValue) }; + } + throw new Error('Unknown variant'); +} + +export function taggedMapAToJSON(value: TaggedMapA): TaggedMapA$Wire { + if (Object.prototype.hasOwnProperty.call(value, 'payload_key')) { + const contentValue = (value as globalThis.Record<'payload_key', Detail>)['payload_key']; + const entries = Object.entries(value).filter(([key]) => key !== 'payload_key'); + const additionalProperties = Object.fromEntries( + entries.map(([key, value]) => { + const additionalValue = value as Detail; + return [key, detailToJSON(additionalValue)] as const; + }), + ); + return { ...additionalProperties, ['payload_key']: detailToJSON(contentValue) }; + } + throw new Error('Unknown variant'); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/TaggedMapB.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/TaggedMapB.ts.golden new file mode 100644 index 000000000..cb54cde22 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/TaggedMapB.ts.golden @@ -0,0 +1,44 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { convert$6FromJSON, convert$6ToJSON } from '../runtime/valueConversion$6'; +import type { Detail, Detail$Wire } from './Detail'; +import { detailFromJSON, detailToJSON } from './Detail'; + +export type TaggedMapB$Wire = globalThis.Readonly> & + globalThis.Readonly>; + +export type TaggedMapB = globalThis.Readonly> & + globalThis.Readonly>; + +export function taggedMapBFromJSON(json: TaggedMapB$Wire): TaggedMapB { + if (Object.prototype.hasOwnProperty.call(json, 'payload_key')) { + const contentValue = (json as globalThis.Record<'payload_key', Detail$Wire>)['payload_key']; + const entries = Object.entries(json).filter(([key]) => key !== 'payload_key'); + const additionalProperties = Object.fromEntries( + entries.map(([key, value]) => { + const additionalValue = value as readonly Detail$Wire[]; + return [key, convert$6FromJSON(additionalValue)] as const; + }), + ); + return { ...additionalProperties, ['payload_key']: detailFromJSON(contentValue) }; + } + throw new Error('Unknown variant'); +} + +export function taggedMapBToJSON(value: TaggedMapB): TaggedMapB$Wire { + if (Object.prototype.hasOwnProperty.call(value, 'payload_key')) { + const contentValue = (value as globalThis.Record<'payload_key', Detail>)['payload_key']; + const entries = Object.entries(value).filter(([key]) => key !== 'payload_key'); + const additionalProperties = Object.fromEntries( + entries.map(([key, value]) => { + const additionalValue = value as readonly Detail[]; + return [key, convert$6ToJSON(additionalValue)] as const; + }), + ); + return { ...additionalProperties, ['payload_key']: detailToJSON(contentValue) }; + } + throw new Error('Unknown variant'); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/TaggedMapChoice.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/TaggedMapChoice.ts.golden new file mode 100644 index 000000000..ccb95e6ce --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/TaggedMapChoice.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { TaggedMapA$Wire } from './TaggedMapA'; +import type { TaggedMapB$Wire } from './TaggedMapB'; + +export type TaggedMapChoice$Wire = TaggedMapA$Wire | TaggedMapB$Wire; + +export type TaggedMapChoice = TaggedMapChoice$Wire; + +export function taggedMapChoiceFromJSON(json: TaggedMapChoice$Wire): TaggedMapChoice { + return json; +} + +export function taggedMapChoiceToJSON(value: TaggedMapChoice): TaggedMapChoice$Wire { + return value; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/Tool.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/Tool.ts.golden new file mode 100644 index 000000000..e4deabcfd --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/Tool.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { ClientTool$Wire } from './ClientTool'; +import type { SearchTool$Wire } from './SearchTool'; + +export type Tool$Wire = ClientTool$Wire | SearchTool$Wire; + +export type Tool = Tool$Wire; + +export function toolFromJSON(json: Tool$Wire): Tool { + return json; +} + +export function toolToJSON(value: Tool): Tool$Wire { + return value; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/ToolAlias.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/ToolAlias.ts.golden new file mode 100644 index 000000000..19920bb13 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/ToolAlias.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Tool, Tool$Wire } from './Tool'; +import { toolFromJSON, toolToJSON } from './Tool'; + +export type ToolAlias$Wire = Tool$Wire; + +export type ToolAlias = Tool; + +export function toolAliasFromJSON(json: ToolAlias$Wire): ToolAlias { + return toolFromJSON(json); +} + +export function toolAliasToJSON(value: ToolAlias): ToolAlias$Wire { + return toolToJSON(value); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/ToolList.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/ToolList.ts.golden new file mode 100644 index 000000000..55ee54279 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/ToolList.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { ClientTool$Wire } from './ClientTool'; +import type { SearchTool$Wire } from './SearchTool'; + +export type ToolList$Wire = readonly ClientTool$Wire[] | readonly SearchTool$Wire[]; + +export type ToolList = ToolList$Wire; + +export function toolListFromJSON(json: ToolList$Wire): ToolList { + return json; +} + +export function toolListToJSON(value: ToolList): ToolList$Wire { + return value; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/ToolMap.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/ToolMap.ts.golden new file mode 100644 index 000000000..42211bb52 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/ToolMap.ts.golden @@ -0,0 +1,21 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { ClientTool$Wire } from './ClientTool'; +import type { SearchTool$Wire } from './SearchTool'; + +export type ToolMap$Wire = + | globalThis.Record + | globalThis.Record; + +export type ToolMap = ToolMap$Wire; + +export function toolMapFromJSON(json: ToolMap$Wire): ToolMap { + return json; +} + +export function toolMapToJSON(value: ToolMap): ToolMap$Wire { + return value; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/ToolRequest.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/ToolRequest.ts.golden new file mode 100644 index 000000000..c44a10e9c --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/ToolRequest.ts.golden @@ -0,0 +1,27 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { convert$21FromJSON, convert$21ToJSON } from '../runtime/valueConversion$21'; +import type { Tool, Tool$Wire } from './Tool'; + +export interface ToolRequest$Wire { + readonly tools: readonly Tool$Wire[]; +} + +export interface ToolRequest { + readonly tools: readonly Tool[]; +} + +export function toolRequestFromJSON(json: ToolRequest$Wire): ToolRequest { + return { + tools: convert$21FromJSON(json.tools), + }; +} + +export function toolRequestToJSON(value: ToolRequest): ToolRequest$Wire { + return { + tools: convert$21ToJSON(value.tools), + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/TwiceNullableOpaque.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/TwiceNullableOpaque.ts.golden new file mode 100644 index 000000000..a402aa4f7 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/TwiceNullableOpaque.ts.golden @@ -0,0 +1,16 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export type TwiceNullableOpaque$Wire = unknown; + +export type TwiceNullableOpaque = TwiceNullableOpaque$Wire; + +export function twiceNullableOpaqueFromJSON(json: TwiceNullableOpaque$Wire): TwiceNullableOpaque { + return json; +} + +export function twiceNullableOpaqueToJSON(value: TwiceNullableOpaque): TwiceNullableOpaque$Wire { + return value; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/TwiceNullableOuter.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/TwiceNullableOuter.ts.golden new file mode 100644 index 000000000..c6a783dfc --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/TwiceNullableOuter.ts.golden @@ -0,0 +1,16 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export type TwiceNullableOuter$Wire = unknown; + +export type TwiceNullableOuter = TwiceNullableOuter$Wire; + +export function twiceNullableOuterFromJSON(json: TwiceNullableOuter$Wire): TwiceNullableOuter { + return json; +} + +export function twiceNullableOuterToJSON(value: TwiceNullableOuter): TwiceNullableOuter$Wire { + return value; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/UntypedNumericChoice.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/UntypedNumericChoice.ts.golden new file mode 100644 index 000000000..3fd8663c3 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/UntypedNumericChoice.ts.golden @@ -0,0 +1,21 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { UntypedNumericOne$Wire } from './UntypedNumericOne'; +import type { UntypedNumericTwo$Wire } from './UntypedNumericTwo'; + +export type UntypedNumericChoice$Wire = UntypedNumericOne$Wire | UntypedNumericTwo$Wire; + +export type UntypedNumericChoice = UntypedNumericChoice$Wire; + +export function untypedNumericChoiceFromJSON( + json: UntypedNumericChoice$Wire, +): UntypedNumericChoice { + return json; +} + +export function untypedNumericChoiceToJSON(value: UntypedNumericChoice): UntypedNumericChoice$Wire { + return value; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/UntypedNumericOne.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/UntypedNumericOne.ts.golden new file mode 100644 index 000000000..1695e895f --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/UntypedNumericOne.ts.golden @@ -0,0 +1,28 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface UntypedNumericOne$Wire { + readonly first_value?: string; + readonly revision: string; +} + +export interface UntypedNumericOne { + readonly firstValue?: string; + readonly revision: string; +} + +export function untypedNumericOneFromJSON(json: UntypedNumericOne$Wire): UntypedNumericOne { + return { + firstValue: json.first_value, + revision: json.revision, + }; +} + +export function untypedNumericOneToJSON(value: UntypedNumericOne): UntypedNumericOne$Wire { + return { + first_value: value.firstValue, + revision: value.revision, + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/UntypedNumericTwo.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/UntypedNumericTwo.ts.golden new file mode 100644 index 000000000..0ebd0aa68 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/UntypedNumericTwo.ts.golden @@ -0,0 +1,28 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface UntypedNumericTwo$Wire { + readonly revision: string; + readonly second_value?: string; +} + +export interface UntypedNumericTwo { + readonly revision: string; + readonly secondValue?: string; +} + +export function untypedNumericTwoFromJSON(json: UntypedNumericTwo$Wire): UntypedNumericTwo { + return { + revision: json.revision, + secondValue: json.second_value, + }; +} + +export function untypedNumericTwoToJSON(value: UntypedNumericTwo): UntypedNumericTwo$Wire { + return { + revision: value.revision, + second_value: value.secondValue, + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/ValueAlias.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/ValueAlias.ts.golden new file mode 100644 index 000000000..3599f082d --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/ValueAlias.ts.golden @@ -0,0 +1,18 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail$Wire } from './Detail'; + +export type ValueAlias$Wire = readonly (globalThis.Record | string)[]; + +export type ValueAlias = readonly (globalThis.Record | string)[]; + +export function valueAliasFromJSON(json: ValueAlias$Wire): ValueAlias { + return json; +} + +export function valueAliasToJSON(value: ValueAlias): ValueAlias$Wire { + return value; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/WrappedArrayOrBoolean.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/WrappedArrayOrBoolean.ts.golden new file mode 100644 index 000000000..62b37b1f0 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/WrappedArrayOrBoolean.ts.golden @@ -0,0 +1,22 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { ArrayWrapped$Wire } from './ArrayWrapped'; + +export type WrappedArrayOrBoolean$Wire = ArrayWrapped$Wire | boolean; + +export type WrappedArrayOrBoolean = WrappedArrayOrBoolean$Wire; + +export function wrappedArrayOrBooleanFromJSON( + json: WrappedArrayOrBoolean$Wire, +): WrappedArrayOrBoolean { + return json; +} + +export function wrappedArrayOrBooleanToJSON( + value: WrappedArrayOrBoolean, +): WrappedArrayOrBoolean$Wire { + return value; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/WrappedMapOrBoolean.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/WrappedMapOrBoolean.ts.golden new file mode 100644 index 000000000..68221fb86 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/WrappedMapOrBoolean.ts.golden @@ -0,0 +1,18 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { MapWrapped$Wire } from './MapWrapped'; + +export type WrappedMapOrBoolean$Wire = MapWrapped$Wire | boolean; + +export type WrappedMapOrBoolean = WrappedMapOrBoolean$Wire; + +export function wrappedMapOrBooleanFromJSON(json: WrappedMapOrBoolean$Wire): WrappedMapOrBoolean { + return json; +} + +export function wrappedMapOrBooleanToJSON(value: WrappedMapOrBoolean): WrappedMapOrBoolean$Wire { + return value; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/WrappedScalarInner.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/WrappedScalarInner.ts.golden new file mode 100644 index 000000000..6d0d76e82 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/WrappedScalarInner.ts.golden @@ -0,0 +1,18 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { NullableScalarInner$Wire } from './NullableScalarInner'; + +export type WrappedScalarInner$Wire = NullableScalarInner$Wire | null; + +export type WrappedScalarInner = WrappedScalarInner$Wire; + +export function wrappedScalarInnerFromJSON(json: WrappedScalarInner$Wire): WrappedScalarInner { + return json; +} + +export function wrappedScalarInnerToJSON(value: WrappedScalarInner): WrappedScalarInner$Wire { + return value; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/WrapperChoice.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/WrapperChoice.ts.golden new file mode 100644 index 000000000..d29c83734 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/WrapperChoice.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { WrapperOne$Wire } from './WrapperOne'; +import type { WrapperTwo$Wire } from './WrapperTwo'; + +export type WrapperChoice$Wire = WrapperOne$Wire | WrapperTwo$Wire; + +export type WrapperChoice = WrapperChoice$Wire; + +export function wrapperChoiceFromJSON(json: WrapperChoice$Wire): WrapperChoice { + return json; +} + +export function wrapperChoiceToJSON(value: WrapperChoice): WrapperChoice$Wire { + return value; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/WrapperOne.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/WrapperOne.ts.golden new file mode 100644 index 000000000..5d9658c83 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/WrapperOne.ts.golden @@ -0,0 +1,28 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface WrapperOne$Wire { + readonly first_value?: string; + readonly revision: number | null; +} + +export interface WrapperOne { + readonly firstValue?: string; + readonly revision: number | null; +} + +export function wrapperOneFromJSON(json: WrapperOne$Wire): WrapperOne { + return { + firstValue: json.first_value, + revision: json.revision, + }; +} + +export function wrapperOneToJSON(value: WrapperOne): WrapperOne$Wire { + return { + first_value: value.firstValue, + revision: value.revision, + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/WrapperTwo.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/WrapperTwo.ts.golden new file mode 100644 index 000000000..dcca3ef4c --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/WrapperTwo.ts.golden @@ -0,0 +1,28 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface WrapperTwo$Wire { + readonly revision: number | null; + readonly second_value?: string; +} + +export interface WrapperTwo { + readonly revision: number | null; + readonly secondValue?: string; +} + +export function wrapperTwoFromJSON(json: WrapperTwo$Wire): WrapperTwo { + return { + revision: json.revision, + secondValue: json.second_value, + }; +} + +export function wrapperTwoToJSON(value: WrapperTwo): WrapperTwo$Wire { + return { + revision: value.revision, + second_value: value.secondValue, + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/index.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/index.ts.golden new file mode 100644 index 000000000..5e37ecbbc --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/models/index.ts.golden @@ -0,0 +1,361 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export type { Adjacent, Adjacent$Wire } from './Adjacent'; +export { adjacentFromJSON, adjacentToJSON } from './Adjacent'; +export type { AdjacentContainer, AdjacentContainer$Wire } from './AdjacentContainer'; +export { adjacentContainerFromJSON, adjacentContainerToJSON } from './AdjacentContainer'; +export type { AdjacentContainerEmpty, AdjacentContainerEmpty$Wire } from './AdjacentContainerEmpty'; +export { + adjacentContainerEmptyFromJSON, + adjacentContainerEmptyToJSON, +} from './AdjacentContainerEmpty'; +export type { AdjacentEmpty, AdjacentEmpty$Wire } from './AdjacentEmpty'; +export { adjacentEmptyFromJSON, adjacentEmptyToJSON } from './AdjacentEmpty'; +export type { AdjacentOrNumber, AdjacentOrNumber$Wire } from './AdjacentOrNumber'; +export { adjacentOrNumberFromJSON, adjacentOrNumberToJSON } from './AdjacentOrNumber'; +export type { AdjacentPrimitive, AdjacentPrimitive$Wire } from './AdjacentPrimitive'; +export { adjacentPrimitiveFromJSON, adjacentPrimitiveToJSON } from './AdjacentPrimitive'; +export type { AdjacentPrimitiveEmpty, AdjacentPrimitiveEmpty$Wire } from './AdjacentPrimitiveEmpty'; +export { + adjacentPrimitiveEmptyFromJSON, + adjacentPrimitiveEmptyToJSON, +} from './AdjacentPrimitiveEmpty'; +export type { AliasChoice, AliasChoice$Wire } from './AliasChoice'; +export { aliasChoiceFromJSON, aliasChoiceToJSON } from './AliasChoice'; +export type { AliasOuter, AliasOuter$Wire } from './AliasOuter'; +export { aliasOuterFromJSON, aliasOuterToJSON } from './AliasOuter'; +export type { + AlreadyNullableEnumParent, + AlreadyNullableEnumParent$Wire, +} from './AlreadyNullableEnumParent'; +export { + alreadyNullableEnumParentFromJSON, + alreadyNullableEnumParentToJSON, +} from './AlreadyNullableEnumParent'; +export type { + AlreadyNullableOuterEnumParent, + AlreadyNullableOuterEnumParent$Wire, +} from './AlreadyNullableOuterEnumParent'; +export { + alreadyNullableOuterEnumParentFromJSON, + alreadyNullableOuterEnumParentToJSON, +} from './AlreadyNullableOuterEnumParent'; +export type { AlternativeTool, AlternativeTool$Wire } from './AlternativeTool'; +export { alternativeToolFromJSON, alternativeToolToJSON } from './AlternativeTool'; +export type { AmbiguousInner, AmbiguousInner$Wire } from './AmbiguousInner'; +export { ambiguousInnerFromJSON, ambiguousInnerToJSON } from './AmbiguousInner'; +export type { AmbiguousMap, AmbiguousMap$Wire } from './AmbiguousMap'; +export { ambiguousMapFromJSON, ambiguousMapToJSON } from './AmbiguousMap'; +export type { AmbiguousMapOuter, AmbiguousMapOuter$Wire } from './AmbiguousMapOuter'; +export { ambiguousMapOuterFromJSON, ambiguousMapOuterToJSON } from './AmbiguousMapOuter'; +export type { AmbiguousOuter, AmbiguousOuter$Wire } from './AmbiguousOuter'; +export { ambiguousOuterFromJSON, ambiguousOuterToJSON } from './AmbiguousOuter'; +export type { AmbiguousTool, AmbiguousTool$Wire } from './AmbiguousTool'; +export { ambiguousToolFromJSON, ambiguousToolToJSON } from './AmbiguousTool'; +export type { ArrayPayload, ArrayPayload$Wire } from './ArrayPayload'; +export { arrayPayloadFromJSON, arrayPayloadToJSON } from './ArrayPayload'; +export type { ArrayPayloadOrBoolean, ArrayPayloadOrBoolean$Wire } from './ArrayPayloadOrBoolean'; +export { + arrayPayloadOrBooleanFromJSON, + arrayPayloadOrBooleanToJSON, +} from './ArrayPayloadOrBoolean'; +export type { ArrayWrapped, ArrayWrapped$Wire } from './ArrayWrapped'; +export { arrayWrappedFromJSON, arrayWrappedToJSON } from './ArrayWrapped'; +export type { BinaryModel, BinaryModel$Wire } from './BinaryModel'; +export { binaryModelFromJSON, binaryModelToJSON } from './BinaryModel'; +export type { BinaryUnion, BinaryUnion$Wire } from './BinaryUnion'; +export { binaryUnionFromJSON, binaryUnionToJSON } from './BinaryUnion'; +export type { ClientTool, ClientTool$Wire } from './ClientTool'; +export { clientToolFromJSON, clientToolToJSON } from './ClientTool'; +export type { + ConstrainedOuterEnumParent, + ConstrainedOuterEnumParent$Wire, +} from './ConstrainedOuterEnumParent'; +export { + constrainedOuterEnumParentFromJSON, + constrainedOuterEnumParentToJSON, +} from './ConstrainedOuterEnumParent'; +export type { ContainerOuterEnum, ContainerOuterEnum$Wire } from './ContainerOuterEnum'; +export { containerOuterEnumFromJSON, containerOuterEnumToJSON } from './ContainerOuterEnum'; +export type { Detail, Detail$Wire } from './Detail'; +export { detailFromJSON, detailToJSON } from './Detail'; +export type { DetailArrayOrObject, DetailArrayOrObject$Wire } from './DetailArrayOrObject'; +export { detailArrayOrObjectFromJSON, detailArrayOrObjectToJSON } from './DetailArrayOrObject'; +export type { DictionaryAlias, DictionaryAlias$Wire } from './DictionaryAlias'; +export { dictionaryAliasFromJSON, dictionaryAliasToJSON } from './DictionaryAlias'; +export type { + EnumFreeOuterEnumParent, + EnumFreeOuterEnumParent$Wire, +} from './EnumFreeOuterEnumParent'; +export { + enumFreeOuterEnumParentFromJSON, + enumFreeOuterEnumParentToJSON, +} from './EnumFreeOuterEnumParent'; +export type { ExplicitNullOverlap, ExplicitNullOverlap$Wire } from './ExplicitNullOverlap'; +export { explicitNullOverlapFromJSON, explicitNullOverlapToJSON } from './ExplicitNullOverlap'; +export type { + ExplicitNullableEnumChoice, + ExplicitNullableEnumChoice$Wire, +} from './ExplicitNullableEnumChoice'; +export { + explicitNullableEnumChoiceFromJSON, + explicitNullableEnumChoiceToJSON, +} from './ExplicitNullableEnumChoice'; +export type { InlineDictionaryValues, InlineDictionaryValues$Wire } from './InlineDictionaryValues'; +export { + inlineDictionaryValuesFromJSON, + inlineDictionaryValuesToJSON, +} from './InlineDictionaryValues'; +export type { InlineMixedValues, InlineMixedValues$Wire } from './InlineMixedValues'; +export { inlineMixedValuesFromJSON, inlineMixedValuesToJSON } from './InlineMixedValues'; +export type { InlineNumericValues, InlineNumericValues$Wire } from './InlineNumericValues'; +export { inlineNumericValuesFromJSON, inlineNumericValuesToJSON } from './InlineNumericValues'; +export type { InlineProperty, InlineProperty$Wire } from './InlineProperty'; +export { inlinePropertyFromJSON, inlinePropertyToJSON } from './InlineProperty'; +export type { MapOuter, MapOuter$Wire } from './MapOuter'; +export { mapOuterFromJSON, mapOuterToJSON } from './MapOuter'; +export type { MapWrapped, MapWrapped$Wire } from './MapWrapped'; +export { mapWrappedFromJSON, mapWrappedToJSON } from './MapWrapped'; +export type { MixedMapA, MixedMapA$Wire } from './MixedMapA'; +export { mixedMapAFromJSON, mixedMapAToJSON } from './MixedMapA'; +export type { MixedMapB, MixedMapB$Wire } from './MixedMapB'; +export { mixedMapBFromJSON, mixedMapBToJSON } from './MixedMapB'; +export type { MixedMapChoice, MixedMapChoice$Wire } from './MixedMapChoice'; +export { mixedMapChoiceFromJSON, mixedMapChoiceToJSON } from './MixedMapChoice'; +export type { NamedContainer, NamedContainer$Wire } from './NamedContainer'; +export { namedContainerFromJSON, namedContainerToJSON } from './NamedContainer'; +export type { NamedValue, NamedValue$Wire } from './NamedValue'; +export { namedValueFromJSON, namedValueToJSON } from './NamedValue'; +export type { NestedDetailLists, NestedDetailLists$Wire } from './NestedDetailLists'; +export { nestedDetailListsFromJSON, nestedDetailListsToJSON } from './NestedDetailLists'; +export type { NestedEnumChoice, NestedEnumChoice$Wire } from './NestedEnumChoice'; +export { nestedEnumChoiceFromJSON, nestedEnumChoiceToJSON } from './NestedEnumChoice'; +export type { NestedEnumNarrow, NestedEnumNarrow$Wire } from './NestedEnumNarrow'; +export { nestedEnumNarrowFromJSON, nestedEnumNarrowToJSON } from './NestedEnumNarrow'; +export type { + NestedExplicitNullOverlap, + NestedExplicitNullOverlap$Wire, +} from './NestedExplicitNullOverlap'; +export { + nestedExplicitNullOverlapFromJSON, + nestedExplicitNullOverlapToJSON, +} from './NestedExplicitNullOverlap'; +export type { + NestedNullableEnumChoice, + NestedNullableEnumChoice$Wire, +} from './NestedNullableEnumChoice'; +export { + nestedNullableEnumChoiceFromJSON, + nestedNullableEnumChoiceToJSON, +} from './NestedNullableEnumChoice'; +export type { + NestedNullableEnumNarrow, + NestedNullableEnumNarrow$Wire, +} from './NestedNullableEnumNarrow'; +export { + nestedNullableEnumNarrowFromJSON, + nestedNullableEnumNarrowToJSON, +} from './NestedNullableEnumNarrow'; +export type { NullableDetail, NullableDetail$Wire } from './NullableDetail'; +export { nullableDetailFromJSON, nullableDetailToJSON } from './NullableDetail'; +export type { NullableEnumParent, NullableEnumParent$Wire } from './NullableEnumParent'; +export { nullableEnumParentFromJSON, nullableEnumParentToJSON } from './NullableEnumParent'; +export type { + NullableInlineEnumParent, + NullableInlineEnumParent$Wire, +} from './NullableInlineEnumParent'; +export { + nullableInlineEnumParentFromJSON, + nullableInlineEnumParentToJSON, +} from './NullableInlineEnumParent'; +export type { NullableMapFallback, NullableMapFallback$Wire } from './NullableMapFallback'; +export { nullableMapFallbackFromJSON, nullableMapFallbackToJSON } from './NullableMapFallback'; +export type { NullableNonNullScalars, NullableNonNullScalars$Wire } from './NullableNonNullScalars'; +export { + nullableNonNullScalarsFromJSON, + nullableNonNullScalarsToJSON, +} from './NullableNonNullScalars'; +export type { + NullableNonNullScalarsItems, + NullableNonNullScalarsItems$Wire, +} from './NullableNonNullScalarsItems'; +export { + nullableNonNullScalarsItemsFromJSON, + nullableNonNullScalarsItemsToJSON, +} from './NullableNonNullScalarsItems'; +export type { NullableNumericChoice, NullableNumericChoice$Wire } from './NullableNumericChoice'; +export { + nullableNumericChoiceFromJSON, + nullableNumericChoiceToJSON, +} from './NullableNumericChoice'; +export type { NullableNumericOne, NullableNumericOne$Wire } from './NullableNumericOne'; +export { nullableNumericOneFromJSON, nullableNumericOneToJSON } from './NullableNumericOne'; +export type { NullableNumericTwo, NullableNumericTwo$Wire } from './NullableNumericTwo'; +export { nullableNumericTwoFromJSON, nullableNumericTwoToJSON } from './NullableNumericTwo'; +export type { NullableOpaque, NullableOpaque$Wire } from './NullableOpaque'; +export { nullableOpaqueFromJSON, nullableOpaqueToJSON } from './NullableOpaque'; +export type { + NullableOpaqueSiblingOuter, + NullableOpaqueSiblingOuter$Wire, +} from './NullableOpaqueSiblingOuter'; +export { + nullableOpaqueSiblingOuterFromJSON, + nullableOpaqueSiblingOuterToJSON, +} from './NullableOpaqueSiblingOuter'; +export type { + NullableOpenExplicitOuter, + NullableOpenExplicitOuter$Wire, +} from './NullableOpenExplicitOuter'; +export { + nullableOpenExplicitOuterFromJSON, + nullableOpenExplicitOuterToJSON, +} from './NullableOpenExplicitOuter'; +export type { NullableOpenInner, NullableOpenInner$Wire } from './NullableOpenInner'; +export { nullableOpenInnerFromJSON, nullableOpenInnerToJSON } from './NullableOpenInner'; +export type { + NullableOpenSiblingOuter, + NullableOpenSiblingOuter$Wire, +} from './NullableOpenSiblingOuter'; +export { + nullableOpenSiblingOuterFromJSON, + nullableOpenSiblingOuterToJSON, +} from './NullableOpenSiblingOuter'; +export type { NullableOverlap, NullableOverlap$Wire } from './NullableOverlap'; +export { nullableOverlapFromJSON, nullableOverlapToJSON } from './NullableOverlap'; +export type { + NullablePlainEnumParent, + NullablePlainEnumParent$Wire, +} from './NullablePlainEnumParent'; +export { + nullablePlainEnumParentFromJSON, + nullablePlainEnumParentToJSON, +} from './NullablePlainEnumParent'; +export type { NullableScalarArray, NullableScalarArray$Wire } from './NullableScalarArray'; +export { nullableScalarArrayFromJSON, nullableScalarArrayToJSON } from './NullableScalarArray'; +export type { NullableScalarInner, NullableScalarInner$Wire } from './NullableScalarInner'; +export { nullableScalarInnerFromJSON, nullableScalarInnerToJSON } from './NullableScalarInner'; +export type { NullableScalarMap, NullableScalarMap$Wire } from './NullableScalarMap'; +export { nullableScalarMapFromJSON, nullableScalarMapToJSON } from './NullableScalarMap'; +export type { NullableScalarParent, NullableScalarParent$Wire } from './NullableScalarParent'; +export { nullableScalarParentFromJSON, nullableScalarParentToJSON } from './NullableScalarParent'; +export type { NullableTool, NullableTool$Wire } from './NullableTool'; +export { nullableToolFromJSON, nullableToolToJSON } from './NullableTool'; +export type { NumericEnumChoice, NumericEnumChoice$Wire } from './NumericEnumChoice'; +export { numericEnumChoiceFromJSON, numericEnumChoiceToJSON } from './NumericEnumChoice'; +export type { NumericOne, NumericOne$Wire } from './NumericOne'; +export { numericOneFromJSON, numericOneToJSON } from './NumericOne'; +export type { NumericTwo, NumericTwo$Wire } from './NumericTwo'; +export { numericTwoFromJSON, numericTwoToJSON } from './NumericTwo'; +export type { OnceNullableOuter, OnceNullableOuter$Wire } from './OnceNullableOuter'; +export { onceNullableOuterFromJSON, onceNullableOuterToJSON } from './OnceNullableOuter'; +export type { OpenAlias, OpenAlias$Wire } from './OpenAlias'; +export { openAliasFromJSON, openAliasToJSON } from './OpenAlias'; +export type { OpenInner, OpenInner$Wire } from './OpenInner'; +export { openInnerFromJSON, openInnerToJSON } from './OpenInner'; +export type { OpenMap, OpenMap$Wire } from './OpenMap'; +export { openMapFromJSON, openMapToJSON } from './OpenMap'; +export type { OpenTool, OpenTool$Wire } from './OpenTool'; +export { openToolFromJSON, openToolToJSON } from './OpenTool'; +export type { OtherDetail, OtherDetail$Wire } from './OtherDetail'; +export { otherDetailFromJSON, otherDetailToJSON } from './OtherDetail'; +export type { Outer, Outer$Wire } from './Outer'; +export { outerFromJSON, outerToJSON } from './Outer'; +export type { PrimitiveArrayChoice, PrimitiveArrayChoice$Wire } from './PrimitiveArrayChoice'; +export { primitiveArrayChoiceFromJSON, primitiveArrayChoiceToJSON } from './PrimitiveArrayChoice'; +export type { PrimitiveMapChoice, PrimitiveMapChoice$Wire } from './PrimitiveMapChoice'; +export { primitiveMapChoiceFromJSON, primitiveMapChoiceToJSON } from './PrimitiveMapChoice'; +export type { RawEnvelope, RawEnvelope$Wire } from './RawEnvelope'; +export { rawEnvelopeFromJSON, rawEnvelopeToJSON } from './RawEnvelope'; +export type { Readonly, Readonly$Wire } from './Readonly'; +export { readonlyFromJSON, readonlyToJSON } from './Readonly'; +export type { Record, Record$Wire } from './Record'; +export { recordFromJSON, recordToJSON } from './Record'; +export type { RecordEmpty, RecordEmpty$Wire } from './RecordEmpty'; +export { recordEmptyFromJSON, recordEmptyToJSON } from './RecordEmpty'; +export type { RecordOrBoolean, RecordOrBoolean$Wire } from './RecordOrBoolean'; +export { recordOrBooleanFromJSON, recordOrBooleanToJSON } from './RecordOrBoolean'; +export type { + RecursiveDictionaryChoice, + RecursiveDictionaryChoice$Wire, +} from './RecursiveDictionaryChoice'; +export { + recursiveDictionaryChoiceFromJSON, + recursiveDictionaryChoiceToJSON, +} from './RecursiveDictionaryChoice'; +export type { RecursiveList, RecursiveList$Wire } from './RecursiveList'; +export { recursiveListFromJSON, recursiveListToJSON } from './RecursiveList'; +export type { + RecursiveOpenDictionary, + RecursiveOpenDictionary$Wire, +} from './RecursiveOpenDictionary'; +export { + recursiveOpenDictionaryFromJSON, + recursiveOpenDictionaryToJSON, +} from './RecursiveOpenDictionary'; +export type { ScalarEnumChoice, ScalarEnumChoice$Wire } from './ScalarEnumChoice'; +export { scalarEnumChoiceFromJSON, scalarEnumChoiceToJSON } from './ScalarEnumChoice'; +export type { SearchTool, SearchTool$Wire } from './SearchTool'; +export { searchToolFromJSON, searchToolToJSON } from './SearchTool'; +export type { TaggedMapA, TaggedMapA$Wire } from './TaggedMapA'; +export { taggedMapAFromJSON, taggedMapAToJSON } from './TaggedMapA'; +export type { TaggedMapB, TaggedMapB$Wire } from './TaggedMapB'; +export { taggedMapBFromJSON, taggedMapBToJSON } from './TaggedMapB'; +export type { TaggedMapChoice, TaggedMapChoice$Wire } from './TaggedMapChoice'; +export { taggedMapChoiceFromJSON, taggedMapChoiceToJSON } from './TaggedMapChoice'; +export type { Tool, Tool$Wire } from './Tool'; +export { toolFromJSON, toolToJSON } from './Tool'; +export type { ToolAlias, ToolAlias$Wire } from './ToolAlias'; +export { toolAliasFromJSON, toolAliasToJSON } from './ToolAlias'; +export type { ToolList, ToolList$Wire } from './ToolList'; +export { toolListFromJSON, toolListToJSON } from './ToolList'; +export type { ToolMap, ToolMap$Wire } from './ToolMap'; +export { toolMapFromJSON, toolMapToJSON } from './ToolMap'; +export type { ToolRequest, ToolRequest$Wire } from './ToolRequest'; +export { toolRequestFromJSON, toolRequestToJSON } from './ToolRequest'; +export type { TwiceNullableOpaque, TwiceNullableOpaque$Wire } from './TwiceNullableOpaque'; +export { twiceNullableOpaqueFromJSON, twiceNullableOpaqueToJSON } from './TwiceNullableOpaque'; +export type { TwiceNullableOuter, TwiceNullableOuter$Wire } from './TwiceNullableOuter'; +export { twiceNullableOuterFromJSON, twiceNullableOuterToJSON } from './TwiceNullableOuter'; +export type { UntypedNumericChoice, UntypedNumericChoice$Wire } from './UntypedNumericChoice'; +export { untypedNumericChoiceFromJSON, untypedNumericChoiceToJSON } from './UntypedNumericChoice'; +export type { UntypedNumericOne, UntypedNumericOne$Wire } from './UntypedNumericOne'; +export { untypedNumericOneFromJSON, untypedNumericOneToJSON } from './UntypedNumericOne'; +export type { UntypedNumericTwo, UntypedNumericTwo$Wire } from './UntypedNumericTwo'; +export { untypedNumericTwoFromJSON, untypedNumericTwoToJSON } from './UntypedNumericTwo'; +export type { ValueAlias, ValueAlias$Wire } from './ValueAlias'; +export { valueAliasFromJSON, valueAliasToJSON } from './ValueAlias'; +export type { WrappedArrayOrBoolean, WrappedArrayOrBoolean$Wire } from './WrappedArrayOrBoolean'; +export { + wrappedArrayOrBooleanFromJSON, + wrappedArrayOrBooleanToJSON, +} from './WrappedArrayOrBoolean'; +export type { WrappedMapOrBoolean, WrappedMapOrBoolean$Wire } from './WrappedMapOrBoolean'; +export { wrappedMapOrBooleanFromJSON, wrappedMapOrBooleanToJSON } from './WrappedMapOrBoolean'; +export type { WrappedScalarInner, WrappedScalarInner$Wire } from './WrappedScalarInner'; +export { wrappedScalarInnerFromJSON, wrappedScalarInnerToJSON } from './WrappedScalarInner'; +export type { WrapperChoice, WrapperChoice$Wire } from './WrapperChoice'; +export { wrapperChoiceFromJSON, wrapperChoiceToJSON } from './WrapperChoice'; +export type { WrapperOne, WrapperOne$Wire } from './WrapperOne'; +export { wrapperOneFromJSON, wrapperOneToJSON } from './WrapperOne'; +export type { WrapperTwo, WrapperTwo$Wire } from './WrapperTwo'; +export { wrapperTwoFromJSON, wrapperTwoToJSON } from './WrapperTwo'; +export type { + ExchangeMultipartRequest, + ExchangeMultipartRequest$Wire, +} from './ExchangeMultipartRequest'; +export { + exchangeMultipartRequestFromJSON, + exchangeMultipartRequestToJSON, +} from './ExchangeMultipartRequest'; +export type { + SendOptionalInlineScalarRequest, + SendOptionalInlineScalarRequest$Wire, +} from './SendOptionalInlineScalarRequest'; +export { + sendOptionalInlineScalarRequestFromJSON, + sendOptionalInlineScalarRequestToJSON, +} from './SendOptionalInlineScalarRequest'; +export type { ExchangeMultipartMultipartRequestBody } from './ExchangeMultipartMultipartRequestBody'; diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/package.json.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/package.json.golden new file mode 100644 index 000000000..cef8d7580 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/package.json.golden @@ -0,0 +1,32 @@ +{ + "name": "untagged-union-conversion", + "version": "1.0.0", + "description": "TypeScript client for Untagged union conversion", + "keywords": [ + "api-client", + "generated", + "openapi", + "typescript" + ], + "files": [ + "dist" + ], + "type": "module", + "main": "./dist/index.mjs", + "types": "./dist/index.d.mts", + "exports": { + ".": { + "default": "./dist/index.mjs", + "types": "./dist/index.d.mts" + } + }, + "scripts": { + "build": "vp pack", + "check": "vp check", + "fmt": "vp fmt" + }, + "devDependencies": { + "typescript": "^7.0.2", + "vite-plus": "^1.5.0" + } +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/runtime.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/runtime.ts.golden new file mode 100644 index 000000000..084a11e06 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/runtime.ts.golden @@ -0,0 +1,667 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export const BASE_PATH = 'http://localhost'.replace(/\/+$/, ''); + +export interface ConfigurationParameters { + basePath?: string; // override base path + fetchApi?: FetchAPI; // override for fetch implementation + middleware?: Middleware[]; // middleware to apply before/after fetch requests + queryParamsStringify?: (params: HTTPQuery) => string; // stringify function for query strings + username?: string | (() => string | Promise); // parameter for basic security + password?: string | (() => string | Promise); // parameter for basic security + apiKey?: string | Promise | ((name: string) => string | Promise); // parameter for apiKey security + accessToken?: + | string + | Promise + | ((name?: string, scopes?: string[]) => string | Promise); // parameter for oauth2 security + headers?: HTTPHeaders; //header params we want to use on every request + credentials?: RequestCredentials; //value for the credentials param we want to use on each request +} + +export class Configuration { + private configuration: ConfigurationParameters; + + constructor(configuration: ConfigurationParameters = {}) { + this.configuration = configuration; + } + + set config(configuration: ConfigurationParameters) { + this.configuration = configuration; + } + + get basePath(): string { + return this.configuration.basePath != null ? this.configuration.basePath : BASE_PATH; + } + + get fetchApi(): FetchAPI | undefined { + return this.configuration.fetchApi; + } + + get middleware(): Middleware[] { + return this.configuration.middleware || []; + } + + get queryParamsStringify(): (params: HTTPQuery) => string { + return this.configuration.queryParamsStringify || querystring; + } + + get username(): (() => string | Promise) | undefined { + const username = this.configuration.username; + if (username) { + return typeof username === 'function' ? username : async () => username; + } + return undefined; + } + + get password(): (() => string | Promise) | undefined { + const password = this.configuration.password; + if (password) { + return typeof password === 'function' ? password : async () => password; + } + return undefined; + } + + get apiKey(): ((name: string) => string | Promise) | undefined { + const apiKey = this.configuration.apiKey; + if (apiKey) { + return typeof apiKey === 'function' ? apiKey : () => apiKey; + } + return undefined; + } + + get accessToken(): ((name?: string, scopes?: string[]) => string | Promise) | undefined { + const accessToken = this.configuration.accessToken; + if (accessToken) { + return typeof accessToken === 'function' ? accessToken : async () => accessToken; + } + return undefined; + } + + get headers(): HTTPHeaders | undefined { + return this.configuration.headers; + } + + get credentials(): RequestCredentials | undefined { + return this.configuration.credentials; + } +} + +export const DefaultConfig = new Configuration(); + +export interface UploadFile { + data: Blob; + filename?: string; +} + +export type UploadFileInput = Blob | File | UploadFile; + +export function uploadFileData(value: UploadFileInput): Blob { + return isUploadFile(value) ? value.data : value; +} + +export function uploadFileFilename(value: UploadFileInput, fallback: string): string { + if (isFile(value) && value.name) { + return value.name; + } + if (isUploadFile(value) && value.filename) { + return value.filename; + } + return fallback; +} + +export function multipartHeaderValue(value: string): string { + return value + .replace(/[\r\n]/g, '') + .replace(/\\/g, '\\\\') + .replace(/"/g, '\\"'); +} + +function isUploadFile(value: UploadFileInput): value is UploadFile { + return typeof value === 'object' && value !== null && 'data' in value; +} + +function isFile(value: UploadFileInput): value is File { + return typeof File !== 'undefined' && value instanceof File; +} + +/** + * This is the base class for all generated API classes. + */ +export class BaseAPI { + private static readonly jsonRegex = new RegExp( + '^(:?application/json|[^;/ \t]+/[^;/ \t]+[+]json)[ \t]*(:?;.*)?$', + 'i', + ); + protected configuration: Configuration; + private middleware: Middleware[]; + + constructor(configuration: Configuration = DefaultConfig) { + this.configuration = configuration; + this.middleware = configuration.middleware; + } + + withMiddleware(this: T, ...middlewares: Middleware[]) { + const next = this.clone(); + next.middleware = next.middleware.concat(...middlewares); + return next; + } + + withPreMiddleware(this: T, ...preMiddlewares: Array) { + const middlewares = preMiddlewares.map((pre) => ({ pre })); + return this.withMiddleware(...middlewares); + } + + withPostMiddleware(this: T, ...postMiddlewares: Array) { + const middlewares = postMiddlewares.map((post) => ({ post })); + return this.withMiddleware(...middlewares); + } + + /** + * Check if the given MIME is a JSON MIME. + * JSON MIME examples: + * application/json + * application/json; charset=UTF8 + * APPLICATION/JSON + * application/vnd.company+json + * @param mime - MIME (Multipurpose Internet Mail Extensions) + * @return True if the given MIME is JSON, false otherwise. + */ + protected isJsonMime(mime: string | null | undefined): boolean { + if (!mime) { + return false; + } + return BaseAPI.jsonRegex.test(mime); + } + + protected async request( + context: RequestOpts, + initOverrides?: RequestInit | InitOverrideFunction, + ): Promise { + const { url, init } = await this.createFetchParams(context, initOverrides); + return await this.fetchApi(url, init); + } + + private async createFetchParams( + context: RequestOpts, + initOverrides?: RequestInit | InitOverrideFunction, + ) { + let url = this.configuration.basePath + context.path; + if (context.query !== undefined && Object.keys(context.query).length !== 0) { + // only add the querystring to the URL if there are query parameters. + // this is done to avoid urls ending with a "?" character which buggy webservers + // do not handle correctly sometimes. + url += '?' + this.configuration.queryParamsStringify(context.query); + } + + const headers = Object.assign({}, this.configuration.headers, context.headers); + Object.keys(headers).forEach((key) => (headers[key] === undefined ? delete headers[key] : {})); + + const token = await this.configuration.accessToken?.(); + if (token) { + headers['Authorization'] = `Bearer ${token}`; + } + + if (!headers['Authorization']) { + const username = await this.configuration.username?.(); + const password = await this.configuration.password?.(); + if (username && password) { + headers['Authorization'] = `Basic ${btoa(`${username}:${password}`)}`; + } + } + + const initOverrideFn = + typeof initOverrides === 'function' ? initOverrides : async () => initOverrides; + + const initParams: HTTPRequestInit = { + method: context.method, + headers, + body: context.body, + credentials: this.configuration.credentials, + signal: context.signal, + }; + + const overriddenInit = { + ...initParams, + ...(await initOverrideFn({ + init: initParams, + context, + })), + }; + + let body: BodyInit | null | undefined; + if ( + isFormData(overriddenInit.body) || + overriddenInit.body instanceof URLSearchParams || + isBlob(overriddenInit.body) + ) { + body = overriddenInit.body; + } else if (this.isJsonMime(headers['Content-Type'])) { + body = JSON.stringify(overriddenInit.body); + } else { + body = overriddenInit.body as BodyInit | null | undefined; + } + + const init: RequestInit = { + ...overriddenInit, + body, + }; + + return { url, init }; + } + + private fetchApi = async (url: string, init: RequestInit) => { + let fetchParams = { url, init }; + for (const middleware of this.middleware) { + if (middleware.pre) { + fetchParams = + (await middleware.pre({ + fetch: this.fetchApi, + ...fetchParams, + })) || fetchParams; + } + } + let response: Response | undefined = undefined; + try { + response = await (this.configuration.fetchApi || fetch)(fetchParams.url, fetchParams.init); + } catch (e) { + for (const middleware of this.middleware) { + if (middleware.onError) { + response = + (await middleware.onError({ + fetch: this.fetchApi, + url: fetchParams.url, + init: fetchParams.init, + error: e, + response: response ? response.clone() : undefined, + })) || response; + } + } + if (response === undefined) { + if (e instanceof Error) { + throw new FetchError( + e, + 'The request failed and the interceptors did not return an alternative response', + ); + } else { + throw e; + } + } + } + for (const middleware of this.middleware) { + if (middleware.post) { + response = + (await middleware.post({ + fetch: this.fetchApi, + url: fetchParams.url, + init: fetchParams.init, + response: response.clone(), + })) || response; + } + } + return response; + }; + + /** + * Create a shallow clone of `this` by constructing a new instance + * and then shallow cloning data members. + */ + private clone(this: T): T { + const constructor = this.constructor as new (configuration: Configuration) => T; + const next = new constructor(this.configuration); + next.middleware = this.middleware.slice(); + return next; + } +} + +function isBlob(value: unknown): value is Blob { + return typeof Blob !== 'undefined' && value instanceof Blob; +} + +function isFormData(value: unknown): value is FormData { + return typeof FormData !== 'undefined' && value instanceof FormData; +} + +export class ResponseError extends Error { + override name = 'ResponseError' as const; + public response: Response; + + constructor(response: Response, msg?: string) { + super(msg); + this.response = response; + } +} + +export class FetchError extends Error { + override name = 'FetchError' as const; + public cause: Error; + + constructor(cause: Error, msg?: string) { + super(msg); + this.cause = cause; + } +} + +export class RequiredError extends Error { + override name = 'RequiredError' as const; + public field: string; + + constructor(field: string, msg?: string) { + super(msg); + this.field = field; + } +} + +export type FetchAPI = WindowOrWorkerGlobalScope['fetch']; + +export type Json = unknown; +export type HTTPMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS' | 'HEAD'; +export type HTTPHeaders = { [key: string]: string }; +export type HTTPQuery = { + [key: string]: + | string + | number + | null + | boolean + | Array + | Set + | HTTPQuery; +}; +export type HTTPBody = Json | Blob | FormData | URLSearchParams; // eslint-disable-line @typescript-eslint/no-redundant-type-constituents +export type HTTPRequestInit = { + headers?: HTTPHeaders; + method: HTTPMethod; + credentials?: RequestCredentials; + body?: HTTPBody; + signal?: AbortSignal; +}; +export type ModelPropertyNaming = 'camelCase' | 'snake_case' | 'PascalCase' | 'original'; + +export type InitOverrideFunction = (requestContext: { + init: HTTPRequestInit; + context: RequestOpts; +}) => Promise; + +export interface FetchParams { + url: string; + init: RequestInit; +} + +export interface RequestOpts { + path: string; + method: HTTPMethod; + headers: HTTPHeaders; + query?: HTTPQuery; + body?: HTTPBody; + signal?: AbortSignal; +} + +export function querystring(params: HTTPQuery, prefix: string = ''): string { + return Object.keys(params) + .map((key) => querystringSingleKey(key, params[key], prefix)) + .filter((part) => part.length > 0) + .join('&'); +} + +function querystringSingleKey( + key: string, + value: + | string + | number + | null + | undefined + | boolean + | Array + | Set + | HTTPQuery, + keyPrefix: string = '', +): string { + const fullKey = keyPrefix + (keyPrefix.length ? `[${key}]` : key); + if (value instanceof Array) { + const multiValue = value + .map((singleValue) => encodeURIComponent(String(singleValue))) + .join(`&${encodeURIComponent(fullKey)}=`); + return `${encodeURIComponent(fullKey)}=${multiValue}`; + } + if (value instanceof Set) { + const valueAsArray = Array.from(value); + return querystringSingleKey(key, valueAsArray, keyPrefix); + } + if (value instanceof Date) { + return `${encodeURIComponent(fullKey)}=${encodeURIComponent(value.toISOString())}`; + } + if (value instanceof Object) { + return querystring(value as HTTPQuery, fullKey); + } + return `${encodeURIComponent(fullKey)}=${encodeURIComponent(String(value))}`; +} + +export function exists(json: Record, key: string): boolean { + const value = json[key]; + return value !== null && value !== undefined; +} + +export function mapValues(data: Record, fn: (item: T) => U): Record { + const result: Record = {}; + for (const key of Object.keys(data)) { + result[key] = fn(data[key]); + } + return result; +} + +export function canConsumeForm(consumes: Consume[]): boolean { + for (const consume of consumes) { + if ('multipart/form-data' === consume.contentType) { + return true; + } + } + return false; +} + +export interface Consume { + contentType: string; +} + +export interface RequestContext { + fetch: FetchAPI; + url: string; + init: RequestInit; +} + +export interface ResponseContext { + fetch: FetchAPI; + url: string; + init: RequestInit; + response: Response; +} + +export interface ErrorContext { + fetch: FetchAPI; + url: string; + init: RequestInit; + error: unknown; + response?: Response; +} + +export interface Middleware { + pre?(context: RequestContext): Promise; + post?(context: ResponseContext): Promise; + onError?(context: ErrorContext): Promise; +} + +export interface ApiResponse { + raw: Response; + value(): Promise; +} + +export type HttpSuccessStatus = + | 200 + | 201 + | 202 + | 203 + | 204 + | 205 + | 206 + | 207 + | 208 + | 209 + | 210 + | 211 + | 212 + | 213 + | 214 + | 215 + | 216 + | 217 + | 218 + | 219 + | 220 + | 221 + | 222 + | 223 + | 224 + | 225 + | 226 + | 227 + | 228 + | 229 + | 230 + | 231 + | 232 + | 233 + | 234 + | 235 + | 236 + | 237 + | 238 + | 239 + | 240 + | 241 + | 242 + | 243 + | 244 + | 245 + | 246 + | 247 + | 248 + | 249 + | 250 + | 251 + | 252 + | 253 + | 254 + | 255 + | 256 + | 257 + | 258 + | 259 + | 260 + | 261 + | 262 + | 263 + | 264 + | 265 + | 266 + | 267 + | 268 + | 269 + | 270 + | 271 + | 272 + | 273 + | 274 + | 275 + | 276 + | 277 + | 278 + | 279 + | 280 + | 281 + | 282 + | 283 + | 284 + | 285 + | 286 + | 287 + | 288 + | 289 + | 290 + | 291 + | 292 + | 293 + | 294 + | 295 + | 296 + | 297 + | 298 + | 299; + +export type HttpSuccessResponse = { status: HttpSuccessStatus }; + +export interface ResponseTransformer { + (json: unknown): T; +} + +class ApiResponseBase { + public readonly status: number; + public readonly ok: boolean; + public readonly statusText: string; + public readonly headers: Headers; + public readonly raw: Response; + + constructor(raw: Response) { + this.raw = raw; + this.status = raw.status; + this.ok = raw.ok; + this.statusText = raw.statusText; + this.headers = raw.headers; + } +} + +export class JSONApiResponse extends ApiResponseBase { + private transformer: ResponseTransformer; + + constructor( + raw: Response, + transformer: ResponseTransformer = (jsonValue: unknown) => jsonValue as T, + ) { + super(raw); + this.transformer = transformer; + } + + async value(): Promise { + return this.transformer(await this.raw.json()); + } +} + +export class VoidApiResponse extends ApiResponseBase { + constructor(raw: Response) { + super(raw); + } + + async value(): Promise { + return undefined; + } +} + +export class BlobApiResponse extends ApiResponseBase { + constructor(raw: Response) { + super(raw); + } + + async value(): Promise { + return await this.raw.blob(); + } +} + +export class TextApiResponse extends ApiResponseBase { + constructor(raw: Response) { + super(raw); + } + + async value(): Promise { + return await this.raw.text(); + } +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$0.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$0.ts.golden new file mode 100644 index 000000000..c8065e1d1 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$0.ts.golden @@ -0,0 +1,22 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { NullableScalarInner, NullableScalarInner$Wire } from '../models/NullableScalarInner'; +import { + nullableScalarInnerFromJSON, + nullableScalarInnerToJSON, +} from '../models/NullableScalarInner'; + +export type Value$0Wire = NullableScalarInner$Wire | null; + +export type Value$0 = NullableScalarInner | null; + +export function convert$0FromJSON(json: NullableScalarInner$Wire | null): Value$0 { + return nullableScalarInnerFromJSON(json); +} + +export function convert$0ToJSON(value: NullableScalarInner | null): Value$0Wire { + return nullableScalarInnerToJSON(value); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$1.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$1.ts.golden new file mode 100644 index 000000000..1466a42d1 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$1.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { NullableScalarInner, NullableScalarInner$Wire } from '../models/NullableScalarInner'; +import { convert$0FromJSON, convert$0ToJSON } from './valueConversion$0'; + +export type Value$1Wire = NullableScalarInner$Wire | null; + +export type Value$1 = NullableScalarInner | null; + +export function convert$1FromJSON(json: NullableScalarInner$Wire | null): Value$1 { + return convert$0FromJSON(json); +} + +export function convert$1ToJSON(value: NullableScalarInner | null): Value$1Wire { + return convert$0ToJSON(value); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$10.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$10.ts.golden new file mode 100644 index 000000000..b88c04f45 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$10.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail, Detail$Wire } from '../models/Detail'; +import { detailFromJSON, detailToJSON } from '../models/Detail'; + +export type Value$10Wire = Detail$Wire[]; + +export type Value$10 = Detail[]; + +export function convert$10FromJSON(json: ReadonlyArray): Value$10 { + return json.map(detailFromJSON); +} + +export function convert$10ToJSON(value: ReadonlyArray): Value$10Wire { + return value.map(detailToJSON); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$11.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$11.ts.golden new file mode 100644 index 000000000..aec061d79 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$11.ts.golden @@ -0,0 +1,25 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { ScalarEnumChoice, ScalarEnumChoice$Wire } from '../models/ScalarEnumChoice'; +import { scalarEnumChoiceFromJSON, scalarEnumChoiceToJSON } from '../models/ScalarEnumChoice'; + +export type Value$11Wire = ScalarEnumChoice$Wire | null; + +export type Value$11 = ScalarEnumChoice | null; + +export function convert$11FromJSON(json: ScalarEnumChoice$Wire | null): Value$11 { + if (json == null) { + return null; + } + return scalarEnumChoiceFromJSON(json); +} + +export function convert$11ToJSON(value: ScalarEnumChoice | null): Value$11Wire { + if (value == null) { + return null; + } + return scalarEnumChoiceToJSON(value); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$12.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$12.ts.golden new file mode 100644 index 000000000..00625e21a --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$12.ts.golden @@ -0,0 +1,26 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { + NullableNonNullScalarsItems, + NullableNonNullScalarsItems$Wire, +} from '../models/NullableNonNullScalarsItems'; +import { convert$13FromJSON, convert$13ToJSON } from './valueConversion$13'; + +export type Value$12Wire = readonly (NullableNonNullScalarsItems$Wire | null)[]; + +export type Value$12 = readonly (NullableNonNullScalarsItems | null)[]; + +export function convert$12FromJSON( + json: ReadonlyArray, +): Value$12 { + return json.map(convert$13FromJSON); +} + +export function convert$12ToJSON( + value: ReadonlyArray, +): Value$12Wire { + return value.map(convert$13ToJSON); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$13.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$13.ts.golden new file mode 100644 index 000000000..e24768436 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$13.ts.golden @@ -0,0 +1,31 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { + NullableNonNullScalarsItems, + NullableNonNullScalarsItems$Wire, +} from '../models/NullableNonNullScalarsItems'; +import { + nullableNonNullScalarsItemsFromJSON, + nullableNonNullScalarsItemsToJSON, +} from '../models/NullableNonNullScalarsItems'; + +export type Value$13Wire = NullableNonNullScalarsItems$Wire | null; + +export type Value$13 = NullableNonNullScalarsItems | null; + +export function convert$13FromJSON(json: NullableNonNullScalarsItems$Wire | null): Value$13 { + if (json == null) { + return null; + } + return nullableNonNullScalarsItemsFromJSON(json); +} + +export function convert$13ToJSON(value: NullableNonNullScalarsItems | null): Value$13Wire { + if (value == null) { + return null; + } + return nullableNonNullScalarsItemsToJSON(value); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$14.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$14.ts.golden new file mode 100644 index 000000000..341ce60f6 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$14.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { NullableScalarInner, NullableScalarInner$Wire } from '../models/NullableScalarInner'; +import { convert$5FromJSON, convert$5ToJSON } from './valueConversion$5'; + +export type Value$14Wire = readonly (NullableScalarInner$Wire | null)[]; + +export type Value$14 = readonly (NullableScalarInner | null)[]; + +export function convert$14FromJSON(json: ReadonlyArray): Value$14 { + return json.map(convert$5FromJSON); +} + +export function convert$14ToJSON(value: ReadonlyArray): Value$14Wire { + return value.map(convert$5ToJSON); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$15.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$15.ts.golden new file mode 100644 index 000000000..6e5f6392c --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$15.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { RecursiveList, RecursiveList$Wire } from '../models/RecursiveList'; +import { recursiveListFromJSON, recursiveListToJSON } from '../models/RecursiveList'; + +export type Value$15Wire = readonly RecursiveList$Wire[]; + +export type Value$15 = readonly RecursiveList[]; + +export function convert$15FromJSON(json: ReadonlyArray): Value$15 { + return json.map(recursiveListFromJSON); +} + +export function convert$15ToJSON(value: ReadonlyArray): Value$15Wire { + return value.map(recursiveListToJSON); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$16.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$16.ts.golden new file mode 100644 index 000000000..d1c8f4026 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$16.ts.golden @@ -0,0 +1,33 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { + RecursiveOpenDictionary, + RecursiveOpenDictionary$Wire, +} from '../models/RecursiveOpenDictionary'; +import { + recursiveOpenDictionaryFromJSON, + recursiveOpenDictionaryToJSON, +} from '../models/RecursiveOpenDictionary'; + +export type Value$16Wire = globalThis.Record; + +export type Value$16 = globalThis.Record; + +export function convert$16FromJSON( + json: globalThis.Record, +): Value$16 { + return Object.fromEntries( + Object.entries(json ?? {}).map(([key, item]) => [key, recursiveOpenDictionaryFromJSON(item)]), + ); +} + +export function convert$16ToJSON( + value: globalThis.Record, +): Value$16Wire { + return Object.fromEntries( + Object.entries(value ?? {}).map(([key, item]) => [key, recursiveOpenDictionaryToJSON(item)]), + ); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$17.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$17.ts.golden new file mode 100644 index 000000000..e460b03be --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$17.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { ClientTool, ClientTool$Wire } from '../models/ClientTool'; +import { clientToolFromJSON, clientToolToJSON } from '../models/ClientTool'; + +export type Value$17Wire = readonly ClientTool$Wire[]; + +export type Value$17 = readonly ClientTool[]; + +export function convert$17FromJSON(json: ReadonlyArray): Value$17 { + return json.map(clientToolFromJSON); +} + +export function convert$17ToJSON(value: ReadonlyArray): Value$17Wire { + return value.map(clientToolToJSON); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$18.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$18.ts.golden new file mode 100644 index 000000000..3fbbe1b0a --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$18.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { SearchTool, SearchTool$Wire } from '../models/SearchTool'; +import { searchToolFromJSON, searchToolToJSON } from '../models/SearchTool'; + +export type Value$18Wire = readonly SearchTool$Wire[]; + +export type Value$18 = readonly SearchTool[]; + +export function convert$18FromJSON(json: ReadonlyArray): Value$18 { + return json.map(searchToolFromJSON); +} + +export function convert$18ToJSON(value: ReadonlyArray): Value$18Wire { + return value.map(searchToolToJSON); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$19.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$19.ts.golden new file mode 100644 index 000000000..946108e51 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$19.ts.golden @@ -0,0 +1,23 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { ClientTool, ClientTool$Wire } from '../models/ClientTool'; +import { clientToolFromJSON, clientToolToJSON } from '../models/ClientTool'; + +export type Value$19Wire = globalThis.Record; + +export type Value$19 = globalThis.Record; + +export function convert$19FromJSON(json: globalThis.Record): Value$19 { + return Object.fromEntries( + Object.entries(json ?? {}).map(([key, item]) => [key, clientToolFromJSON(item)]), + ); +} + +export function convert$19ToJSON(value: globalThis.Record): Value$19Wire { + return Object.fromEntries( + Object.entries(value ?? {}).map(([key, item]) => [key, clientToolToJSON(item)]), + ); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$2.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$2.ts.golden new file mode 100644 index 000000000..df5ecc540 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$2.ts.golden @@ -0,0 +1,25 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { NullableScalarInner, NullableScalarInner$Wire } from '../models/NullableScalarInner'; +import { convert$0FromJSON, convert$0ToJSON } from './valueConversion$0'; + +export type Value$2Wire = NullableScalarInner$Wire | null; + +export type Value$2 = NullableScalarInner | null; + +export function convert$2FromJSON(json: NullableScalarInner$Wire | null): Value$2 { + if (json == null) { + return null; + } + return convert$0FromJSON(json); +} + +export function convert$2ToJSON(value: NullableScalarInner | null): Value$2Wire { + if (value == null) { + return null; + } + return convert$0ToJSON(value); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$20.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$20.ts.golden new file mode 100644 index 000000000..c3b7d48f3 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$20.ts.golden @@ -0,0 +1,23 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { SearchTool, SearchTool$Wire } from '../models/SearchTool'; +import { searchToolFromJSON, searchToolToJSON } from '../models/SearchTool'; + +export type Value$20Wire = globalThis.Record; + +export type Value$20 = globalThis.Record; + +export function convert$20FromJSON(json: globalThis.Record): Value$20 { + return Object.fromEntries( + Object.entries(json ?? {}).map(([key, item]) => [key, searchToolFromJSON(item)]), + ); +} + +export function convert$20ToJSON(value: globalThis.Record): Value$20Wire { + return Object.fromEntries( + Object.entries(value ?? {}).map(([key, item]) => [key, searchToolToJSON(item)]), + ); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$21.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$21.ts.golden new file mode 100644 index 000000000..3d4cd7d3b --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$21.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Tool, Tool$Wire } from '../models/Tool'; +import { toolFromJSON, toolToJSON } from '../models/Tool'; + +export type Value$21Wire = readonly Tool$Wire[]; + +export type Value$21 = readonly Tool[]; + +export function convert$21FromJSON(json: ReadonlyArray): Value$21 { + return json.map(toolFromJSON); +} + +export function convert$21ToJSON(value: ReadonlyArray): Value$21Wire { + return value.map(toolToJSON); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$22.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$22.ts.golden new file mode 100644 index 000000000..02bfa3318 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$22.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Tool, Tool$Wire } from '../models/Tool'; +import { convert$23FromJSON, convert$23ToJSON } from './valueConversion$23'; + +export type Value$22Wire = Tool$Wire[][]; + +export type Value$22 = Tool[][]; + +export function convert$22FromJSON(json: ReadonlyArray>): Value$22 { + return json.map(convert$23FromJSON); +} + +export function convert$22ToJSON(value: ReadonlyArray>): Value$22Wire { + return value.map(convert$23ToJSON); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$23.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$23.ts.golden new file mode 100644 index 000000000..afa19f99c --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$23.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Tool, Tool$Wire } from '../models/Tool'; +import { toolFromJSON, toolToJSON } from '../models/Tool'; + +export type Value$23Wire = Tool$Wire[]; + +export type Value$23 = Tool[]; + +export function convert$23FromJSON(json: ReadonlyArray): Value$23 { + return json.map(toolFromJSON); +} + +export function convert$23ToJSON(value: ReadonlyArray): Value$23Wire { + return value.map(toolToJSON); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$24.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$24.ts.golden new file mode 100644 index 000000000..4cb52e3e4 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$24.ts.golden @@ -0,0 +1,25 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Tool, Tool$Wire } from '../models/Tool'; +import { convert$25FromJSON, convert$25ToJSON } from './valueConversion$25'; + +export type Value$24Wire = Tool$Wire[] | null; + +export type Value$24 = Tool[] | null; + +export function convert$24FromJSON(json: ReadonlyArray | null): Value$24 { + if (json == null) { + return null; + } + return convert$25FromJSON(json); +} + +export function convert$24ToJSON(value: ReadonlyArray | null): Value$24Wire { + if (value == null) { + return null; + } + return convert$25ToJSON(value); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$25.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$25.ts.golden new file mode 100644 index 000000000..b4b9ac600 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$25.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Tool, Tool$Wire } from '../models/Tool'; +import { toolFromJSON, toolToJSON } from '../models/Tool'; + +export type Value$25Wire = Tool$Wire[]; + +export type Value$25 = Tool[]; + +export function convert$25FromJSON(json: ReadonlyArray): Value$25 { + return json.map(toolFromJSON); +} + +export function convert$25ToJSON(value: ReadonlyArray): Value$25Wire { + return value.map(toolToJSON); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$3.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$3.ts.golden new file mode 100644 index 000000000..ddd040655 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$3.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { NullableScalarInner, NullableScalarInner$Wire } from '../models/NullableScalarInner'; +import { convert$4FromJSON, convert$4ToJSON } from './valueConversion$4'; + +export type Value$3Wire = readonly (NullableScalarInner$Wire | null)[]; + +export type Value$3 = readonly (NullableScalarInner | null)[]; + +export function convert$3FromJSON(json: ReadonlyArray): Value$3 { + return json.map(convert$4FromJSON); +} + +export function convert$3ToJSON(value: ReadonlyArray): Value$3Wire { + return value.map(convert$4ToJSON); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$4.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$4.ts.golden new file mode 100644 index 000000000..a1c8e121b --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$4.ts.golden @@ -0,0 +1,25 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { NullableScalarInner, NullableScalarInner$Wire } from '../models/NullableScalarInner'; +import { convert$5FromJSON, convert$5ToJSON } from './valueConversion$5'; + +export type Value$4Wire = NullableScalarInner$Wire | null; + +export type Value$4 = NullableScalarInner | null; + +export function convert$4FromJSON(json: NullableScalarInner$Wire | null): Value$4 { + if (json == null) { + return null; + } + return convert$5FromJSON(json); +} + +export function convert$4ToJSON(value: NullableScalarInner | null): Value$4Wire { + if (value == null) { + return null; + } + return convert$5ToJSON(value); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$5.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$5.ts.golden new file mode 100644 index 000000000..d9f62811e --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$5.ts.golden @@ -0,0 +1,22 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { NullableScalarInner, NullableScalarInner$Wire } from '../models/NullableScalarInner'; +import { + nullableScalarInnerFromJSON, + nullableScalarInnerToJSON, +} from '../models/NullableScalarInner'; + +export type Value$5Wire = NullableScalarInner$Wire | null; + +export type Value$5 = NullableScalarInner | null; + +export function convert$5FromJSON(json: NullableScalarInner$Wire | null): Value$5 { + return nullableScalarInnerFromJSON(json); +} + +export function convert$5ToJSON(value: NullableScalarInner | null): Value$5Wire { + return nullableScalarInnerToJSON(value); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$6.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$6.ts.golden new file mode 100644 index 000000000..efd551baf --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$6.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail, Detail$Wire } from '../models/Detail'; +import { detailFromJSON, detailToJSON } from '../models/Detail'; + +export type Value$6Wire = readonly Detail$Wire[]; + +export type Value$6 = readonly Detail[]; + +export function convert$6FromJSON(json: ReadonlyArray): Value$6 { + return json.map(detailFromJSON); +} + +export function convert$6ToJSON(value: ReadonlyArray): Value$6Wire { + return value.map(detailToJSON); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$7.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$7.ts.golden new file mode 100644 index 000000000..b8459a5d7 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$7.ts.golden @@ -0,0 +1,23 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail, Detail$Wire } from '../models/Detail'; +import { detailFromJSON, detailToJSON } from '../models/Detail'; + +export type Value$7Wire = globalThis.Record; + +export type Value$7 = globalThis.Record; + +export function convert$7FromJSON(json: globalThis.Record): Value$7 { + return Object.fromEntries( + Object.entries(json ?? {}).map(([key, item]) => [key, detailFromJSON(item)]), + ); +} + +export function convert$7ToJSON(value: globalThis.Record): Value$7Wire { + return Object.fromEntries( + Object.entries(value ?? {}).map(([key, item]) => [key, detailToJSON(item)]), + ); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$8.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$8.ts.golden new file mode 100644 index 000000000..05185ced1 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$8.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { NamedValue, NamedValue$Wire } from '../models/NamedValue'; +import { namedValueFromJSON, namedValueToJSON } from '../models/NamedValue'; + +export type Value$8Wire = readonly NamedValue$Wire[]; + +export type Value$8 = readonly NamedValue[]; + +export function convert$8FromJSON(json: ReadonlyArray): Value$8 { + return json.map(namedValueFromJSON); +} + +export function convert$8ToJSON(value: ReadonlyArray): Value$8Wire { + return value.map(namedValueToJSON); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$9.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$9.ts.golden new file mode 100644 index 000000000..42077da02 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/runtime/valueConversion$9.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail, Detail$Wire } from '../models/Detail'; +import { convert$10FromJSON, convert$10ToJSON } from './valueConversion$10'; + +export type Value$9Wire = readonly Detail$Wire[][]; + +export type Value$9 = readonly Detail[][]; + +export function convert$9FromJSON(json: ReadonlyArray>): Value$9 { + return json.map(convert$10FromJSON); +} + +export function convert$9ToJSON(value: ReadonlyArray>): Value$9Wire { + return value.map(convert$10ToJSON); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/tsconfig.esm.json.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/tsconfig.esm.json.golden new file mode 100644 index 000000000..769c716fd --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/tsconfig.esm.json.golden @@ -0,0 +1,7 @@ +{ + "compilerOptions": { + "module": "ES2020", + "outDir": "dist/esm" + }, + "extends": "./tsconfig.json" +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/tsconfig.json.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/tsconfig.json.golden new file mode 100644 index 000000000..9f125842c --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/tsconfig.json.golden @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "declaration": true, + "declarationMap": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "lib": ["ES2020", "DOM"], + "module": "ES2020", + "moduleResolution": "bundler", + "outDir": "./dist", + "resolveJsonModule": true, + "rootDir": "./", + "skipLibCheck": true, + "sourceMap": true, + "strict": true, + "target": "ES2020", + "typeRoots": ["node_modules/@types"] + }, + "exclude": ["dist", "node_modules"], + "include": ["**/*.ts"] +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/vite.config.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/vite.config.ts.golden new file mode 100644 index 000000000..065569dd3 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-raw/vite.config.ts.golden @@ -0,0 +1,20 @@ +import { defineConfig } from 'vite-plus'; + +export default defineConfig({ + fmt: { + singleQuote: true, + ignorePatterns: ['dist/**'], + }, + lint: { + options: { + typeAware: true, + typeCheck: true, + }, + ignorePatterns: ['node_modules', 'dist'], + }, + pack: { + entry: ['index.ts'], + dts: true, + format: ['esm'], + }, +}); diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/README.md.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/README.md.golden new file mode 100644 index 000000000..9a0608684 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/README.md.golden @@ -0,0 +1,264 @@ +# untagged-union-conversion + +Generated API client + +**Version:** 1.0.0 + +## Overview + +This package provides a TypeScript/JavaScript client for the Untagged union conversion API. It uses the native [Fetch API](https://fetch.spec.whatwg.org/) for HTTP requests and works in both Node.js and browser environments. + +## Features + +- ✨ **Type-safe** - Full TypeScript support with generated types +- 🚀 **Modern** - Uses native Fetch API, no external HTTP dependencies +- 🔧 **Configurable** - Flexible configuration options +- 🎯 **Middleware** - Support for request/response interceptors +- 📦 **Tree-shakeable** - Import only what you need +- 🌐 **Universal** - Works in Node.js and browsers + +## Installation + +### From npm (published package) + +```bash +npm install untagged-union-conversion +``` + +### From local path (development) + +Add the package to your `package.json` using the `file:` protocol: + +```json +{ + "dependencies": { + "untagged-union-conversion": "file:../../path/to/generated/package" + } +} +``` + +Then run: + +```bash +npm install +``` + +## Quick Start + +```typescript +import { Configuration, ConversionApi } from 'untagged-union-conversion'; + +// Create a configuration +const config = new Configuration({ + basePath: 'https://api.example.com', + headers: { + Authorization: 'Bearer YOUR_TOKEN', + }, +}); + +// Initialize the API client +const api = new ConversionApi(config); + +// Make API calls +try { + const result = await api.someMethod(); + console.log(result); +} catch (error) { + console.error('API Error:', error); +} +``` + +## Configuration + +The `Configuration` class accepts the following options: + +```typescript +interface ConfigurationParameters { + /** Base URL for API requests */ + basePath?: string; + + /** Custom fetch implementation */ + fetchApi?: typeof fetch; + + /** Request/response middleware */ + middleware?: Middleware[]; + + /** Custom query string serializer */ + queryParamsStringify?: (params: HTTPQuery) => string; + + /** Default headers for all requests */ + headers?: Record; + + /** Credentials mode for requests */ + credentials?: RequestCredentials; +} +``` + +### Example with custom configuration + +```typescript +const config = new Configuration({ + basePath: 'https://api.example.com', + headers: { + 'X-API-Key': 'your-api-key', + 'Content-Type': 'application/json', + }, + credentials: 'include', +}); +``` + +## Middleware + +Add custom middleware to intercept requests and responses: + +```typescript +import { Configuration, Middleware } from 'untagged-union-conversion'; + +const loggingMiddleware: Middleware = { + pre: async (context) => { + console.log('Request:', context.url); + return context; + }, + post: async (context) => { + console.log('Response:', context.response.status); + return context.response; + }, + onError: async (context) => { + console.error('Error:', context.error); + return undefined; + }, +}; + +const config = new Configuration({ + basePath: 'https://api.example.com', + middleware: [loggingMiddleware], +}); +``` + +## Error Handling + +The client throws typed errors for different failure scenarios: + +```typescript +import { ResponseError, FetchError, RequiredError } from 'untagged-union-conversion'; + +try { + const result = await api.someMethod(); +} catch (error) { + if (error instanceof ResponseError) { + // HTTP error response (4xx, 5xx) + console.error('HTTP Error:', error.response.status); + } else if (error instanceof FetchError) { + // Network or fetch error + console.error('Network Error:', error.cause); + } else if (error instanceof RequiredError) { + // Missing required parameter + console.error('Missing field:', error.field); + } +} +``` + +## API Reference + +This package exports the following: + +- **Configuration** - Client configuration class +- **BaseAPI** - Base class for all API clients +- **API Classes** - Generated API client classes (e.g., `UserApi`, `PostApi`) +- **Models** - Generated TypeScript interfaces for request/response types +- **Errors** - `ResponseError`, `FetchError`, `RequiredError` +- **Types** - TypeScript type definitions + +## Development + +### Building + +To build the package: + +```bash +npm install +npm run build +``` + +This will compile TypeScript to JavaScript in the `dist/` directory. + +### Building for ESM + +To build ES modules: + +```bash +npm run build +``` + +### Formatting and validation + +After generating or regenerating this client, install its development dependencies, +run all checks on the generated files and build the library: + +```bash +vp install +vp check +vp pack +``` + +The `check` and `build` npm scripts run the same Vite+ commands. +Use `vp fmt` (or the `fmt` script) to format any subsequent manual edits. +`vp check` validates formatting, lint rules, and types without modifying files. +Build output in `dist/` is excluded from formatting. + +## TypeScript Support + +This package includes TypeScript type definitions. No additional `@types` package is needed. + +### TypeScript Configuration + +This package works with standard TypeScript configurations. If you're using a bundler-based setup, you may want to configure: + +```json +{ + "compilerOptions": { + "moduleResolution": "bundler" + } +} +``` + +### Type Imports + +```typescript +import type { User, CreateUserRequest } from 'untagged-union-conversion'; + +const user: User = { + id: 1, + name: 'John Doe', + email: 'john@example.com', +}; +``` + +## Browser Support + +This package uses the native Fetch API, which is supported in: + +- Chrome 42+ +- Firefox 39+ +- Safari 10.1+ +- Edge 14+ +- Node.js 18+ (native fetch) +- Node.js <18 (with `node-fetch` polyfill) + +For older browsers, you may need to include a fetch polyfill. + +## License + +This is an auto-generated API client. Please refer to your API documentation for license information. + +## Support + +For issues related to the API itself, please contact the API provider. + +For issues with this generated client, please check the OpenAPI specification used to generate it. + +--- + +**Generated by OpenAPI Generator** + +API Version: 1.0.0 diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/apis/ConversionApi.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/apis/ConversionApi.ts.golden new file mode 100644 index 000000000..5d1b77c71 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/apis/ConversionApi.ts.golden @@ -0,0 +1,787 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { ExchangeMultipartMultipartRequestBody } from '../models/ExchangeMultipartMultipartRequestBody'; +import type { NullableDetail } from '../models/NullableDetail'; +import { nullableDetailToJSON } from '../models/NullableDetail'; +import type { NullableScalarInner } from '../models/NullableScalarInner'; +import { nullableScalarInnerToJSON } from '../models/NullableScalarInner'; +import type { SendOptionalInlineScalarRequest } from '../models/SendOptionalInlineScalarRequest'; +import { sendOptionalInlineScalarRequestToJSON } from '../models/SendOptionalInlineScalarRequest'; +import type { Tool, Tool$Wire } from '../models/Tool'; +import type { + Configuration, + HTTPQuery, + HttpSuccessResponse, + InitOverrideFunction, +} from '../runtime/runtime'; +import { + BaseAPI, + DefaultConfig, + JSONApiResponse, + RequiredError, + ResponseError, + VoidApiResponse, + multipartHeaderValue, + uploadFileData, + uploadFileFilename, +} from '../runtime/runtime'; +import { convert$35FromJSON, convert$35ToJSON } from '../runtime/valueConversion$35'; +import { convert$37FromJSON, convert$37ToJSON } from '../runtime/valueConversion$37'; +import { convert$39ToJSON } from '../runtime/valueConversion$39'; + +export interface ApiExchangeMatrixRequest { + body: Tool[][]; +} + +export interface ApiExchangeMultipartRequest { + body: ExchangeMultipartMultipartRequestBody; +} + +export interface ApiExchangeNullableRequest { + body: Tool[] | null; +} + +export interface ApiSendOptionalDetailRequest { + body?: NullableDetail; +} + +export interface ApiSendOptionalInlineScalarRequest { + body?: SendOptionalInlineScalarRequest; +} + +export interface ApiSendOptionalScalarRequest { + body?: NullableScalarInner; +} + +export interface ApiSendRequiredScalarRequest { + body: NullableScalarInner; +} + +export type ExchangeMatrixRawResponse = + | (JSONApiResponse & { status: 200 }) + | (JSONApiResponse & HttpSuccessResponse); +export type ExchangeMultipartRawResponse = + | (VoidApiResponse & { status: 204 }) + | (VoidApiResponse & HttpSuccessResponse); +export type ExchangeNullableRawResponse = + | (JSONApiResponse & { status: 200 }) + | (JSONApiResponse & HttpSuccessResponse); +export type SendOptionalDetailRawResponse = + | (VoidApiResponse & { status: 204 }) + | (VoidApiResponse & HttpSuccessResponse); +export type SendOptionalInlineScalarRawResponse = + | (VoidApiResponse & { status: 204 }) + | (VoidApiResponse & HttpSuccessResponse); +export type SendOptionalScalarRawResponse = + | (VoidApiResponse & { status: 204 }) + | (VoidApiResponse & HttpSuccessResponse); +export type SendRequiredScalarRawResponse = + | (VoidApiResponse & { status: 204 }) + | (VoidApiResponse & HttpSuccessResponse); + +export type ExchangeMatrixErrorDetail = + | { + kind: 'badRequest'; + status: 400; + raw: Response; + value(): Promise; + } + | { + kind: 'unexpected'; + status: number; + raw: Response; + value(): Promise; + }; + +export class ExchangeMatrixError extends ResponseError { + readonly detail: ExchangeMatrixErrorDetail; + + constructor( + response: Response, + detail: ExchangeMatrixErrorDetail, + msg = 'Response returned an error code', + ) { + super(response, msg); + this.detail = detail; + } +} + +export type ExchangeMultipartErrorDetail = { + kind: 'unexpected'; + status: number; + raw: Response; + value(): Promise; +}; + +export class ExchangeMultipartError extends ResponseError { + readonly detail: ExchangeMultipartErrorDetail; + + constructor( + response: Response, + detail: ExchangeMultipartErrorDetail, + msg = 'Response returned an error code', + ) { + super(response, msg); + this.detail = detail; + } +} + +export type ExchangeNullableErrorDetail = { + kind: 'unexpected'; + status: number; + raw: Response; + value(): Promise; +}; + +export class ExchangeNullableError extends ResponseError { + readonly detail: ExchangeNullableErrorDetail; + + constructor( + response: Response, + detail: ExchangeNullableErrorDetail, + msg = 'Response returned an error code', + ) { + super(response, msg); + this.detail = detail; + } +} + +export type SendOptionalDetailErrorDetail = { + kind: 'unexpected'; + status: number; + raw: Response; + value(): Promise; +}; + +export class SendOptionalDetailError extends ResponseError { + readonly detail: SendOptionalDetailErrorDetail; + + constructor( + response: Response, + detail: SendOptionalDetailErrorDetail, + msg = 'Response returned an error code', + ) { + super(response, msg); + this.detail = detail; + } +} + +export type SendOptionalInlineScalarErrorDetail = { + kind: 'unexpected'; + status: number; + raw: Response; + value(): Promise; +}; + +export class SendOptionalInlineScalarError extends ResponseError { + readonly detail: SendOptionalInlineScalarErrorDetail; + + constructor( + response: Response, + detail: SendOptionalInlineScalarErrorDetail, + msg = 'Response returned an error code', + ) { + super(response, msg); + this.detail = detail; + } +} + +export type SendOptionalScalarErrorDetail = { + kind: 'unexpected'; + status: number; + raw: Response; + value(): Promise; +}; + +export class SendOptionalScalarError extends ResponseError { + readonly detail: SendOptionalScalarErrorDetail; + + constructor( + response: Response, + detail: SendOptionalScalarErrorDetail, + msg = 'Response returned an error code', + ) { + super(response, msg); + this.detail = detail; + } +} + +export type SendRequiredScalarErrorDetail = { + kind: 'unexpected'; + status: number; + raw: Response; + value(): Promise; +}; + +export class SendRequiredScalarError extends ResponseError { + readonly detail: SendRequiredScalarErrorDetail; + + constructor( + response: Response, + detail: SendRequiredScalarErrorDetail, + msg = 'Response returned an error code', + ) { + super(response, msg); + this.detail = detail; + } +} + +export interface ConversionApiInterface { + exchangeMatrixRaw: ( + requestParameters: ApiExchangeMatrixRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ) => Promise; + exchangeMatrix: ( + requestParameters: ApiExchangeMatrixRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ) => Promise; + exchangeMultipartRaw: ( + requestParameters: ApiExchangeMultipartRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ) => Promise; + exchangeMultipart: ( + requestParameters: ApiExchangeMultipartRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ) => Promise; + exchangeNullableRaw: ( + requestParameters: ApiExchangeNullableRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ) => Promise; + exchangeNullable: ( + requestParameters: ApiExchangeNullableRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ) => Promise; + sendOptionalDetailRaw: ( + requestParameters: ApiSendOptionalDetailRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ) => Promise; + sendOptionalDetail: ( + requestParameters: ApiSendOptionalDetailRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ) => Promise; + sendOptionalInlineScalarRaw: ( + requestParameters: ApiSendOptionalInlineScalarRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ) => Promise; + sendOptionalInlineScalar: ( + requestParameters: ApiSendOptionalInlineScalarRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ) => Promise; + sendOptionalScalarRaw: ( + requestParameters: ApiSendOptionalScalarRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ) => Promise; + sendOptionalScalar: ( + requestParameters: ApiSendOptionalScalarRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ) => Promise; + sendRequiredScalarRaw: ( + requestParameters: ApiSendRequiredScalarRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ) => Promise; + sendRequiredScalar: ( + requestParameters: ApiSendRequiredScalarRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ) => Promise; +} + +export class ConversionApi extends BaseAPI implements ConversionApiInterface { + /** + * Initialize the API client + */ + constructor(configuration?: Configuration) { + super(configuration ?? DefaultConfig); + } + + async exchangeMatrixRaw( + requestParameters: ApiExchangeMatrixRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ): Promise { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { + throw new RequiredError( + 'body', + 'Required parameter "body" was null or undefined when calling exchangeMatrixRaw().', + ); + } + // Build path with path parameters + const urlPath = `/matrix`; + // Build query parameters + const queryParameters: HTTPQuery = {}; + // Build headers + const headerParameters: Record = { + 'Content-Type': 'application/json', + }; + + // Prepare request body + const requestBody = convert$35ToJSON(requestParameters['body']); + // Make request + const response = await this.request( + { + path: urlPath, + method: 'POST', + headers: headerParameters, + query: queryParameters, + body: requestBody, + }, + initOverrides, + ); + + // Handle responses + if (!(response.status >= 200 && response.status < 300)) { + const errorRaw = response.clone(); + if (response.status === 400) { + let errorValue: Promise | undefined; + throw new ExchangeMatrixError(response, { + kind: 'badRequest', + status: response.status as 400, + raw: response, + value: () => { + errorValue ??= new JSONApiResponse(errorRaw.clone(), (json) => { + return convert$35FromJSON(json as Tool$Wire[][]); + }).value(); + return errorValue; + }, + }); + } else { + let errorValue: Promise | undefined; + throw new ExchangeMatrixError(response, { + kind: 'unexpected', + status: response.status, + raw: response, + value: () => { + errorValue ??= errorRaw.clone().blob(); + return errorValue; + }, + }); + } + } + if (response.status === 200) { + return new JSONApiResponse(response, (json) => { + return convert$35FromJSON(json as Tool$Wire[][]); + }) as JSONApiResponse & { status: 200 }; + } else { + return new JSONApiResponse(response) as JSONApiResponse & HttpSuccessResponse; + } + } + + async exchangeMatrix( + requestParameters: ApiExchangeMatrixRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ): Promise { + const response = await this.exchangeMatrixRaw(requestParameters, initOverrides); + return (await response.value()) as Tool[][]; + } + + async exchangeMultipartRaw( + requestParameters: ApiExchangeMultipartRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ): Promise { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { + throw new RequiredError( + 'body', + 'Required parameter "body" was null or undefined when calling exchangeMultipartRaw().', + ); + } + // Build path with path parameters + const urlPath = `/multipart`; + // Build query parameters + const queryParameters: HTTPQuery = {}; + // Build headers + const headerParameters: Record = {}; + + // Prepare request body + const multipartBody = requestParameters['body']; + const multipartBoundary = '----openapi-nexus-' + Math.random().toString(16).slice(2); + const multipartChunks: Array = []; + headerParameters['Content-Type'] = 'multipart/form-data; boundary=' + multipartBoundary; + const multipartValue$0 = multipartBody['payloadValues']; + multipartChunks.push( + '--', + multipartBoundary, + '\r\nContent-Disposition: form-data; name="payload_values"\r\nContent-Type: application/json\r\n\r\n', + ); + multipartChunks.push(JSON.stringify(convert$39ToJSON(multipartValue$0))); + multipartChunks.push('\r\n'); + const multipartValue$1 = multipartBody['uploadFile']; + if (multipartValue$1 !== undefined && multipartValue$1 !== null) { + const multipartFilename$1 = uploadFileFilename(multipartValue$1, 'upload_file'); + multipartChunks.push( + '--', + multipartBoundary, + '\r\nContent-Disposition: form-data; name="upload_file"; filename="', + multipartHeaderValue(multipartFilename$1), + '"\r\nContent-Type: application/octet-stream\r\n\r\n', + ); + multipartChunks.push(uploadFileData(multipartValue$1)); + multipartChunks.push('\r\n'); + } + multipartChunks.push('--' + multipartBoundary + '--\r\n'); + const requestBody = new Blob(multipartChunks); + // Make request + const response = await this.request( + { + path: urlPath, + method: 'POST', + headers: headerParameters, + query: queryParameters, + body: requestBody, + }, + initOverrides, + ); + + // Handle responses + if (!(response.status >= 200 && response.status < 300)) { + const errorRaw = response.clone(); + { + let errorValue: Promise | undefined; + throw new ExchangeMultipartError(response, { + kind: 'unexpected', + status: response.status, + raw: response, + value: () => { + errorValue ??= errorRaw.clone().blob(); + return errorValue; + }, + }); + } + } + if (response.status === 204) { + return new VoidApiResponse(response) as VoidApiResponse & { status: 204 }; + } else { + return new VoidApiResponse(response) as VoidApiResponse & HttpSuccessResponse; + } + } + + async exchangeMultipart( + requestParameters: ApiExchangeMultipartRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ): Promise { + const response = await this.exchangeMultipartRaw(requestParameters, initOverrides); + return await response.value(); + } + + async exchangeNullableRaw( + requestParameters: ApiExchangeNullableRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ): Promise { + if (requestParameters['body'] === undefined) { + throw new RequiredError( + 'body', + 'Required parameter "body" was undefined when calling exchangeNullableRaw().', + ); + } + // Build path with path parameters + const urlPath = `/nullable`; + // Build query parameters + const queryParameters: HTTPQuery = {}; + // Build headers + const headerParameters: Record = { + 'Content-Type': 'application/json', + }; + + // Prepare request body + const requestBody = convert$37ToJSON(requestParameters['body']); + // Make request + const response = await this.request( + { + path: urlPath, + method: 'POST', + headers: headerParameters, + query: queryParameters, + body: requestBody, + }, + initOverrides, + ); + + // Handle responses + if (!(response.status >= 200 && response.status < 300)) { + const errorRaw = response.clone(); + { + let errorValue: Promise | undefined; + throw new ExchangeNullableError(response, { + kind: 'unexpected', + status: response.status, + raw: response, + value: () => { + errorValue ??= errorRaw.clone().blob(); + return errorValue; + }, + }); + } + } + if (response.status === 200) { + return new JSONApiResponse(response, (json) => { + return convert$37FromJSON(json as Tool$Wire[] | null); + }) as JSONApiResponse & { status: 200 }; + } else { + return new JSONApiResponse(response) as JSONApiResponse & HttpSuccessResponse; + } + } + + async exchangeNullable( + requestParameters: ApiExchangeNullableRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ): Promise { + const response = await this.exchangeNullableRaw(requestParameters, initOverrides); + return (await response.value()) as Tool[] | null; + } + + async sendOptionalDetailRaw( + requestParameters: ApiSendOptionalDetailRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ): Promise { + // Build path with path parameters + const urlPath = `/optional-detail`; + // Build query parameters + const queryParameters: HTTPQuery = {}; + // Build headers + const headerParameters: Record = { + 'Content-Type': 'application/json', + }; + + // Prepare request body + const requestBody = (() => { + const bodyValue = requestParameters['body']; + if (bodyValue === undefined) { + return undefined; + } + return nullableDetailToJSON(bodyValue); + })(); + // Make request + const response = await this.request( + { + path: urlPath, + method: 'POST', + headers: headerParameters, + query: queryParameters, + body: requestBody, + }, + initOverrides, + ); + + // Handle responses + if (!(response.status >= 200 && response.status < 300)) { + const errorRaw = response.clone(); + { + let errorValue: Promise | undefined; + throw new SendOptionalDetailError(response, { + kind: 'unexpected', + status: response.status, + raw: response, + value: () => { + errorValue ??= errorRaw.clone().blob(); + return errorValue; + }, + }); + } + } + if (response.status === 204) { + return new VoidApiResponse(response) as VoidApiResponse & { status: 204 }; + } else { + return new VoidApiResponse(response) as VoidApiResponse & HttpSuccessResponse; + } + } + + async sendOptionalDetail( + requestParameters: ApiSendOptionalDetailRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ): Promise { + const response = await this.sendOptionalDetailRaw(requestParameters, initOverrides); + return await response.value(); + } + + async sendOptionalInlineScalarRaw( + requestParameters: ApiSendOptionalInlineScalarRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ): Promise { + // Build path with path parameters + const urlPath = `/optional-inline-scalar`; + // Build query parameters + const queryParameters: HTTPQuery = {}; + // Build headers + const headerParameters: Record = { + 'Content-Type': 'application/json', + }; + + // Prepare request body + const requestBody = (() => { + const bodyValue = requestParameters['body']; + if (bodyValue === undefined) { + return undefined; + } + return sendOptionalInlineScalarRequestToJSON(bodyValue); + })(); + // Make request + const response = await this.request( + { + path: urlPath, + method: 'POST', + headers: headerParameters, + query: queryParameters, + body: requestBody, + }, + initOverrides, + ); + + // Handle responses + if (!(response.status >= 200 && response.status < 300)) { + const errorRaw = response.clone(); + { + let errorValue: Promise | undefined; + throw new SendOptionalInlineScalarError(response, { + kind: 'unexpected', + status: response.status, + raw: response, + value: () => { + errorValue ??= errorRaw.clone().blob(); + return errorValue; + }, + }); + } + } + if (response.status === 204) { + return new VoidApiResponse(response) as VoidApiResponse & { status: 204 }; + } else { + return new VoidApiResponse(response) as VoidApiResponse & HttpSuccessResponse; + } + } + + async sendOptionalInlineScalar( + requestParameters: ApiSendOptionalInlineScalarRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ): Promise { + const response = await this.sendOptionalInlineScalarRaw(requestParameters, initOverrides); + return await response.value(); + } + + async sendOptionalScalarRaw( + requestParameters: ApiSendOptionalScalarRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ): Promise { + // Build path with path parameters + const urlPath = `/optional-scalar`; + // Build query parameters + const queryParameters: HTTPQuery = {}; + // Build headers + const headerParameters: Record = { + 'Content-Type': 'application/json', + }; + + // Prepare request body + const requestBody = (() => { + const bodyValue = requestParameters['body']; + if (bodyValue === undefined) { + return undefined; + } + return nullableScalarInnerToJSON(bodyValue); + })(); + // Make request + const response = await this.request( + { + path: urlPath, + method: 'POST', + headers: headerParameters, + query: queryParameters, + body: requestBody, + }, + initOverrides, + ); + + // Handle responses + if (!(response.status >= 200 && response.status < 300)) { + const errorRaw = response.clone(); + { + let errorValue: Promise | undefined; + throw new SendOptionalScalarError(response, { + kind: 'unexpected', + status: response.status, + raw: response, + value: () => { + errorValue ??= errorRaw.clone().blob(); + return errorValue; + }, + }); + } + } + if (response.status === 204) { + return new VoidApiResponse(response) as VoidApiResponse & { status: 204 }; + } else { + return new VoidApiResponse(response) as VoidApiResponse & HttpSuccessResponse; + } + } + + async sendOptionalScalar( + requestParameters: ApiSendOptionalScalarRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ): Promise { + const response = await this.sendOptionalScalarRaw(requestParameters, initOverrides); + return await response.value(); + } + + async sendRequiredScalarRaw( + requestParameters: ApiSendRequiredScalarRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ): Promise { + if (requestParameters['body'] === undefined) { + throw new RequiredError( + 'body', + 'Required parameter "body" was undefined when calling sendRequiredScalarRaw().', + ); + } + // Build path with path parameters + const urlPath = `/required-scalar`; + // Build query parameters + const queryParameters: HTTPQuery = {}; + // Build headers + const headerParameters: Record = { + 'Content-Type': 'application/json', + }; + + // Prepare request body + const requestBody = nullableScalarInnerToJSON(requestParameters['body']); + // Make request + const response = await this.request( + { + path: urlPath, + method: 'POST', + headers: headerParameters, + query: queryParameters, + body: requestBody, + }, + initOverrides, + ); + + // Handle responses + if (!(response.status >= 200 && response.status < 300)) { + const errorRaw = response.clone(); + { + let errorValue: Promise | undefined; + throw new SendRequiredScalarError(response, { + kind: 'unexpected', + status: response.status, + raw: response, + value: () => { + errorValue ??= errorRaw.clone().blob(); + return errorValue; + }, + }); + } + } + if (response.status === 204) { + return new VoidApiResponse(response) as VoidApiResponse & { status: 204 }; + } else { + return new VoidApiResponse(response) as VoidApiResponse & HttpSuccessResponse; + } + } + + async sendRequiredScalar( + requestParameters: ApiSendRequiredScalarRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ): Promise { + const response = await this.sendRequiredScalarRaw(requestParameters, initOverrides); + return await response.value(); + } +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/apis/index.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/apis/index.ts.golden new file mode 100644 index 000000000..24b35e481 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/apis/index.ts.golden @@ -0,0 +1,39 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export type { + ApiExchangeMatrixRequest, + ExchangeMatrixRawResponse, + ExchangeMatrixErrorDetail, + ApiExchangeMultipartRequest, + ExchangeMultipartRawResponse, + ExchangeMultipartErrorDetail, + ApiExchangeNullableRequest, + ExchangeNullableRawResponse, + ExchangeNullableErrorDetail, + ApiSendOptionalDetailRequest, + SendOptionalDetailRawResponse, + SendOptionalDetailErrorDetail, + ApiSendOptionalInlineScalarRequest, + SendOptionalInlineScalarRawResponse, + SendOptionalInlineScalarErrorDetail, + ApiSendOptionalScalarRequest, + SendOptionalScalarRawResponse, + SendOptionalScalarErrorDetail, + ApiSendRequiredScalarRequest, + SendRequiredScalarRawResponse, + SendRequiredScalarErrorDetail, + ConversionApiInterface, +} from './ConversionApi'; +export { + ConversionApi, + ExchangeMatrixError, + ExchangeMultipartError, + ExchangeNullableError, + SendOptionalDetailError, + SendOptionalInlineScalarError, + SendOptionalScalarError, + SendRequiredScalarError, +} from './ConversionApi'; diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/index.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/index.ts.golden new file mode 100644 index 000000000..8ba5e6eb9 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/index.ts.golden @@ -0,0 +1,8 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export * from './runtime/runtime'; +export * from './apis'; +export * from './models'; diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/Adjacent.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/Adjacent.ts.golden new file mode 100644 index 000000000..a3580a4b6 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/Adjacent.ts.golden @@ -0,0 +1,43 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail, Detail$Wire } from './Detail'; +import { detailFromJSON, detailToJSON } from './Detail'; + +export type Adjacent$Wire = + | globalThis.Record<'event_type', 'empty'> + | (globalThis.Record<'event_type', 'data'> & globalThis.Record<'event_data', Detail$Wire>); + +export type Adjacent = + | globalThis.Record<'eventType', 'empty'> + | (globalThis.Record<'eventType', 'data'> & globalThis.Record<'eventData', Detail>); + +export function adjacentFromJSON(json: Adjacent$Wire): Adjacent { + if (json['event_type'] === 'empty') { + return { ['eventType']: 'empty' } as Adjacent; + } + if (json['event_type'] === 'data') { + return { + ['eventType']: 'data', + ['eventData']: detailFromJSON( + (json as globalThis.Record<'event_data', Detail$Wire>)['event_data'], + ), + } as Adjacent; + } + throw new Error('Unknown variant'); +} + +export function adjacentToJSON(value: Adjacent): Adjacent$Wire { + if (value['eventType'] === 'empty') { + return { ['event_type']: 'empty' } as Adjacent$Wire; + } + if (value['eventType'] === 'data') { + return { + ['event_type']: 'data', + ['event_data']: detailToJSON((value as globalThis.Record<'eventData', Detail>)['eventData']), + } as Adjacent$Wire; + } + throw new Error('Unknown variant'); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/AdjacentContainer.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/AdjacentContainer.ts.golden new file mode 100644 index 000000000..6f4872ec2 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/AdjacentContainer.ts.golden @@ -0,0 +1,60 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { convert$0FromJSON, convert$0ToJSON } from '../runtime/valueConversion$0'; +import type { Detail, Detail$Wire } from './Detail'; + +export type AdjacentContainer$Wire = + | globalThis.Record<'event_type', 'empty'> + | (globalThis.Record<'event_type', 'data'> & + globalThis.Record< + 'event_data', + readonly (globalThis.Record | string)[] + >); + +export type AdjacentContainer = + | globalThis.Record<'eventType', 'empty'> + | (globalThis.Record<'eventType', 'data'> & + globalThis.Record<'eventData', readonly (globalThis.Record | string)[]>); + +export function adjacentContainerFromJSON(json: AdjacentContainer$Wire): AdjacentContainer { + if (json['event_type'] === 'empty') { + return { ['eventType']: 'empty' } as AdjacentContainer; + } + if (json['event_type'] === 'data') { + return { + ['eventType']: 'data', + ['eventData']: convert$0FromJSON( + ( + json as globalThis.Record< + 'event_data', + readonly (globalThis.Record | string)[] + > + )['event_data'], + ), + } as AdjacentContainer; + } + throw new Error('Unknown variant'); +} + +export function adjacentContainerToJSON(value: AdjacentContainer): AdjacentContainer$Wire { + if (value['eventType'] === 'empty') { + return { ['event_type']: 'empty' } as AdjacentContainer$Wire; + } + if (value['eventType'] === 'data') { + return { + ['event_type']: 'data', + ['event_data']: convert$0ToJSON( + ( + value as globalThis.Record< + 'eventData', + readonly (globalThis.Record | string)[] + > + )['eventData'], + ), + } as AdjacentContainer$Wire; + } + throw new Error('Unknown variant'); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/AdjacentContainerEmpty.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/AdjacentContainerEmpty.ts.golden new file mode 100644 index 000000000..b59330795 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/AdjacentContainerEmpty.ts.golden @@ -0,0 +1,28 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface AdjacentContainerEmpty$Wire { + readonly event_type: 'empty'; +} + +export interface AdjacentContainerEmpty { + readonly eventType: 'empty'; +} + +export function adjacentContainerEmptyFromJSON( + json: AdjacentContainerEmpty$Wire, +): AdjacentContainerEmpty { + return { + eventType: json.event_type, + }; +} + +export function adjacentContainerEmptyToJSON( + value: AdjacentContainerEmpty, +): AdjacentContainerEmpty$Wire { + return { + event_type: value.eventType, + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/AdjacentEmpty.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/AdjacentEmpty.ts.golden new file mode 100644 index 000000000..665908893 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/AdjacentEmpty.ts.golden @@ -0,0 +1,24 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface AdjacentEmpty$Wire { + readonly event_type: 'empty'; +} + +export interface AdjacentEmpty { + readonly eventType: 'empty'; +} + +export function adjacentEmptyFromJSON(json: AdjacentEmpty$Wire): AdjacentEmpty { + return { + eventType: json.event_type, + }; +} + +export function adjacentEmptyToJSON(value: AdjacentEmpty): AdjacentEmpty$Wire { + return { + event_type: value.eventType, + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/AdjacentOrNumber.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/AdjacentOrNumber.ts.golden new file mode 100644 index 000000000..5fdec5afb --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/AdjacentOrNumber.ts.golden @@ -0,0 +1,47 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$4FromJSON, + matches$4ToJSON, + matches$6FromJSON, + matches$6ToJSON, +} from '../runtime/unionMatch'; +import type { Adjacent, Adjacent$Wire } from './Adjacent'; +import { adjacentFromJSON, adjacentToJSON } from './Adjacent'; + +export type AdjacentOrNumber$Wire = Adjacent$Wire | number; + +export type AdjacentOrNumber = Adjacent | number; + +export function adjacentOrNumberFromJSON(json: AdjacentOrNumber$Wire): AdjacentOrNumber { + const candidate: unknown = json; + const matches = [matches$6FromJSON(candidate), matches$4FromJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous AdjacentOrNumber untagged union value'); + } + if (matches[0]) { + return adjacentFromJSON(json as unknown as Adjacent$Wire); + } + if (matches[1]) { + return json as unknown as number; + } + throw new TypeError('No matching AdjacentOrNumber untagged union member'); +} + +export function adjacentOrNumberToJSON(value: AdjacentOrNumber): AdjacentOrNumber$Wire { + const candidate: unknown = value; + const matches = [matches$6ToJSON(candidate), matches$4ToJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous AdjacentOrNumber untagged union value'); + } + if (matches[0]) { + return adjacentToJSON(value as unknown as Adjacent); + } + if (matches[1]) { + return value as unknown as number; + } + throw new TypeError('No matching AdjacentOrNumber untagged union member'); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/AdjacentPrimitive.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/AdjacentPrimitive.ts.golden new file mode 100644 index 000000000..55b59f406 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/AdjacentPrimitive.ts.golden @@ -0,0 +1,44 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { convert$3FromJSON, convert$3ToJSON } from '../runtime/valueConversion$3'; + +export type AdjacentPrimitive$Wire = + | globalThis.Record<'event_type', 'empty'> + | (globalThis.Record<'event_type', 'data'> & globalThis.Record<'event_data', readonly number[]>); + +export type AdjacentPrimitive = + | globalThis.Record<'eventType', 'empty'> + | (globalThis.Record<'eventType', 'data'> & globalThis.Record<'eventData', readonly number[]>); + +export function adjacentPrimitiveFromJSON(json: AdjacentPrimitive$Wire): AdjacentPrimitive { + if (json['event_type'] === 'empty') { + return { ['eventType']: 'empty' } as AdjacentPrimitive; + } + if (json['event_type'] === 'data') { + return { + ['eventType']: 'data', + ['eventData']: convert$3FromJSON( + (json as globalThis.Record<'event_data', readonly number[]>)['event_data'], + ), + } as AdjacentPrimitive; + } + throw new Error('Unknown variant'); +} + +export function adjacentPrimitiveToJSON(value: AdjacentPrimitive): AdjacentPrimitive$Wire { + if (value['eventType'] === 'empty') { + return { ['event_type']: 'empty' } as AdjacentPrimitive$Wire; + } + if (value['eventType'] === 'data') { + return { + ['event_type']: 'data', + ['event_data']: convert$3ToJSON( + (value as globalThis.Record<'eventData', readonly number[]>)['eventData'], + ), + } as AdjacentPrimitive$Wire; + } + throw new Error('Unknown variant'); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/AdjacentPrimitiveEmpty.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/AdjacentPrimitiveEmpty.ts.golden new file mode 100644 index 000000000..bcc6ac635 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/AdjacentPrimitiveEmpty.ts.golden @@ -0,0 +1,28 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface AdjacentPrimitiveEmpty$Wire { + readonly event_type: 'empty'; +} + +export interface AdjacentPrimitiveEmpty { + readonly eventType: 'empty'; +} + +export function adjacentPrimitiveEmptyFromJSON( + json: AdjacentPrimitiveEmpty$Wire, +): AdjacentPrimitiveEmpty { + return { + eventType: json.event_type, + }; +} + +export function adjacentPrimitiveEmptyToJSON( + value: AdjacentPrimitiveEmpty, +): AdjacentPrimitiveEmpty$Wire { + return { + event_type: value.eventType, + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/AliasChoice.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/AliasChoice.ts.golden new file mode 100644 index 000000000..8814ec047 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/AliasChoice.ts.golden @@ -0,0 +1,49 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$11FromJSON, + matches$11ToJSON, + matches$1FromJSON, + matches$1ToJSON, +} from '../runtime/unionMatch'; +import type { Detail, Detail$Wire } from './Detail'; +import { detailFromJSON, detailToJSON } from './Detail'; +import type { ValueAlias, ValueAlias$Wire } from './ValueAlias'; +import { valueAliasFromJSON, valueAliasToJSON } from './ValueAlias'; + +export type AliasChoice$Wire = ValueAlias$Wire | Detail$Wire; + +export type AliasChoice = ValueAlias | Detail; + +export function aliasChoiceFromJSON(json: AliasChoice$Wire): AliasChoice { + const candidate: unknown = json; + const matches = [matches$11FromJSON(candidate), matches$1FromJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous AliasChoice untagged union value'); + } + if (matches[0]) { + return valueAliasFromJSON(json as unknown as ValueAlias$Wire); + } + if (matches[1]) { + return detailFromJSON(json as unknown as Detail$Wire); + } + throw new TypeError('No matching AliasChoice untagged union member'); +} + +export function aliasChoiceToJSON(value: AliasChoice): AliasChoice$Wire { + const candidate: unknown = value; + const matches = [matches$11ToJSON(candidate), matches$1ToJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous AliasChoice untagged union value'); + } + if (matches[0]) { + return valueAliasToJSON(value as unknown as ValueAlias); + } + if (matches[1]) { + return detailToJSON(value as unknown as Detail); + } + throw new TypeError('No matching AliasChoice untagged union member'); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/AliasOuter.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/AliasOuter.ts.golden new file mode 100644 index 000000000..4e0a77e37 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/AliasOuter.ts.golden @@ -0,0 +1,49 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$13FromJSON, + matches$13ToJSON, + matches$1FromJSON, + matches$1ToJSON, +} from '../runtime/unionMatch'; +import type { OpenAlias, OpenAlias$Wire } from './OpenAlias'; +import { openAliasFromJSON, openAliasToJSON } from './OpenAlias'; +import type { SearchTool, SearchTool$Wire } from './SearchTool'; +import { searchToolFromJSON, searchToolToJSON } from './SearchTool'; + +export type AliasOuter$Wire = unknown; + +export type AliasOuter = unknown; + +export function aliasOuterFromJSON(json: AliasOuter$Wire): AliasOuter { + const candidate: unknown = json; + const matches = [matches$1FromJSON(candidate), matches$13FromJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous AliasOuter untagged union value'); + } + if (matches[0]) { + return openAliasFromJSON(json as unknown as OpenAlias$Wire); + } + if (matches[1]) { + return searchToolFromJSON(json as unknown as SearchTool$Wire); + } + return openAliasFromJSON(json as unknown as OpenAlias$Wire); +} + +export function aliasOuterToJSON(value: AliasOuter): AliasOuter$Wire { + const candidate: unknown = value; + const matches = [matches$1ToJSON(candidate), matches$13ToJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous AliasOuter untagged union value'); + } + if (matches[0]) { + return openAliasToJSON(value as unknown as OpenAlias); + } + if (matches[1]) { + return searchToolToJSON(value as unknown as SearchTool); + } + return openAliasToJSON(value as unknown as OpenAlias); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/AlreadyNullableEnumParent.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/AlreadyNullableEnumParent.ts.golden new file mode 100644 index 000000000..3ade40c02 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/AlreadyNullableEnumParent.ts.golden @@ -0,0 +1,31 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { convert$5FromJSON, convert$5ToJSON } from '../runtime/valueConversion$5'; +import type { NullableScalarInner, NullableScalarInner$Wire } from './NullableScalarInner'; + +export interface AlreadyNullableEnumParent$Wire { + readonly scalar_value: NullableScalarInner$Wire | null; +} + +export interface AlreadyNullableEnumParent { + readonly scalarValue: NullableScalarInner | null; +} + +export function alreadyNullableEnumParentFromJSON( + json: AlreadyNullableEnumParent$Wire, +): AlreadyNullableEnumParent { + return { + scalarValue: convert$5FromJSON(json.scalar_value), + }; +} + +export function alreadyNullableEnumParentToJSON( + value: AlreadyNullableEnumParent, +): AlreadyNullableEnumParent$Wire { + return { + scalar_value: convert$5ToJSON(value.scalarValue), + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/AlreadyNullableOuterEnumParent.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/AlreadyNullableOuterEnumParent.ts.golden new file mode 100644 index 000000000..ba67c7849 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/AlreadyNullableOuterEnumParent.ts.golden @@ -0,0 +1,31 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { convert$6FromJSON, convert$6ToJSON } from '../runtime/valueConversion$6'; +import type { NullableScalarInner, NullableScalarInner$Wire } from './NullableScalarInner'; + +export interface AlreadyNullableOuterEnumParent$Wire { + readonly scalar_value: NullableScalarInner$Wire | null; +} + +export interface AlreadyNullableOuterEnumParent { + readonly scalarValue: NullableScalarInner | null; +} + +export function alreadyNullableOuterEnumParentFromJSON( + json: AlreadyNullableOuterEnumParent$Wire, +): AlreadyNullableOuterEnumParent { + return { + scalarValue: convert$6FromJSON(json.scalar_value), + }; +} + +export function alreadyNullableOuterEnumParentToJSON( + value: AlreadyNullableOuterEnumParent, +): AlreadyNullableOuterEnumParent$Wire { + return { + scalar_value: convert$6ToJSON(value.scalarValue), + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/AlternativeTool.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/AlternativeTool.ts.golden new file mode 100644 index 000000000..ea69f7eec --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/AlternativeTool.ts.golden @@ -0,0 +1,32 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface AlternativeTool$Wire { + readonly input_schema: globalThis.Record; + readonly name: string; + readonly second_option?: boolean; +} + +export interface AlternativeTool { + readonly inputSchema: globalThis.Record; + readonly name: string; + readonly secondOption?: boolean; +} + +export function alternativeToolFromJSON(json: AlternativeTool$Wire): AlternativeTool { + return { + inputSchema: json.input_schema, + name: json.name, + secondOption: json.second_option, + }; +} + +export function alternativeToolToJSON(value: AlternativeTool): AlternativeTool$Wire { + return { + input_schema: value.inputSchema, + name: value.name, + second_option: value.secondOption, + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/AmbiguousInner.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/AmbiguousInner.ts.golden new file mode 100644 index 000000000..dc1418ae0 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/AmbiguousInner.ts.golden @@ -0,0 +1,49 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$17FromJSON, + matches$17ToJSON, + matches$1FromJSON, + matches$1ToJSON, +} from '../runtime/unionMatch'; +import type { Detail, Detail$Wire } from './Detail'; +import { detailFromJSON, detailToJSON } from './Detail'; +import type { OtherDetail, OtherDetail$Wire } from './OtherDetail'; +import { otherDetailFromJSON, otherDetailToJSON } from './OtherDetail'; + +export type AmbiguousInner$Wire = unknown; + +export type AmbiguousInner = unknown; + +export function ambiguousInnerFromJSON(json: AmbiguousInner$Wire): AmbiguousInner { + const candidate: unknown = json; + const matches = [matches$1FromJSON(candidate), matches$17FromJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous AmbiguousInner untagged union value'); + } + if (matches[0]) { + return detailFromJSON(json as unknown as Detail$Wire); + } + if (matches[1]) { + return otherDetailFromJSON(json as unknown as OtherDetail$Wire); + } + return json as unknown as unknown; +} + +export function ambiguousInnerToJSON(value: AmbiguousInner): AmbiguousInner$Wire { + const candidate: unknown = value; + const matches = [matches$1ToJSON(candidate), matches$17ToJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous AmbiguousInner untagged union value'); + } + if (matches[0]) { + return detailToJSON(value as unknown as Detail); + } + if (matches[1]) { + return otherDetailToJSON(value as unknown as OtherDetail); + } + return value as unknown as unknown; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/AmbiguousMap.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/AmbiguousMap.ts.golden new file mode 100644 index 000000000..88def1499 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/AmbiguousMap.ts.golden @@ -0,0 +1,40 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { ambiguousInnerFromJSON, ambiguousInnerToJSON } from './AmbiguousInner'; + +export interface AmbiguousMap$Wire extends Readonly> {} + +export interface AmbiguousMap extends Readonly> {} + +export function ambiguousMapFromJSON(json: AmbiguousMap$Wire): AmbiguousMap { + const sourceEntries = Object.entries(json); + const additionalEntries = sourceEntries; + const additionalProperties = Object.fromEntries( + additionalEntries.map(([key, value]) => { + const additionalValue = value; + return [key, ambiguousInnerFromJSON(additionalValue)] as const; + }), + ); + const result = { + ...additionalProperties, + }; + return result; +} + +export function ambiguousMapToJSON(value: AmbiguousMap): AmbiguousMap$Wire { + const sourceEntries = Object.entries(value); + const additionalEntries = sourceEntries; + const additionalProperties = Object.fromEntries( + additionalEntries.map(([key, value]) => { + const additionalValue = value; + return [key, ambiguousInnerToJSON(additionalValue)] as const; + }), + ); + const result = { + ...additionalProperties, + }; + return result; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/AmbiguousMapOuter.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/AmbiguousMapOuter.ts.golden new file mode 100644 index 000000000..7f7d827ea --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/AmbiguousMapOuter.ts.golden @@ -0,0 +1,36 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { matches$18FromJSON, matches$18ToJSON } from '../runtime/unionMatch'; +import type { AmbiguousMap, AmbiguousMap$Wire } from './AmbiguousMap'; +import { ambiguousMapFromJSON, ambiguousMapToJSON } from './AmbiguousMap'; + +export type AmbiguousMapOuter$Wire = unknown; + +export type AmbiguousMapOuter = unknown; + +export function ambiguousMapOuterFromJSON(json: AmbiguousMapOuter$Wire): AmbiguousMapOuter { + const candidate: unknown = json; + const matches = [matches$18FromJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous AmbiguousMapOuter untagged union value'); + } + if (matches[0]) { + return ambiguousMapFromJSON(json as unknown as AmbiguousMap$Wire); + } + return json as unknown as unknown; +} + +export function ambiguousMapOuterToJSON(value: AmbiguousMapOuter): AmbiguousMapOuter$Wire { + const candidate: unknown = value; + const matches = [matches$18ToJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous AmbiguousMapOuter untagged union value'); + } + if (matches[0]) { + return ambiguousMapToJSON(value as unknown as AmbiguousMap); + } + return value as unknown as unknown; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/AmbiguousOuter.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/AmbiguousOuter.ts.golden new file mode 100644 index 000000000..23d42bd89 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/AmbiguousOuter.ts.golden @@ -0,0 +1,47 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$21FromJSON, + matches$21ToJSON, + matches$2FromJSON, + matches$2ToJSON, +} from '../runtime/unionMatch'; +import type { AmbiguousInner, AmbiguousInner$Wire } from './AmbiguousInner'; +import { ambiguousInnerFromJSON, ambiguousInnerToJSON } from './AmbiguousInner'; + +export type AmbiguousOuter$Wire = unknown; + +export type AmbiguousOuter = unknown; + +export function ambiguousOuterFromJSON(json: AmbiguousOuter$Wire): AmbiguousOuter { + const candidate: unknown = json; + const matches = [matches$21FromJSON(candidate), matches$2FromJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous AmbiguousOuter untagged union value'); + } + if (matches[0]) { + return ambiguousInnerFromJSON(json as unknown as AmbiguousInner$Wire); + } + if (matches[1]) { + return json as unknown as string; + } + return ambiguousInnerFromJSON(json as unknown as AmbiguousInner$Wire); +} + +export function ambiguousOuterToJSON(value: AmbiguousOuter): AmbiguousOuter$Wire { + const candidate: unknown = value; + const matches = [matches$21ToJSON(candidate), matches$2ToJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous AmbiguousOuter untagged union value'); + } + if (matches[0]) { + return ambiguousInnerToJSON(value as unknown as AmbiguousInner); + } + if (matches[1]) { + return value as unknown as string; + } + return ambiguousInnerToJSON(value as unknown as AmbiguousInner); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/AmbiguousTool.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/AmbiguousTool.ts.golden new file mode 100644 index 000000000..74112da61 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/AmbiguousTool.ts.golden @@ -0,0 +1,49 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$23FromJSON, + matches$23ToJSON, + matches$24FromJSON, + matches$24ToJSON, +} from '../runtime/unionMatch'; +import type { AlternativeTool, AlternativeTool$Wire } from './AlternativeTool'; +import { alternativeToolFromJSON, alternativeToolToJSON } from './AlternativeTool'; +import type { ClientTool, ClientTool$Wire } from './ClientTool'; +import { clientToolFromJSON, clientToolToJSON } from './ClientTool'; + +export type AmbiguousTool$Wire = ClientTool$Wire | AlternativeTool$Wire; + +export type AmbiguousTool = ClientTool | AlternativeTool; + +export function ambiguousToolFromJSON(json: AmbiguousTool$Wire): AmbiguousTool { + const candidate: unknown = json; + const matches = [matches$23FromJSON(candidate), matches$24FromJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous AmbiguousTool untagged union value'); + } + if (matches[0]) { + return clientToolFromJSON(json as unknown as ClientTool$Wire); + } + if (matches[1]) { + return alternativeToolFromJSON(json as unknown as AlternativeTool$Wire); + } + throw new TypeError('No matching AmbiguousTool untagged union member'); +} + +export function ambiguousToolToJSON(value: AmbiguousTool): AmbiguousTool$Wire { + const candidate: unknown = value; + const matches = [matches$23ToJSON(candidate), matches$24ToJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous AmbiguousTool untagged union value'); + } + if (matches[0]) { + return clientToolToJSON(value as unknown as ClientTool); + } + if (matches[1]) { + return alternativeToolToJSON(value as unknown as AlternativeTool); + } + throw new TypeError('No matching AmbiguousTool untagged union member'); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/ArrayPayload.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/ArrayPayload.ts.golden new file mode 100644 index 000000000..cbde8a749 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/ArrayPayload.ts.golden @@ -0,0 +1,24 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface ArrayPayload$Wire { + readonly wire_items: readonly string[]; +} + +export interface ArrayPayload { + readonly wireItems: readonly string[]; +} + +export function arrayPayloadFromJSON(json: ArrayPayload$Wire): ArrayPayload { + return { + wireItems: json.wire_items, + }; +} + +export function arrayPayloadToJSON(value: ArrayPayload): ArrayPayload$Wire { + return { + wire_items: value.wireItems, + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/ArrayPayloadOrBoolean.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/ArrayPayloadOrBoolean.ts.golden new file mode 100644 index 000000000..5f9d46b82 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/ArrayPayloadOrBoolean.ts.golden @@ -0,0 +1,51 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$25FromJSON, + matches$25ToJSON, + matches$5FromJSON, + matches$5ToJSON, +} from '../runtime/unionMatch'; +import type { ArrayPayload, ArrayPayload$Wire } from './ArrayPayload'; +import { arrayPayloadFromJSON, arrayPayloadToJSON } from './ArrayPayload'; + +export type ArrayPayloadOrBoolean$Wire = ArrayPayload$Wire | boolean; + +export type ArrayPayloadOrBoolean = ArrayPayload | boolean; + +export function arrayPayloadOrBooleanFromJSON( + json: ArrayPayloadOrBoolean$Wire, +): ArrayPayloadOrBoolean { + const candidate: unknown = json; + const matches = [matches$25FromJSON(candidate), matches$5FromJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous ArrayPayloadOrBoolean untagged union value'); + } + if (matches[0]) { + return arrayPayloadFromJSON(json as unknown as ArrayPayload$Wire); + } + if (matches[1]) { + return json as unknown as boolean; + } + throw new TypeError('No matching ArrayPayloadOrBoolean untagged union member'); +} + +export function arrayPayloadOrBooleanToJSON( + value: ArrayPayloadOrBoolean, +): ArrayPayloadOrBoolean$Wire { + const candidate: unknown = value; + const matches = [matches$25ToJSON(candidate), matches$5ToJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous ArrayPayloadOrBoolean untagged union value'); + } + if (matches[0]) { + return arrayPayloadToJSON(value as unknown as ArrayPayload); + } + if (matches[1]) { + return value as unknown as boolean; + } + throw new TypeError('No matching ArrayPayloadOrBoolean untagged union member'); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/ArrayWrapped.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/ArrayWrapped.ts.golden new file mode 100644 index 000000000..d71b89dfa --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/ArrayWrapped.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { DetailArrayOrObject, DetailArrayOrObject$Wire } from './DetailArrayOrObject'; +import { detailArrayOrObjectFromJSON, detailArrayOrObjectToJSON } from './DetailArrayOrObject'; + +export type ArrayWrapped$Wire = DetailArrayOrObject$Wire; + +export type ArrayWrapped = DetailArrayOrObject; + +export function arrayWrappedFromJSON(json: ArrayWrapped$Wire): ArrayWrapped { + return detailArrayOrObjectFromJSON(json); +} + +export function arrayWrappedToJSON(value: ArrayWrapped): ArrayWrapped$Wire { + return detailArrayOrObjectToJSON(value); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/BinaryModel.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/BinaryModel.ts.golden new file mode 100644 index 000000000..4febefc7f --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/BinaryModel.ts.golden @@ -0,0 +1,28 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface BinaryModel$Wire { + readonly binary_data: Blob | File; + readonly field_name: string; +} + +export interface BinaryModel { + readonly binaryData: Blob | File; + readonly fieldName: string; +} + +export function binaryModelFromJSON(json: BinaryModel$Wire): BinaryModel { + return { + binaryData: json.binary_data, + fieldName: json.field_name, + }; +} + +export function binaryModelToJSON(value: BinaryModel): BinaryModel$Wire { + return { + binary_data: value.binaryData, + field_name: value.fieldName, + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/BinaryUnion.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/BinaryUnion.ts.golden new file mode 100644 index 000000000..e2410df62 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/BinaryUnion.ts.golden @@ -0,0 +1,47 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$27FromJSON, + matches$27ToJSON, + matches$5FromJSON, + matches$5ToJSON, +} from '../runtime/unionMatch'; +import type { BinaryModel, BinaryModel$Wire } from './BinaryModel'; +import { binaryModelFromJSON, binaryModelToJSON } from './BinaryModel'; + +export type BinaryUnion$Wire = BinaryModel$Wire | boolean; + +export type BinaryUnion = BinaryModel | boolean; + +export function binaryUnionFromJSON(json: BinaryUnion$Wire): BinaryUnion { + const candidate: unknown = json; + const matches = [matches$27FromJSON(candidate), matches$5FromJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous BinaryUnion untagged union value'); + } + if (matches[0]) { + return binaryModelFromJSON(json as unknown as BinaryModel$Wire); + } + if (matches[1]) { + return json as unknown as boolean; + } + throw new TypeError('No matching BinaryUnion untagged union member'); +} + +export function binaryUnionToJSON(value: BinaryUnion): BinaryUnion$Wire { + const candidate: unknown = value; + const matches = [matches$27ToJSON(candidate), matches$5ToJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous BinaryUnion untagged union value'); + } + if (matches[0]) { + return binaryModelToJSON(value as unknown as BinaryModel); + } + if (matches[1]) { + return value as unknown as boolean; + } + throw new TypeError('No matching BinaryUnion untagged union member'); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/ClientTool.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/ClientTool.ts.golden new file mode 100644 index 000000000..8f4d94ac9 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/ClientTool.ts.golden @@ -0,0 +1,28 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface ClientTool$Wire { + readonly input_schema: globalThis.Record; + readonly name: string; +} + +export interface ClientTool { + readonly inputSchema: globalThis.Record; + readonly name: string; +} + +export function clientToolFromJSON(json: ClientTool$Wire): ClientTool { + return { + inputSchema: json.input_schema, + name: json.name, + }; +} + +export function clientToolToJSON(value: ClientTool): ClientTool$Wire { + return { + input_schema: value.inputSchema, + name: value.name, + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/ConstrainedOuterEnumParent.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/ConstrainedOuterEnumParent.ts.golden new file mode 100644 index 000000000..bfb4340d5 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/ConstrainedOuterEnumParent.ts.golden @@ -0,0 +1,31 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { convert$7FromJSON, convert$7ToJSON } from '../runtime/valueConversion$7'; +import type { NullableScalarInner, NullableScalarInner$Wire } from './NullableScalarInner'; + +export interface ConstrainedOuterEnumParent$Wire { + readonly scalar_value: NullableScalarInner$Wire | null; +} + +export interface ConstrainedOuterEnumParent { + readonly scalarValue: NullableScalarInner | null; +} + +export function constrainedOuterEnumParentFromJSON( + json: ConstrainedOuterEnumParent$Wire, +): ConstrainedOuterEnumParent { + return { + scalarValue: convert$7FromJSON(json.scalar_value), + }; +} + +export function constrainedOuterEnumParentToJSON( + value: ConstrainedOuterEnumParent, +): ConstrainedOuterEnumParent$Wire { + return { + scalar_value: convert$7ToJSON(value.scalarValue), + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/ContainerOuterEnum.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/ContainerOuterEnum.ts.golden new file mode 100644 index 000000000..d800e4094 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/ContainerOuterEnum.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { convert$8FromJSON, convert$8ToJSON } from '../runtime/valueConversion$8'; +import type { NullableScalarInner, NullableScalarInner$Wire } from './NullableScalarInner'; + +export type ContainerOuterEnum$Wire = readonly (NullableScalarInner$Wire | null)[]; + +export type ContainerOuterEnum = readonly (NullableScalarInner | null)[]; + +export function containerOuterEnumFromJSON(json: ContainerOuterEnum$Wire): ContainerOuterEnum { + return convert$8FromJSON(json); +} + +export function containerOuterEnumToJSON(value: ContainerOuterEnum): ContainerOuterEnum$Wire { + return convert$8ToJSON(value); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/Detail.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/Detail.ts.golden new file mode 100644 index 000000000..f9714af2f --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/Detail.ts.golden @@ -0,0 +1,24 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface Detail$Wire { + readonly wire_name: string; +} + +export interface Detail { + readonly wireName: string; +} + +export function detailFromJSON(json: Detail$Wire): Detail { + return { + wireName: json.wire_name, + }; +} + +export function detailToJSON(value: Detail): Detail$Wire { + return { + wire_name: value.wireName, + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/DetailArrayOrObject.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/DetailArrayOrObject.ts.golden new file mode 100644 index 000000000..73a233c56 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/DetailArrayOrObject.ts.golden @@ -0,0 +1,49 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$1FromJSON, + matches$1ToJSON, + matches$29FromJSON, + matches$29ToJSON, +} from '../runtime/unionMatch'; +import type { Value$11, Value$11Wire } from '../runtime/valueConversion$11'; +import { convert$11FromJSON, convert$11ToJSON } from '../runtime/valueConversion$11'; +import type { Detail, Detail$Wire } from './Detail'; +import { detailFromJSON, detailToJSON } from './Detail'; + +export type DetailArrayOrObject$Wire = readonly Detail$Wire[] | Detail$Wire; + +export type DetailArrayOrObject = readonly Detail[] | Detail; + +export function detailArrayOrObjectFromJSON(json: DetailArrayOrObject$Wire): DetailArrayOrObject { + const candidate: unknown = json; + const matches = [matches$29FromJSON(candidate), matches$1FromJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous DetailArrayOrObject untagged union value'); + } + if (matches[0]) { + return convert$11FromJSON(json as unknown as Value$11Wire); + } + if (matches[1]) { + return detailFromJSON(json as unknown as Detail$Wire); + } + throw new TypeError('No matching DetailArrayOrObject untagged union member'); +} + +export function detailArrayOrObjectToJSON(value: DetailArrayOrObject): DetailArrayOrObject$Wire { + const candidate: unknown = value; + const matches = [matches$29ToJSON(candidate), matches$1ToJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous DetailArrayOrObject untagged union value'); + } + if (matches[0]) { + return convert$11ToJSON(value as unknown as Value$11); + } + if (matches[1]) { + return detailToJSON(value as unknown as Detail); + } + throw new TypeError('No matching DetailArrayOrObject untagged union member'); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/DictionaryAlias.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/DictionaryAlias.ts.golden new file mode 100644 index 000000000..2c26c5575 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/DictionaryAlias.ts.golden @@ -0,0 +1,41 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail, Detail$Wire } from './Detail'; +import { detailFromJSON, detailToJSON } from './Detail'; + +export type DictionaryAlias$Wire = Readonly>; + +export type DictionaryAlias = Readonly>; + +export function dictionaryAliasFromJSON(json: DictionaryAlias$Wire): DictionaryAlias { + const sourceEntries = Object.entries(json); + const additionalEntries = sourceEntries; + const additionalProperties = Object.fromEntries( + additionalEntries.map(([key, value]) => { + const additionalValue = value; + return [key, detailFromJSON(additionalValue)] as const; + }), + ); + const result = { + ...additionalProperties, + }; + return result; +} + +export function dictionaryAliasToJSON(value: DictionaryAlias): DictionaryAlias$Wire { + const sourceEntries = Object.entries(value); + const additionalEntries = sourceEntries; + const additionalProperties = Object.fromEntries( + additionalEntries.map(([key, value]) => { + const additionalValue = value; + return [key, detailToJSON(additionalValue)] as const; + }), + ); + const result = { + ...additionalProperties, + }; + return result; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/EnumFreeOuterEnumParent.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/EnumFreeOuterEnumParent.ts.golden new file mode 100644 index 000000000..7a468e9e5 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/EnumFreeOuterEnumParent.ts.golden @@ -0,0 +1,31 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { convert$5FromJSON, convert$5ToJSON } from '../runtime/valueConversion$5'; +import type { NullableScalarInner, NullableScalarInner$Wire } from './NullableScalarInner'; + +export interface EnumFreeOuterEnumParent$Wire { + readonly scalar_value: NullableScalarInner$Wire | null; +} + +export interface EnumFreeOuterEnumParent { + readonly scalarValue: NullableScalarInner | null; +} + +export function enumFreeOuterEnumParentFromJSON( + json: EnumFreeOuterEnumParent$Wire, +): EnumFreeOuterEnumParent { + return { + scalarValue: json.scalar_value === null ? null : convert$5FromJSON(json.scalar_value), + }; +} + +export function enumFreeOuterEnumParentToJSON( + value: EnumFreeOuterEnumParent, +): EnumFreeOuterEnumParent$Wire { + return { + scalar_value: value.scalarValue === null ? null : convert$5ToJSON(value.scalarValue), + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/ExchangeMultipartMultipartRequestBody.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/ExchangeMultipartMultipartRequestBody.ts.golden new file mode 100644 index 000000000..67e02464e --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/ExchangeMultipartMultipartRequestBody.ts.golden @@ -0,0 +1,12 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { UploadFileInput } from '../runtime/runtime'; +import type { Detail } from './Detail'; + +export interface ExchangeMultipartMultipartRequestBody { + payloadValues: readonly (globalThis.Record | string)[]; + uploadFile?: UploadFileInput; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/ExchangeMultipartRequest.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/ExchangeMultipartRequest.ts.golden new file mode 100644 index 000000000..5c0478b0d --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/ExchangeMultipartRequest.ts.golden @@ -0,0 +1,35 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { convert$0FromJSON, convert$0ToJSON } from '../runtime/valueConversion$0'; +import type { Detail, Detail$Wire } from './Detail'; + +export interface ExchangeMultipartRequest$Wire { + readonly payload_values: readonly (globalThis.Record | string)[]; + readonly upload_file?: Blob | File; +} + +export interface ExchangeMultipartRequest { + readonly payloadValues: readonly (globalThis.Record | string)[]; + readonly uploadFile?: Blob | File; +} + +export function exchangeMultipartRequestFromJSON( + json: ExchangeMultipartRequest$Wire, +): ExchangeMultipartRequest { + return { + payloadValues: convert$0FromJSON(json.payload_values), + uploadFile: json.upload_file, + }; +} + +export function exchangeMultipartRequestToJSON( + value: ExchangeMultipartRequest, +): ExchangeMultipartRequest$Wire { + return { + payload_values: convert$0ToJSON(value.payloadValues), + upload_file: value.uploadFile, + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/ExplicitNullOverlap.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/ExplicitNullOverlap.ts.golden new file mode 100644 index 000000000..c9550d4a1 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/ExplicitNullOverlap.ts.golden @@ -0,0 +1,55 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$30FromJSON, + matches$30ToJSON, + matches$32FromJSON, + matches$32ToJSON, +} from '../runtime/unionMatch'; +import type { NullableDetail, NullableDetail$Wire } from './NullableDetail'; +import { nullableDetailFromJSON, nullableDetailToJSON } from './NullableDetail'; +import type { NullableTool, NullableTool$Wire } from './NullableTool'; +import { nullableToolFromJSON, nullableToolToJSON } from './NullableTool'; + +export type ExplicitNullOverlap$Wire = NullableDetail$Wire | NullableTool$Wire | null; + +export type ExplicitNullOverlap = NullableDetail | NullableTool | null; + +export function explicitNullOverlapFromJSON(json: ExplicitNullOverlap$Wire): ExplicitNullOverlap { + const candidate: unknown = json; + const matches = [matches$30FromJSON(candidate), matches$32FromJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous ExplicitNullOverlap untagged union value'); + } + if (matches[0]) { + return nullableDetailFromJSON(json as unknown as NullableDetail$Wire); + } + if (matches[1]) { + return nullableToolFromJSON(json as unknown as NullableTool$Wire); + } + if (candidate === null) { + return null; + } + throw new TypeError('No matching ExplicitNullOverlap untagged union member'); +} + +export function explicitNullOverlapToJSON(value: ExplicitNullOverlap): ExplicitNullOverlap$Wire { + const candidate: unknown = value; + const matches = [matches$30ToJSON(candidate), matches$32ToJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous ExplicitNullOverlap untagged union value'); + } + if (matches[0]) { + return nullableDetailToJSON(value as unknown as NullableDetail); + } + if (matches[1]) { + return nullableToolToJSON(value as unknown as NullableTool); + } + if (candidate === null) { + return null; + } + throw new TypeError('No matching ExplicitNullOverlap untagged union member'); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/ExplicitNullableEnumChoice.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/ExplicitNullableEnumChoice.ts.golden new file mode 100644 index 000000000..869638455 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/ExplicitNullableEnumChoice.ts.golden @@ -0,0 +1,51 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$1FromJSON, + matches$1ToJSON, + matches$33FromJSON, + matches$33ToJSON, +} from '../runtime/unionMatch'; +import type { Detail, Detail$Wire } from './Detail'; +import { detailFromJSON, detailToJSON } from './Detail'; + +export type ExplicitNullableEnumChoice$Wire = readonly (number | null)[] | Detail$Wire; + +export type ExplicitNullableEnumChoice = readonly (number | null)[] | Detail; + +export function explicitNullableEnumChoiceFromJSON( + json: ExplicitNullableEnumChoice$Wire, +): ExplicitNullableEnumChoice { + const candidate: unknown = json; + const matches = [matches$33FromJSON(candidate), matches$1FromJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous ExplicitNullableEnumChoice untagged union value'); + } + if (matches[0]) { + return json as unknown as readonly (number | null)[]; + } + if (matches[1]) { + return detailFromJSON(json as unknown as Detail$Wire); + } + throw new TypeError('No matching ExplicitNullableEnumChoice untagged union member'); +} + +export function explicitNullableEnumChoiceToJSON( + value: ExplicitNullableEnumChoice, +): ExplicitNullableEnumChoice$Wire { + const candidate: unknown = value; + const matches = [matches$33ToJSON(candidate), matches$1ToJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous ExplicitNullableEnumChoice untagged union value'); + } + if (matches[0]) { + return value as unknown as readonly (number | null)[]; + } + if (matches[1]) { + return detailToJSON(value as unknown as Detail); + } + throw new TypeError('No matching ExplicitNullableEnumChoice untagged union member'); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/InlineDictionaryValues.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/InlineDictionaryValues.ts.golden new file mode 100644 index 000000000..e68ae1a40 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/InlineDictionaryValues.ts.golden @@ -0,0 +1,56 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$37FromJSON, + matches$37ToJSON, + matches$5FromJSON, + matches$5ToJSON, +} from '../runtime/unionMatch'; +import type { Value$13, Value$13Wire } from '../runtime/valueConversion$13'; +import { convert$13FromJSON, convert$13ToJSON } from '../runtime/valueConversion$13'; +import type { Detail, Detail$Wire } from './Detail'; + +export type InlineDictionaryValues$Wire = + | globalThis.Record | string> + | boolean; + +export type InlineDictionaryValues = + | globalThis.Record | string> + | boolean; + +export function inlineDictionaryValuesFromJSON( + json: InlineDictionaryValues$Wire, +): InlineDictionaryValues { + const candidate: unknown = json; + const matches = [matches$37FromJSON(candidate), matches$5FromJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous InlineDictionaryValues untagged union value'); + } + if (matches[0]) { + return convert$13FromJSON(json as unknown as Value$13Wire); + } + if (matches[1]) { + return json as unknown as boolean; + } + throw new TypeError('No matching InlineDictionaryValues untagged union member'); +} + +export function inlineDictionaryValuesToJSON( + value: InlineDictionaryValues, +): InlineDictionaryValues$Wire { + const candidate: unknown = value; + const matches = [matches$37ToJSON(candidate), matches$5ToJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous InlineDictionaryValues untagged union value'); + } + if (matches[0]) { + return convert$13ToJSON(value as unknown as Value$13); + } + if (matches[1]) { + return value as unknown as boolean; + } + throw new TypeError('No matching InlineDictionaryValues untagged union member'); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/InlineMixedValues.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/InlineMixedValues.ts.golden new file mode 100644 index 000000000..21d94794b --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/InlineMixedValues.ts.golden @@ -0,0 +1,51 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$11FromJSON, + matches$11ToJSON, + matches$1FromJSON, + matches$1ToJSON, +} from '../runtime/unionMatch'; +import type { Value$0, Value$0Wire } from '../runtime/valueConversion$0'; +import { convert$0FromJSON, convert$0ToJSON } from '../runtime/valueConversion$0'; +import type { Detail, Detail$Wire } from './Detail'; +import { detailFromJSON, detailToJSON } from './Detail'; + +export type InlineMixedValues$Wire = + | readonly (globalThis.Record | string)[] + | Detail$Wire; + +export type InlineMixedValues = readonly (globalThis.Record | string)[] | Detail; + +export function inlineMixedValuesFromJSON(json: InlineMixedValues$Wire): InlineMixedValues { + const candidate: unknown = json; + const matches = [matches$11FromJSON(candidate), matches$1FromJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous InlineMixedValues untagged union value'); + } + if (matches[0]) { + return convert$0FromJSON(json as unknown as Value$0Wire); + } + if (matches[1]) { + return detailFromJSON(json as unknown as Detail$Wire); + } + throw new TypeError('No matching InlineMixedValues untagged union member'); +} + +export function inlineMixedValuesToJSON(value: InlineMixedValues): InlineMixedValues$Wire { + const candidate: unknown = value; + const matches = [matches$11ToJSON(candidate), matches$1ToJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous InlineMixedValues untagged union value'); + } + if (matches[0]) { + return convert$0ToJSON(value as unknown as Value$0); + } + if (matches[1]) { + return detailToJSON(value as unknown as Detail); + } + throw new TypeError('No matching InlineMixedValues untagged union member'); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/InlineNumericValues.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/InlineNumericValues.ts.golden new file mode 100644 index 000000000..f6163cc94 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/InlineNumericValues.ts.golden @@ -0,0 +1,37 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { matches$38FromJSON, matches$38ToJSON } from '../runtime/unionMatch'; +import type { Value$15, Value$15Wire } from '../runtime/valueConversion$15'; +import { convert$15FromJSON, convert$15ToJSON } from '../runtime/valueConversion$15'; +import type { Detail, Detail$Wire } from './Detail'; + +export type InlineNumericValues$Wire = readonly (globalThis.Record | number)[]; + +export type InlineNumericValues = readonly (globalThis.Record | number)[]; + +export function inlineNumericValuesFromJSON(json: InlineNumericValues$Wire): InlineNumericValues { + const candidate: unknown = json; + const matches = [matches$38FromJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous InlineNumericValues untagged union value'); + } + if (matches[0]) { + return convert$15FromJSON(json as unknown as Value$15Wire); + } + throw new TypeError('No matching InlineNumericValues untagged union member'); +} + +export function inlineNumericValuesToJSON(value: InlineNumericValues): InlineNumericValues$Wire { + const candidate: unknown = value; + const matches = [matches$38ToJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous InlineNumericValues untagged union value'); + } + if (matches[0]) { + return convert$15ToJSON(value as unknown as Value$15); + } + throw new TypeError('No matching InlineNumericValues untagged union member'); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/InlineProperty.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/InlineProperty.ts.golden new file mode 100644 index 000000000..8a61a3876 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/InlineProperty.ts.golden @@ -0,0 +1,27 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { convert$0FromJSON, convert$0ToJSON } from '../runtime/valueConversion$0'; +import type { Detail, Detail$Wire } from './Detail'; + +export interface InlineProperty$Wire { + readonly entries: readonly (globalThis.Record | string)[]; +} + +export interface InlineProperty { + readonly entries: readonly (globalThis.Record | string)[]; +} + +export function inlinePropertyFromJSON(json: InlineProperty$Wire): InlineProperty { + return { + entries: convert$0FromJSON(json.entries), + }; +} + +export function inlinePropertyToJSON(value: InlineProperty): InlineProperty$Wire { + return { + entries: convert$0ToJSON(value.entries), + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/MapOuter.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/MapOuter.ts.golden new file mode 100644 index 000000000..9071c403b --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/MapOuter.ts.golden @@ -0,0 +1,36 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { matches$40FromJSON, matches$40ToJSON } from '../runtime/unionMatch'; +import type { OpenMap, OpenMap$Wire } from './OpenMap'; +import { openMapFromJSON, openMapToJSON } from './OpenMap'; + +export type MapOuter$Wire = unknown; + +export type MapOuter = unknown; + +export function mapOuterFromJSON(json: MapOuter$Wire): MapOuter { + const candidate: unknown = json; + const matches = [matches$40FromJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous MapOuter untagged union value'); + } + if (matches[0]) { + return openMapFromJSON(json as unknown as OpenMap$Wire); + } + return json as unknown as unknown; +} + +export function mapOuterToJSON(value: MapOuter): MapOuter$Wire { + const candidate: unknown = value; + const matches = [matches$40ToJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous MapOuter untagged union value'); + } + if (matches[0]) { + return openMapToJSON(value as unknown as OpenMap); + } + return value as unknown as unknown; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/MapWrapped.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/MapWrapped.ts.golden new file mode 100644 index 000000000..e60b0a99e --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/MapWrapped.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { NullableMapFallback, NullableMapFallback$Wire } from './NullableMapFallback'; +import { nullableMapFallbackFromJSON, nullableMapFallbackToJSON } from './NullableMapFallback'; + +export type MapWrapped$Wire = NullableMapFallback$Wire; + +export type MapWrapped = NullableMapFallback; + +export function mapWrappedFromJSON(json: MapWrapped$Wire): MapWrapped { + return nullableMapFallbackFromJSON(json); +} + +export function mapWrappedToJSON(value: MapWrapped): MapWrapped$Wire { + return nullableMapFallbackToJSON(value); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/MixedMapA.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/MixedMapA.ts.golden new file mode 100644 index 000000000..48f3d112a --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/MixedMapA.ts.golden @@ -0,0 +1,51 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail, Detail$Wire } from './Detail'; +import { detailFromJSON, detailToJSON } from './Detail'; + +export interface MixedMapA$Wire extends Readonly> { + readonly marker: string; +} + +export interface MixedMapA extends Readonly> { + readonly marker: string; +} + +export function mixedMapAFromJSON(json: MixedMapA$Wire): MixedMapA { + const sourceEntries = Object.entries(json).filter(([key]) => { + return !['marker'].includes(key); + }); + const additionalEntries = sourceEntries; + const additionalProperties = Object.fromEntries( + additionalEntries.map(([key, value]) => { + const additionalValue = value as Detail$Wire; + return [key, detailFromJSON(additionalValue)] as const; + }), + ); + const result = { + ...additionalProperties, + marker: json.marker, + }; + return result; +} + +export function mixedMapAToJSON(value: MixedMapA): MixedMapA$Wire { + const sourceEntries = Object.entries(value).filter(([key]) => { + return !['marker'].includes(key); + }); + const additionalEntries = sourceEntries; + const additionalProperties = Object.fromEntries( + additionalEntries.map(([key, value]) => { + const additionalValue = value as Detail; + return [key, detailToJSON(additionalValue)] as const; + }), + ); + const result = { + ...additionalProperties, + marker: value.marker, + }; + return result; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/MixedMapB.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/MixedMapB.ts.golden new file mode 100644 index 000000000..c907b6cc2 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/MixedMapB.ts.golden @@ -0,0 +1,51 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { convert$11FromJSON, convert$11ToJSON } from '../runtime/valueConversion$11'; +import type { Detail, Detail$Wire } from './Detail'; + +export interface MixedMapB$Wire extends Readonly> { + readonly marker: string; +} + +export interface MixedMapB extends Readonly> { + readonly marker: string; +} + +export function mixedMapBFromJSON(json: MixedMapB$Wire): MixedMapB { + const sourceEntries = Object.entries(json).filter(([key]) => { + return !['marker'].includes(key); + }); + const additionalEntries = sourceEntries; + const additionalProperties = Object.fromEntries( + additionalEntries.map(([key, value]) => { + const additionalValue = value as readonly Detail$Wire[]; + return [key, convert$11FromJSON(additionalValue)] as const; + }), + ); + const result = { + ...additionalProperties, + marker: json.marker, + }; + return result; +} + +export function mixedMapBToJSON(value: MixedMapB): MixedMapB$Wire { + const sourceEntries = Object.entries(value).filter(([key]) => { + return !['marker'].includes(key); + }); + const additionalEntries = sourceEntries; + const additionalProperties = Object.fromEntries( + additionalEntries.map(([key, value]) => { + const additionalValue = value as readonly Detail[]; + return [key, convert$11ToJSON(additionalValue)] as const; + }), + ); + const result = { + ...additionalProperties, + marker: value.marker, + }; + return result; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/MixedMapChoice.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/MixedMapChoice.ts.golden new file mode 100644 index 000000000..5d24d8ee4 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/MixedMapChoice.ts.golden @@ -0,0 +1,49 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$43FromJSON, + matches$43ToJSON, + matches$45FromJSON, + matches$45ToJSON, +} from '../runtime/unionMatch'; +import type { MixedMapA, MixedMapA$Wire } from './MixedMapA'; +import { mixedMapAFromJSON, mixedMapAToJSON } from './MixedMapA'; +import type { MixedMapB, MixedMapB$Wire } from './MixedMapB'; +import { mixedMapBFromJSON, mixedMapBToJSON } from './MixedMapB'; + +export type MixedMapChoice$Wire = MixedMapA$Wire | MixedMapB$Wire; + +export type MixedMapChoice = MixedMapA | MixedMapB; + +export function mixedMapChoiceFromJSON(json: MixedMapChoice$Wire): MixedMapChoice { + const candidate: unknown = json; + const matches = [matches$43FromJSON(candidate), matches$45FromJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous MixedMapChoice untagged union value'); + } + if (matches[0]) { + return mixedMapAFromJSON(json as unknown as MixedMapA$Wire); + } + if (matches[1]) { + return mixedMapBFromJSON(json as unknown as MixedMapB$Wire); + } + throw new TypeError('No matching MixedMapChoice untagged union member'); +} + +export function mixedMapChoiceToJSON(value: MixedMapChoice): MixedMapChoice$Wire { + const candidate: unknown = value; + const matches = [matches$43ToJSON(candidate), matches$45ToJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous MixedMapChoice untagged union value'); + } + if (matches[0]) { + return mixedMapAToJSON(value as unknown as MixedMapA); + } + if (matches[1]) { + return mixedMapBToJSON(value as unknown as MixedMapB); + } + throw new TypeError('No matching MixedMapChoice untagged union member'); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NamedContainer.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NamedContainer.ts.golden new file mode 100644 index 000000000..08b72fc79 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NamedContainer.ts.golden @@ -0,0 +1,50 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$11FromJSON, + matches$11ToJSON, + matches$1FromJSON, + matches$1ToJSON, +} from '../runtime/unionMatch'; +import type { Value$17, Value$17Wire } from '../runtime/valueConversion$17'; +import { convert$17FromJSON, convert$17ToJSON } from '../runtime/valueConversion$17'; +import type { Detail, Detail$Wire } from './Detail'; +import { detailFromJSON, detailToJSON } from './Detail'; +import type { NamedValue, NamedValue$Wire } from './NamedValue'; + +export type NamedContainer$Wire = readonly NamedValue$Wire[] | Detail$Wire; + +export type NamedContainer = readonly NamedValue[] | Detail; + +export function namedContainerFromJSON(json: NamedContainer$Wire): NamedContainer { + const candidate: unknown = json; + const matches = [matches$11FromJSON(candidate), matches$1FromJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous NamedContainer untagged union value'); + } + if (matches[0]) { + return convert$17FromJSON(json as unknown as Value$17Wire); + } + if (matches[1]) { + return detailFromJSON(json as unknown as Detail$Wire); + } + throw new TypeError('No matching NamedContainer untagged union member'); +} + +export function namedContainerToJSON(value: NamedContainer): NamedContainer$Wire { + const candidate: unknown = value; + const matches = [matches$11ToJSON(candidate), matches$1ToJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous NamedContainer untagged union value'); + } + if (matches[0]) { + return convert$17ToJSON(value as unknown as Value$17); + } + if (matches[1]) { + return detailToJSON(value as unknown as Detail); + } + throw new TypeError('No matching NamedContainer untagged union member'); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NamedValue.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NamedValue.ts.golden new file mode 100644 index 000000000..823fea353 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NamedValue.ts.golden @@ -0,0 +1,48 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$0FromJSON, + matches$0ToJSON, + matches$2FromJSON, + matches$2ToJSON, +} from '../runtime/unionMatch'; +import type { Value$12, Value$12Wire } from '../runtime/valueConversion$12'; +import { convert$12FromJSON, convert$12ToJSON } from '../runtime/valueConversion$12'; +import type { Detail, Detail$Wire } from './Detail'; + +export type NamedValue$Wire = globalThis.Record | string; + +export type NamedValue = globalThis.Record | string; + +export function namedValueFromJSON(json: NamedValue$Wire): NamedValue { + const candidate: unknown = json; + const matches = [matches$0FromJSON(candidate), matches$2FromJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous NamedValue untagged union value'); + } + if (matches[0]) { + return convert$12FromJSON(json as unknown as Value$12Wire); + } + if (matches[1]) { + return json as unknown as string; + } + throw new TypeError('No matching NamedValue untagged union member'); +} + +export function namedValueToJSON(value: NamedValue): NamedValue$Wire { + const candidate: unknown = value; + const matches = [matches$0ToJSON(candidate), matches$2ToJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous NamedValue untagged union value'); + } + if (matches[0]) { + return convert$12ToJSON(value as unknown as Value$12); + } + if (matches[1]) { + return value as unknown as string; + } + throw new TypeError('No matching NamedValue untagged union member'); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NestedDetailLists.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NestedDetailLists.ts.golden new file mode 100644 index 000000000..4a7d6a2a1 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NestedDetailLists.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { convert$18FromJSON, convert$18ToJSON } from '../runtime/valueConversion$18'; +import type { Detail, Detail$Wire } from './Detail'; + +export type NestedDetailLists$Wire = readonly Detail$Wire[][]; + +export type NestedDetailLists = readonly Detail[][]; + +export function nestedDetailListsFromJSON(json: NestedDetailLists$Wire): NestedDetailLists { + return convert$18FromJSON(json); +} + +export function nestedDetailListsToJSON(value: NestedDetailLists): NestedDetailLists$Wire { + return convert$18ToJSON(value); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NestedEnumChoice.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NestedEnumChoice.ts.golden new file mode 100644 index 000000000..c4e5e0e7d --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NestedEnumChoice.ts.golden @@ -0,0 +1,49 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$46FromJSON, + matches$46ToJSON, + matches$50FromJSON, + matches$50ToJSON, +} from '../runtime/unionMatch'; +import type { NestedEnumNarrow, NestedEnumNarrow$Wire } from './NestedEnumNarrow'; +import { nestedEnumNarrowFromJSON, nestedEnumNarrowToJSON } from './NestedEnumNarrow'; +import type { NumericTwo, NumericTwo$Wire } from './NumericTwo'; +import { numericTwoFromJSON, numericTwoToJSON } from './NumericTwo'; + +export type NestedEnumChoice$Wire = NestedEnumNarrow$Wire | NumericTwo$Wire; + +export type NestedEnumChoice = NestedEnumNarrow | NumericTwo; + +export function nestedEnumChoiceFromJSON(json: NestedEnumChoice$Wire): NestedEnumChoice { + const candidate: unknown = json; + const matches = [matches$46FromJSON(candidate), matches$50FromJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous NestedEnumChoice untagged union value'); + } + if (matches[0]) { + return nestedEnumNarrowFromJSON(json as unknown as NestedEnumNarrow$Wire); + } + if (matches[1]) { + return numericTwoFromJSON(json as unknown as NumericTwo$Wire); + } + throw new TypeError('No matching NestedEnumChoice untagged union member'); +} + +export function nestedEnumChoiceToJSON(value: NestedEnumChoice): NestedEnumChoice$Wire { + const candidate: unknown = value; + const matches = [matches$46ToJSON(candidate), matches$50ToJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous NestedEnumChoice untagged union value'); + } + if (matches[0]) { + return nestedEnumNarrowToJSON(value as unknown as NestedEnumNarrow); + } + if (matches[1]) { + return numericTwoToJSON(value as unknown as NumericTwo); + } + throw new TypeError('No matching NestedEnumChoice untagged union member'); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NestedEnumNarrow.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NestedEnumNarrow.ts.golden new file mode 100644 index 000000000..8249e388e --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NestedEnumNarrow.ts.golden @@ -0,0 +1,28 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface NestedEnumNarrow$Wire { + readonly first_value?: string; + readonly revision: number | null; +} + +export interface NestedEnumNarrow { + readonly firstValue?: string; + readonly revision: number | null; +} + +export function nestedEnumNarrowFromJSON(json: NestedEnumNarrow$Wire): NestedEnumNarrow { + return { + firstValue: json.first_value, + revision: json.revision, + }; +} + +export function nestedEnumNarrowToJSON(value: NestedEnumNarrow): NestedEnumNarrow$Wire { + return { + first_value: value.firstValue, + revision: value.revision, + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NestedExplicitNullOverlap.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NestedExplicitNullOverlap.ts.golden new file mode 100644 index 000000000..d04931764 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NestedExplicitNullOverlap.ts.golden @@ -0,0 +1,57 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$52FromJSON, + matches$52ToJSON, + matches$5FromJSON, + matches$5ToJSON, +} from '../runtime/unionMatch'; +import type { NullableOverlap, NullableOverlap$Wire } from './NullableOverlap'; +import { nullableOverlapFromJSON, nullableOverlapToJSON } from './NullableOverlap'; + +export type NestedExplicitNullOverlap$Wire = NullableOverlap$Wire | boolean | null; + +export type NestedExplicitNullOverlap = NullableOverlap | boolean | null; + +export function nestedExplicitNullOverlapFromJSON( + json: NestedExplicitNullOverlap$Wire, +): NestedExplicitNullOverlap { + const candidate: unknown = json; + const matches = [matches$52FromJSON(candidate), matches$5FromJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous NestedExplicitNullOverlap untagged union value'); + } + if (matches[0]) { + return nullableOverlapFromJSON(json as unknown as NullableOverlap$Wire); + } + if (matches[1]) { + return json as unknown as boolean; + } + if (candidate === null) { + return null; + } + throw new TypeError('No matching NestedExplicitNullOverlap untagged union member'); +} + +export function nestedExplicitNullOverlapToJSON( + value: NestedExplicitNullOverlap, +): NestedExplicitNullOverlap$Wire { + const candidate: unknown = value; + const matches = [matches$52ToJSON(candidate), matches$5ToJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous NestedExplicitNullOverlap untagged union value'); + } + if (matches[0]) { + return nullableOverlapToJSON(value as unknown as NullableOverlap); + } + if (matches[1]) { + return value as unknown as boolean; + } + if (candidate === null) { + return null; + } + throw new TypeError('No matching NestedExplicitNullOverlap untagged union member'); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NestedNullableEnumChoice.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NestedNullableEnumChoice.ts.golden new file mode 100644 index 000000000..8a229b69d --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NestedNullableEnumChoice.ts.golden @@ -0,0 +1,59 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$50FromJSON, + matches$50ToJSON, + matches$53FromJSON, + matches$53ToJSON, +} from '../runtime/unionMatch'; +import type { + NestedNullableEnumNarrow, + NestedNullableEnumNarrow$Wire, +} from './NestedNullableEnumNarrow'; +import { + nestedNullableEnumNarrowFromJSON, + nestedNullableEnumNarrowToJSON, +} from './NestedNullableEnumNarrow'; +import type { NumericTwo, NumericTwo$Wire } from './NumericTwo'; +import { numericTwoFromJSON, numericTwoToJSON } from './NumericTwo'; + +export type NestedNullableEnumChoice$Wire = NestedNullableEnumNarrow$Wire | NumericTwo$Wire; + +export type NestedNullableEnumChoice = NestedNullableEnumNarrow | NumericTwo; + +export function nestedNullableEnumChoiceFromJSON( + json: NestedNullableEnumChoice$Wire, +): NestedNullableEnumChoice { + const candidate: unknown = json; + const matches = [matches$53FromJSON(candidate), matches$50FromJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous NestedNullableEnumChoice untagged union value'); + } + if (matches[0]) { + return nestedNullableEnumNarrowFromJSON(json as unknown as NestedNullableEnumNarrow$Wire); + } + if (matches[1]) { + return numericTwoFromJSON(json as unknown as NumericTwo$Wire); + } + throw new TypeError('No matching NestedNullableEnumChoice untagged union member'); +} + +export function nestedNullableEnumChoiceToJSON( + value: NestedNullableEnumChoice, +): NestedNullableEnumChoice$Wire { + const candidate: unknown = value; + const matches = [matches$53ToJSON(candidate), matches$50ToJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous NestedNullableEnumChoice untagged union value'); + } + if (matches[0]) { + return nestedNullableEnumNarrowToJSON(value as unknown as NestedNullableEnumNarrow); + } + if (matches[1]) { + return numericTwoToJSON(value as unknown as NumericTwo); + } + throw new TypeError('No matching NestedNullableEnumChoice untagged union member'); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NestedNullableEnumNarrow.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NestedNullableEnumNarrow.ts.golden new file mode 100644 index 000000000..5d361f019 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NestedNullableEnumNarrow.ts.golden @@ -0,0 +1,32 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface NestedNullableEnumNarrow$Wire { + readonly first_value?: string; + readonly revision: number | null; +} + +export interface NestedNullableEnumNarrow { + readonly firstValue?: string; + readonly revision: number | null; +} + +export function nestedNullableEnumNarrowFromJSON( + json: NestedNullableEnumNarrow$Wire, +): NestedNullableEnumNarrow { + return { + firstValue: json.first_value, + revision: json.revision, + }; +} + +export function nestedNullableEnumNarrowToJSON( + value: NestedNullableEnumNarrow, +): NestedNullableEnumNarrow$Wire { + return { + first_value: value.firstValue, + revision: value.revision, + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NullableDetail.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NullableDetail.ts.golden new file mode 100644 index 000000000..7e45300cf --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NullableDetail.ts.golden @@ -0,0 +1,42 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { matches$1FromJSON, matches$1ToJSON } from '../runtime/unionMatch'; +import type { Detail, Detail$Wire } from './Detail'; +import { detailFromJSON, detailToJSON } from './Detail'; + +export type NullableDetail$Wire = Detail$Wire | null; + +export type NullableDetail = Detail | null; + +export function nullableDetailFromJSON(json: NullableDetail$Wire): NullableDetail { + const candidate: unknown = json; + const matches = [matches$1FromJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous NullableDetail untagged union value'); + } + if (matches[0]) { + return detailFromJSON(json as unknown as Detail$Wire); + } + if (candidate === null) { + return null; + } + throw new TypeError('No matching NullableDetail untagged union member'); +} + +export function nullableDetailToJSON(value: NullableDetail): NullableDetail$Wire { + const candidate: unknown = value; + const matches = [matches$1ToJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous NullableDetail untagged union value'); + } + if (matches[0]) { + return detailToJSON(value as unknown as Detail); + } + if (candidate === null) { + return null; + } + throw new TypeError('No matching NullableDetail untagged union member'); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NullableEnumParent.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NullableEnumParent.ts.golden new file mode 100644 index 000000000..1317fcdfe --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NullableEnumParent.ts.golden @@ -0,0 +1,27 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { convert$20FromJSON, convert$20ToJSON } from '../runtime/valueConversion$20'; +import type { ScalarEnumChoice, ScalarEnumChoice$Wire } from './ScalarEnumChoice'; + +export interface NullableEnumParent$Wire { + readonly scalar_value: ScalarEnumChoice$Wire | null; +} + +export interface NullableEnumParent { + readonly scalarValue: ScalarEnumChoice | null; +} + +export function nullableEnumParentFromJSON(json: NullableEnumParent$Wire): NullableEnumParent { + return { + scalarValue: convert$20FromJSON(json.scalar_value), + }; +} + +export function nullableEnumParentToJSON(value: NullableEnumParent): NullableEnumParent$Wire { + return { + scalar_value: convert$20ToJSON(value.scalarValue), + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NullableInlineEnumParent.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NullableInlineEnumParent.ts.golden new file mode 100644 index 000000000..bac7157dd --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NullableInlineEnumParent.ts.golden @@ -0,0 +1,30 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { convert$21FromJSON, convert$21ToJSON } from '../runtime/valueConversion$21'; + +export interface NullableInlineEnumParent$Wire { + readonly scalar_value: number | boolean | null; +} + +export interface NullableInlineEnumParent { + readonly scalarValue: number | boolean | null; +} + +export function nullableInlineEnumParentFromJSON( + json: NullableInlineEnumParent$Wire, +): NullableInlineEnumParent { + return { + scalarValue: convert$21FromJSON(json.scalar_value), + }; +} + +export function nullableInlineEnumParentToJSON( + value: NullableInlineEnumParent, +): NullableInlineEnumParent$Wire { + return { + scalar_value: convert$21ToJSON(value.scalarValue), + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NullableMapFallback.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NullableMapFallback.ts.golden new file mode 100644 index 000000000..d5e66f337 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NullableMapFallback.ts.golden @@ -0,0 +1,47 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$1FromJSON, + matches$1ToJSON, + matches$57FromJSON, + matches$57ToJSON, +} from '../runtime/unionMatch'; +import type { Detail, Detail$Wire } from './Detail'; +import { detailFromJSON, detailToJSON } from './Detail'; + +export type NullableMapFallback$Wire = globalThis.Record | null | Detail$Wire; + +export type NullableMapFallback = globalThis.Record | null | Detail; + +export function nullableMapFallbackFromJSON(json: NullableMapFallback$Wire): NullableMapFallback { + const candidate: unknown = json; + const matches = [matches$1FromJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous NullableMapFallback untagged union value'); + } + if (matches[0]) { + return detailFromJSON(json as unknown as Detail$Wire); + } + if (matches$57FromJSON(candidate)) { + return json as unknown as globalThis.Record | null; + } + throw new TypeError('No matching NullableMapFallback untagged union member'); +} + +export function nullableMapFallbackToJSON(value: NullableMapFallback): NullableMapFallback$Wire { + const candidate: unknown = value; + const matches = [matches$1ToJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous NullableMapFallback untagged union value'); + } + if (matches[0]) { + return detailToJSON(value as unknown as Detail); + } + if (matches$57ToJSON(candidate)) { + return value as unknown as globalThis.Record | null; + } + throw new TypeError('No matching NullableMapFallback untagged union member'); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NullableNonNullScalars.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NullableNonNullScalars.ts.golden new file mode 100644 index 000000000..36b6af103 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NullableNonNullScalars.ts.golden @@ -0,0 +1,26 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { convert$23FromJSON, convert$23ToJSON } from '../runtime/valueConversion$23'; +import type { + NullableNonNullScalarsItems, + NullableNonNullScalarsItems$Wire, +} from './NullableNonNullScalarsItems'; + +export type NullableNonNullScalars$Wire = readonly (NullableNonNullScalarsItems$Wire | null)[]; + +export type NullableNonNullScalars = readonly (NullableNonNullScalarsItems | null)[]; + +export function nullableNonNullScalarsFromJSON( + json: NullableNonNullScalars$Wire, +): NullableNonNullScalars { + return convert$23FromJSON(json); +} + +export function nullableNonNullScalarsToJSON( + value: NullableNonNullScalars, +): NullableNonNullScalars$Wire { + return convert$23ToJSON(value); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NullableNonNullScalarsItems.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NullableNonNullScalarsItems.ts.golden new file mode 100644 index 000000000..ee594cc09 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NullableNonNullScalarsItems.ts.golden @@ -0,0 +1,49 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$3FromJSON, + matches$3ToJSON, + matches$4FromJSON, + matches$4ToJSON, +} from '../runtime/unionMatch'; + +export type NullableNonNullScalarsItems$Wire = number; + +export type NullableNonNullScalarsItems = number; + +export function nullableNonNullScalarsItemsFromJSON( + json: NullableNonNullScalarsItems$Wire, +): NullableNonNullScalarsItems { + const candidate: unknown = json; + const matches = [matches$3FromJSON(candidate), matches$4FromJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous NullableNonNullScalarsItems untagged union value'); + } + if (matches[0]) { + return json as unknown as number; + } + if (matches[1]) { + return json as unknown as number; + } + throw new TypeError('No matching NullableNonNullScalarsItems untagged union member'); +} + +export function nullableNonNullScalarsItemsToJSON( + value: NullableNonNullScalarsItems, +): NullableNonNullScalarsItems$Wire { + const candidate: unknown = value; + const matches = [matches$3ToJSON(candidate), matches$4ToJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous NullableNonNullScalarsItems untagged union value'); + } + if (matches[0]) { + return value as unknown as number; + } + if (matches[1]) { + return value as unknown as number; + } + throw new TypeError('No matching NullableNonNullScalarsItems untagged union member'); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NullableNumericChoice.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NullableNumericChoice.ts.golden new file mode 100644 index 000000000..37a0d274b --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NullableNumericChoice.ts.golden @@ -0,0 +1,53 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$58FromJSON, + matches$58ToJSON, + matches$61FromJSON, + matches$61ToJSON, +} from '../runtime/unionMatch'; +import type { NullableNumericOne, NullableNumericOne$Wire } from './NullableNumericOne'; +import { nullableNumericOneFromJSON, nullableNumericOneToJSON } from './NullableNumericOne'; +import type { NullableNumericTwo, NullableNumericTwo$Wire } from './NullableNumericTwo'; +import { nullableNumericTwoFromJSON, nullableNumericTwoToJSON } from './NullableNumericTwo'; + +export type NullableNumericChoice$Wire = NullableNumericOne$Wire | NullableNumericTwo$Wire; + +export type NullableNumericChoice = NullableNumericOne | NullableNumericTwo; + +export function nullableNumericChoiceFromJSON( + json: NullableNumericChoice$Wire, +): NullableNumericChoice { + const candidate: unknown = json; + const matches = [matches$58FromJSON(candidate), matches$61FromJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous NullableNumericChoice untagged union value'); + } + if (matches[0]) { + return nullableNumericOneFromJSON(json as unknown as NullableNumericOne$Wire); + } + if (matches[1]) { + return nullableNumericTwoFromJSON(json as unknown as NullableNumericTwo$Wire); + } + throw new TypeError('No matching NullableNumericChoice untagged union member'); +} + +export function nullableNumericChoiceToJSON( + value: NullableNumericChoice, +): NullableNumericChoice$Wire { + const candidate: unknown = value; + const matches = [matches$58ToJSON(candidate), matches$61ToJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous NullableNumericChoice untagged union value'); + } + if (matches[0]) { + return nullableNumericOneToJSON(value as unknown as NullableNumericOne); + } + if (matches[1]) { + return nullableNumericTwoToJSON(value as unknown as NullableNumericTwo); + } + throw new TypeError('No matching NullableNumericChoice untagged union member'); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NullableNumericOne.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NullableNumericOne.ts.golden new file mode 100644 index 000000000..e7d632515 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NullableNumericOne.ts.golden @@ -0,0 +1,28 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface NullableNumericOne$Wire { + readonly first_value?: string; + readonly revision: number | null; +} + +export interface NullableNumericOne { + readonly firstValue?: string; + readonly revision: number | null; +} + +export function nullableNumericOneFromJSON(json: NullableNumericOne$Wire): NullableNumericOne { + return { + firstValue: json.first_value, + revision: json.revision, + }; +} + +export function nullableNumericOneToJSON(value: NullableNumericOne): NullableNumericOne$Wire { + return { + first_value: value.firstValue, + revision: value.revision, + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NullableNumericTwo.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NullableNumericTwo.ts.golden new file mode 100644 index 000000000..e681475f2 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NullableNumericTwo.ts.golden @@ -0,0 +1,28 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface NullableNumericTwo$Wire { + readonly revision: number | null; + readonly second_value?: string; +} + +export interface NullableNumericTwo { + readonly revision: number | null; + readonly secondValue?: string; +} + +export function nullableNumericTwoFromJSON(json: NullableNumericTwo$Wire): NullableNumericTwo { + return { + revision: json.revision, + secondValue: json.second_value, + }; +} + +export function nullableNumericTwoToJSON(value: NullableNumericTwo): NullableNumericTwo$Wire { + return { + revision: value.revision, + second_value: value.secondValue, + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NullableOpaque.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NullableOpaque.ts.golden new file mode 100644 index 000000000..d4858f227 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NullableOpaque.ts.golden @@ -0,0 +1,24 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export type NullableOpaque$Wire = unknown; + +export type NullableOpaque = unknown; + +export function nullableOpaqueFromJSON(json: NullableOpaque$Wire): NullableOpaque { + const candidate: unknown = json; + if (candidate === null) { + return null; + } + return json as unknown as unknown; +} + +export function nullableOpaqueToJSON(value: NullableOpaque): NullableOpaque$Wire { + const candidate: unknown = value; + if (candidate === null) { + return null; + } + return value as unknown as unknown; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NullableOpaqueSiblingOuter.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NullableOpaqueSiblingOuter.ts.golden new file mode 100644 index 000000000..1f48d995c --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NullableOpaqueSiblingOuter.ts.golden @@ -0,0 +1,40 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { matches$64FromJSON, matches$64ToJSON } from '../runtime/unionMatch'; +import type { NullableOpaque, NullableOpaque$Wire } from './NullableOpaque'; +import { nullableOpaqueFromJSON, nullableOpaqueToJSON } from './NullableOpaque'; + +export type NullableOpaqueSiblingOuter$Wire = unknown; + +export type NullableOpaqueSiblingOuter = unknown; + +export function nullableOpaqueSiblingOuterFromJSON( + json: NullableOpaqueSiblingOuter$Wire, +): NullableOpaqueSiblingOuter { + const candidate: unknown = json; + const matches = [matches$64FromJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous NullableOpaqueSiblingOuter untagged union value'); + } + if (matches[0]) { + return json as unknown as boolean | null; + } + return nullableOpaqueFromJSON(json as unknown as NullableOpaque$Wire); +} + +export function nullableOpaqueSiblingOuterToJSON( + value: NullableOpaqueSiblingOuter, +): NullableOpaqueSiblingOuter$Wire { + const candidate: unknown = value; + const matches = [matches$64ToJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous NullableOpaqueSiblingOuter untagged union value'); + } + if (matches[0]) { + return value as unknown as boolean | null; + } + return nullableOpaqueToJSON(value as unknown as NullableOpaque); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NullableOpenExplicitOuter.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NullableOpenExplicitOuter.ts.golden new file mode 100644 index 000000000..80758fb65 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NullableOpenExplicitOuter.ts.golden @@ -0,0 +1,46 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { matches$65FromJSON, matches$65ToJSON } from '../runtime/unionMatch'; +import type { NullableOpenInner, NullableOpenInner$Wire } from './NullableOpenInner'; +import { nullableOpenInnerFromJSON, nullableOpenInnerToJSON } from './NullableOpenInner'; + +export type NullableOpenExplicitOuter$Wire = unknown; + +export type NullableOpenExplicitOuter = unknown; + +export function nullableOpenExplicitOuterFromJSON( + json: NullableOpenExplicitOuter$Wire, +): NullableOpenExplicitOuter { + const candidate: unknown = json; + const matches = [matches$65FromJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous NullableOpenExplicitOuter untagged union value'); + } + if (matches[0]) { + return nullableOpenInnerFromJSON(json as unknown as NullableOpenInner$Wire); + } + if (candidate === null) { + return null; + } + return nullableOpenInnerFromJSON(json as unknown as NullableOpenInner$Wire); +} + +export function nullableOpenExplicitOuterToJSON( + value: NullableOpenExplicitOuter, +): NullableOpenExplicitOuter$Wire { + const candidate: unknown = value; + const matches = [matches$65ToJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous NullableOpenExplicitOuter untagged union value'); + } + if (matches[0]) { + return nullableOpenInnerToJSON(value as unknown as NullableOpenInner); + } + if (candidate === null) { + return null; + } + return nullableOpenInnerToJSON(value as unknown as NullableOpenInner); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NullableOpenInner.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NullableOpenInner.ts.golden new file mode 100644 index 000000000..be1cb9aa0 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NullableOpenInner.ts.golden @@ -0,0 +1,45 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$66FromJSON, + matches$66ToJSON, + matches$67FromJSON, + matches$67ToJSON, +} from '../runtime/unionMatch'; + +export type NullableOpenInner$Wire = unknown; + +export type NullableOpenInner = unknown; + +export function nullableOpenInnerFromJSON(json: NullableOpenInner$Wire): NullableOpenInner { + const candidate: unknown = json; + const matches = [matches$66FromJSON(candidate), matches$67FromJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous NullableOpenInner untagged union value'); + } + if (matches[0]) { + return json as unknown as string | null; + } + if (matches[1]) { + return json as unknown as number | null; + } + return json as unknown as unknown; +} + +export function nullableOpenInnerToJSON(value: NullableOpenInner): NullableOpenInner$Wire { + const candidate: unknown = value; + const matches = [matches$66ToJSON(candidate), matches$67ToJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous NullableOpenInner untagged union value'); + } + if (matches[0]) { + return value as unknown as string | null; + } + if (matches[1]) { + return value as unknown as number | null; + } + return value as unknown as unknown; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NullableOpenSiblingOuter.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NullableOpenSiblingOuter.ts.golden new file mode 100644 index 000000000..9aed011d6 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NullableOpenSiblingOuter.ts.golden @@ -0,0 +1,51 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$64FromJSON, + matches$64ToJSON, + matches$65FromJSON, + matches$65ToJSON, +} from '../runtime/unionMatch'; +import type { NullableOpenInner, NullableOpenInner$Wire } from './NullableOpenInner'; +import { nullableOpenInnerFromJSON, nullableOpenInnerToJSON } from './NullableOpenInner'; + +export type NullableOpenSiblingOuter$Wire = unknown; + +export type NullableOpenSiblingOuter = unknown; + +export function nullableOpenSiblingOuterFromJSON( + json: NullableOpenSiblingOuter$Wire, +): NullableOpenSiblingOuter { + const candidate: unknown = json; + const matches = [matches$65FromJSON(candidate), matches$64FromJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous NullableOpenSiblingOuter untagged union value'); + } + if (matches[0]) { + return nullableOpenInnerFromJSON(json as unknown as NullableOpenInner$Wire); + } + if (matches[1]) { + return json as unknown as boolean | null; + } + return nullableOpenInnerFromJSON(json as unknown as NullableOpenInner$Wire); +} + +export function nullableOpenSiblingOuterToJSON( + value: NullableOpenSiblingOuter, +): NullableOpenSiblingOuter$Wire { + const candidate: unknown = value; + const matches = [matches$65ToJSON(candidate), matches$64ToJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous NullableOpenSiblingOuter untagged union value'); + } + if (matches[0]) { + return nullableOpenInnerToJSON(value as unknown as NullableOpenInner); + } + if (matches[1]) { + return value as unknown as boolean | null; + } + return nullableOpenInnerToJSON(value as unknown as NullableOpenInner); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NullableOverlap.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NullableOverlap.ts.golden new file mode 100644 index 000000000..ab2cce52c --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NullableOverlap.ts.golden @@ -0,0 +1,49 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$30FromJSON, + matches$30ToJSON, + matches$32FromJSON, + matches$32ToJSON, +} from '../runtime/unionMatch'; +import type { NullableDetail, NullableDetail$Wire } from './NullableDetail'; +import { nullableDetailFromJSON, nullableDetailToJSON } from './NullableDetail'; +import type { NullableTool, NullableTool$Wire } from './NullableTool'; +import { nullableToolFromJSON, nullableToolToJSON } from './NullableTool'; + +export type NullableOverlap$Wire = NullableDetail$Wire | NullableTool$Wire; + +export type NullableOverlap = NullableDetail | NullableTool; + +export function nullableOverlapFromJSON(json: NullableOverlap$Wire): NullableOverlap { + const candidate: unknown = json; + const matches = [matches$30FromJSON(candidate), matches$32FromJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous NullableOverlap untagged union value'); + } + if (matches[0]) { + return nullableDetailFromJSON(json as unknown as NullableDetail$Wire); + } + if (matches[1]) { + return nullableToolFromJSON(json as unknown as NullableTool$Wire); + } + throw new TypeError('No matching NullableOverlap untagged union member'); +} + +export function nullableOverlapToJSON(value: NullableOverlap): NullableOverlap$Wire { + const candidate: unknown = value; + const matches = [matches$30ToJSON(candidate), matches$32ToJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous NullableOverlap untagged union value'); + } + if (matches[0]) { + return nullableDetailToJSON(value as unknown as NullableDetail); + } + if (matches[1]) { + return nullableToolToJSON(value as unknown as NullableTool); + } + throw new TypeError('No matching NullableOverlap untagged union member'); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NullablePlainEnumParent.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NullablePlainEnumParent.ts.golden new file mode 100644 index 000000000..6183c6642 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NullablePlainEnumParent.ts.golden @@ -0,0 +1,31 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { ScalarEnumChoice, ScalarEnumChoice$Wire } from './ScalarEnumChoice'; +import { scalarEnumChoiceFromJSON, scalarEnumChoiceToJSON } from './ScalarEnumChoice'; + +export interface NullablePlainEnumParent$Wire { + readonly scalar_value: ScalarEnumChoice$Wire | null; +} + +export interface NullablePlainEnumParent { + readonly scalarValue: ScalarEnumChoice | null; +} + +export function nullablePlainEnumParentFromJSON( + json: NullablePlainEnumParent$Wire, +): NullablePlainEnumParent { + return { + scalarValue: json.scalar_value === null ? null : scalarEnumChoiceFromJSON(json.scalar_value), + }; +} + +export function nullablePlainEnumParentToJSON( + value: NullablePlainEnumParent, +): NullablePlainEnumParent$Wire { + return { + scalar_value: value.scalarValue === null ? null : scalarEnumChoiceToJSON(value.scalarValue), + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NullableScalarArray.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NullableScalarArray.ts.golden new file mode 100644 index 000000000..3dd47ea7a --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NullableScalarArray.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { convert$25FromJSON, convert$25ToJSON } from '../runtime/valueConversion$25'; +import type { NullableScalarInner, NullableScalarInner$Wire } from './NullableScalarInner'; + +export type NullableScalarArray$Wire = readonly (NullableScalarInner$Wire | null)[]; + +export type NullableScalarArray = readonly (NullableScalarInner | null)[]; + +export function nullableScalarArrayFromJSON(json: NullableScalarArray$Wire): NullableScalarArray { + return convert$25FromJSON(json); +} + +export function nullableScalarArrayToJSON(value: NullableScalarArray): NullableScalarArray$Wire { + return convert$25ToJSON(value); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NullableScalarInner.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NullableScalarInner.ts.golden new file mode 100644 index 000000000..3a04eb750 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NullableScalarInner.ts.golden @@ -0,0 +1,45 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$66FromJSON, + matches$66ToJSON, + matches$67FromJSON, + matches$67ToJSON, +} from '../runtime/unionMatch'; + +export type NullableScalarInner$Wire = string | null | number; + +export type NullableScalarInner = string | null | number; + +export function nullableScalarInnerFromJSON(json: NullableScalarInner$Wire): NullableScalarInner { + const candidate: unknown = json; + const matches = [matches$66FromJSON(candidate), matches$67FromJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous NullableScalarInner untagged union value'); + } + if (matches[0]) { + return json as unknown as string | null; + } + if (matches[1]) { + return json as unknown as number | null; + } + throw new TypeError('No matching NullableScalarInner untagged union member'); +} + +export function nullableScalarInnerToJSON(value: NullableScalarInner): NullableScalarInner$Wire { + const candidate: unknown = value; + const matches = [matches$66ToJSON(candidate), matches$67ToJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous NullableScalarInner untagged union value'); + } + if (matches[0]) { + return value as unknown as string | null; + } + if (matches[1]) { + return value as unknown as number | null; + } + throw new TypeError('No matching NullableScalarInner untagged union member'); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NullableScalarMap.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NullableScalarMap.ts.golden new file mode 100644 index 000000000..7a4d88098 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NullableScalarMap.ts.golden @@ -0,0 +1,43 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { convert$5FromJSON, convert$5ToJSON } from '../runtime/valueConversion$5'; +import type { NullableScalarInner, NullableScalarInner$Wire } from './NullableScalarInner'; + +export interface NullableScalarMap$Wire extends Readonly< + Record +> {} + +export interface NullableScalarMap extends Readonly> {} + +export function nullableScalarMapFromJSON(json: NullableScalarMap$Wire): NullableScalarMap { + const sourceEntries = Object.entries(json); + const additionalEntries = sourceEntries; + const additionalProperties = Object.fromEntries( + additionalEntries.map(([key, value]) => { + const additionalValue = value; + return [key, convert$5FromJSON(additionalValue)] as const; + }), + ); + const result = { + ...additionalProperties, + }; + return result; +} + +export function nullableScalarMapToJSON(value: NullableScalarMap): NullableScalarMap$Wire { + const sourceEntries = Object.entries(value); + const additionalEntries = sourceEntries; + const additionalProperties = Object.fromEntries( + additionalEntries.map(([key, value]) => { + const additionalValue = value; + return [key, convert$5ToJSON(additionalValue)] as const; + }), + ); + const result = { + ...additionalProperties, + }; + return result; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NullableScalarParent.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NullableScalarParent.ts.golden new file mode 100644 index 000000000..efe8fbe79 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NullableScalarParent.ts.golden @@ -0,0 +1,29 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { NullableScalarInner, NullableScalarInner$Wire } from './NullableScalarInner'; +import { nullableScalarInnerFromJSON, nullableScalarInnerToJSON } from './NullableScalarInner'; + +export interface NullableScalarParent$Wire { + readonly inner_value: NullableScalarInner$Wire | null; +} + +export interface NullableScalarParent { + readonly innerValue: NullableScalarInner | null; +} + +export function nullableScalarParentFromJSON( + json: NullableScalarParent$Wire, +): NullableScalarParent { + return { + innerValue: nullableScalarInnerFromJSON(json.inner_value), + }; +} + +export function nullableScalarParentToJSON(value: NullableScalarParent): NullableScalarParent$Wire { + return { + inner_value: nullableScalarInnerToJSON(value.innerValue), + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NullableTool.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NullableTool.ts.golden new file mode 100644 index 000000000..3f1123809 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NullableTool.ts.golden @@ -0,0 +1,42 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { matches$68FromJSON, matches$68ToJSON } from '../runtime/unionMatch'; +import type { Tool, Tool$Wire } from './Tool'; +import { toolFromJSON, toolToJSON } from './Tool'; + +export type NullableTool$Wire = Tool$Wire | null; + +export type NullableTool = Tool | null; + +export function nullableToolFromJSON(json: NullableTool$Wire): NullableTool { + const candidate: unknown = json; + const matches = [matches$68FromJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous NullableTool untagged union value'); + } + if (matches[0]) { + return toolFromJSON(json as unknown as Tool$Wire); + } + if (candidate === null) { + return null; + } + throw new TypeError('No matching NullableTool untagged union member'); +} + +export function nullableToolToJSON(value: NullableTool): NullableTool$Wire { + const candidate: unknown = value; + const matches = [matches$68ToJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous NullableTool untagged union value'); + } + if (matches[0]) { + return toolToJSON(value as unknown as Tool); + } + if (candidate === null) { + return null; + } + throw new TypeError('No matching NullableTool untagged union member'); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NumericEnumChoice.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NumericEnumChoice.ts.golden new file mode 100644 index 000000000..560667c36 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NumericEnumChoice.ts.golden @@ -0,0 +1,49 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$50FromJSON, + matches$50ToJSON, + matches$69FromJSON, + matches$69ToJSON, +} from '../runtime/unionMatch'; +import type { NumericOne, NumericOne$Wire } from './NumericOne'; +import { numericOneFromJSON, numericOneToJSON } from './NumericOne'; +import type { NumericTwo, NumericTwo$Wire } from './NumericTwo'; +import { numericTwoFromJSON, numericTwoToJSON } from './NumericTwo'; + +export type NumericEnumChoice$Wire = NumericOne$Wire | NumericTwo$Wire; + +export type NumericEnumChoice = NumericOne | NumericTwo; + +export function numericEnumChoiceFromJSON(json: NumericEnumChoice$Wire): NumericEnumChoice { + const candidate: unknown = json; + const matches = [matches$69FromJSON(candidate), matches$50FromJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous NumericEnumChoice untagged union value'); + } + if (matches[0]) { + return numericOneFromJSON(json as unknown as NumericOne$Wire); + } + if (matches[1]) { + return numericTwoFromJSON(json as unknown as NumericTwo$Wire); + } + throw new TypeError('No matching NumericEnumChoice untagged union member'); +} + +export function numericEnumChoiceToJSON(value: NumericEnumChoice): NumericEnumChoice$Wire { + const candidate: unknown = value; + const matches = [matches$69ToJSON(candidate), matches$50ToJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous NumericEnumChoice untagged union value'); + } + if (matches[0]) { + return numericOneToJSON(value as unknown as NumericOne); + } + if (matches[1]) { + return numericTwoToJSON(value as unknown as NumericTwo); + } + throw new TypeError('No matching NumericEnumChoice untagged union member'); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NumericOne.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NumericOne.ts.golden new file mode 100644 index 000000000..fd62c06ef --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NumericOne.ts.golden @@ -0,0 +1,28 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface NumericOne$Wire { + readonly first_value?: string; + readonly revision: number; +} + +export interface NumericOne { + readonly firstValue?: string; + readonly revision: number; +} + +export function numericOneFromJSON(json: NumericOne$Wire): NumericOne { + return { + firstValue: json.first_value, + revision: json.revision, + }; +} + +export function numericOneToJSON(value: NumericOne): NumericOne$Wire { + return { + first_value: value.firstValue, + revision: value.revision, + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NumericTwo.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NumericTwo.ts.golden new file mode 100644 index 000000000..4750f2e0d --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/NumericTwo.ts.golden @@ -0,0 +1,28 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface NumericTwo$Wire { + readonly revision: number; + readonly second_value?: string; +} + +export interface NumericTwo { + readonly revision: number; + readonly secondValue?: string; +} + +export function numericTwoFromJSON(json: NumericTwo$Wire): NumericTwo { + return { + revision: json.revision, + secondValue: json.second_value, + }; +} + +export function numericTwoToJSON(value: NumericTwo): NumericTwo$Wire { + return { + revision: value.revision, + second_value: value.secondValue, + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/OnceNullableOuter.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/OnceNullableOuter.ts.golden new file mode 100644 index 000000000..eded4204a --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/OnceNullableOuter.ts.golden @@ -0,0 +1,38 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { matches$30FromJSON, matches$30ToJSON } from '../runtime/unionMatch'; +import type { NullableDetail, NullableDetail$Wire } from './NullableDetail'; +import { nullableDetailFromJSON, nullableDetailToJSON } from './NullableDetail'; +import type { NullableOpaque, NullableOpaque$Wire } from './NullableOpaque'; +import { nullableOpaqueFromJSON, nullableOpaqueToJSON } from './NullableOpaque'; + +export type OnceNullableOuter$Wire = unknown; + +export type OnceNullableOuter = unknown; + +export function onceNullableOuterFromJSON(json: OnceNullableOuter$Wire): OnceNullableOuter { + const candidate: unknown = json; + const matches = [matches$30FromJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous OnceNullableOuter untagged union value'); + } + if (matches[0]) { + return nullableDetailFromJSON(json as unknown as NullableDetail$Wire); + } + return nullableOpaqueFromJSON(json as unknown as NullableOpaque$Wire); +} + +export function onceNullableOuterToJSON(value: OnceNullableOuter): OnceNullableOuter$Wire { + const candidate: unknown = value; + const matches = [matches$30ToJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous OnceNullableOuter untagged union value'); + } + if (matches[0]) { + return nullableDetailToJSON(value as unknown as NullableDetail); + } + return nullableOpaqueToJSON(value as unknown as NullableOpaque); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/OpenAlias.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/OpenAlias.ts.golden new file mode 100644 index 000000000..8046608e8 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/OpenAlias.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { OpenInner, OpenInner$Wire } from './OpenInner'; +import { openInnerFromJSON, openInnerToJSON } from './OpenInner'; + +export type OpenAlias$Wire = OpenInner$Wire; + +export type OpenAlias = OpenInner; + +export function openAliasFromJSON(json: OpenAlias$Wire): OpenAlias { + return openInnerFromJSON(json); +} + +export function openAliasToJSON(value: OpenAlias): OpenAlias$Wire { + return openInnerToJSON(value); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/OpenInner.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/OpenInner.ts.golden new file mode 100644 index 000000000..fe0088b1e --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/OpenInner.ts.golden @@ -0,0 +1,36 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { matches$1FromJSON, matches$1ToJSON } from '../runtime/unionMatch'; +import type { Detail, Detail$Wire } from './Detail'; +import { detailFromJSON, detailToJSON } from './Detail'; + +export type OpenInner$Wire = unknown; + +export type OpenInner = unknown; + +export function openInnerFromJSON(json: OpenInner$Wire): OpenInner { + const candidate: unknown = json; + const matches = [matches$1FromJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous OpenInner untagged union value'); + } + if (matches[0]) { + return detailFromJSON(json as unknown as Detail$Wire); + } + return json as unknown as unknown; +} + +export function openInnerToJSON(value: OpenInner): OpenInner$Wire { + const candidate: unknown = value; + const matches = [matches$1ToJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous OpenInner untagged union value'); + } + if (matches[0]) { + return detailToJSON(value as unknown as Detail); + } + return value as unknown as unknown; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/OpenMap.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/OpenMap.ts.golden new file mode 100644 index 000000000..2bec8ab19 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/OpenMap.ts.golden @@ -0,0 +1,40 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { openInnerFromJSON, openInnerToJSON } from './OpenInner'; + +export interface OpenMap$Wire extends Readonly> {} + +export interface OpenMap extends Readonly> {} + +export function openMapFromJSON(json: OpenMap$Wire): OpenMap { + const sourceEntries = Object.entries(json); + const additionalEntries = sourceEntries; + const additionalProperties = Object.fromEntries( + additionalEntries.map(([key, value]) => { + const additionalValue = value; + return [key, openInnerFromJSON(additionalValue)] as const; + }), + ); + const result = { + ...additionalProperties, + }; + return result; +} + +export function openMapToJSON(value: OpenMap): OpenMap$Wire { + const sourceEntries = Object.entries(value); + const additionalEntries = sourceEntries; + const additionalProperties = Object.fromEntries( + additionalEntries.map(([key, value]) => { + const additionalValue = value; + return [key, openInnerToJSON(additionalValue)] as const; + }), + ); + const result = { + ...additionalProperties, + }; + return result; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/OpenTool.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/OpenTool.ts.golden new file mode 100644 index 000000000..037135392 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/OpenTool.ts.golden @@ -0,0 +1,36 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { matches$23FromJSON, matches$23ToJSON } from '../runtime/unionMatch'; +import type { ClientTool, ClientTool$Wire } from './ClientTool'; +import { clientToolFromJSON, clientToolToJSON } from './ClientTool'; + +export type OpenTool$Wire = unknown; + +export type OpenTool = unknown; + +export function openToolFromJSON(json: OpenTool$Wire): OpenTool { + const candidate: unknown = json; + const matches = [matches$23FromJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous OpenTool untagged union value'); + } + if (matches[0]) { + return clientToolFromJSON(json as unknown as ClientTool$Wire); + } + return json as unknown as unknown; +} + +export function openToolToJSON(value: OpenTool): OpenTool$Wire { + const candidate: unknown = value; + const matches = [matches$23ToJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous OpenTool untagged union value'); + } + if (matches[0]) { + return clientToolToJSON(value as unknown as ClientTool); + } + return value as unknown as unknown; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/OtherDetail.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/OtherDetail.ts.golden new file mode 100644 index 000000000..9bb6b93d7 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/OtherDetail.ts.golden @@ -0,0 +1,28 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface OtherDetail$Wire { + readonly optional_flag?: boolean; + readonly wire_name: string; +} + +export interface OtherDetail { + readonly optionalFlag?: boolean; + readonly wireName: string; +} + +export function otherDetailFromJSON(json: OtherDetail$Wire): OtherDetail { + return { + optionalFlag: json.optional_flag, + wireName: json.wire_name, + }; +} + +export function otherDetailToJSON(value: OtherDetail): OtherDetail$Wire { + return { + optional_flag: value.optionalFlag, + wire_name: value.wireName, + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/Outer.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/Outer.ts.golden new file mode 100644 index 000000000..ec747e36b --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/Outer.ts.golden @@ -0,0 +1,47 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$1FromJSON, + matches$1ToJSON, + matches$2FromJSON, + matches$2ToJSON, +} from '../runtime/unionMatch'; +import type { OpenInner, OpenInner$Wire } from './OpenInner'; +import { openInnerFromJSON, openInnerToJSON } from './OpenInner'; + +export type Outer$Wire = unknown; + +export type Outer = unknown; + +export function outerFromJSON(json: Outer$Wire): Outer { + const candidate: unknown = json; + const matches = [matches$1FromJSON(candidate), matches$2FromJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous Outer untagged union value'); + } + if (matches[0]) { + return openInnerFromJSON(json as unknown as OpenInner$Wire); + } + if (matches[1]) { + return json as unknown as string; + } + return openInnerFromJSON(json as unknown as OpenInner$Wire); +} + +export function outerToJSON(value: Outer): Outer$Wire { + const candidate: unknown = value; + const matches = [matches$1ToJSON(candidate), matches$2ToJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous Outer untagged union value'); + } + if (matches[0]) { + return openInnerToJSON(value as unknown as OpenInner); + } + if (matches[1]) { + return value as unknown as string; + } + return openInnerToJSON(value as unknown as OpenInner); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/PrimitiveArrayChoice.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/PrimitiveArrayChoice.ts.golden new file mode 100644 index 000000000..8a67f254f --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/PrimitiveArrayChoice.ts.golden @@ -0,0 +1,51 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$1FromJSON, + matches$1ToJSON, + matches$70FromJSON, + matches$70ToJSON, +} from '../runtime/unionMatch'; +import type { Value$3, Value$3Wire } from '../runtime/valueConversion$3'; +import { convert$3FromJSON, convert$3ToJSON } from '../runtime/valueConversion$3'; +import type { Detail, Detail$Wire } from './Detail'; +import { detailFromJSON, detailToJSON } from './Detail'; + +export type PrimitiveArrayChoice$Wire = readonly number[] | Detail$Wire; + +export type PrimitiveArrayChoice = readonly number[] | Detail; + +export function primitiveArrayChoiceFromJSON( + json: PrimitiveArrayChoice$Wire, +): PrimitiveArrayChoice { + const candidate: unknown = json; + const matches = [matches$70FromJSON(candidate), matches$1FromJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous PrimitiveArrayChoice untagged union value'); + } + if (matches[0]) { + return convert$3FromJSON(json as unknown as Value$3Wire); + } + if (matches[1]) { + return detailFromJSON(json as unknown as Detail$Wire); + } + throw new TypeError('No matching PrimitiveArrayChoice untagged union member'); +} + +export function primitiveArrayChoiceToJSON(value: PrimitiveArrayChoice): PrimitiveArrayChoice$Wire { + const candidate: unknown = value; + const matches = [matches$70ToJSON(candidate), matches$1ToJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous PrimitiveArrayChoice untagged union value'); + } + if (matches[0]) { + return convert$3ToJSON(value as unknown as Value$3); + } + if (matches[1]) { + return detailToJSON(value as unknown as Detail); + } + throw new TypeError('No matching PrimitiveArrayChoice untagged union member'); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/PrimitiveMapChoice.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/PrimitiveMapChoice.ts.golden new file mode 100644 index 000000000..659f8a8f6 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/PrimitiveMapChoice.ts.golden @@ -0,0 +1,49 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$1FromJSON, + matches$1ToJSON, + matches$72FromJSON, + matches$72ToJSON, +} from '../runtime/unionMatch'; +import type { Value$26, Value$26Wire } from '../runtime/valueConversion$26'; +import { convert$26FromJSON, convert$26ToJSON } from '../runtime/valueConversion$26'; +import type { Detail, Detail$Wire } from './Detail'; +import { detailFromJSON, detailToJSON } from './Detail'; + +export type PrimitiveMapChoice$Wire = globalThis.Record | Detail$Wire; + +export type PrimitiveMapChoice = globalThis.Record | Detail; + +export function primitiveMapChoiceFromJSON(json: PrimitiveMapChoice$Wire): PrimitiveMapChoice { + const candidate: unknown = json; + const matches = [matches$72FromJSON(candidate), matches$1FromJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous PrimitiveMapChoice untagged union value'); + } + if (matches[0]) { + return convert$26FromJSON(json as unknown as Value$26Wire); + } + if (matches[1]) { + return detailFromJSON(json as unknown as Detail$Wire); + } + throw new TypeError('No matching PrimitiveMapChoice untagged union member'); +} + +export function primitiveMapChoiceToJSON(value: PrimitiveMapChoice): PrimitiveMapChoice$Wire { + const candidate: unknown = value; + const matches = [matches$72ToJSON(candidate), matches$1ToJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous PrimitiveMapChoice untagged union value'); + } + if (matches[0]) { + return convert$26ToJSON(value as unknown as Value$26); + } + if (matches[1]) { + return detailToJSON(value as unknown as Detail); + } + throw new TypeError('No matching PrimitiveMapChoice untagged union member'); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/RawEnvelope.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/RawEnvelope.ts.golden new file mode 100644 index 000000000..508fa4eb9 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/RawEnvelope.ts.golden @@ -0,0 +1,37 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { AliasChoice, AliasChoice$Wire } from './AliasChoice'; +import { aliasChoiceFromJSON, aliasChoiceToJSON } from './AliasChoice'; +import type { Detail, Detail$Wire } from './Detail'; +import { detailFromJSON, detailToJSON } from './Detail'; + +export interface RawEnvelope$Wire { + readonly plain_detail: Detail$Wire; + readonly raw_choice: AliasChoice$Wire; + readonly request_id: string; +} + +export interface RawEnvelope { + readonly plainDetail: Detail; + readonly rawChoice: AliasChoice; + readonly requestId: string; +} + +export function rawEnvelopeFromJSON(json: RawEnvelope$Wire): RawEnvelope { + return { + plainDetail: detailFromJSON(json.plain_detail), + rawChoice: aliasChoiceFromJSON(json.raw_choice), + requestId: json.request_id, + }; +} + +export function rawEnvelopeToJSON(value: RawEnvelope): RawEnvelope$Wire { + return { + plain_detail: detailToJSON(value.plainDetail), + raw_choice: aliasChoiceToJSON(value.rawChoice), + request_id: value.requestId, + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/Readonly.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/Readonly.ts.golden new file mode 100644 index 000000000..ffd814862 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/Readonly.ts.golden @@ -0,0 +1,44 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { convert$11FromJSON, convert$11ToJSON } from '../runtime/valueConversion$11'; +import type { Detail, Detail$Wire } from './Detail'; +import { detailFromJSON, detailToJSON } from './Detail'; + +export type Readonly$Wire = globalThis.Readonly> & + globalThis.Readonly>; + +export type Readonly = globalThis.Readonly> & + globalThis.Readonly>; + +export function readonlyFromJSON(json: Readonly$Wire): Readonly { + if (Object.prototype.hasOwnProperty.call(json, 'extension_key')) { + const contentValue = (json as globalThis.Record<'extension_key', Detail$Wire>)['extension_key']; + const entries = Object.entries(json).filter(([key]) => key !== 'extension_key'); + const additionalProperties = Object.fromEntries( + entries.map(([key, value]) => { + const additionalValue = value as readonly Detail$Wire[]; + return [key, convert$11FromJSON(additionalValue)] as const; + }), + ); + return { ...additionalProperties, ['extension_key']: detailFromJSON(contentValue) }; + } + throw new Error('Unknown variant'); +} + +export function readonlyToJSON(value: Readonly): Readonly$Wire { + if (Object.prototype.hasOwnProperty.call(value, 'extension_key')) { + const contentValue = (value as globalThis.Record<'extension_key', Detail>)['extension_key']; + const entries = Object.entries(value).filter(([key]) => key !== 'extension_key'); + const additionalProperties = Object.fromEntries( + entries.map(([key, value]) => { + const additionalValue = value as readonly Detail[]; + return [key, convert$11ToJSON(additionalValue)] as const; + }), + ); + return { ...additionalProperties, ['extension_key']: detailToJSON(contentValue) }; + } + throw new Error('Unknown variant'); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/Record.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/Record.ts.golden new file mode 100644 index 000000000..b221a9594 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/Record.ts.golden @@ -0,0 +1,43 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail, Detail$Wire } from './Detail'; +import { detailFromJSON, detailToJSON } from './Detail'; + +export type Record$Wire = + | globalThis.Record<'event_type', 'empty'> + | (globalThis.Record<'event_type', 'data'> & globalThis.Record<'event_data', Detail$Wire>); + +export type Record = + | globalThis.Record<'eventType', 'empty'> + | (globalThis.Record<'eventType', 'data'> & globalThis.Record<'eventData', Detail>); + +export function recordFromJSON(json: Record$Wire): Record { + if (json['event_type'] === 'empty') { + return { ['eventType']: 'empty' } as Record; + } + if (json['event_type'] === 'data') { + return { + ['eventType']: 'data', + ['eventData']: detailFromJSON( + (json as globalThis.Record<'event_data', Detail$Wire>)['event_data'], + ), + } as Record; + } + throw new Error('Unknown variant'); +} + +export function recordToJSON(value: Record): Record$Wire { + if (value['eventType'] === 'empty') { + return { ['event_type']: 'empty' } as Record$Wire; + } + if (value['eventType'] === 'data') { + return { + ['event_type']: 'data', + ['event_data']: detailToJSON((value as globalThis.Record<'eventData', Detail>)['eventData']), + } as Record$Wire; + } + throw new Error('Unknown variant'); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/RecordEmpty.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/RecordEmpty.ts.golden new file mode 100644 index 000000000..9fd01d883 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/RecordEmpty.ts.golden @@ -0,0 +1,24 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface RecordEmpty$Wire { + readonly event_type: 'empty'; +} + +export interface RecordEmpty { + readonly eventType: 'empty'; +} + +export function recordEmptyFromJSON(json: RecordEmpty$Wire): RecordEmpty { + return { + eventType: json.event_type, + }; +} + +export function recordEmptyToJSON(value: RecordEmpty): RecordEmpty$Wire { + return { + event_type: value.eventType, + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/RecordOrBoolean.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/RecordOrBoolean.ts.golden new file mode 100644 index 000000000..31fa141ea --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/RecordOrBoolean.ts.golden @@ -0,0 +1,47 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$5FromJSON, + matches$5ToJSON, + matches$6FromJSON, + matches$6ToJSON, +} from '../runtime/unionMatch'; +import type { Record, Record$Wire } from './Record'; +import { recordFromJSON, recordToJSON } from './Record'; + +export type RecordOrBoolean$Wire = Record$Wire | boolean; + +export type RecordOrBoolean = Record | boolean; + +export function recordOrBooleanFromJSON(json: RecordOrBoolean$Wire): RecordOrBoolean { + const candidate: unknown = json; + const matches = [matches$6FromJSON(candidate), matches$5FromJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous RecordOrBoolean untagged union value'); + } + if (matches[0]) { + return recordFromJSON(json as unknown as Record$Wire); + } + if (matches[1]) { + return json as unknown as boolean; + } + throw new TypeError('No matching RecordOrBoolean untagged union member'); +} + +export function recordOrBooleanToJSON(value: RecordOrBoolean): RecordOrBoolean$Wire { + const candidate: unknown = value; + const matches = [matches$6ToJSON(candidate), matches$5ToJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous RecordOrBoolean untagged union value'); + } + if (matches[0]) { + return recordToJSON(value as unknown as Record); + } + if (matches[1]) { + return value as unknown as boolean; + } + throw new TypeError('No matching RecordOrBoolean untagged union member'); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/RecursiveDictionaryChoice.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/RecursiveDictionaryChoice.ts.golden new file mode 100644 index 000000000..056f2defb --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/RecursiveDictionaryChoice.ts.golden @@ -0,0 +1,46 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { matches$73FromJSON, matches$73ToJSON } from '../runtime/unionMatch'; +import type { + RecursiveOpenDictionary, + RecursiveOpenDictionary$Wire, +} from './RecursiveOpenDictionary'; +import { + recursiveOpenDictionaryFromJSON, + recursiveOpenDictionaryToJSON, +} from './RecursiveOpenDictionary'; + +export type RecursiveDictionaryChoice$Wire = unknown; + +export type RecursiveDictionaryChoice = unknown; + +export function recursiveDictionaryChoiceFromJSON( + json: RecursiveDictionaryChoice$Wire, +): RecursiveDictionaryChoice { + const candidate: unknown = json; + const matches = [matches$73FromJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous RecursiveDictionaryChoice untagged union value'); + } + if (matches[0]) { + return recursiveOpenDictionaryFromJSON(json as unknown as RecursiveOpenDictionary$Wire); + } + return json as unknown as unknown; +} + +export function recursiveDictionaryChoiceToJSON( + value: RecursiveDictionaryChoice, +): RecursiveDictionaryChoice$Wire { + const candidate: unknown = value; + const matches = [matches$73ToJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous RecursiveDictionaryChoice untagged union value'); + } + if (matches[0]) { + return recursiveOpenDictionaryToJSON(value as unknown as RecursiveOpenDictionary); + } + return value as unknown as unknown; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/RecursiveList.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/RecursiveList.ts.golden new file mode 100644 index 000000000..d1ff01f1a --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/RecursiveList.ts.golden @@ -0,0 +1,49 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$1FromJSON, + matches$1ToJSON, + matches$81FromJSON, + matches$81ToJSON, +} from '../runtime/unionMatch'; +import type { Value$28, Value$28Wire } from '../runtime/valueConversion$28'; +import { convert$28FromJSON, convert$28ToJSON } from '../runtime/valueConversion$28'; +import type { Detail, Detail$Wire } from './Detail'; +import { detailFromJSON, detailToJSON } from './Detail'; + +export type RecursiveList$Wire = readonly RecursiveList$Wire[] | Detail$Wire; + +export type RecursiveList = readonly RecursiveList[] | Detail; + +export function recursiveListFromJSON(json: RecursiveList$Wire): RecursiveList { + const candidate: unknown = json; + const matches = [matches$81FromJSON(candidate), matches$1FromJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous RecursiveList untagged union value'); + } + if (matches[0]) { + return convert$28FromJSON(json as unknown as Value$28Wire); + } + if (matches[1]) { + return detailFromJSON(json as unknown as Detail$Wire); + } + throw new TypeError('No matching RecursiveList untagged union member'); +} + +export function recursiveListToJSON(value: RecursiveList): RecursiveList$Wire { + const candidate: unknown = value; + const matches = [matches$81ToJSON(candidate), matches$1ToJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous RecursiveList untagged union value'); + } + if (matches[0]) { + return convert$28ToJSON(value as unknown as Value$28); + } + if (matches[1]) { + return detailToJSON(value as unknown as Detail); + } + throw new TypeError('No matching RecursiveList untagged union member'); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/RecursiveOpenDictionary.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/RecursiveOpenDictionary.ts.golden new file mode 100644 index 000000000..9679ecc86 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/RecursiveOpenDictionary.ts.golden @@ -0,0 +1,53 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$1FromJSON, + matches$1ToJSON, + matches$74FromJSON, + matches$74ToJSON, +} from '../runtime/unionMatch'; +import type { Value$29, Value$29Wire } from '../runtime/valueConversion$29'; +import { convert$29FromJSON, convert$29ToJSON } from '../runtime/valueConversion$29'; +import type { Detail, Detail$Wire } from './Detail'; +import { detailFromJSON, detailToJSON } from './Detail'; + +export type RecursiveOpenDictionary$Wire = unknown; + +export type RecursiveOpenDictionary = unknown; + +export function recursiveOpenDictionaryFromJSON( + json: RecursiveOpenDictionary$Wire, +): RecursiveOpenDictionary { + const candidate: unknown = json; + const matches = [matches$1FromJSON(candidate), matches$74FromJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous RecursiveOpenDictionary untagged union value'); + } + if (matches[0]) { + return detailFromJSON(json as unknown as Detail$Wire); + } + if (matches[1]) { + return convert$29FromJSON(json as unknown as Value$29Wire); + } + return json as unknown as unknown; +} + +export function recursiveOpenDictionaryToJSON( + value: RecursiveOpenDictionary, +): RecursiveOpenDictionary$Wire { + const candidate: unknown = value; + const matches = [matches$1ToJSON(candidate), matches$74ToJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous RecursiveOpenDictionary untagged union value'); + } + if (matches[0]) { + return detailToJSON(value as unknown as Detail); + } + if (matches[1]) { + return convert$29ToJSON(value as unknown as Value$29); + } + return value as unknown as unknown; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/ScalarEnumChoice.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/ScalarEnumChoice.ts.golden new file mode 100644 index 000000000..521e55d77 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/ScalarEnumChoice.ts.golden @@ -0,0 +1,45 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$3FromJSON, + matches$3ToJSON, + matches$5FromJSON, + matches$5ToJSON, +} from '../runtime/unionMatch'; + +export type ScalarEnumChoice$Wire = number | boolean; + +export type ScalarEnumChoice = number | boolean; + +export function scalarEnumChoiceFromJSON(json: ScalarEnumChoice$Wire): ScalarEnumChoice { + const candidate: unknown = json; + const matches = [matches$3FromJSON(candidate), matches$5FromJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous ScalarEnumChoice untagged union value'); + } + if (matches[0]) { + return json as unknown as number; + } + if (matches[1]) { + return json as unknown as boolean; + } + throw new TypeError('No matching ScalarEnumChoice untagged union member'); +} + +export function scalarEnumChoiceToJSON(value: ScalarEnumChoice): ScalarEnumChoice$Wire { + const candidate: unknown = value; + const matches = [matches$3ToJSON(candidate), matches$5ToJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous ScalarEnumChoice untagged union value'); + } + if (matches[0]) { + return value as unknown as number; + } + if (matches[1]) { + return value as unknown as boolean; + } + throw new TypeError('No matching ScalarEnumChoice untagged union member'); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/SearchTool.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/SearchTool.ts.golden new file mode 100644 index 000000000..9992b2695 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/SearchTool.ts.golden @@ -0,0 +1,32 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface SearchTool$Wire { + readonly max_uses?: number; + readonly name: 'web_search'; + readonly type: 'web_search_20250305'; +} + +export interface SearchTool { + readonly maxUses?: number; + readonly name: 'web_search'; + readonly type: 'web_search_20250305'; +} + +export function searchToolFromJSON(json: SearchTool$Wire): SearchTool { + return { + maxUses: json.max_uses, + name: json.name, + type: json['type'], + }; +} + +export function searchToolToJSON(value: SearchTool): SearchTool$Wire { + return { + max_uses: value.maxUses, + name: value.name, + type: value['type'], + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/SendOptionalInlineScalarRequest.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/SendOptionalInlineScalarRequest.ts.golden new file mode 100644 index 000000000..dcbcb68da --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/SendOptionalInlineScalarRequest.ts.golden @@ -0,0 +1,49 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$66FromJSON, + matches$66ToJSON, + matches$67FromJSON, + matches$67ToJSON, +} from '../runtime/unionMatch'; + +export type SendOptionalInlineScalarRequest$Wire = string | null | number; + +export type SendOptionalInlineScalarRequest = string | null | number; + +export function sendOptionalInlineScalarRequestFromJSON( + json: SendOptionalInlineScalarRequest$Wire, +): SendOptionalInlineScalarRequest { + const candidate: unknown = json; + const matches = [matches$66FromJSON(candidate), matches$67FromJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous SendOptionalInlineScalarRequest untagged union value'); + } + if (matches[0]) { + return json as unknown as string | null; + } + if (matches[1]) { + return json as unknown as number | null; + } + throw new TypeError('No matching SendOptionalInlineScalarRequest untagged union member'); +} + +export function sendOptionalInlineScalarRequestToJSON( + value: SendOptionalInlineScalarRequest, +): SendOptionalInlineScalarRequest$Wire { + const candidate: unknown = value; + const matches = [matches$66ToJSON(candidate), matches$67ToJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous SendOptionalInlineScalarRequest untagged union value'); + } + if (matches[0]) { + return value as unknown as string | null; + } + if (matches[1]) { + return value as unknown as number | null; + } + throw new TypeError('No matching SendOptionalInlineScalarRequest untagged union member'); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/TaggedMapA.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/TaggedMapA.ts.golden new file mode 100644 index 000000000..8aa8640e2 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/TaggedMapA.ts.golden @@ -0,0 +1,43 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail, Detail$Wire } from './Detail'; +import { detailFromJSON, detailToJSON } from './Detail'; + +export type TaggedMapA$Wire = globalThis.Readonly> & + globalThis.Readonly>; + +export type TaggedMapA = globalThis.Readonly> & + globalThis.Readonly>; + +export function taggedMapAFromJSON(json: TaggedMapA$Wire): TaggedMapA { + if (Object.prototype.hasOwnProperty.call(json, 'payload_key')) { + const contentValue = (json as globalThis.Record<'payload_key', Detail$Wire>)['payload_key']; + const entries = Object.entries(json).filter(([key]) => key !== 'payload_key'); + const additionalProperties = Object.fromEntries( + entries.map(([key, value]) => { + const additionalValue = value as Detail$Wire; + return [key, detailFromJSON(additionalValue)] as const; + }), + ); + return { ...additionalProperties, ['payload_key']: detailFromJSON(contentValue) }; + } + throw new Error('Unknown variant'); +} + +export function taggedMapAToJSON(value: TaggedMapA): TaggedMapA$Wire { + if (Object.prototype.hasOwnProperty.call(value, 'payload_key')) { + const contentValue = (value as globalThis.Record<'payload_key', Detail>)['payload_key']; + const entries = Object.entries(value).filter(([key]) => key !== 'payload_key'); + const additionalProperties = Object.fromEntries( + entries.map(([key, value]) => { + const additionalValue = value as Detail; + return [key, detailToJSON(additionalValue)] as const; + }), + ); + return { ...additionalProperties, ['payload_key']: detailToJSON(contentValue) }; + } + throw new Error('Unknown variant'); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/TaggedMapB.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/TaggedMapB.ts.golden new file mode 100644 index 000000000..4c59d515e --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/TaggedMapB.ts.golden @@ -0,0 +1,44 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { convert$11FromJSON, convert$11ToJSON } from '../runtime/valueConversion$11'; +import type { Detail, Detail$Wire } from './Detail'; +import { detailFromJSON, detailToJSON } from './Detail'; + +export type TaggedMapB$Wire = globalThis.Readonly> & + globalThis.Readonly>; + +export type TaggedMapB = globalThis.Readonly> & + globalThis.Readonly>; + +export function taggedMapBFromJSON(json: TaggedMapB$Wire): TaggedMapB { + if (Object.prototype.hasOwnProperty.call(json, 'payload_key')) { + const contentValue = (json as globalThis.Record<'payload_key', Detail$Wire>)['payload_key']; + const entries = Object.entries(json).filter(([key]) => key !== 'payload_key'); + const additionalProperties = Object.fromEntries( + entries.map(([key, value]) => { + const additionalValue = value as readonly Detail$Wire[]; + return [key, convert$11FromJSON(additionalValue)] as const; + }), + ); + return { ...additionalProperties, ['payload_key']: detailFromJSON(contentValue) }; + } + throw new Error('Unknown variant'); +} + +export function taggedMapBToJSON(value: TaggedMapB): TaggedMapB$Wire { + if (Object.prototype.hasOwnProperty.call(value, 'payload_key')) { + const contentValue = (value as globalThis.Record<'payload_key', Detail>)['payload_key']; + const entries = Object.entries(value).filter(([key]) => key !== 'payload_key'); + const additionalProperties = Object.fromEntries( + entries.map(([key, value]) => { + const additionalValue = value as readonly Detail[]; + return [key, convert$11ToJSON(additionalValue)] as const; + }), + ); + return { ...additionalProperties, ['payload_key']: detailToJSON(contentValue) }; + } + throw new Error('Unknown variant'); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/TaggedMapChoice.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/TaggedMapChoice.ts.golden new file mode 100644 index 000000000..1adbe1f9d --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/TaggedMapChoice.ts.golden @@ -0,0 +1,49 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$83FromJSON, + matches$83ToJSON, + matches$85FromJSON, + matches$85ToJSON, +} from '../runtime/unionMatch'; +import type { TaggedMapA, TaggedMapA$Wire } from './TaggedMapA'; +import { taggedMapAFromJSON, taggedMapAToJSON } from './TaggedMapA'; +import type { TaggedMapB, TaggedMapB$Wire } from './TaggedMapB'; +import { taggedMapBFromJSON, taggedMapBToJSON } from './TaggedMapB'; + +export type TaggedMapChoice$Wire = TaggedMapA$Wire | TaggedMapB$Wire; + +export type TaggedMapChoice = TaggedMapA | TaggedMapB; + +export function taggedMapChoiceFromJSON(json: TaggedMapChoice$Wire): TaggedMapChoice { + const candidate: unknown = json; + const matches = [matches$83FromJSON(candidate), matches$85FromJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous TaggedMapChoice untagged union value'); + } + if (matches[0]) { + return taggedMapAFromJSON(json as unknown as TaggedMapA$Wire); + } + if (matches[1]) { + return taggedMapBFromJSON(json as unknown as TaggedMapB$Wire); + } + throw new TypeError('No matching TaggedMapChoice untagged union member'); +} + +export function taggedMapChoiceToJSON(value: TaggedMapChoice): TaggedMapChoice$Wire { + const candidate: unknown = value; + const matches = [matches$83ToJSON(candidate), matches$85ToJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous TaggedMapChoice untagged union value'); + } + if (matches[0]) { + return taggedMapAToJSON(value as unknown as TaggedMapA); + } + if (matches[1]) { + return taggedMapBToJSON(value as unknown as TaggedMapB); + } + throw new TypeError('No matching TaggedMapChoice untagged union member'); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/Tool.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/Tool.ts.golden new file mode 100644 index 000000000..2fe494493 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/Tool.ts.golden @@ -0,0 +1,49 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$13FromJSON, + matches$13ToJSON, + matches$23FromJSON, + matches$23ToJSON, +} from '../runtime/unionMatch'; +import type { ClientTool, ClientTool$Wire } from './ClientTool'; +import { clientToolFromJSON, clientToolToJSON } from './ClientTool'; +import type { SearchTool, SearchTool$Wire } from './SearchTool'; +import { searchToolFromJSON, searchToolToJSON } from './SearchTool'; + +export type Tool$Wire = ClientTool$Wire | SearchTool$Wire; + +export type Tool = ClientTool | SearchTool; + +export function toolFromJSON(json: Tool$Wire): Tool { + const candidate: unknown = json; + const matches = [matches$23FromJSON(candidate), matches$13FromJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous Tool untagged union value'); + } + if (matches[0]) { + return clientToolFromJSON(json as unknown as ClientTool$Wire); + } + if (matches[1]) { + return searchToolFromJSON(json as unknown as SearchTool$Wire); + } + throw new TypeError('No matching Tool untagged union member'); +} + +export function toolToJSON(value: Tool): Tool$Wire { + const candidate: unknown = value; + const matches = [matches$23ToJSON(candidate), matches$13ToJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous Tool untagged union value'); + } + if (matches[0]) { + return clientToolToJSON(value as unknown as ClientTool); + } + if (matches[1]) { + return searchToolToJSON(value as unknown as SearchTool); + } + throw new TypeError('No matching Tool untagged union member'); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/ToolAlias.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/ToolAlias.ts.golden new file mode 100644 index 000000000..19920bb13 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/ToolAlias.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Tool, Tool$Wire } from './Tool'; +import { toolFromJSON, toolToJSON } from './Tool'; + +export type ToolAlias$Wire = Tool$Wire; + +export type ToolAlias = Tool; + +export function toolAliasFromJSON(json: ToolAlias$Wire): ToolAlias { + return toolFromJSON(json); +} + +export function toolAliasToJSON(value: ToolAlias): ToolAlias$Wire { + return toolToJSON(value); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/ToolList.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/ToolList.ts.golden new file mode 100644 index 000000000..35df7ccc7 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/ToolList.ts.golden @@ -0,0 +1,51 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$86FromJSON, + matches$86ToJSON, + matches$87FromJSON, + matches$87ToJSON, +} from '../runtime/unionMatch'; +import type { Value$30, Value$30Wire } from '../runtime/valueConversion$30'; +import { convert$30FromJSON, convert$30ToJSON } from '../runtime/valueConversion$30'; +import type { Value$31, Value$31Wire } from '../runtime/valueConversion$31'; +import { convert$31FromJSON, convert$31ToJSON } from '../runtime/valueConversion$31'; +import type { ClientTool, ClientTool$Wire } from './ClientTool'; +import type { SearchTool, SearchTool$Wire } from './SearchTool'; + +export type ToolList$Wire = readonly ClientTool$Wire[] | readonly SearchTool$Wire[]; + +export type ToolList = readonly ClientTool[] | readonly SearchTool[]; + +export function toolListFromJSON(json: ToolList$Wire): ToolList { + const candidate: unknown = json; + const matches = [matches$86FromJSON(candidate), matches$87FromJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous ToolList untagged union value'); + } + if (matches[0]) { + return convert$30FromJSON(json as unknown as Value$30Wire); + } + if (matches[1]) { + return convert$31FromJSON(json as unknown as Value$31Wire); + } + throw new TypeError('No matching ToolList untagged union member'); +} + +export function toolListToJSON(value: ToolList): ToolList$Wire { + const candidate: unknown = value; + const matches = [matches$86ToJSON(candidate), matches$87ToJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous ToolList untagged union value'); + } + if (matches[0]) { + return convert$30ToJSON(value as unknown as Value$30); + } + if (matches[1]) { + return convert$31ToJSON(value as unknown as Value$31); + } + throw new TypeError('No matching ToolList untagged union member'); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/ToolMap.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/ToolMap.ts.golden new file mode 100644 index 000000000..0bf6ecb35 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/ToolMap.ts.golden @@ -0,0 +1,53 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$88FromJSON, + matches$88ToJSON, + matches$89FromJSON, + matches$89ToJSON, +} from '../runtime/unionMatch'; +import type { Value$32, Value$32Wire } from '../runtime/valueConversion$32'; +import { convert$32FromJSON, convert$32ToJSON } from '../runtime/valueConversion$32'; +import type { Value$33, Value$33Wire } from '../runtime/valueConversion$33'; +import { convert$33FromJSON, convert$33ToJSON } from '../runtime/valueConversion$33'; +import type { ClientTool, ClientTool$Wire } from './ClientTool'; +import type { SearchTool, SearchTool$Wire } from './SearchTool'; + +export type ToolMap$Wire = + | globalThis.Record + | globalThis.Record; + +export type ToolMap = globalThis.Record | globalThis.Record; + +export function toolMapFromJSON(json: ToolMap$Wire): ToolMap { + const candidate: unknown = json; + const matches = [matches$88FromJSON(candidate), matches$89FromJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous ToolMap untagged union value'); + } + if (matches[0]) { + return convert$32FromJSON(json as unknown as Value$32Wire); + } + if (matches[1]) { + return convert$33FromJSON(json as unknown as Value$33Wire); + } + throw new TypeError('No matching ToolMap untagged union member'); +} + +export function toolMapToJSON(value: ToolMap): ToolMap$Wire { + const candidate: unknown = value; + const matches = [matches$88ToJSON(candidate), matches$89ToJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous ToolMap untagged union value'); + } + if (matches[0]) { + return convert$32ToJSON(value as unknown as Value$32); + } + if (matches[1]) { + return convert$33ToJSON(value as unknown as Value$33); + } + throw new TypeError('No matching ToolMap untagged union member'); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/ToolRequest.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/ToolRequest.ts.golden new file mode 100644 index 000000000..5ef47e068 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/ToolRequest.ts.golden @@ -0,0 +1,27 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { convert$34FromJSON, convert$34ToJSON } from '../runtime/valueConversion$34'; +import type { Tool, Tool$Wire } from './Tool'; + +export interface ToolRequest$Wire { + readonly tools: readonly Tool$Wire[]; +} + +export interface ToolRequest { + readonly tools: readonly Tool[]; +} + +export function toolRequestFromJSON(json: ToolRequest$Wire): ToolRequest { + return { + tools: convert$34FromJSON(json.tools), + }; +} + +export function toolRequestToJSON(value: ToolRequest): ToolRequest$Wire { + return { + tools: convert$34ToJSON(value.tools), + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/TwiceNullableOpaque.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/TwiceNullableOpaque.ts.golden new file mode 100644 index 000000000..c0107b457 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/TwiceNullableOpaque.ts.golden @@ -0,0 +1,27 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { NullableOpaque, NullableOpaque$Wire } from './NullableOpaque'; +import { nullableOpaqueFromJSON, nullableOpaqueToJSON } from './NullableOpaque'; + +export type TwiceNullableOpaque$Wire = unknown; + +export type TwiceNullableOpaque = unknown; + +export function twiceNullableOpaqueFromJSON(json: TwiceNullableOpaque$Wire): TwiceNullableOpaque { + const candidate: unknown = json; + if (candidate === null) { + return null; + } + return nullableOpaqueFromJSON(json as unknown as NullableOpaque$Wire); +} + +export function twiceNullableOpaqueToJSON(value: TwiceNullableOpaque): TwiceNullableOpaque$Wire { + const candidate: unknown = value; + if (candidate === null) { + return null; + } + return nullableOpaqueToJSON(value as unknown as NullableOpaque); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/TwiceNullableOuter.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/TwiceNullableOuter.ts.golden new file mode 100644 index 000000000..4bff99602 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/TwiceNullableOuter.ts.golden @@ -0,0 +1,38 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { matches$30FromJSON, matches$30ToJSON } from '../runtime/unionMatch'; +import type { NullableDetail, NullableDetail$Wire } from './NullableDetail'; +import { nullableDetailFromJSON, nullableDetailToJSON } from './NullableDetail'; +import type { TwiceNullableOpaque, TwiceNullableOpaque$Wire } from './TwiceNullableOpaque'; +import { twiceNullableOpaqueFromJSON, twiceNullableOpaqueToJSON } from './TwiceNullableOpaque'; + +export type TwiceNullableOuter$Wire = unknown; + +export type TwiceNullableOuter = unknown; + +export function twiceNullableOuterFromJSON(json: TwiceNullableOuter$Wire): TwiceNullableOuter { + const candidate: unknown = json; + const matches = [matches$30FromJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous TwiceNullableOuter untagged union value'); + } + if (matches[0]) { + return nullableDetailFromJSON(json as unknown as NullableDetail$Wire); + } + return twiceNullableOpaqueFromJSON(json as unknown as TwiceNullableOpaque$Wire); +} + +export function twiceNullableOuterToJSON(value: TwiceNullableOuter): TwiceNullableOuter$Wire { + const candidate: unknown = value; + const matches = [matches$30ToJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous TwiceNullableOuter untagged union value'); + } + if (matches[0]) { + return nullableDetailToJSON(value as unknown as NullableDetail); + } + return twiceNullableOpaqueToJSON(value as unknown as TwiceNullableOpaque); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/UntypedNumericChoice.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/UntypedNumericChoice.ts.golden new file mode 100644 index 000000000..7fbb6ca81 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/UntypedNumericChoice.ts.golden @@ -0,0 +1,51 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$90FromJSON, + matches$90ToJSON, + matches$91FromJSON, + matches$91ToJSON, +} from '../runtime/unionMatch'; +import type { UntypedNumericOne, UntypedNumericOne$Wire } from './UntypedNumericOne'; +import { untypedNumericOneFromJSON, untypedNumericOneToJSON } from './UntypedNumericOne'; +import type { UntypedNumericTwo, UntypedNumericTwo$Wire } from './UntypedNumericTwo'; +import { untypedNumericTwoFromJSON, untypedNumericTwoToJSON } from './UntypedNumericTwo'; + +export type UntypedNumericChoice$Wire = UntypedNumericOne$Wire | UntypedNumericTwo$Wire; + +export type UntypedNumericChoice = UntypedNumericOne | UntypedNumericTwo; + +export function untypedNumericChoiceFromJSON( + json: UntypedNumericChoice$Wire, +): UntypedNumericChoice { + const candidate: unknown = json; + const matches = [matches$90FromJSON(candidate), matches$91FromJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous UntypedNumericChoice untagged union value'); + } + if (matches[0]) { + return untypedNumericOneFromJSON(json as unknown as UntypedNumericOne$Wire); + } + if (matches[1]) { + return untypedNumericTwoFromJSON(json as unknown as UntypedNumericTwo$Wire); + } + throw new TypeError('No matching UntypedNumericChoice untagged union member'); +} + +export function untypedNumericChoiceToJSON(value: UntypedNumericChoice): UntypedNumericChoice$Wire { + const candidate: unknown = value; + const matches = [matches$90ToJSON(candidate), matches$91ToJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous UntypedNumericChoice untagged union value'); + } + if (matches[0]) { + return untypedNumericOneToJSON(value as unknown as UntypedNumericOne); + } + if (matches[1]) { + return untypedNumericTwoToJSON(value as unknown as UntypedNumericTwo); + } + throw new TypeError('No matching UntypedNumericChoice untagged union member'); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/UntypedNumericOne.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/UntypedNumericOne.ts.golden new file mode 100644 index 000000000..1695e895f --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/UntypedNumericOne.ts.golden @@ -0,0 +1,28 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface UntypedNumericOne$Wire { + readonly first_value?: string; + readonly revision: string; +} + +export interface UntypedNumericOne { + readonly firstValue?: string; + readonly revision: string; +} + +export function untypedNumericOneFromJSON(json: UntypedNumericOne$Wire): UntypedNumericOne { + return { + firstValue: json.first_value, + revision: json.revision, + }; +} + +export function untypedNumericOneToJSON(value: UntypedNumericOne): UntypedNumericOne$Wire { + return { + first_value: value.firstValue, + revision: value.revision, + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/UntypedNumericTwo.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/UntypedNumericTwo.ts.golden new file mode 100644 index 000000000..0ebd0aa68 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/UntypedNumericTwo.ts.golden @@ -0,0 +1,28 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface UntypedNumericTwo$Wire { + readonly revision: string; + readonly second_value?: string; +} + +export interface UntypedNumericTwo { + readonly revision: string; + readonly secondValue?: string; +} + +export function untypedNumericTwoFromJSON(json: UntypedNumericTwo$Wire): UntypedNumericTwo { + return { + revision: json.revision, + secondValue: json.second_value, + }; +} + +export function untypedNumericTwoToJSON(value: UntypedNumericTwo): UntypedNumericTwo$Wire { + return { + revision: value.revision, + second_value: value.secondValue, + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/ValueAlias.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/ValueAlias.ts.golden new file mode 100644 index 000000000..eb42f2ce8 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/ValueAlias.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { convert$0FromJSON, convert$0ToJSON } from '../runtime/valueConversion$0'; +import type { Detail, Detail$Wire } from './Detail'; + +export type ValueAlias$Wire = readonly (globalThis.Record | string)[]; + +export type ValueAlias = readonly (globalThis.Record | string)[]; + +export function valueAliasFromJSON(json: ValueAlias$Wire): ValueAlias { + return convert$0FromJSON(json); +} + +export function valueAliasToJSON(value: ValueAlias): ValueAlias$Wire { + return convert$0ToJSON(value); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/WrappedArrayOrBoolean.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/WrappedArrayOrBoolean.ts.golden new file mode 100644 index 000000000..2ed8ed556 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/WrappedArrayOrBoolean.ts.golden @@ -0,0 +1,51 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$5FromJSON, + matches$5ToJSON, + matches$92FromJSON, + matches$92ToJSON, +} from '../runtime/unionMatch'; +import type { ArrayWrapped, ArrayWrapped$Wire } from './ArrayWrapped'; +import { arrayWrappedFromJSON, arrayWrappedToJSON } from './ArrayWrapped'; + +export type WrappedArrayOrBoolean$Wire = ArrayWrapped$Wire | boolean; + +export type WrappedArrayOrBoolean = ArrayWrapped | boolean; + +export function wrappedArrayOrBooleanFromJSON( + json: WrappedArrayOrBoolean$Wire, +): WrappedArrayOrBoolean { + const candidate: unknown = json; + const matches = [matches$92FromJSON(candidate), matches$5FromJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous WrappedArrayOrBoolean untagged union value'); + } + if (matches[0]) { + return arrayWrappedFromJSON(json as unknown as ArrayWrapped$Wire); + } + if (matches[1]) { + return json as unknown as boolean; + } + throw new TypeError('No matching WrappedArrayOrBoolean untagged union member'); +} + +export function wrappedArrayOrBooleanToJSON( + value: WrappedArrayOrBoolean, +): WrappedArrayOrBoolean$Wire { + const candidate: unknown = value; + const matches = [matches$92ToJSON(candidate), matches$5ToJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous WrappedArrayOrBoolean untagged union value'); + } + if (matches[0]) { + return arrayWrappedToJSON(value as unknown as ArrayWrapped); + } + if (matches[1]) { + return value as unknown as boolean; + } + throw new TypeError('No matching WrappedArrayOrBoolean untagged union member'); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/WrappedMapOrBoolean.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/WrappedMapOrBoolean.ts.golden new file mode 100644 index 000000000..97ca5e93a --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/WrappedMapOrBoolean.ts.golden @@ -0,0 +1,55 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$1FromJSON, + matches$1ToJSON, + matches$57FromJSON, + matches$57ToJSON, + matches$5FromJSON, + matches$5ToJSON, +} from '../runtime/unionMatch'; +import type { MapWrapped, MapWrapped$Wire } from './MapWrapped'; +import { mapWrappedFromJSON, mapWrappedToJSON } from './MapWrapped'; + +export type WrappedMapOrBoolean$Wire = MapWrapped$Wire | boolean; + +export type WrappedMapOrBoolean = MapWrapped | boolean; + +export function wrappedMapOrBooleanFromJSON(json: WrappedMapOrBoolean$Wire): WrappedMapOrBoolean { + const candidate: unknown = json; + const matches = [matches$1FromJSON(candidate), matches$5FromJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous WrappedMapOrBoolean untagged union value'); + } + if (matches[0]) { + return mapWrappedFromJSON(json as unknown as MapWrapped$Wire); + } + if (matches[1]) { + return json as unknown as boolean; + } + if (matches$57FromJSON(candidate)) { + return mapWrappedFromJSON(json as unknown as MapWrapped$Wire); + } + throw new TypeError('No matching WrappedMapOrBoolean untagged union member'); +} + +export function wrappedMapOrBooleanToJSON(value: WrappedMapOrBoolean): WrappedMapOrBoolean$Wire { + const candidate: unknown = value; + const matches = [matches$1ToJSON(candidate), matches$5ToJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous WrappedMapOrBoolean untagged union value'); + } + if (matches[0]) { + return mapWrappedToJSON(value as unknown as MapWrapped); + } + if (matches[1]) { + return value as unknown as boolean; + } + if (matches$57ToJSON(candidate)) { + return mapWrappedToJSON(value as unknown as MapWrapped); + } + throw new TypeError('No matching WrappedMapOrBoolean untagged union member'); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/WrappedScalarInner.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/WrappedScalarInner.ts.golden new file mode 100644 index 000000000..5c0cdd5a3 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/WrappedScalarInner.ts.golden @@ -0,0 +1,42 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { matches$65FromJSON, matches$65ToJSON } from '../runtime/unionMatch'; +import type { NullableScalarInner, NullableScalarInner$Wire } from './NullableScalarInner'; +import { nullableScalarInnerFromJSON, nullableScalarInnerToJSON } from './NullableScalarInner'; + +export type WrappedScalarInner$Wire = NullableScalarInner$Wire | null; + +export type WrappedScalarInner = NullableScalarInner | null; + +export function wrappedScalarInnerFromJSON(json: WrappedScalarInner$Wire): WrappedScalarInner { + const candidate: unknown = json; + const matches = [matches$65FromJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous WrappedScalarInner untagged union value'); + } + if (matches[0]) { + return nullableScalarInnerFromJSON(json as unknown as NullableScalarInner$Wire); + } + if (candidate === null) { + return null; + } + throw new TypeError('No matching WrappedScalarInner untagged union member'); +} + +export function wrappedScalarInnerToJSON(value: WrappedScalarInner): WrappedScalarInner$Wire { + const candidate: unknown = value; + const matches = [matches$65ToJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous WrappedScalarInner untagged union value'); + } + if (matches[0]) { + return nullableScalarInnerToJSON(value as unknown as NullableScalarInner); + } + if (candidate === null) { + return null; + } + throw new TypeError('No matching WrappedScalarInner untagged union member'); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/WrapperChoice.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/WrapperChoice.ts.golden new file mode 100644 index 000000000..cd45ebe12 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/WrapperChoice.ts.golden @@ -0,0 +1,49 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$58FromJSON, + matches$58ToJSON, + matches$61FromJSON, + matches$61ToJSON, +} from '../runtime/unionMatch'; +import type { WrapperOne, WrapperOne$Wire } from './WrapperOne'; +import { wrapperOneFromJSON, wrapperOneToJSON } from './WrapperOne'; +import type { WrapperTwo, WrapperTwo$Wire } from './WrapperTwo'; +import { wrapperTwoFromJSON, wrapperTwoToJSON } from './WrapperTwo'; + +export type WrapperChoice$Wire = WrapperOne$Wire | WrapperTwo$Wire; + +export type WrapperChoice = WrapperOne | WrapperTwo; + +export function wrapperChoiceFromJSON(json: WrapperChoice$Wire): WrapperChoice { + const candidate: unknown = json; + const matches = [matches$58FromJSON(candidate), matches$61FromJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous WrapperChoice untagged union value'); + } + if (matches[0]) { + return wrapperOneFromJSON(json as unknown as WrapperOne$Wire); + } + if (matches[1]) { + return wrapperTwoFromJSON(json as unknown as WrapperTwo$Wire); + } + throw new TypeError('No matching WrapperChoice untagged union member'); +} + +export function wrapperChoiceToJSON(value: WrapperChoice): WrapperChoice$Wire { + const candidate: unknown = value; + const matches = [matches$58ToJSON(candidate), matches$61ToJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous WrapperChoice untagged union value'); + } + if (matches[0]) { + return wrapperOneToJSON(value as unknown as WrapperOne); + } + if (matches[1]) { + return wrapperTwoToJSON(value as unknown as WrapperTwo); + } + throw new TypeError('No matching WrapperChoice untagged union member'); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/WrapperOne.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/WrapperOne.ts.golden new file mode 100644 index 000000000..5d9658c83 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/WrapperOne.ts.golden @@ -0,0 +1,28 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface WrapperOne$Wire { + readonly first_value?: string; + readonly revision: number | null; +} + +export interface WrapperOne { + readonly firstValue?: string; + readonly revision: number | null; +} + +export function wrapperOneFromJSON(json: WrapperOne$Wire): WrapperOne { + return { + firstValue: json.first_value, + revision: json.revision, + }; +} + +export function wrapperOneToJSON(value: WrapperOne): WrapperOne$Wire { + return { + first_value: value.firstValue, + revision: value.revision, + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/WrapperTwo.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/WrapperTwo.ts.golden new file mode 100644 index 000000000..dcca3ef4c --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/WrapperTwo.ts.golden @@ -0,0 +1,28 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface WrapperTwo$Wire { + readonly revision: number | null; + readonly second_value?: string; +} + +export interface WrapperTwo { + readonly revision: number | null; + readonly secondValue?: string; +} + +export function wrapperTwoFromJSON(json: WrapperTwo$Wire): WrapperTwo { + return { + revision: json.revision, + secondValue: json.second_value, + }; +} + +export function wrapperTwoToJSON(value: WrapperTwo): WrapperTwo$Wire { + return { + revision: value.revision, + second_value: value.secondValue, + }; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/index.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/index.ts.golden new file mode 100644 index 000000000..5e37ecbbc --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/models/index.ts.golden @@ -0,0 +1,361 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export type { Adjacent, Adjacent$Wire } from './Adjacent'; +export { adjacentFromJSON, adjacentToJSON } from './Adjacent'; +export type { AdjacentContainer, AdjacentContainer$Wire } from './AdjacentContainer'; +export { adjacentContainerFromJSON, adjacentContainerToJSON } from './AdjacentContainer'; +export type { AdjacentContainerEmpty, AdjacentContainerEmpty$Wire } from './AdjacentContainerEmpty'; +export { + adjacentContainerEmptyFromJSON, + adjacentContainerEmptyToJSON, +} from './AdjacentContainerEmpty'; +export type { AdjacentEmpty, AdjacentEmpty$Wire } from './AdjacentEmpty'; +export { adjacentEmptyFromJSON, adjacentEmptyToJSON } from './AdjacentEmpty'; +export type { AdjacentOrNumber, AdjacentOrNumber$Wire } from './AdjacentOrNumber'; +export { adjacentOrNumberFromJSON, adjacentOrNumberToJSON } from './AdjacentOrNumber'; +export type { AdjacentPrimitive, AdjacentPrimitive$Wire } from './AdjacentPrimitive'; +export { adjacentPrimitiveFromJSON, adjacentPrimitiveToJSON } from './AdjacentPrimitive'; +export type { AdjacentPrimitiveEmpty, AdjacentPrimitiveEmpty$Wire } from './AdjacentPrimitiveEmpty'; +export { + adjacentPrimitiveEmptyFromJSON, + adjacentPrimitiveEmptyToJSON, +} from './AdjacentPrimitiveEmpty'; +export type { AliasChoice, AliasChoice$Wire } from './AliasChoice'; +export { aliasChoiceFromJSON, aliasChoiceToJSON } from './AliasChoice'; +export type { AliasOuter, AliasOuter$Wire } from './AliasOuter'; +export { aliasOuterFromJSON, aliasOuterToJSON } from './AliasOuter'; +export type { + AlreadyNullableEnumParent, + AlreadyNullableEnumParent$Wire, +} from './AlreadyNullableEnumParent'; +export { + alreadyNullableEnumParentFromJSON, + alreadyNullableEnumParentToJSON, +} from './AlreadyNullableEnumParent'; +export type { + AlreadyNullableOuterEnumParent, + AlreadyNullableOuterEnumParent$Wire, +} from './AlreadyNullableOuterEnumParent'; +export { + alreadyNullableOuterEnumParentFromJSON, + alreadyNullableOuterEnumParentToJSON, +} from './AlreadyNullableOuterEnumParent'; +export type { AlternativeTool, AlternativeTool$Wire } from './AlternativeTool'; +export { alternativeToolFromJSON, alternativeToolToJSON } from './AlternativeTool'; +export type { AmbiguousInner, AmbiguousInner$Wire } from './AmbiguousInner'; +export { ambiguousInnerFromJSON, ambiguousInnerToJSON } from './AmbiguousInner'; +export type { AmbiguousMap, AmbiguousMap$Wire } from './AmbiguousMap'; +export { ambiguousMapFromJSON, ambiguousMapToJSON } from './AmbiguousMap'; +export type { AmbiguousMapOuter, AmbiguousMapOuter$Wire } from './AmbiguousMapOuter'; +export { ambiguousMapOuterFromJSON, ambiguousMapOuterToJSON } from './AmbiguousMapOuter'; +export type { AmbiguousOuter, AmbiguousOuter$Wire } from './AmbiguousOuter'; +export { ambiguousOuterFromJSON, ambiguousOuterToJSON } from './AmbiguousOuter'; +export type { AmbiguousTool, AmbiguousTool$Wire } from './AmbiguousTool'; +export { ambiguousToolFromJSON, ambiguousToolToJSON } from './AmbiguousTool'; +export type { ArrayPayload, ArrayPayload$Wire } from './ArrayPayload'; +export { arrayPayloadFromJSON, arrayPayloadToJSON } from './ArrayPayload'; +export type { ArrayPayloadOrBoolean, ArrayPayloadOrBoolean$Wire } from './ArrayPayloadOrBoolean'; +export { + arrayPayloadOrBooleanFromJSON, + arrayPayloadOrBooleanToJSON, +} from './ArrayPayloadOrBoolean'; +export type { ArrayWrapped, ArrayWrapped$Wire } from './ArrayWrapped'; +export { arrayWrappedFromJSON, arrayWrappedToJSON } from './ArrayWrapped'; +export type { BinaryModel, BinaryModel$Wire } from './BinaryModel'; +export { binaryModelFromJSON, binaryModelToJSON } from './BinaryModel'; +export type { BinaryUnion, BinaryUnion$Wire } from './BinaryUnion'; +export { binaryUnionFromJSON, binaryUnionToJSON } from './BinaryUnion'; +export type { ClientTool, ClientTool$Wire } from './ClientTool'; +export { clientToolFromJSON, clientToolToJSON } from './ClientTool'; +export type { + ConstrainedOuterEnumParent, + ConstrainedOuterEnumParent$Wire, +} from './ConstrainedOuterEnumParent'; +export { + constrainedOuterEnumParentFromJSON, + constrainedOuterEnumParentToJSON, +} from './ConstrainedOuterEnumParent'; +export type { ContainerOuterEnum, ContainerOuterEnum$Wire } from './ContainerOuterEnum'; +export { containerOuterEnumFromJSON, containerOuterEnumToJSON } from './ContainerOuterEnum'; +export type { Detail, Detail$Wire } from './Detail'; +export { detailFromJSON, detailToJSON } from './Detail'; +export type { DetailArrayOrObject, DetailArrayOrObject$Wire } from './DetailArrayOrObject'; +export { detailArrayOrObjectFromJSON, detailArrayOrObjectToJSON } from './DetailArrayOrObject'; +export type { DictionaryAlias, DictionaryAlias$Wire } from './DictionaryAlias'; +export { dictionaryAliasFromJSON, dictionaryAliasToJSON } from './DictionaryAlias'; +export type { + EnumFreeOuterEnumParent, + EnumFreeOuterEnumParent$Wire, +} from './EnumFreeOuterEnumParent'; +export { + enumFreeOuterEnumParentFromJSON, + enumFreeOuterEnumParentToJSON, +} from './EnumFreeOuterEnumParent'; +export type { ExplicitNullOverlap, ExplicitNullOverlap$Wire } from './ExplicitNullOverlap'; +export { explicitNullOverlapFromJSON, explicitNullOverlapToJSON } from './ExplicitNullOverlap'; +export type { + ExplicitNullableEnumChoice, + ExplicitNullableEnumChoice$Wire, +} from './ExplicitNullableEnumChoice'; +export { + explicitNullableEnumChoiceFromJSON, + explicitNullableEnumChoiceToJSON, +} from './ExplicitNullableEnumChoice'; +export type { InlineDictionaryValues, InlineDictionaryValues$Wire } from './InlineDictionaryValues'; +export { + inlineDictionaryValuesFromJSON, + inlineDictionaryValuesToJSON, +} from './InlineDictionaryValues'; +export type { InlineMixedValues, InlineMixedValues$Wire } from './InlineMixedValues'; +export { inlineMixedValuesFromJSON, inlineMixedValuesToJSON } from './InlineMixedValues'; +export type { InlineNumericValues, InlineNumericValues$Wire } from './InlineNumericValues'; +export { inlineNumericValuesFromJSON, inlineNumericValuesToJSON } from './InlineNumericValues'; +export type { InlineProperty, InlineProperty$Wire } from './InlineProperty'; +export { inlinePropertyFromJSON, inlinePropertyToJSON } from './InlineProperty'; +export type { MapOuter, MapOuter$Wire } from './MapOuter'; +export { mapOuterFromJSON, mapOuterToJSON } from './MapOuter'; +export type { MapWrapped, MapWrapped$Wire } from './MapWrapped'; +export { mapWrappedFromJSON, mapWrappedToJSON } from './MapWrapped'; +export type { MixedMapA, MixedMapA$Wire } from './MixedMapA'; +export { mixedMapAFromJSON, mixedMapAToJSON } from './MixedMapA'; +export type { MixedMapB, MixedMapB$Wire } from './MixedMapB'; +export { mixedMapBFromJSON, mixedMapBToJSON } from './MixedMapB'; +export type { MixedMapChoice, MixedMapChoice$Wire } from './MixedMapChoice'; +export { mixedMapChoiceFromJSON, mixedMapChoiceToJSON } from './MixedMapChoice'; +export type { NamedContainer, NamedContainer$Wire } from './NamedContainer'; +export { namedContainerFromJSON, namedContainerToJSON } from './NamedContainer'; +export type { NamedValue, NamedValue$Wire } from './NamedValue'; +export { namedValueFromJSON, namedValueToJSON } from './NamedValue'; +export type { NestedDetailLists, NestedDetailLists$Wire } from './NestedDetailLists'; +export { nestedDetailListsFromJSON, nestedDetailListsToJSON } from './NestedDetailLists'; +export type { NestedEnumChoice, NestedEnumChoice$Wire } from './NestedEnumChoice'; +export { nestedEnumChoiceFromJSON, nestedEnumChoiceToJSON } from './NestedEnumChoice'; +export type { NestedEnumNarrow, NestedEnumNarrow$Wire } from './NestedEnumNarrow'; +export { nestedEnumNarrowFromJSON, nestedEnumNarrowToJSON } from './NestedEnumNarrow'; +export type { + NestedExplicitNullOverlap, + NestedExplicitNullOverlap$Wire, +} from './NestedExplicitNullOverlap'; +export { + nestedExplicitNullOverlapFromJSON, + nestedExplicitNullOverlapToJSON, +} from './NestedExplicitNullOverlap'; +export type { + NestedNullableEnumChoice, + NestedNullableEnumChoice$Wire, +} from './NestedNullableEnumChoice'; +export { + nestedNullableEnumChoiceFromJSON, + nestedNullableEnumChoiceToJSON, +} from './NestedNullableEnumChoice'; +export type { + NestedNullableEnumNarrow, + NestedNullableEnumNarrow$Wire, +} from './NestedNullableEnumNarrow'; +export { + nestedNullableEnumNarrowFromJSON, + nestedNullableEnumNarrowToJSON, +} from './NestedNullableEnumNarrow'; +export type { NullableDetail, NullableDetail$Wire } from './NullableDetail'; +export { nullableDetailFromJSON, nullableDetailToJSON } from './NullableDetail'; +export type { NullableEnumParent, NullableEnumParent$Wire } from './NullableEnumParent'; +export { nullableEnumParentFromJSON, nullableEnumParentToJSON } from './NullableEnumParent'; +export type { + NullableInlineEnumParent, + NullableInlineEnumParent$Wire, +} from './NullableInlineEnumParent'; +export { + nullableInlineEnumParentFromJSON, + nullableInlineEnumParentToJSON, +} from './NullableInlineEnumParent'; +export type { NullableMapFallback, NullableMapFallback$Wire } from './NullableMapFallback'; +export { nullableMapFallbackFromJSON, nullableMapFallbackToJSON } from './NullableMapFallback'; +export type { NullableNonNullScalars, NullableNonNullScalars$Wire } from './NullableNonNullScalars'; +export { + nullableNonNullScalarsFromJSON, + nullableNonNullScalarsToJSON, +} from './NullableNonNullScalars'; +export type { + NullableNonNullScalarsItems, + NullableNonNullScalarsItems$Wire, +} from './NullableNonNullScalarsItems'; +export { + nullableNonNullScalarsItemsFromJSON, + nullableNonNullScalarsItemsToJSON, +} from './NullableNonNullScalarsItems'; +export type { NullableNumericChoice, NullableNumericChoice$Wire } from './NullableNumericChoice'; +export { + nullableNumericChoiceFromJSON, + nullableNumericChoiceToJSON, +} from './NullableNumericChoice'; +export type { NullableNumericOne, NullableNumericOne$Wire } from './NullableNumericOne'; +export { nullableNumericOneFromJSON, nullableNumericOneToJSON } from './NullableNumericOne'; +export type { NullableNumericTwo, NullableNumericTwo$Wire } from './NullableNumericTwo'; +export { nullableNumericTwoFromJSON, nullableNumericTwoToJSON } from './NullableNumericTwo'; +export type { NullableOpaque, NullableOpaque$Wire } from './NullableOpaque'; +export { nullableOpaqueFromJSON, nullableOpaqueToJSON } from './NullableOpaque'; +export type { + NullableOpaqueSiblingOuter, + NullableOpaqueSiblingOuter$Wire, +} from './NullableOpaqueSiblingOuter'; +export { + nullableOpaqueSiblingOuterFromJSON, + nullableOpaqueSiblingOuterToJSON, +} from './NullableOpaqueSiblingOuter'; +export type { + NullableOpenExplicitOuter, + NullableOpenExplicitOuter$Wire, +} from './NullableOpenExplicitOuter'; +export { + nullableOpenExplicitOuterFromJSON, + nullableOpenExplicitOuterToJSON, +} from './NullableOpenExplicitOuter'; +export type { NullableOpenInner, NullableOpenInner$Wire } from './NullableOpenInner'; +export { nullableOpenInnerFromJSON, nullableOpenInnerToJSON } from './NullableOpenInner'; +export type { + NullableOpenSiblingOuter, + NullableOpenSiblingOuter$Wire, +} from './NullableOpenSiblingOuter'; +export { + nullableOpenSiblingOuterFromJSON, + nullableOpenSiblingOuterToJSON, +} from './NullableOpenSiblingOuter'; +export type { NullableOverlap, NullableOverlap$Wire } from './NullableOverlap'; +export { nullableOverlapFromJSON, nullableOverlapToJSON } from './NullableOverlap'; +export type { + NullablePlainEnumParent, + NullablePlainEnumParent$Wire, +} from './NullablePlainEnumParent'; +export { + nullablePlainEnumParentFromJSON, + nullablePlainEnumParentToJSON, +} from './NullablePlainEnumParent'; +export type { NullableScalarArray, NullableScalarArray$Wire } from './NullableScalarArray'; +export { nullableScalarArrayFromJSON, nullableScalarArrayToJSON } from './NullableScalarArray'; +export type { NullableScalarInner, NullableScalarInner$Wire } from './NullableScalarInner'; +export { nullableScalarInnerFromJSON, nullableScalarInnerToJSON } from './NullableScalarInner'; +export type { NullableScalarMap, NullableScalarMap$Wire } from './NullableScalarMap'; +export { nullableScalarMapFromJSON, nullableScalarMapToJSON } from './NullableScalarMap'; +export type { NullableScalarParent, NullableScalarParent$Wire } from './NullableScalarParent'; +export { nullableScalarParentFromJSON, nullableScalarParentToJSON } from './NullableScalarParent'; +export type { NullableTool, NullableTool$Wire } from './NullableTool'; +export { nullableToolFromJSON, nullableToolToJSON } from './NullableTool'; +export type { NumericEnumChoice, NumericEnumChoice$Wire } from './NumericEnumChoice'; +export { numericEnumChoiceFromJSON, numericEnumChoiceToJSON } from './NumericEnumChoice'; +export type { NumericOne, NumericOne$Wire } from './NumericOne'; +export { numericOneFromJSON, numericOneToJSON } from './NumericOne'; +export type { NumericTwo, NumericTwo$Wire } from './NumericTwo'; +export { numericTwoFromJSON, numericTwoToJSON } from './NumericTwo'; +export type { OnceNullableOuter, OnceNullableOuter$Wire } from './OnceNullableOuter'; +export { onceNullableOuterFromJSON, onceNullableOuterToJSON } from './OnceNullableOuter'; +export type { OpenAlias, OpenAlias$Wire } from './OpenAlias'; +export { openAliasFromJSON, openAliasToJSON } from './OpenAlias'; +export type { OpenInner, OpenInner$Wire } from './OpenInner'; +export { openInnerFromJSON, openInnerToJSON } from './OpenInner'; +export type { OpenMap, OpenMap$Wire } from './OpenMap'; +export { openMapFromJSON, openMapToJSON } from './OpenMap'; +export type { OpenTool, OpenTool$Wire } from './OpenTool'; +export { openToolFromJSON, openToolToJSON } from './OpenTool'; +export type { OtherDetail, OtherDetail$Wire } from './OtherDetail'; +export { otherDetailFromJSON, otherDetailToJSON } from './OtherDetail'; +export type { Outer, Outer$Wire } from './Outer'; +export { outerFromJSON, outerToJSON } from './Outer'; +export type { PrimitiveArrayChoice, PrimitiveArrayChoice$Wire } from './PrimitiveArrayChoice'; +export { primitiveArrayChoiceFromJSON, primitiveArrayChoiceToJSON } from './PrimitiveArrayChoice'; +export type { PrimitiveMapChoice, PrimitiveMapChoice$Wire } from './PrimitiveMapChoice'; +export { primitiveMapChoiceFromJSON, primitiveMapChoiceToJSON } from './PrimitiveMapChoice'; +export type { RawEnvelope, RawEnvelope$Wire } from './RawEnvelope'; +export { rawEnvelopeFromJSON, rawEnvelopeToJSON } from './RawEnvelope'; +export type { Readonly, Readonly$Wire } from './Readonly'; +export { readonlyFromJSON, readonlyToJSON } from './Readonly'; +export type { Record, Record$Wire } from './Record'; +export { recordFromJSON, recordToJSON } from './Record'; +export type { RecordEmpty, RecordEmpty$Wire } from './RecordEmpty'; +export { recordEmptyFromJSON, recordEmptyToJSON } from './RecordEmpty'; +export type { RecordOrBoolean, RecordOrBoolean$Wire } from './RecordOrBoolean'; +export { recordOrBooleanFromJSON, recordOrBooleanToJSON } from './RecordOrBoolean'; +export type { + RecursiveDictionaryChoice, + RecursiveDictionaryChoice$Wire, +} from './RecursiveDictionaryChoice'; +export { + recursiveDictionaryChoiceFromJSON, + recursiveDictionaryChoiceToJSON, +} from './RecursiveDictionaryChoice'; +export type { RecursiveList, RecursiveList$Wire } from './RecursiveList'; +export { recursiveListFromJSON, recursiveListToJSON } from './RecursiveList'; +export type { + RecursiveOpenDictionary, + RecursiveOpenDictionary$Wire, +} from './RecursiveOpenDictionary'; +export { + recursiveOpenDictionaryFromJSON, + recursiveOpenDictionaryToJSON, +} from './RecursiveOpenDictionary'; +export type { ScalarEnumChoice, ScalarEnumChoice$Wire } from './ScalarEnumChoice'; +export { scalarEnumChoiceFromJSON, scalarEnumChoiceToJSON } from './ScalarEnumChoice'; +export type { SearchTool, SearchTool$Wire } from './SearchTool'; +export { searchToolFromJSON, searchToolToJSON } from './SearchTool'; +export type { TaggedMapA, TaggedMapA$Wire } from './TaggedMapA'; +export { taggedMapAFromJSON, taggedMapAToJSON } from './TaggedMapA'; +export type { TaggedMapB, TaggedMapB$Wire } from './TaggedMapB'; +export { taggedMapBFromJSON, taggedMapBToJSON } from './TaggedMapB'; +export type { TaggedMapChoice, TaggedMapChoice$Wire } from './TaggedMapChoice'; +export { taggedMapChoiceFromJSON, taggedMapChoiceToJSON } from './TaggedMapChoice'; +export type { Tool, Tool$Wire } from './Tool'; +export { toolFromJSON, toolToJSON } from './Tool'; +export type { ToolAlias, ToolAlias$Wire } from './ToolAlias'; +export { toolAliasFromJSON, toolAliasToJSON } from './ToolAlias'; +export type { ToolList, ToolList$Wire } from './ToolList'; +export { toolListFromJSON, toolListToJSON } from './ToolList'; +export type { ToolMap, ToolMap$Wire } from './ToolMap'; +export { toolMapFromJSON, toolMapToJSON } from './ToolMap'; +export type { ToolRequest, ToolRequest$Wire } from './ToolRequest'; +export { toolRequestFromJSON, toolRequestToJSON } from './ToolRequest'; +export type { TwiceNullableOpaque, TwiceNullableOpaque$Wire } from './TwiceNullableOpaque'; +export { twiceNullableOpaqueFromJSON, twiceNullableOpaqueToJSON } from './TwiceNullableOpaque'; +export type { TwiceNullableOuter, TwiceNullableOuter$Wire } from './TwiceNullableOuter'; +export { twiceNullableOuterFromJSON, twiceNullableOuterToJSON } from './TwiceNullableOuter'; +export type { UntypedNumericChoice, UntypedNumericChoice$Wire } from './UntypedNumericChoice'; +export { untypedNumericChoiceFromJSON, untypedNumericChoiceToJSON } from './UntypedNumericChoice'; +export type { UntypedNumericOne, UntypedNumericOne$Wire } from './UntypedNumericOne'; +export { untypedNumericOneFromJSON, untypedNumericOneToJSON } from './UntypedNumericOne'; +export type { UntypedNumericTwo, UntypedNumericTwo$Wire } from './UntypedNumericTwo'; +export { untypedNumericTwoFromJSON, untypedNumericTwoToJSON } from './UntypedNumericTwo'; +export type { ValueAlias, ValueAlias$Wire } from './ValueAlias'; +export { valueAliasFromJSON, valueAliasToJSON } from './ValueAlias'; +export type { WrappedArrayOrBoolean, WrappedArrayOrBoolean$Wire } from './WrappedArrayOrBoolean'; +export { + wrappedArrayOrBooleanFromJSON, + wrappedArrayOrBooleanToJSON, +} from './WrappedArrayOrBoolean'; +export type { WrappedMapOrBoolean, WrappedMapOrBoolean$Wire } from './WrappedMapOrBoolean'; +export { wrappedMapOrBooleanFromJSON, wrappedMapOrBooleanToJSON } from './WrappedMapOrBoolean'; +export type { WrappedScalarInner, WrappedScalarInner$Wire } from './WrappedScalarInner'; +export { wrappedScalarInnerFromJSON, wrappedScalarInnerToJSON } from './WrappedScalarInner'; +export type { WrapperChoice, WrapperChoice$Wire } from './WrapperChoice'; +export { wrapperChoiceFromJSON, wrapperChoiceToJSON } from './WrapperChoice'; +export type { WrapperOne, WrapperOne$Wire } from './WrapperOne'; +export { wrapperOneFromJSON, wrapperOneToJSON } from './WrapperOne'; +export type { WrapperTwo, WrapperTwo$Wire } from './WrapperTwo'; +export { wrapperTwoFromJSON, wrapperTwoToJSON } from './WrapperTwo'; +export type { + ExchangeMultipartRequest, + ExchangeMultipartRequest$Wire, +} from './ExchangeMultipartRequest'; +export { + exchangeMultipartRequestFromJSON, + exchangeMultipartRequestToJSON, +} from './ExchangeMultipartRequest'; +export type { + SendOptionalInlineScalarRequest, + SendOptionalInlineScalarRequest$Wire, +} from './SendOptionalInlineScalarRequest'; +export { + sendOptionalInlineScalarRequestFromJSON, + sendOptionalInlineScalarRequestToJSON, +} from './SendOptionalInlineScalarRequest'; +export type { ExchangeMultipartMultipartRequestBody } from './ExchangeMultipartMultipartRequestBody'; diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/package.json.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/package.json.golden new file mode 100644 index 000000000..cef8d7580 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/package.json.golden @@ -0,0 +1,32 @@ +{ + "name": "untagged-union-conversion", + "version": "1.0.0", + "description": "TypeScript client for Untagged union conversion", + "keywords": [ + "api-client", + "generated", + "openapi", + "typescript" + ], + "files": [ + "dist" + ], + "type": "module", + "main": "./dist/index.mjs", + "types": "./dist/index.d.mts", + "exports": { + ".": { + "default": "./dist/index.mjs", + "types": "./dist/index.d.mts" + } + }, + "scripts": { + "build": "vp pack", + "check": "vp check", + "fmt": "vp fmt" + }, + "devDependencies": { + "typescript": "^7.0.2", + "vite-plus": "^1.5.0" + } +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/runtime.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/runtime.ts.golden new file mode 100644 index 000000000..084a11e06 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/runtime.ts.golden @@ -0,0 +1,667 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export const BASE_PATH = 'http://localhost'.replace(/\/+$/, ''); + +export interface ConfigurationParameters { + basePath?: string; // override base path + fetchApi?: FetchAPI; // override for fetch implementation + middleware?: Middleware[]; // middleware to apply before/after fetch requests + queryParamsStringify?: (params: HTTPQuery) => string; // stringify function for query strings + username?: string | (() => string | Promise); // parameter for basic security + password?: string | (() => string | Promise); // parameter for basic security + apiKey?: string | Promise | ((name: string) => string | Promise); // parameter for apiKey security + accessToken?: + | string + | Promise + | ((name?: string, scopes?: string[]) => string | Promise); // parameter for oauth2 security + headers?: HTTPHeaders; //header params we want to use on every request + credentials?: RequestCredentials; //value for the credentials param we want to use on each request +} + +export class Configuration { + private configuration: ConfigurationParameters; + + constructor(configuration: ConfigurationParameters = {}) { + this.configuration = configuration; + } + + set config(configuration: ConfigurationParameters) { + this.configuration = configuration; + } + + get basePath(): string { + return this.configuration.basePath != null ? this.configuration.basePath : BASE_PATH; + } + + get fetchApi(): FetchAPI | undefined { + return this.configuration.fetchApi; + } + + get middleware(): Middleware[] { + return this.configuration.middleware || []; + } + + get queryParamsStringify(): (params: HTTPQuery) => string { + return this.configuration.queryParamsStringify || querystring; + } + + get username(): (() => string | Promise) | undefined { + const username = this.configuration.username; + if (username) { + return typeof username === 'function' ? username : async () => username; + } + return undefined; + } + + get password(): (() => string | Promise) | undefined { + const password = this.configuration.password; + if (password) { + return typeof password === 'function' ? password : async () => password; + } + return undefined; + } + + get apiKey(): ((name: string) => string | Promise) | undefined { + const apiKey = this.configuration.apiKey; + if (apiKey) { + return typeof apiKey === 'function' ? apiKey : () => apiKey; + } + return undefined; + } + + get accessToken(): ((name?: string, scopes?: string[]) => string | Promise) | undefined { + const accessToken = this.configuration.accessToken; + if (accessToken) { + return typeof accessToken === 'function' ? accessToken : async () => accessToken; + } + return undefined; + } + + get headers(): HTTPHeaders | undefined { + return this.configuration.headers; + } + + get credentials(): RequestCredentials | undefined { + return this.configuration.credentials; + } +} + +export const DefaultConfig = new Configuration(); + +export interface UploadFile { + data: Blob; + filename?: string; +} + +export type UploadFileInput = Blob | File | UploadFile; + +export function uploadFileData(value: UploadFileInput): Blob { + return isUploadFile(value) ? value.data : value; +} + +export function uploadFileFilename(value: UploadFileInput, fallback: string): string { + if (isFile(value) && value.name) { + return value.name; + } + if (isUploadFile(value) && value.filename) { + return value.filename; + } + return fallback; +} + +export function multipartHeaderValue(value: string): string { + return value + .replace(/[\r\n]/g, '') + .replace(/\\/g, '\\\\') + .replace(/"/g, '\\"'); +} + +function isUploadFile(value: UploadFileInput): value is UploadFile { + return typeof value === 'object' && value !== null && 'data' in value; +} + +function isFile(value: UploadFileInput): value is File { + return typeof File !== 'undefined' && value instanceof File; +} + +/** + * This is the base class for all generated API classes. + */ +export class BaseAPI { + private static readonly jsonRegex = new RegExp( + '^(:?application/json|[^;/ \t]+/[^;/ \t]+[+]json)[ \t]*(:?;.*)?$', + 'i', + ); + protected configuration: Configuration; + private middleware: Middleware[]; + + constructor(configuration: Configuration = DefaultConfig) { + this.configuration = configuration; + this.middleware = configuration.middleware; + } + + withMiddleware(this: T, ...middlewares: Middleware[]) { + const next = this.clone(); + next.middleware = next.middleware.concat(...middlewares); + return next; + } + + withPreMiddleware(this: T, ...preMiddlewares: Array) { + const middlewares = preMiddlewares.map((pre) => ({ pre })); + return this.withMiddleware(...middlewares); + } + + withPostMiddleware(this: T, ...postMiddlewares: Array) { + const middlewares = postMiddlewares.map((post) => ({ post })); + return this.withMiddleware(...middlewares); + } + + /** + * Check if the given MIME is a JSON MIME. + * JSON MIME examples: + * application/json + * application/json; charset=UTF8 + * APPLICATION/JSON + * application/vnd.company+json + * @param mime - MIME (Multipurpose Internet Mail Extensions) + * @return True if the given MIME is JSON, false otherwise. + */ + protected isJsonMime(mime: string | null | undefined): boolean { + if (!mime) { + return false; + } + return BaseAPI.jsonRegex.test(mime); + } + + protected async request( + context: RequestOpts, + initOverrides?: RequestInit | InitOverrideFunction, + ): Promise { + const { url, init } = await this.createFetchParams(context, initOverrides); + return await this.fetchApi(url, init); + } + + private async createFetchParams( + context: RequestOpts, + initOverrides?: RequestInit | InitOverrideFunction, + ) { + let url = this.configuration.basePath + context.path; + if (context.query !== undefined && Object.keys(context.query).length !== 0) { + // only add the querystring to the URL if there are query parameters. + // this is done to avoid urls ending with a "?" character which buggy webservers + // do not handle correctly sometimes. + url += '?' + this.configuration.queryParamsStringify(context.query); + } + + const headers = Object.assign({}, this.configuration.headers, context.headers); + Object.keys(headers).forEach((key) => (headers[key] === undefined ? delete headers[key] : {})); + + const token = await this.configuration.accessToken?.(); + if (token) { + headers['Authorization'] = `Bearer ${token}`; + } + + if (!headers['Authorization']) { + const username = await this.configuration.username?.(); + const password = await this.configuration.password?.(); + if (username && password) { + headers['Authorization'] = `Basic ${btoa(`${username}:${password}`)}`; + } + } + + const initOverrideFn = + typeof initOverrides === 'function' ? initOverrides : async () => initOverrides; + + const initParams: HTTPRequestInit = { + method: context.method, + headers, + body: context.body, + credentials: this.configuration.credentials, + signal: context.signal, + }; + + const overriddenInit = { + ...initParams, + ...(await initOverrideFn({ + init: initParams, + context, + })), + }; + + let body: BodyInit | null | undefined; + if ( + isFormData(overriddenInit.body) || + overriddenInit.body instanceof URLSearchParams || + isBlob(overriddenInit.body) + ) { + body = overriddenInit.body; + } else if (this.isJsonMime(headers['Content-Type'])) { + body = JSON.stringify(overriddenInit.body); + } else { + body = overriddenInit.body as BodyInit | null | undefined; + } + + const init: RequestInit = { + ...overriddenInit, + body, + }; + + return { url, init }; + } + + private fetchApi = async (url: string, init: RequestInit) => { + let fetchParams = { url, init }; + for (const middleware of this.middleware) { + if (middleware.pre) { + fetchParams = + (await middleware.pre({ + fetch: this.fetchApi, + ...fetchParams, + })) || fetchParams; + } + } + let response: Response | undefined = undefined; + try { + response = await (this.configuration.fetchApi || fetch)(fetchParams.url, fetchParams.init); + } catch (e) { + for (const middleware of this.middleware) { + if (middleware.onError) { + response = + (await middleware.onError({ + fetch: this.fetchApi, + url: fetchParams.url, + init: fetchParams.init, + error: e, + response: response ? response.clone() : undefined, + })) || response; + } + } + if (response === undefined) { + if (e instanceof Error) { + throw new FetchError( + e, + 'The request failed and the interceptors did not return an alternative response', + ); + } else { + throw e; + } + } + } + for (const middleware of this.middleware) { + if (middleware.post) { + response = + (await middleware.post({ + fetch: this.fetchApi, + url: fetchParams.url, + init: fetchParams.init, + response: response.clone(), + })) || response; + } + } + return response; + }; + + /** + * Create a shallow clone of `this` by constructing a new instance + * and then shallow cloning data members. + */ + private clone(this: T): T { + const constructor = this.constructor as new (configuration: Configuration) => T; + const next = new constructor(this.configuration); + next.middleware = this.middleware.slice(); + return next; + } +} + +function isBlob(value: unknown): value is Blob { + return typeof Blob !== 'undefined' && value instanceof Blob; +} + +function isFormData(value: unknown): value is FormData { + return typeof FormData !== 'undefined' && value instanceof FormData; +} + +export class ResponseError extends Error { + override name = 'ResponseError' as const; + public response: Response; + + constructor(response: Response, msg?: string) { + super(msg); + this.response = response; + } +} + +export class FetchError extends Error { + override name = 'FetchError' as const; + public cause: Error; + + constructor(cause: Error, msg?: string) { + super(msg); + this.cause = cause; + } +} + +export class RequiredError extends Error { + override name = 'RequiredError' as const; + public field: string; + + constructor(field: string, msg?: string) { + super(msg); + this.field = field; + } +} + +export type FetchAPI = WindowOrWorkerGlobalScope['fetch']; + +export type Json = unknown; +export type HTTPMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS' | 'HEAD'; +export type HTTPHeaders = { [key: string]: string }; +export type HTTPQuery = { + [key: string]: + | string + | number + | null + | boolean + | Array + | Set + | HTTPQuery; +}; +export type HTTPBody = Json | Blob | FormData | URLSearchParams; // eslint-disable-line @typescript-eslint/no-redundant-type-constituents +export type HTTPRequestInit = { + headers?: HTTPHeaders; + method: HTTPMethod; + credentials?: RequestCredentials; + body?: HTTPBody; + signal?: AbortSignal; +}; +export type ModelPropertyNaming = 'camelCase' | 'snake_case' | 'PascalCase' | 'original'; + +export type InitOverrideFunction = (requestContext: { + init: HTTPRequestInit; + context: RequestOpts; +}) => Promise; + +export interface FetchParams { + url: string; + init: RequestInit; +} + +export interface RequestOpts { + path: string; + method: HTTPMethod; + headers: HTTPHeaders; + query?: HTTPQuery; + body?: HTTPBody; + signal?: AbortSignal; +} + +export function querystring(params: HTTPQuery, prefix: string = ''): string { + return Object.keys(params) + .map((key) => querystringSingleKey(key, params[key], prefix)) + .filter((part) => part.length > 0) + .join('&'); +} + +function querystringSingleKey( + key: string, + value: + | string + | number + | null + | undefined + | boolean + | Array + | Set + | HTTPQuery, + keyPrefix: string = '', +): string { + const fullKey = keyPrefix + (keyPrefix.length ? `[${key}]` : key); + if (value instanceof Array) { + const multiValue = value + .map((singleValue) => encodeURIComponent(String(singleValue))) + .join(`&${encodeURIComponent(fullKey)}=`); + return `${encodeURIComponent(fullKey)}=${multiValue}`; + } + if (value instanceof Set) { + const valueAsArray = Array.from(value); + return querystringSingleKey(key, valueAsArray, keyPrefix); + } + if (value instanceof Date) { + return `${encodeURIComponent(fullKey)}=${encodeURIComponent(value.toISOString())}`; + } + if (value instanceof Object) { + return querystring(value as HTTPQuery, fullKey); + } + return `${encodeURIComponent(fullKey)}=${encodeURIComponent(String(value))}`; +} + +export function exists(json: Record, key: string): boolean { + const value = json[key]; + return value !== null && value !== undefined; +} + +export function mapValues(data: Record, fn: (item: T) => U): Record { + const result: Record = {}; + for (const key of Object.keys(data)) { + result[key] = fn(data[key]); + } + return result; +} + +export function canConsumeForm(consumes: Consume[]): boolean { + for (const consume of consumes) { + if ('multipart/form-data' === consume.contentType) { + return true; + } + } + return false; +} + +export interface Consume { + contentType: string; +} + +export interface RequestContext { + fetch: FetchAPI; + url: string; + init: RequestInit; +} + +export interface ResponseContext { + fetch: FetchAPI; + url: string; + init: RequestInit; + response: Response; +} + +export interface ErrorContext { + fetch: FetchAPI; + url: string; + init: RequestInit; + error: unknown; + response?: Response; +} + +export interface Middleware { + pre?(context: RequestContext): Promise; + post?(context: ResponseContext): Promise; + onError?(context: ErrorContext): Promise; +} + +export interface ApiResponse { + raw: Response; + value(): Promise; +} + +export type HttpSuccessStatus = + | 200 + | 201 + | 202 + | 203 + | 204 + | 205 + | 206 + | 207 + | 208 + | 209 + | 210 + | 211 + | 212 + | 213 + | 214 + | 215 + | 216 + | 217 + | 218 + | 219 + | 220 + | 221 + | 222 + | 223 + | 224 + | 225 + | 226 + | 227 + | 228 + | 229 + | 230 + | 231 + | 232 + | 233 + | 234 + | 235 + | 236 + | 237 + | 238 + | 239 + | 240 + | 241 + | 242 + | 243 + | 244 + | 245 + | 246 + | 247 + | 248 + | 249 + | 250 + | 251 + | 252 + | 253 + | 254 + | 255 + | 256 + | 257 + | 258 + | 259 + | 260 + | 261 + | 262 + | 263 + | 264 + | 265 + | 266 + | 267 + | 268 + | 269 + | 270 + | 271 + | 272 + | 273 + | 274 + | 275 + | 276 + | 277 + | 278 + | 279 + | 280 + | 281 + | 282 + | 283 + | 284 + | 285 + | 286 + | 287 + | 288 + | 289 + | 290 + | 291 + | 292 + | 293 + | 294 + | 295 + | 296 + | 297 + | 298 + | 299; + +export type HttpSuccessResponse = { status: HttpSuccessStatus }; + +export interface ResponseTransformer { + (json: unknown): T; +} + +class ApiResponseBase { + public readonly status: number; + public readonly ok: boolean; + public readonly statusText: string; + public readonly headers: Headers; + public readonly raw: Response; + + constructor(raw: Response) { + this.raw = raw; + this.status = raw.status; + this.ok = raw.ok; + this.statusText = raw.statusText; + this.headers = raw.headers; + } +} + +export class JSONApiResponse extends ApiResponseBase { + private transformer: ResponseTransformer; + + constructor( + raw: Response, + transformer: ResponseTransformer = (jsonValue: unknown) => jsonValue as T, + ) { + super(raw); + this.transformer = transformer; + } + + async value(): Promise { + return this.transformer(await this.raw.json()); + } +} + +export class VoidApiResponse extends ApiResponseBase { + constructor(raw: Response) { + super(raw); + } + + async value(): Promise { + return undefined; + } +} + +export class BlobApiResponse extends ApiResponseBase { + constructor(raw: Response) { + super(raw); + } + + async value(): Promise { + return await this.raw.blob(); + } +} + +export class TextApiResponse extends ApiResponseBase { + constructor(raw: Response) { + super(raw); + } + + async value(): Promise { + return await this.raw.text(); + } +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/unionMatch.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/unionMatch.ts.golden new file mode 100644 index 000000000..db847f09b --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/unionMatch.ts.golden @@ -0,0 +1,1884 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export function matches$0FromJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + return Object.values(candidate).every(matches$1FromJSON); +} + +export function matches$0ToJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + return Object.values(candidate).every(matches$1ToJSON); +} + +export function matches$1FromJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (!Object.prototype.hasOwnProperty.call(candidate, 'wire_name')) { + return false; + } + if (!matches$2FromJSON(object['wire_name'])) { + return false; + } + return true; +} + +export function matches$1ToJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (!Object.prototype.hasOwnProperty.call(candidate, 'wireName')) { + return false; + } + if (!matches$2ToJSON(object['wireName'])) { + return false; + } + return true; +} + +export function matches$2FromJSON(candidate: unknown): boolean { + return typeof candidate === 'string'; +} + +export function matches$2ToJSON(candidate: unknown): boolean { + return typeof candidate === 'string'; +} + +export function matches$3FromJSON(candidate: unknown): boolean { + return typeof candidate === 'number' && Number.isInteger(candidate); +} + +export function matches$3ToJSON(candidate: unknown): boolean { + return typeof candidate === 'number' && Number.isInteger(candidate); +} + +export function matches$4FromJSON(candidate: unknown): boolean { + return typeof candidate === 'number' && Number.isFinite(candidate); +} + +export function matches$4ToJSON(candidate: unknown): boolean { + return typeof candidate === 'number' && Number.isFinite(candidate); +} + +export function matches$5FromJSON(candidate: unknown): boolean { + return typeof candidate === 'boolean'; +} + +export function matches$5ToJSON(candidate: unknown): boolean { + return typeof candidate === 'boolean'; +} + +export function matches$6FromJSON(candidate: unknown): boolean { + if (matches$7FromJSON(candidate)) { + return true; + } + if (matches$9FromJSON(candidate)) { + return true; + } + return false; +} + +export function matches$6ToJSON(candidate: unknown): boolean { + if (matches$7ToJSON(candidate)) { + return true; + } + if (matches$9ToJSON(candidate)) { + return true; + } + return false; +} + +export function matches$7FromJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (!Object.prototype.hasOwnProperty.call(candidate, 'event_type')) { + return false; + } + if (!matches$8FromJSON(object['event_type'])) { + return false; + } + return true; +} + +export function matches$7ToJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (!Object.prototype.hasOwnProperty.call(candidate, 'eventType')) { + return false; + } + if (!matches$8ToJSON(object['eventType'])) { + return false; + } + return true; +} + +export function matches$8FromJSON(candidate: unknown): boolean { + return candidate === 'empty'; +} + +export function matches$8ToJSON(candidate: unknown): boolean { + return candidate === 'empty'; +} + +export function matches$9FromJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (!Object.prototype.hasOwnProperty.call(candidate, 'event_type')) { + return false; + } + if (!matches$10FromJSON(object['event_type'])) { + return false; + } + if (!Object.prototype.hasOwnProperty.call(candidate, 'event_data')) { + return false; + } + if (!matches$1FromJSON(object['event_data'])) { + return false; + } + return true; +} + +export function matches$9ToJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (!Object.prototype.hasOwnProperty.call(candidate, 'eventType')) { + return false; + } + if (!matches$10ToJSON(object['eventType'])) { + return false; + } + if (!Object.prototype.hasOwnProperty.call(candidate, 'eventData')) { + return false; + } + if (!matches$1ToJSON(object['eventData'])) { + return false; + } + return true; +} + +export function matches$10FromJSON(candidate: unknown): boolean { + return candidate === 'data'; +} + +export function matches$10ToJSON(candidate: unknown): boolean { + return candidate === 'data'; +} + +export function matches$11FromJSON(candidate: unknown): boolean { + if (!Array.isArray(candidate)) { + return false; + } + return candidate.every(matches$12FromJSON); +} + +export function matches$11ToJSON(candidate: unknown): boolean { + if (!Array.isArray(candidate)) { + return false; + } + return candidate.every(matches$12ToJSON); +} + +export function matches$12FromJSON(candidate: unknown): boolean { + if (matches$0FromJSON(candidate)) { + return true; + } + if (matches$2FromJSON(candidate)) { + return true; + } + return false; +} + +export function matches$12ToJSON(candidate: unknown): boolean { + if (matches$0ToJSON(candidate)) { + return true; + } + if (matches$2ToJSON(candidate)) { + return true; + } + return false; +} + +export function matches$13FromJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (Object.prototype.hasOwnProperty.call(candidate, 'max_uses')) { + const item = object['max_uses']; + if (item !== undefined && !matches$3FromJSON(item)) { + return false; + } + } + if (!Object.prototype.hasOwnProperty.call(candidate, 'name')) { + return false; + } + if (!matches$14FromJSON(object['name'])) { + return false; + } + if (!Object.prototype.hasOwnProperty.call(candidate, 'type')) { + return false; + } + if (!matches$15FromJSON(object['type'])) { + return false; + } + return true; +} + +export function matches$13ToJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (Object.prototype.hasOwnProperty.call(candidate, 'maxUses')) { + const item = object['maxUses']; + if (item !== undefined && !matches$3ToJSON(item)) { + return false; + } + } + if (!Object.prototype.hasOwnProperty.call(candidate, 'name')) { + return false; + } + if (!matches$14ToJSON(object['name'])) { + return false; + } + if (!Object.prototype.hasOwnProperty.call(candidate, 'type')) { + return false; + } + if (!matches$15ToJSON(object['type'])) { + return false; + } + return true; +} + +export function matches$14FromJSON(candidate: unknown): boolean { + return candidate === 'web_search'; +} + +export function matches$14ToJSON(candidate: unknown): boolean { + return candidate === 'web_search'; +} + +export function matches$15FromJSON(candidate: unknown): boolean { + return candidate === 'web_search_20250305'; +} + +export function matches$15ToJSON(candidate: unknown): boolean { + return candidate === 'web_search_20250305'; +} + +export function matches$17FromJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (Object.prototype.hasOwnProperty.call(candidate, 'optional_flag')) { + const item = object['optional_flag']; + if (item !== undefined && !matches$5FromJSON(item)) { + return false; + } + } + if (!Object.prototype.hasOwnProperty.call(candidate, 'wire_name')) { + return false; + } + if (!matches$2FromJSON(object['wire_name'])) { + return false; + } + return true; +} + +export function matches$17ToJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (Object.prototype.hasOwnProperty.call(candidate, 'optionalFlag')) { + const item = object['optionalFlag']; + if (item !== undefined && !matches$5ToJSON(item)) { + return false; + } + } + if (!Object.prototype.hasOwnProperty.call(candidate, 'wireName')) { + return false; + } + if (!matches$2ToJSON(object['wireName'])) { + return false; + } + return true; +} + +export function matches$18FromJSON(candidate: unknown): boolean { + if (!matches$19FromJSON(candidate)) { + return false; + } + return Object.values(candidate as globalThis.Record).some(matches$21FromJSON); +} + +export function matches$18ToJSON(candidate: unknown): boolean { + if (!matches$19ToJSON(candidate)) { + return false; + } + return Object.values(candidate as globalThis.Record).some(matches$21ToJSON); +} + +export function matches$19FromJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + return true; +} + +export function matches$19ToJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + return true; +} + +export function matches$21FromJSON(candidate: unknown): boolean { + if (matches$1FromJSON(candidate)) { + return true; + } + if (matches$17FromJSON(candidate)) { + return true; + } + return false; +} + +export function matches$21ToJSON(candidate: unknown): boolean { + if (matches$1ToJSON(candidate)) { + return true; + } + if (matches$17ToJSON(candidate)) { + return true; + } + return false; +} + +export function matches$22FromJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + return true; +} + +export function matches$22ToJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + return true; +} + +export function matches$23FromJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (!Object.prototype.hasOwnProperty.call(candidate, 'input_schema')) { + return false; + } + if (!matches$22FromJSON(object['input_schema'])) { + return false; + } + if (!Object.prototype.hasOwnProperty.call(candidate, 'name')) { + return false; + } + if (!matches$2FromJSON(object['name'])) { + return false; + } + return true; +} + +export function matches$23ToJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (!Object.prototype.hasOwnProperty.call(candidate, 'inputSchema')) { + return false; + } + if (!matches$22ToJSON(object['inputSchema'])) { + return false; + } + if (!Object.prototype.hasOwnProperty.call(candidate, 'name')) { + return false; + } + if (!matches$2ToJSON(object['name'])) { + return false; + } + return true; +} + +export function matches$24FromJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (!Object.prototype.hasOwnProperty.call(candidate, 'input_schema')) { + return false; + } + if (!matches$22FromJSON(object['input_schema'])) { + return false; + } + if (!Object.prototype.hasOwnProperty.call(candidate, 'name')) { + return false; + } + if (!matches$2FromJSON(object['name'])) { + return false; + } + if (Object.prototype.hasOwnProperty.call(candidate, 'second_option')) { + const item = object['second_option']; + if (item !== undefined && !matches$5FromJSON(item)) { + return false; + } + } + return true; +} + +export function matches$24ToJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (!Object.prototype.hasOwnProperty.call(candidate, 'inputSchema')) { + return false; + } + if (!matches$22ToJSON(object['inputSchema'])) { + return false; + } + if (!Object.prototype.hasOwnProperty.call(candidate, 'name')) { + return false; + } + if (!matches$2ToJSON(object['name'])) { + return false; + } + if (Object.prototype.hasOwnProperty.call(candidate, 'secondOption')) { + const item = object['secondOption']; + if (item !== undefined && !matches$5ToJSON(item)) { + return false; + } + } + return true; +} + +export function matches$25FromJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (!Object.prototype.hasOwnProperty.call(candidate, 'wire_items')) { + return false; + } + if (!matches$26FromJSON(object['wire_items'])) { + return false; + } + return true; +} + +export function matches$25ToJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (!Object.prototype.hasOwnProperty.call(candidate, 'wireItems')) { + return false; + } + if (!matches$26ToJSON(object['wireItems'])) { + return false; + } + return true; +} + +export function matches$26FromJSON(candidate: unknown): boolean { + if (!Array.isArray(candidate)) { + return false; + } + return candidate.every(matches$2FromJSON); +} + +export function matches$26ToJSON(candidate: unknown): boolean { + if (!Array.isArray(candidate)) { + return false; + } + return candidate.every(matches$2ToJSON); +} + +export function matches$27FromJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (!Object.prototype.hasOwnProperty.call(candidate, 'binary_data')) { + return false; + } + if (!matches$28FromJSON(object['binary_data'])) { + return false; + } + if (!Object.prototype.hasOwnProperty.call(candidate, 'field_name')) { + return false; + } + if (!matches$2FromJSON(object['field_name'])) { + return false; + } + return true; +} + +export function matches$27ToJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (!Object.prototype.hasOwnProperty.call(candidate, 'binaryData')) { + return false; + } + if (!matches$28ToJSON(object['binaryData'])) { + return false; + } + if (!Object.prototype.hasOwnProperty.call(candidate, 'fieldName')) { + return false; + } + if (!matches$2ToJSON(object['fieldName'])) { + return false; + } + return true; +} + +export function matches$28FromJSON(candidate: unknown): boolean { + if (typeof Blob !== 'undefined' && candidate instanceof Blob) { + return true; + } + return typeof File !== 'undefined' && candidate instanceof File; +} + +export function matches$28ToJSON(candidate: unknown): boolean { + if (typeof Blob !== 'undefined' && candidate instanceof Blob) { + return true; + } + return typeof File !== 'undefined' && candidate instanceof File; +} + +export function matches$29FromJSON(candidate: unknown): boolean { + if (!Array.isArray(candidate)) { + return false; + } + return candidate.every(matches$1FromJSON); +} + +export function matches$29ToJSON(candidate: unknown): boolean { + if (!Array.isArray(candidate)) { + return false; + } + return candidate.every(matches$1ToJSON); +} + +export function matches$30FromJSON(candidate: unknown): boolean { + if (matches$31FromJSON(candidate)) { + return true; + } + if (matches$1FromJSON(candidate)) { + return true; + } + return false; +} + +export function matches$30ToJSON(candidate: unknown): boolean { + if (matches$31ToJSON(candidate)) { + return true; + } + if (matches$1ToJSON(candidate)) { + return true; + } + return false; +} + +export function matches$31FromJSON(candidate: unknown): boolean { + return candidate === null; +} + +export function matches$31ToJSON(candidate: unknown): boolean { + return candidate === null; +} + +export function matches$32FromJSON(candidate: unknown): boolean { + if (matches$31FromJSON(candidate)) { + return true; + } + if (matches$23FromJSON(candidate)) { + return true; + } + if (matches$13FromJSON(candidate)) { + return true; + } + return false; +} + +export function matches$32ToJSON(candidate: unknown): boolean { + if (matches$31ToJSON(candidate)) { + return true; + } + if (matches$23ToJSON(candidate)) { + return true; + } + if (matches$13ToJSON(candidate)) { + return true; + } + return false; +} + +export function matches$33FromJSON(candidate: unknown): boolean { + if (!Array.isArray(candidate)) { + return false; + } + return candidate.every(matches$34FromJSON); +} + +export function matches$33ToJSON(candidate: unknown): boolean { + if (!Array.isArray(candidate)) { + return false; + } + return candidate.every(matches$34ToJSON); +} + +export function matches$34FromJSON(candidate: unknown): boolean { + return candidate === null || matches$35FromJSON(candidate); +} + +export function matches$34ToJSON(candidate: unknown): boolean { + return candidate === null || matches$35ToJSON(candidate); +} + +export function matches$35FromJSON(candidate: unknown): boolean { + if (!matches$3FromJSON(candidate)) { + return false; + } + if (!matches$36FromJSON(candidate)) { + return false; + } + return true; +} + +export function matches$35ToJSON(candidate: unknown): boolean { + if (!matches$3ToJSON(candidate)) { + return false; + } + if (!matches$36ToJSON(candidate)) { + return false; + } + return true; +} + +export function matches$36FromJSON(candidate: unknown): boolean { + return candidate === 1; +} + +export function matches$36ToJSON(candidate: unknown): boolean { + return candidate === 1; +} + +export function matches$37FromJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + return Object.values(candidate).every(matches$12FromJSON); +} + +export function matches$37ToJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + return Object.values(candidate).every(matches$12ToJSON); +} + +export function matches$38FromJSON(candidate: unknown): boolean { + if (!Array.isArray(candidate)) { + return false; + } + return candidate.every(matches$39FromJSON); +} + +export function matches$38ToJSON(candidate: unknown): boolean { + if (!Array.isArray(candidate)) { + return false; + } + return candidate.every(matches$39ToJSON); +} + +export function matches$39FromJSON(candidate: unknown): boolean { + if (matches$0FromJSON(candidate)) { + return true; + } + if (matches$3FromJSON(candidate)) { + return true; + } + if (matches$4FromJSON(candidate)) { + return true; + } + return false; +} + +export function matches$39ToJSON(candidate: unknown): boolean { + if (matches$0ToJSON(candidate)) { + return true; + } + if (matches$3ToJSON(candidate)) { + return true; + } + if (matches$4ToJSON(candidate)) { + return true; + } + return false; +} + +export function matches$40FromJSON(candidate: unknown): boolean { + if (!matches$41FromJSON(candidate)) { + return false; + } + return Object.values(candidate as globalThis.Record).some(matches$1FromJSON); +} + +export function matches$40ToJSON(candidate: unknown): boolean { + if (!matches$41ToJSON(candidate)) { + return false; + } + return Object.values(candidate as globalThis.Record).some(matches$1ToJSON); +} + +export function matches$41FromJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + return true; +} + +export function matches$41ToJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + return true; +} + +export function matches$43FromJSON(candidate: unknown): boolean { + if (!matches$44FromJSON(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + const declared = ['marker']; + for (const [key, item] of Object.entries(object)) { + if (!declared.includes(key) && !matches$1FromJSON(item)) { + return false; + } + } + return true; +} + +export function matches$43ToJSON(candidate: unknown): boolean { + if (!matches$44ToJSON(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + const declared = ['marker']; + for (const [key, item] of Object.entries(object)) { + if (!declared.includes(key) && !matches$1ToJSON(item)) { + return false; + } + } + return true; +} + +export function matches$44FromJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (!Object.prototype.hasOwnProperty.call(candidate, 'marker')) { + return false; + } + if (!matches$2FromJSON(object['marker'])) { + return false; + } + return true; +} + +export function matches$44ToJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (!Object.prototype.hasOwnProperty.call(candidate, 'marker')) { + return false; + } + if (!matches$2ToJSON(object['marker'])) { + return false; + } + return true; +} + +export function matches$45FromJSON(candidate: unknown): boolean { + if (!matches$44FromJSON(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + const declared = ['marker']; + for (const [key, item] of Object.entries(object)) { + if (!declared.includes(key) && !matches$29FromJSON(item)) { + return false; + } + } + return true; +} + +export function matches$45ToJSON(candidate: unknown): boolean { + if (!matches$44ToJSON(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + const declared = ['marker']; + for (const [key, item] of Object.entries(object)) { + if (!declared.includes(key) && !matches$29ToJSON(item)) { + return false; + } + } + return true; +} + +export function matches$46FromJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (Object.prototype.hasOwnProperty.call(candidate, 'first_value')) { + const item = object['first_value']; + if (item !== undefined && !matches$2FromJSON(item)) { + return false; + } + } + if (!Object.prototype.hasOwnProperty.call(candidate, 'revision')) { + return false; + } + if (!matches$47FromJSON(object['revision'])) { + return false; + } + return true; +} + +export function matches$46ToJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (Object.prototype.hasOwnProperty.call(candidate, 'firstValue')) { + const item = object['firstValue']; + if (item !== undefined && !matches$2ToJSON(item)) { + return false; + } + } + if (!Object.prototype.hasOwnProperty.call(candidate, 'revision')) { + return false; + } + if (!matches$47ToJSON(object['revision'])) { + return false; + } + return true; +} + +export function matches$47FromJSON(candidate: unknown): boolean { + if (!matches$34FromJSON(candidate)) { + return false; + } + if (!matches$48FromJSON(candidate)) { + return false; + } + return true; +} + +export function matches$47ToJSON(candidate: unknown): boolean { + if (!matches$34ToJSON(candidate)) { + return false; + } + if (!matches$48ToJSON(candidate)) { + return false; + } + return true; +} + +export function matches$48FromJSON(candidate: unknown): boolean { + if (matches$36FromJSON(candidate)) { + return true; + } + if (matches$49FromJSON(candidate)) { + return true; + } + return false; +} + +export function matches$48ToJSON(candidate: unknown): boolean { + if (matches$36ToJSON(candidate)) { + return true; + } + if (matches$49ToJSON(candidate)) { + return true; + } + return false; +} + +export function matches$49FromJSON(candidate: unknown): boolean { + return candidate === 2; +} + +export function matches$49ToJSON(candidate: unknown): boolean { + return candidate === 2; +} + +export function matches$50FromJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (!Object.prototype.hasOwnProperty.call(candidate, 'revision')) { + return false; + } + if (!matches$51FromJSON(object['revision'])) { + return false; + } + if (Object.prototype.hasOwnProperty.call(candidate, 'second_value')) { + const item = object['second_value']; + if (item !== undefined && !matches$2FromJSON(item)) { + return false; + } + } + return true; +} + +export function matches$50ToJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (!Object.prototype.hasOwnProperty.call(candidate, 'revision')) { + return false; + } + if (!matches$51ToJSON(object['revision'])) { + return false; + } + if (Object.prototype.hasOwnProperty.call(candidate, 'secondValue')) { + const item = object['secondValue']; + if (item !== undefined && !matches$2ToJSON(item)) { + return false; + } + } + return true; +} + +export function matches$51FromJSON(candidate: unknown): boolean { + if (!matches$3FromJSON(candidate)) { + return false; + } + if (!matches$49FromJSON(candidate)) { + return false; + } + return true; +} + +export function matches$51ToJSON(candidate: unknown): boolean { + if (!matches$3ToJSON(candidate)) { + return false; + } + if (!matches$49ToJSON(candidate)) { + return false; + } + return true; +} + +export function matches$52FromJSON(candidate: unknown): boolean { + if (matches$31FromJSON(candidate)) { + return true; + } + if (matches$1FromJSON(candidate)) { + return true; + } + if (matches$23FromJSON(candidate)) { + return true; + } + if (matches$13FromJSON(candidate)) { + return true; + } + return false; +} + +export function matches$52ToJSON(candidate: unknown): boolean { + if (matches$31ToJSON(candidate)) { + return true; + } + if (matches$1ToJSON(candidate)) { + return true; + } + if (matches$23ToJSON(candidate)) { + return true; + } + if (matches$13ToJSON(candidate)) { + return true; + } + return false; +} + +export function matches$53FromJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (Object.prototype.hasOwnProperty.call(candidate, 'first_value')) { + const item = object['first_value']; + if (item !== undefined && !matches$2FromJSON(item)) { + return false; + } + } + if (!Object.prototype.hasOwnProperty.call(candidate, 'revision')) { + return false; + } + if (!matches$54FromJSON(object['revision'])) { + return false; + } + return true; +} + +export function matches$53ToJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (Object.prototype.hasOwnProperty.call(candidate, 'firstValue')) { + const item = object['firstValue']; + if (item !== undefined && !matches$2ToJSON(item)) { + return false; + } + } + if (!Object.prototype.hasOwnProperty.call(candidate, 'revision')) { + return false; + } + if (!matches$54ToJSON(object['revision'])) { + return false; + } + return true; +} + +export function matches$54FromJSON(candidate: unknown): boolean { + if (!matches$34FromJSON(candidate)) { + return false; + } + if (!matches$55FromJSON(candidate)) { + return false; + } + return true; +} + +export function matches$54ToJSON(candidate: unknown): boolean { + if (!matches$34ToJSON(candidate)) { + return false; + } + if (!matches$55ToJSON(candidate)) { + return false; + } + return true; +} + +export function matches$55FromJSON(candidate: unknown): boolean { + if (matches$36FromJSON(candidate)) { + return true; + } + if (matches$56FromJSON(candidate)) { + return true; + } + return false; +} + +export function matches$55ToJSON(candidate: unknown): boolean { + if (matches$36ToJSON(candidate)) { + return true; + } + if (matches$56ToJSON(candidate)) { + return true; + } + return false; +} + +export function matches$56FromJSON(candidate: unknown): boolean { + return candidate === null; +} + +export function matches$56ToJSON(candidate: unknown): boolean { + return candidate === null; +} + +export function matches$57FromJSON(candidate: unknown): boolean { + if (matches$31FromJSON(candidate)) { + return true; + } + if (matches$22FromJSON(candidate)) { + return true; + } + return false; +} + +export function matches$57ToJSON(candidate: unknown): boolean { + if (matches$31ToJSON(candidate)) { + return true; + } + if (matches$22ToJSON(candidate)) { + return true; + } + return false; +} + +export function matches$58FromJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (Object.prototype.hasOwnProperty.call(candidate, 'first_value')) { + const item = object['first_value']; + if (item !== undefined && !matches$2FromJSON(item)) { + return false; + } + } + if (!Object.prototype.hasOwnProperty.call(candidate, 'revision')) { + return false; + } + if (!matches$59FromJSON(object['revision'])) { + return false; + } + return true; +} + +export function matches$58ToJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (Object.prototype.hasOwnProperty.call(candidate, 'firstValue')) { + const item = object['firstValue']; + if (item !== undefined && !matches$2ToJSON(item)) { + return false; + } + } + if (!Object.prototype.hasOwnProperty.call(candidate, 'revision')) { + return false; + } + if (!matches$59ToJSON(object['revision'])) { + return false; + } + return true; +} + +export function matches$59FromJSON(candidate: unknown): boolean { + if (!matches$60FromJSON(candidate)) { + return false; + } + if (!matches$36FromJSON(candidate)) { + return false; + } + return true; +} + +export function matches$59ToJSON(candidate: unknown): boolean { + if (!matches$60ToJSON(candidate)) { + return false; + } + if (!matches$36ToJSON(candidate)) { + return false; + } + return true; +} + +export function matches$60FromJSON(candidate: unknown): boolean { + return candidate === null || matches$3FromJSON(candidate); +} + +export function matches$60ToJSON(candidate: unknown): boolean { + return candidate === null || matches$3ToJSON(candidate); +} + +export function matches$61FromJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (!Object.prototype.hasOwnProperty.call(candidate, 'revision')) { + return false; + } + if (!matches$62FromJSON(object['revision'])) { + return false; + } + if (Object.prototype.hasOwnProperty.call(candidate, 'second_value')) { + const item = object['second_value']; + if (item !== undefined && !matches$2FromJSON(item)) { + return false; + } + } + return true; +} + +export function matches$61ToJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (!Object.prototype.hasOwnProperty.call(candidate, 'revision')) { + return false; + } + if (!matches$62ToJSON(object['revision'])) { + return false; + } + if (Object.prototype.hasOwnProperty.call(candidate, 'secondValue')) { + const item = object['secondValue']; + if (item !== undefined && !matches$2ToJSON(item)) { + return false; + } + } + return true; +} + +export function matches$62FromJSON(candidate: unknown): boolean { + if (!matches$60FromJSON(candidate)) { + return false; + } + if (!matches$63FromJSON(candidate)) { + return false; + } + return true; +} + +export function matches$62ToJSON(candidate: unknown): boolean { + if (!matches$60ToJSON(candidate)) { + return false; + } + if (!matches$63ToJSON(candidate)) { + return false; + } + return true; +} + +export function matches$63FromJSON(candidate: unknown): boolean { + if (matches$49FromJSON(candidate)) { + return true; + } + if (matches$56FromJSON(candidate)) { + return true; + } + return false; +} + +export function matches$63ToJSON(candidate: unknown): boolean { + if (matches$49ToJSON(candidate)) { + return true; + } + if (matches$56ToJSON(candidate)) { + return true; + } + return false; +} + +export function matches$64FromJSON(candidate: unknown): boolean { + if (matches$31FromJSON(candidate)) { + return true; + } + if (matches$5FromJSON(candidate)) { + return true; + } + return false; +} + +export function matches$64ToJSON(candidate: unknown): boolean { + if (matches$31ToJSON(candidate)) { + return true; + } + if (matches$5ToJSON(candidate)) { + return true; + } + return false; +} + +export function matches$65FromJSON(candidate: unknown): boolean { + if (matches$31FromJSON(candidate)) { + return true; + } + if (matches$2FromJSON(candidate)) { + return true; + } + if (matches$4FromJSON(candidate)) { + return true; + } + return false; +} + +export function matches$65ToJSON(candidate: unknown): boolean { + if (matches$31ToJSON(candidate)) { + return true; + } + if (matches$2ToJSON(candidate)) { + return true; + } + if (matches$4ToJSON(candidate)) { + return true; + } + return false; +} + +export function matches$66FromJSON(candidate: unknown): boolean { + if (matches$31FromJSON(candidate)) { + return true; + } + if (matches$2FromJSON(candidate)) { + return true; + } + return false; +} + +export function matches$66ToJSON(candidate: unknown): boolean { + if (matches$31ToJSON(candidate)) { + return true; + } + if (matches$2ToJSON(candidate)) { + return true; + } + return false; +} + +export function matches$67FromJSON(candidate: unknown): boolean { + if (matches$31FromJSON(candidate)) { + return true; + } + if (matches$4FromJSON(candidate)) { + return true; + } + return false; +} + +export function matches$67ToJSON(candidate: unknown): boolean { + if (matches$31ToJSON(candidate)) { + return true; + } + if (matches$4ToJSON(candidate)) { + return true; + } + return false; +} + +export function matches$68FromJSON(candidate: unknown): boolean { + if (matches$23FromJSON(candidate)) { + return true; + } + if (matches$13FromJSON(candidate)) { + return true; + } + return false; +} + +export function matches$68ToJSON(candidate: unknown): boolean { + if (matches$23ToJSON(candidate)) { + return true; + } + if (matches$13ToJSON(candidate)) { + return true; + } + return false; +} + +export function matches$69FromJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (Object.prototype.hasOwnProperty.call(candidate, 'first_value')) { + const item = object['first_value']; + if (item !== undefined && !matches$2FromJSON(item)) { + return false; + } + } + if (!Object.prototype.hasOwnProperty.call(candidate, 'revision')) { + return false; + } + if (!matches$35FromJSON(object['revision'])) { + return false; + } + return true; +} + +export function matches$69ToJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (Object.prototype.hasOwnProperty.call(candidate, 'firstValue')) { + const item = object['firstValue']; + if (item !== undefined && !matches$2ToJSON(item)) { + return false; + } + } + if (!Object.prototype.hasOwnProperty.call(candidate, 'revision')) { + return false; + } + if (!matches$35ToJSON(object['revision'])) { + return false; + } + return true; +} + +export function matches$70FromJSON(candidate: unknown): boolean { + if (!Array.isArray(candidate)) { + return false; + } + return candidate.every(matches$71FromJSON); +} + +export function matches$70ToJSON(candidate: unknown): boolean { + if (!Array.isArray(candidate)) { + return false; + } + return candidate.every(matches$71ToJSON); +} + +export function matches$71FromJSON(candidate: unknown): boolean { + if (matches$3FromJSON(candidate)) { + return true; + } + if (matches$4FromJSON(candidate)) { + return true; + } + return false; +} + +export function matches$71ToJSON(candidate: unknown): boolean { + if (matches$3ToJSON(candidate)) { + return true; + } + if (matches$4ToJSON(candidate)) { + return true; + } + return false; +} + +export function matches$72FromJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + return Object.values(candidate).every(matches$71FromJSON); +} + +export function matches$72ToJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + return Object.values(candidate).every(matches$71ToJSON); +} + +export function matches$73FromJSON(candidate: unknown): boolean { + if (matches$1FromJSON(candidate)) { + return true; + } + if (matches$74FromJSON(candidate)) { + return true; + } + return false; +} + +export function matches$73ToJSON(candidate: unknown): boolean { + if (matches$1ToJSON(candidate)) { + return true; + } + if (matches$74ToJSON(candidate)) { + return true; + } + return false; +} + +export function matches$74FromJSON(candidate: unknown): boolean { + if (!matches$75FromJSON(candidate)) { + return false; + } + return Object.values(candidate as globalThis.Record).some(matches$77FromJSON); +} + +export function matches$74ToJSON(candidate: unknown): boolean { + if (!matches$75ToJSON(candidate)) { + return false; + } + return Object.values(candidate as globalThis.Record).some(matches$77ToJSON); +} + +export function matches$75FromJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + return true; +} + +export function matches$75ToJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + return true; +} + +export function matches$76FromJSON(candidate: unknown): boolean { + return matchRecursiveOpenDictionaryFromJSON(candidate) > 0; +} + +export function matches$76ToJSON(candidate: unknown): boolean { + return matchRecursiveOpenDictionaryToJSON(candidate) > 0; +} + +export function matches$77FromJSON(candidate: unknown): boolean { + return matchRecursiveOpenDictionaryFromJSON(candidate) === 2; +} + +export function matches$77ToJSON(candidate: unknown): boolean { + return matchRecursiveOpenDictionaryToJSON(candidate) === 2; +} + +export function matches$79FromJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + return Object.values(candidate).every(matches$80FromJSON); +} + +export function matches$79ToJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + return Object.values(candidate).every(matches$80ToJSON); +} + +export function matches$80FromJSON(candidate: unknown): boolean { + return matchRecursiveOpenDictionaryFromJSON(candidate) === 1; +} + +export function matches$80ToJSON(candidate: unknown): boolean { + return matchRecursiveOpenDictionaryToJSON(candidate) === 1; +} + +export function matches$81FromJSON(candidate: unknown): boolean { + if (!Array.isArray(candidate)) { + return false; + } + return candidate.every(matches$82FromJSON); +} + +export function matches$81ToJSON(candidate: unknown): boolean { + if (!Array.isArray(candidate)) { + return false; + } + return candidate.every(matches$82ToJSON); +} + +export function matches$82FromJSON(candidate: unknown): boolean { + return matchRecursiveListFromJSON(candidate) > 0; +} + +export function matches$82ToJSON(candidate: unknown): boolean { + return matchRecursiveListToJSON(candidate) > 0; +} + +export function matches$83FromJSON(candidate: unknown): boolean { + if (!matches$84FromJSON(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + const declared = ['payload_key']; + for (const [key, item] of Object.entries(object)) { + if (!declared.includes(key) && !matches$1FromJSON(item)) { + return false; + } + } + return true; +} + +export function matches$83ToJSON(candidate: unknown): boolean { + if (!matches$84ToJSON(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + const declared = ['payload_key']; + for (const [key, item] of Object.entries(object)) { + if (!declared.includes(key) && !matches$1ToJSON(item)) { + return false; + } + } + return true; +} + +export function matches$84FromJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (!Object.prototype.hasOwnProperty.call(candidate, 'payload_key')) { + return false; + } + if (!matches$1FromJSON(object['payload_key'])) { + return false; + } + return true; +} + +export function matches$84ToJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (!Object.prototype.hasOwnProperty.call(candidate, 'payload_key')) { + return false; + } + if (!matches$1ToJSON(object['payload_key'])) { + return false; + } + return true; +} + +export function matches$85FromJSON(candidate: unknown): boolean { + if (!matches$84FromJSON(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + const declared = ['payload_key']; + for (const [key, item] of Object.entries(object)) { + if (!declared.includes(key) && !matches$29FromJSON(item)) { + return false; + } + } + return true; +} + +export function matches$85ToJSON(candidate: unknown): boolean { + if (!matches$84ToJSON(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + const declared = ['payload_key']; + for (const [key, item] of Object.entries(object)) { + if (!declared.includes(key) && !matches$29ToJSON(item)) { + return false; + } + } + return true; +} + +export function matches$86FromJSON(candidate: unknown): boolean { + if (!Array.isArray(candidate)) { + return false; + } + return candidate.every(matches$23FromJSON); +} + +export function matches$86ToJSON(candidate: unknown): boolean { + if (!Array.isArray(candidate)) { + return false; + } + return candidate.every(matches$23ToJSON); +} + +export function matches$87FromJSON(candidate: unknown): boolean { + if (!Array.isArray(candidate)) { + return false; + } + return candidate.every(matches$13FromJSON); +} + +export function matches$87ToJSON(candidate: unknown): boolean { + if (!Array.isArray(candidate)) { + return false; + } + return candidate.every(matches$13ToJSON); +} + +export function matches$88FromJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + return Object.values(candidate).every(matches$23FromJSON); +} + +export function matches$88ToJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + return Object.values(candidate).every(matches$23ToJSON); +} + +export function matches$89FromJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + return Object.values(candidate).every(matches$13FromJSON); +} + +export function matches$89ToJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + return Object.values(candidate).every(matches$13ToJSON); +} + +export function matches$90FromJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (Object.prototype.hasOwnProperty.call(candidate, 'first_value')) { + const item = object['first_value']; + if (item !== undefined && !matches$2FromJSON(item)) { + return false; + } + } + if (!Object.prototype.hasOwnProperty.call(candidate, 'revision')) { + return false; + } + if (!matches$36FromJSON(object['revision'])) { + return false; + } + return true; +} + +export function matches$90ToJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (Object.prototype.hasOwnProperty.call(candidate, 'firstValue')) { + const item = object['firstValue']; + if (item !== undefined && !matches$2ToJSON(item)) { + return false; + } + } + if (!Object.prototype.hasOwnProperty.call(candidate, 'revision')) { + return false; + } + if (!matches$36ToJSON(object['revision'])) { + return false; + } + return true; +} + +export function matches$91FromJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (!Object.prototype.hasOwnProperty.call(candidate, 'revision')) { + return false; + } + if (!matches$49FromJSON(object['revision'])) { + return false; + } + if (Object.prototype.hasOwnProperty.call(candidate, 'second_value')) { + const item = object['second_value']; + if (item !== undefined && !matches$2FromJSON(item)) { + return false; + } + } + return true; +} + +export function matches$91ToJSON(candidate: unknown): boolean { + if (candidate === null || typeof candidate !== 'object' || Array.isArray(candidate)) { + return false; + } + const object = candidate as globalThis.Record; + if (!Object.prototype.hasOwnProperty.call(candidate, 'revision')) { + return false; + } + if (!matches$49ToJSON(object['revision'])) { + return false; + } + if (Object.prototype.hasOwnProperty.call(candidate, 'secondValue')) { + const item = object['secondValue']; + if (item !== undefined && !matches$2ToJSON(item)) { + return false; + } + } + return true; +} + +export function matches$92FromJSON(candidate: unknown): boolean { + if (matches$29FromJSON(candidate)) { + return true; + } + if (matches$1FromJSON(candidate)) { + return true; + } + return false; +} + +export function matches$92ToJSON(candidate: unknown): boolean { + if (matches$29ToJSON(candidate)) { + return true; + } + if (matches$1ToJSON(candidate)) { + return true; + } + return false; +} + +export function matches$93FromJSON(candidate: unknown): boolean { + if (matches$81FromJSON(candidate)) { + return true; + } + if (matches$1FromJSON(candidate)) { + return true; + } + return false; +} + +export function matches$93ToJSON(candidate: unknown): boolean { + if (matches$81ToJSON(candidate)) { + return true; + } + if (matches$1ToJSON(candidate)) { + return true; + } + return false; +} + +export function matchRecursiveListFromJSON(candidate: unknown): number { + if (matches$93FromJSON(candidate)) { + return 2; + } + return 0; +} + +export function matchRecursiveListToJSON(candidate: unknown): number { + if (matches$93ToJSON(candidate)) { + return 2; + } + return 0; +} + +export function matchRecursiveOpenDictionaryFromJSON(candidate: unknown): number { + if (matches$73FromJSON(candidate)) { + return 2; + } + return 1; +} + +export function matchRecursiveOpenDictionaryToJSON(candidate: unknown): number { + if (matches$73ToJSON(candidate)) { + return 2; + } + return 1; +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$0.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$0.ts.golden new file mode 100644 index 000000000..749b67e7e --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$0.ts.golden @@ -0,0 +1,23 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail, Detail$Wire } from '../models/Detail'; +import { convert$1FromJSON, convert$1ToJSON } from './valueConversion$1'; + +export type Value$0Wire = readonly (globalThis.Record | string)[]; + +export type Value$0 = readonly (globalThis.Record | string)[]; + +export function convert$0FromJSON( + json: ReadonlyArray | string>, +): Value$0 { + return json.map(convert$1FromJSON); +} + +export function convert$0ToJSON( + value: ReadonlyArray | string>, +): Value$0Wire { + return value.map(convert$1ToJSON); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$1.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$1.ts.golden new file mode 100644 index 000000000..f9465d25b --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$1.ts.golden @@ -0,0 +1,48 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail, Detail$Wire } from '../models/Detail'; +import { + matches$0FromJSON, + matches$0ToJSON, + matches$2FromJSON, + matches$2ToJSON, +} from '../runtime/unionMatch'; +import type { Value$2, Value$2Wire } from './valueConversion$2'; +import { convert$2FromJSON, convert$2ToJSON } from './valueConversion$2'; + +export type Value$1Wire = globalThis.Record | string; + +export type Value$1 = globalThis.Record | string; + +export function convert$1FromJSON(json: globalThis.Record | string): Value$1 { + const candidate: unknown = json; + const matches = [matches$0FromJSON(candidate), matches$2FromJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous inline untagged union value'); + } + if (matches[0]) { + return convert$2FromJSON(json as unknown as Value$2Wire); + } + if (matches[1]) { + return json as unknown as string; + } + throw new TypeError('No matching inline untagged union member'); +} + +export function convert$1ToJSON(value: globalThis.Record | string): Value$1Wire { + const candidate: unknown = value; + const matches = [matches$0ToJSON(candidate), matches$2ToJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous inline untagged union value'); + } + if (matches[0]) { + return convert$2ToJSON(value as unknown as Value$2); + } + if (matches[1]) { + return value as unknown as string; + } + throw new TypeError('No matching inline untagged union member'); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$10.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$10.ts.golden new file mode 100644 index 000000000..dbdd4d749 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$10.ts.golden @@ -0,0 +1,22 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { NullableScalarInner, NullableScalarInner$Wire } from '../models/NullableScalarInner'; +import { + nullableScalarInnerFromJSON, + nullableScalarInnerToJSON, +} from '../models/NullableScalarInner'; + +export type Value$10Wire = NullableScalarInner$Wire | null; + +export type Value$10 = NullableScalarInner | null; + +export function convert$10FromJSON(json: NullableScalarInner$Wire | null): Value$10 { + return nullableScalarInnerFromJSON(json); +} + +export function convert$10ToJSON(value: NullableScalarInner | null): Value$10Wire { + return nullableScalarInnerToJSON(value); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$11.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$11.ts.golden new file mode 100644 index 000000000..21583b7af --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$11.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail, Detail$Wire } from '../models/Detail'; +import { detailFromJSON, detailToJSON } from '../models/Detail'; + +export type Value$11Wire = readonly Detail$Wire[]; + +export type Value$11 = readonly Detail[]; + +export function convert$11FromJSON(json: ReadonlyArray): Value$11 { + return json.map(detailFromJSON); +} + +export function convert$11ToJSON(value: ReadonlyArray): Value$11Wire { + return value.map(detailToJSON); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$12.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$12.ts.golden new file mode 100644 index 000000000..f60bf44a7 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$12.ts.golden @@ -0,0 +1,23 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail, Detail$Wire } from '../models/Detail'; +import { detailFromJSON, detailToJSON } from '../models/Detail'; + +export type Value$12Wire = globalThis.Record; + +export type Value$12 = globalThis.Record; + +export function convert$12FromJSON(json: globalThis.Record): Value$12 { + return Object.fromEntries( + Object.entries(json ?? {}).map(([key, item]) => [key, detailFromJSON(item)]), + ); +} + +export function convert$12ToJSON(value: globalThis.Record): Value$12Wire { + return Object.fromEntries( + Object.entries(value ?? {}).map(([key, item]) => [key, detailToJSON(item)]), + ); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$13.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$13.ts.golden new file mode 100644 index 000000000..a9cb240cd --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$13.ts.golden @@ -0,0 +1,30 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail, Detail$Wire } from '../models/Detail'; +import { convert$14FromJSON, convert$14ToJSON } from './valueConversion$14'; + +export type Value$13Wire = globalThis.Record< + string, + globalThis.Record | string +>; + +export type Value$13 = globalThis.Record | string>; + +export function convert$13FromJSON( + json: globalThis.Record | string>, +): Value$13 { + return Object.fromEntries( + Object.entries(json ?? {}).map(([key, item]) => [key, convert$14FromJSON(item)]), + ); +} + +export function convert$13ToJSON( + value: globalThis.Record | string>, +): Value$13Wire { + return Object.fromEntries( + Object.entries(value ?? {}).map(([key, item]) => [key, convert$14ToJSON(item)]), + ); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$14.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$14.ts.golden new file mode 100644 index 000000000..cc6b2f53c --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$14.ts.golden @@ -0,0 +1,50 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail, Detail$Wire } from '../models/Detail'; +import { + matches$0FromJSON, + matches$0ToJSON, + matches$2FromJSON, + matches$2ToJSON, +} from '../runtime/unionMatch'; +import type { Value$12, Value$12Wire } from './valueConversion$12'; +import { convert$12FromJSON, convert$12ToJSON } from './valueConversion$12'; + +export type Value$14Wire = globalThis.Record | string; + +export type Value$14 = globalThis.Record | string; + +export function convert$14FromJSON( + json: globalThis.Record | string, +): Value$14 { + const candidate: unknown = json; + const matches = [matches$0FromJSON(candidate), matches$2FromJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous inline untagged union value'); + } + if (matches[0]) { + return convert$12FromJSON(json as unknown as Value$12Wire); + } + if (matches[1]) { + return json as unknown as string; + } + throw new TypeError('No matching inline untagged union member'); +} + +export function convert$14ToJSON(value: globalThis.Record | string): Value$14Wire { + const candidate: unknown = value; + const matches = [matches$0ToJSON(candidate), matches$2ToJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous inline untagged union value'); + } + if (matches[0]) { + return convert$12ToJSON(value as unknown as Value$12); + } + if (matches[1]) { + return value as unknown as string; + } + throw new TypeError('No matching inline untagged union member'); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$15.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$15.ts.golden new file mode 100644 index 000000000..4846290d7 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$15.ts.golden @@ -0,0 +1,23 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail, Detail$Wire } from '../models/Detail'; +import { convert$16FromJSON, convert$16ToJSON } from './valueConversion$16'; + +export type Value$15Wire = readonly (globalThis.Record | number)[]; + +export type Value$15 = readonly (globalThis.Record | number)[]; + +export function convert$15FromJSON( + json: ReadonlyArray | number>, +): Value$15 { + return json.map(convert$16FromJSON); +} + +export function convert$15ToJSON( + value: ReadonlyArray | number>, +): Value$15Wire { + return value.map(convert$16ToJSON); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$16.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$16.ts.golden new file mode 100644 index 000000000..61d31e575 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$16.ts.golden @@ -0,0 +1,66 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail, Detail$Wire } from '../models/Detail'; +import { + matches$0FromJSON, + matches$0ToJSON, + matches$3FromJSON, + matches$3ToJSON, + matches$4FromJSON, + matches$4ToJSON, +} from '../runtime/unionMatch'; +import type { Value$2, Value$2Wire } from './valueConversion$2'; +import { convert$2FromJSON, convert$2ToJSON } from './valueConversion$2'; + +export type Value$16Wire = globalThis.Record | number; + +export type Value$16 = globalThis.Record | number; + +export function convert$16FromJSON( + json: globalThis.Record | number, +): Value$16 { + const candidate: unknown = json; + const matches = [ + matches$0FromJSON(candidate), + matches$3FromJSON(candidate), + matches$4FromJSON(candidate), + ]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous inline untagged union value'); + } + if (matches[0]) { + return convert$2FromJSON(json as unknown as Value$2Wire); + } + if (matches[1]) { + return json as unknown as number; + } + if (matches[2]) { + return json as unknown as number; + } + throw new TypeError('No matching inline untagged union member'); +} + +export function convert$16ToJSON(value: globalThis.Record | number): Value$16Wire { + const candidate: unknown = value; + const matches = [ + matches$0ToJSON(candidate), + matches$3ToJSON(candidate), + matches$4ToJSON(candidate), + ]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous inline untagged union value'); + } + if (matches[0]) { + return convert$2ToJSON(value as unknown as Value$2); + } + if (matches[1]) { + return value as unknown as number; + } + if (matches[2]) { + return value as unknown as number; + } + throw new TypeError('No matching inline untagged union member'); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$17.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$17.ts.golden new file mode 100644 index 000000000..382d38b78 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$17.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { NamedValue, NamedValue$Wire } from '../models/NamedValue'; +import { namedValueFromJSON, namedValueToJSON } from '../models/NamedValue'; + +export type Value$17Wire = readonly NamedValue$Wire[]; + +export type Value$17 = readonly NamedValue[]; + +export function convert$17FromJSON(json: ReadonlyArray): Value$17 { + return json.map(namedValueFromJSON); +} + +export function convert$17ToJSON(value: ReadonlyArray): Value$17Wire { + return value.map(namedValueToJSON); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$18.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$18.ts.golden new file mode 100644 index 000000000..8750aeb73 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$18.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail, Detail$Wire } from '../models/Detail'; +import { convert$19FromJSON, convert$19ToJSON } from './valueConversion$19'; + +export type Value$18Wire = readonly Detail$Wire[][]; + +export type Value$18 = readonly Detail[][]; + +export function convert$18FromJSON(json: ReadonlyArray>): Value$18 { + return json.map(convert$19FromJSON); +} + +export function convert$18ToJSON(value: ReadonlyArray>): Value$18Wire { + return value.map(convert$19ToJSON); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$19.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$19.ts.golden new file mode 100644 index 000000000..f4ebb22dd --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$19.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail, Detail$Wire } from '../models/Detail'; +import { detailFromJSON, detailToJSON } from '../models/Detail'; + +export type Value$19Wire = Detail$Wire[]; + +export type Value$19 = Detail[]; + +export function convert$19FromJSON(json: ReadonlyArray): Value$19 { + return json.map(detailFromJSON); +} + +export function convert$19ToJSON(value: ReadonlyArray): Value$19Wire { + return value.map(detailToJSON); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$2.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$2.ts.golden new file mode 100644 index 000000000..7b464d5a8 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$2.ts.golden @@ -0,0 +1,23 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail, Detail$Wire } from '../models/Detail'; +import { detailFromJSON, detailToJSON } from '../models/Detail'; + +export type Value$2Wire = globalThis.Record; + +export type Value$2 = globalThis.Record; + +export function convert$2FromJSON(json: globalThis.Record): Value$2 { + return Object.fromEntries( + Object.entries(json ?? {}).map(([key, item]) => [key, detailFromJSON(item)]), + ); +} + +export function convert$2ToJSON(value: globalThis.Record): Value$2Wire { + return Object.fromEntries( + Object.entries(value ?? {}).map(([key, item]) => [key, detailToJSON(item)]), + ); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$20.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$20.ts.golden new file mode 100644 index 000000000..97fab7c63 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$20.ts.golden @@ -0,0 +1,25 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { ScalarEnumChoice, ScalarEnumChoice$Wire } from '../models/ScalarEnumChoice'; +import { scalarEnumChoiceFromJSON, scalarEnumChoiceToJSON } from '../models/ScalarEnumChoice'; + +export type Value$20Wire = ScalarEnumChoice$Wire | null; + +export type Value$20 = ScalarEnumChoice | null; + +export function convert$20FromJSON(json: ScalarEnumChoice$Wire | null): Value$20 { + if (json == null) { + return null; + } + return scalarEnumChoiceFromJSON(json); +} + +export function convert$20ToJSON(value: ScalarEnumChoice | null): Value$20Wire { + if (value == null) { + return null; + } + return scalarEnumChoiceToJSON(value); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$21.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$21.ts.golden new file mode 100644 index 000000000..2834fd572 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$21.ts.golden @@ -0,0 +1,24 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { convert$22FromJSON, convert$22ToJSON } from './valueConversion$22'; + +export type Value$21Wire = number | boolean | null; + +export type Value$21 = number | boolean | null; + +export function convert$21FromJSON(json: number | boolean | null): Value$21 { + if (json == null) { + return null; + } + return convert$22FromJSON(json); +} + +export function convert$21ToJSON(value: number | boolean | null): Value$21Wire { + if (value == null) { + return null; + } + return convert$22ToJSON(value); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$22.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$22.ts.golden new file mode 100644 index 000000000..83c4efbd8 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$22.ts.golden @@ -0,0 +1,45 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$3FromJSON, + matches$3ToJSON, + matches$5FromJSON, + matches$5ToJSON, +} from '../runtime/unionMatch'; + +export type Value$22Wire = number | boolean; + +export type Value$22 = number | boolean; + +export function convert$22FromJSON(json: number | boolean): Value$22 { + const candidate: unknown = json; + const matches = [matches$3FromJSON(candidate), matches$5FromJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous inline untagged union value'); + } + if (matches[0]) { + return json as unknown as number; + } + if (matches[1]) { + return json as unknown as boolean; + } + throw new TypeError('No matching inline untagged union member'); +} + +export function convert$22ToJSON(value: number | boolean): Value$22Wire { + const candidate: unknown = value; + const matches = [matches$3ToJSON(candidate), matches$5ToJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous inline untagged union value'); + } + if (matches[0]) { + return value as unknown as number; + } + if (matches[1]) { + return value as unknown as boolean; + } + throw new TypeError('No matching inline untagged union member'); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$23.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$23.ts.golden new file mode 100644 index 000000000..59ab2c2e0 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$23.ts.golden @@ -0,0 +1,26 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { + NullableNonNullScalarsItems, + NullableNonNullScalarsItems$Wire, +} from '../models/NullableNonNullScalarsItems'; +import { convert$24FromJSON, convert$24ToJSON } from './valueConversion$24'; + +export type Value$23Wire = readonly (NullableNonNullScalarsItems$Wire | null)[]; + +export type Value$23 = readonly (NullableNonNullScalarsItems | null)[]; + +export function convert$23FromJSON( + json: ReadonlyArray, +): Value$23 { + return json.map(convert$24FromJSON); +} + +export function convert$23ToJSON( + value: ReadonlyArray, +): Value$23Wire { + return value.map(convert$24ToJSON); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$24.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$24.ts.golden new file mode 100644 index 000000000..4789ce70a --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$24.ts.golden @@ -0,0 +1,31 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { + NullableNonNullScalarsItems, + NullableNonNullScalarsItems$Wire, +} from '../models/NullableNonNullScalarsItems'; +import { + nullableNonNullScalarsItemsFromJSON, + nullableNonNullScalarsItemsToJSON, +} from '../models/NullableNonNullScalarsItems'; + +export type Value$24Wire = NullableNonNullScalarsItems$Wire | null; + +export type Value$24 = NullableNonNullScalarsItems | null; + +export function convert$24FromJSON(json: NullableNonNullScalarsItems$Wire | null): Value$24 { + if (json == null) { + return null; + } + return nullableNonNullScalarsItemsFromJSON(json); +} + +export function convert$24ToJSON(value: NullableNonNullScalarsItems | null): Value$24Wire { + if (value == null) { + return null; + } + return nullableNonNullScalarsItemsToJSON(value); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$25.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$25.ts.golden new file mode 100644 index 000000000..0145d6124 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$25.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { NullableScalarInner, NullableScalarInner$Wire } from '../models/NullableScalarInner'; +import { convert$10FromJSON, convert$10ToJSON } from './valueConversion$10'; + +export type Value$25Wire = readonly (NullableScalarInner$Wire | null)[]; + +export type Value$25 = readonly (NullableScalarInner | null)[]; + +export function convert$25FromJSON(json: ReadonlyArray): Value$25 { + return json.map(convert$10FromJSON); +} + +export function convert$25ToJSON(value: ReadonlyArray): Value$25Wire { + return value.map(convert$10ToJSON); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$26.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$26.ts.golden new file mode 100644 index 000000000..6940ba2db --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$26.ts.golden @@ -0,0 +1,22 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { convert$27FromJSON, convert$27ToJSON } from './valueConversion$27'; + +export type Value$26Wire = globalThis.Record; + +export type Value$26 = globalThis.Record; + +export function convert$26FromJSON(json: globalThis.Record): Value$26 { + return Object.fromEntries( + Object.entries(json ?? {}).map(([key, item]) => [key, convert$27FromJSON(item)]), + ); +} + +export function convert$26ToJSON(value: globalThis.Record): Value$26Wire { + return Object.fromEntries( + Object.entries(value ?? {}).map(([key, item]) => [key, convert$27ToJSON(item)]), + ); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$27.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$27.ts.golden new file mode 100644 index 000000000..6159215a3 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$27.ts.golden @@ -0,0 +1,45 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$3FromJSON, + matches$3ToJSON, + matches$4FromJSON, + matches$4ToJSON, +} from '../runtime/unionMatch'; + +export type Value$27Wire = number; + +export type Value$27 = number; + +export function convert$27FromJSON(json: number): Value$27 { + const candidate: unknown = json; + const matches = [matches$3FromJSON(candidate), matches$4FromJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous inline untagged union value'); + } + if (matches[0]) { + return json as unknown as number; + } + if (matches[1]) { + return json as unknown as number; + } + throw new TypeError('No matching inline untagged union member'); +} + +export function convert$27ToJSON(value: number): Value$27Wire { + const candidate: unknown = value; + const matches = [matches$3ToJSON(candidate), matches$4ToJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous inline untagged union value'); + } + if (matches[0]) { + return value as unknown as number; + } + if (matches[1]) { + return value as unknown as number; + } + throw new TypeError('No matching inline untagged union member'); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$28.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$28.ts.golden new file mode 100644 index 000000000..095990381 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$28.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { RecursiveList, RecursiveList$Wire } from '../models/RecursiveList'; +import { recursiveListFromJSON, recursiveListToJSON } from '../models/RecursiveList'; + +export type Value$28Wire = readonly RecursiveList$Wire[]; + +export type Value$28 = readonly RecursiveList[]; + +export function convert$28FromJSON(json: ReadonlyArray): Value$28 { + return json.map(recursiveListFromJSON); +} + +export function convert$28ToJSON(value: ReadonlyArray): Value$28Wire { + return value.map(recursiveListToJSON); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$29.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$29.ts.golden new file mode 100644 index 000000000..06af8d4a2 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$29.ts.golden @@ -0,0 +1,33 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { + RecursiveOpenDictionary, + RecursiveOpenDictionary$Wire, +} from '../models/RecursiveOpenDictionary'; +import { + recursiveOpenDictionaryFromJSON, + recursiveOpenDictionaryToJSON, +} from '../models/RecursiveOpenDictionary'; + +export type Value$29Wire = globalThis.Record; + +export type Value$29 = globalThis.Record; + +export function convert$29FromJSON( + json: globalThis.Record, +): Value$29 { + return Object.fromEntries( + Object.entries(json ?? {}).map(([key, item]) => [key, recursiveOpenDictionaryFromJSON(item)]), + ); +} + +export function convert$29ToJSON( + value: globalThis.Record, +): Value$29Wire { + return Object.fromEntries( + Object.entries(value ?? {}).map(([key, item]) => [key, recursiveOpenDictionaryToJSON(item)]), + ); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$3.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$3.ts.golden new file mode 100644 index 000000000..5c8843f1f --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$3.ts.golden @@ -0,0 +1,18 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { convert$4FromJSON, convert$4ToJSON } from './valueConversion$4'; + +export type Value$3Wire = readonly number[]; + +export type Value$3 = readonly number[]; + +export function convert$3FromJSON(json: ReadonlyArray): Value$3 { + return json.map(convert$4FromJSON); +} + +export function convert$3ToJSON(value: ReadonlyArray): Value$3Wire { + return value.map(convert$4ToJSON); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$30.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$30.ts.golden new file mode 100644 index 000000000..141b85a29 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$30.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { ClientTool, ClientTool$Wire } from '../models/ClientTool'; +import { clientToolFromJSON, clientToolToJSON } from '../models/ClientTool'; + +export type Value$30Wire = readonly ClientTool$Wire[]; + +export type Value$30 = readonly ClientTool[]; + +export function convert$30FromJSON(json: ReadonlyArray): Value$30 { + return json.map(clientToolFromJSON); +} + +export function convert$30ToJSON(value: ReadonlyArray): Value$30Wire { + return value.map(clientToolToJSON); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$31.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$31.ts.golden new file mode 100644 index 000000000..c27ab86ef --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$31.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { SearchTool, SearchTool$Wire } from '../models/SearchTool'; +import { searchToolFromJSON, searchToolToJSON } from '../models/SearchTool'; + +export type Value$31Wire = readonly SearchTool$Wire[]; + +export type Value$31 = readonly SearchTool[]; + +export function convert$31FromJSON(json: ReadonlyArray): Value$31 { + return json.map(searchToolFromJSON); +} + +export function convert$31ToJSON(value: ReadonlyArray): Value$31Wire { + return value.map(searchToolToJSON); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$32.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$32.ts.golden new file mode 100644 index 000000000..75441a8d0 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$32.ts.golden @@ -0,0 +1,23 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { ClientTool, ClientTool$Wire } from '../models/ClientTool'; +import { clientToolFromJSON, clientToolToJSON } from '../models/ClientTool'; + +export type Value$32Wire = globalThis.Record; + +export type Value$32 = globalThis.Record; + +export function convert$32FromJSON(json: globalThis.Record): Value$32 { + return Object.fromEntries( + Object.entries(json ?? {}).map(([key, item]) => [key, clientToolFromJSON(item)]), + ); +} + +export function convert$32ToJSON(value: globalThis.Record): Value$32Wire { + return Object.fromEntries( + Object.entries(value ?? {}).map(([key, item]) => [key, clientToolToJSON(item)]), + ); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$33.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$33.ts.golden new file mode 100644 index 000000000..e586341e6 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$33.ts.golden @@ -0,0 +1,23 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { SearchTool, SearchTool$Wire } from '../models/SearchTool'; +import { searchToolFromJSON, searchToolToJSON } from '../models/SearchTool'; + +export type Value$33Wire = globalThis.Record; + +export type Value$33 = globalThis.Record; + +export function convert$33FromJSON(json: globalThis.Record): Value$33 { + return Object.fromEntries( + Object.entries(json ?? {}).map(([key, item]) => [key, searchToolFromJSON(item)]), + ); +} + +export function convert$33ToJSON(value: globalThis.Record): Value$33Wire { + return Object.fromEntries( + Object.entries(value ?? {}).map(([key, item]) => [key, searchToolToJSON(item)]), + ); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$34.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$34.ts.golden new file mode 100644 index 000000000..fd7476ff4 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$34.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Tool, Tool$Wire } from '../models/Tool'; +import { toolFromJSON, toolToJSON } from '../models/Tool'; + +export type Value$34Wire = readonly Tool$Wire[]; + +export type Value$34 = readonly Tool[]; + +export function convert$34FromJSON(json: ReadonlyArray): Value$34 { + return json.map(toolFromJSON); +} + +export function convert$34ToJSON(value: ReadonlyArray): Value$34Wire { + return value.map(toolToJSON); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$35.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$35.ts.golden new file mode 100644 index 000000000..6c053a5ea --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$35.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Tool, Tool$Wire } from '../models/Tool'; +import { convert$36FromJSON, convert$36ToJSON } from './valueConversion$36'; + +export type Value$35Wire = Tool$Wire[][]; + +export type Value$35 = Tool[][]; + +export function convert$35FromJSON(json: ReadonlyArray>): Value$35 { + return json.map(convert$36FromJSON); +} + +export function convert$35ToJSON(value: ReadonlyArray>): Value$35Wire { + return value.map(convert$36ToJSON); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$36.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$36.ts.golden new file mode 100644 index 000000000..ffd60171d --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$36.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Tool, Tool$Wire } from '../models/Tool'; +import { toolFromJSON, toolToJSON } from '../models/Tool'; + +export type Value$36Wire = Tool$Wire[]; + +export type Value$36 = Tool[]; + +export function convert$36FromJSON(json: ReadonlyArray): Value$36 { + return json.map(toolFromJSON); +} + +export function convert$36ToJSON(value: ReadonlyArray): Value$36Wire { + return value.map(toolToJSON); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$37.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$37.ts.golden new file mode 100644 index 000000000..f866475d0 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$37.ts.golden @@ -0,0 +1,25 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Tool, Tool$Wire } from '../models/Tool'; +import { convert$38FromJSON, convert$38ToJSON } from './valueConversion$38'; + +export type Value$37Wire = Tool$Wire[] | null; + +export type Value$37 = Tool[] | null; + +export function convert$37FromJSON(json: ReadonlyArray | null): Value$37 { + if (json == null) { + return null; + } + return convert$38FromJSON(json); +} + +export function convert$37ToJSON(value: ReadonlyArray | null): Value$37Wire { + if (value == null) { + return null; + } + return convert$38ToJSON(value); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$38.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$38.ts.golden new file mode 100644 index 000000000..1b078e45a --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$38.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Tool, Tool$Wire } from '../models/Tool'; +import { toolFromJSON, toolToJSON } from '../models/Tool'; + +export type Value$38Wire = Tool$Wire[]; + +export type Value$38 = Tool[]; + +export function convert$38FromJSON(json: ReadonlyArray): Value$38 { + return json.map(toolFromJSON); +} + +export function convert$38ToJSON(value: ReadonlyArray): Value$38Wire { + return value.map(toolToJSON); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$39.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$39.ts.golden new file mode 100644 index 000000000..c9867bbc4 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$39.ts.golden @@ -0,0 +1,23 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail, Detail$Wire } from '../models/Detail'; +import { convert$40FromJSON, convert$40ToJSON } from './valueConversion$40'; + +export type Value$39Wire = (globalThis.Record | string)[]; + +export type Value$39 = (globalThis.Record | string)[]; + +export function convert$39FromJSON( + json: ReadonlyArray | string>, +): Value$39 { + return json.map(convert$40FromJSON); +} + +export function convert$39ToJSON( + value: ReadonlyArray | string>, +): Value$39Wire { + return value.map(convert$40ToJSON); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$4.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$4.ts.golden new file mode 100644 index 000000000..ed31995ff --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$4.ts.golden @@ -0,0 +1,45 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import { + matches$3FromJSON, + matches$3ToJSON, + matches$4FromJSON, + matches$4ToJSON, +} from '../runtime/unionMatch'; + +export type Value$4Wire = number; + +export type Value$4 = number; + +export function convert$4FromJSON(json: number): Value$4 { + const candidate: unknown = json; + const matches = [matches$3FromJSON(candidate), matches$4FromJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous inline untagged union value'); + } + if (matches[0]) { + return json as unknown as number; + } + if (matches[1]) { + return json as unknown as number; + } + throw new TypeError('No matching inline untagged union member'); +} + +export function convert$4ToJSON(value: number): Value$4Wire { + const candidate: unknown = value; + const matches = [matches$3ToJSON(candidate), matches$4ToJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous inline untagged union value'); + } + if (matches[0]) { + return value as unknown as number; + } + if (matches[1]) { + return value as unknown as number; + } + throw new TypeError('No matching inline untagged union member'); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$40.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$40.ts.golden new file mode 100644 index 000000000..c35ad3ad3 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$40.ts.golden @@ -0,0 +1,50 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail, Detail$Wire } from '../models/Detail'; +import { + matches$0FromJSON, + matches$0ToJSON, + matches$2FromJSON, + matches$2ToJSON, +} from '../runtime/unionMatch'; +import type { Value$41, Value$41Wire } from './valueConversion$41'; +import { convert$41FromJSON, convert$41ToJSON } from './valueConversion$41'; + +export type Value$40Wire = globalThis.Record | string; + +export type Value$40 = globalThis.Record | string; + +export function convert$40FromJSON( + json: globalThis.Record | string, +): Value$40 { + const candidate: unknown = json; + const matches = [matches$0FromJSON(candidate), matches$2FromJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous inline untagged union value'); + } + if (matches[0]) { + return convert$41FromJSON(json as unknown as Value$41Wire); + } + if (matches[1]) { + return json as unknown as string; + } + throw new TypeError('No matching inline untagged union member'); +} + +export function convert$40ToJSON(value: globalThis.Record | string): Value$40Wire { + const candidate: unknown = value; + const matches = [matches$0ToJSON(candidate), matches$2ToJSON(candidate)]; + if (matches.filter(Boolean).length > 1) { + throw new TypeError('Ambiguous inline untagged union value'); + } + if (matches[0]) { + return convert$41ToJSON(value as unknown as Value$41); + } + if (matches[1]) { + return value as unknown as string; + } + throw new TypeError('No matching inline untagged union member'); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$41.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$41.ts.golden new file mode 100644 index 000000000..1a910fc9d --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$41.ts.golden @@ -0,0 +1,23 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail, Detail$Wire } from '../models/Detail'; +import { detailFromJSON, detailToJSON } from '../models/Detail'; + +export type Value$41Wire = globalThis.Record; + +export type Value$41 = globalThis.Record; + +export function convert$41FromJSON(json: globalThis.Record): Value$41 { + return Object.fromEntries( + Object.entries(json ?? {}).map(([key, item]) => [key, detailFromJSON(item)]), + ); +} + +export function convert$41ToJSON(value: globalThis.Record): Value$41Wire { + return Object.fromEntries( + Object.entries(value ?? {}).map(([key, item]) => [key, detailToJSON(item)]), + ); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$5.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$5.ts.golden new file mode 100644 index 000000000..d9f62811e --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$5.ts.golden @@ -0,0 +1,22 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { NullableScalarInner, NullableScalarInner$Wire } from '../models/NullableScalarInner'; +import { + nullableScalarInnerFromJSON, + nullableScalarInnerToJSON, +} from '../models/NullableScalarInner'; + +export type Value$5Wire = NullableScalarInner$Wire | null; + +export type Value$5 = NullableScalarInner | null; + +export function convert$5FromJSON(json: NullableScalarInner$Wire | null): Value$5 { + return nullableScalarInnerFromJSON(json); +} + +export function convert$5ToJSON(value: NullableScalarInner | null): Value$5Wire { + return nullableScalarInnerToJSON(value); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$6.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$6.ts.golden new file mode 100644 index 000000000..39b326ff6 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$6.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { NullableScalarInner, NullableScalarInner$Wire } from '../models/NullableScalarInner'; +import { convert$5FromJSON, convert$5ToJSON } from './valueConversion$5'; + +export type Value$6Wire = NullableScalarInner$Wire | null; + +export type Value$6 = NullableScalarInner | null; + +export function convert$6FromJSON(json: NullableScalarInner$Wire | null): Value$6 { + return convert$5FromJSON(json); +} + +export function convert$6ToJSON(value: NullableScalarInner | null): Value$6Wire { + return convert$5ToJSON(value); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$7.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$7.ts.golden new file mode 100644 index 000000000..140a41e00 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$7.ts.golden @@ -0,0 +1,25 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { NullableScalarInner, NullableScalarInner$Wire } from '../models/NullableScalarInner'; +import { convert$5FromJSON, convert$5ToJSON } from './valueConversion$5'; + +export type Value$7Wire = NullableScalarInner$Wire | null; + +export type Value$7 = NullableScalarInner | null; + +export function convert$7FromJSON(json: NullableScalarInner$Wire | null): Value$7 { + if (json == null) { + return null; + } + return convert$5FromJSON(json); +} + +export function convert$7ToJSON(value: NullableScalarInner | null): Value$7Wire { + if (value == null) { + return null; + } + return convert$5ToJSON(value); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$8.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$8.ts.golden new file mode 100644 index 000000000..658b0543a --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$8.ts.golden @@ -0,0 +1,19 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { NullableScalarInner, NullableScalarInner$Wire } from '../models/NullableScalarInner'; +import { convert$9FromJSON, convert$9ToJSON } from './valueConversion$9'; + +export type Value$8Wire = readonly (NullableScalarInner$Wire | null)[]; + +export type Value$8 = readonly (NullableScalarInner | null)[]; + +export function convert$8FromJSON(json: ReadonlyArray): Value$8 { + return json.map(convert$9FromJSON); +} + +export function convert$8ToJSON(value: ReadonlyArray): Value$8Wire { + return value.map(convert$9ToJSON); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$9.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$9.ts.golden new file mode 100644 index 000000000..c10c565ea --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/runtime/valueConversion$9.ts.golden @@ -0,0 +1,25 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { NullableScalarInner, NullableScalarInner$Wire } from '../models/NullableScalarInner'; +import { convert$10FromJSON, convert$10ToJSON } from './valueConversion$10'; + +export type Value$9Wire = NullableScalarInner$Wire | null; + +export type Value$9 = NullableScalarInner | null; + +export function convert$9FromJSON(json: NullableScalarInner$Wire | null): Value$9 { + if (json == null) { + return null; + } + return convert$10FromJSON(json); +} + +export function convert$9ToJSON(value: NullableScalarInner | null): Value$9Wire { + if (value == null) { + return null; + } + return convert$10ToJSON(value); +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/tsconfig.esm.json.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/tsconfig.esm.json.golden new file mode 100644 index 000000000..769c716fd --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/tsconfig.esm.json.golden @@ -0,0 +1,7 @@ +{ + "compilerOptions": { + "module": "ES2020", + "outDir": "dist/esm" + }, + "extends": "./tsconfig.json" +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/tsconfig.json.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/tsconfig.json.golden new file mode 100644 index 000000000..9f125842c --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/tsconfig.json.golden @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "declaration": true, + "declarationMap": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "lib": ["ES2020", "DOM"], + "module": "ES2020", + "moduleResolution": "bundler", + "outDir": "./dist", + "resolveJsonModule": true, + "rootDir": "./", + "skipLibCheck": true, + "sourceMap": true, + "strict": true, + "target": "ES2020", + "typeRoots": ["node_modules/@types"] + }, + "exclude": ["dist", "node_modules"], + "include": ["**/*.ts"] +} diff --git a/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/vite.config.ts.golden b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/vite.config.ts.golden new file mode 100644 index 000000000..065569dd3 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/ts-untagged-union-strict/vite.config.ts.golden @@ -0,0 +1,20 @@ +import { defineConfig } from 'vite-plus'; + +export default defineConfig({ + fmt: { + singleQuote: true, + ignorePatterns: ['dist/**'], + }, + lint: { + options: { + typeAware: true, + typeCheck: true, + }, + ignorePatterns: ['node_modules', 'dist'], + }, + pack: { + entry: ['index.ts'], + dts: true, + format: ['esm'], + }, +}); diff --git a/tests/golden/typescript/typescript-fetch/type-aliases-complex-union/apis/DefaultApi.ts.golden b/tests/golden/typescript/typescript-fetch/type-aliases-complex-union/apis/DefaultApi.ts.golden index 1b662393a..ddc05a53e 100644 --- a/tests/golden/typescript/typescript-fetch/type-aliases-complex-union/apis/DefaultApi.ts.golden +++ b/tests/golden/typescript/typescript-fetch/type-aliases-complex-union/apis/DefaultApi.ts.golden @@ -70,7 +70,7 @@ export class DefaultApi extends BaseAPI implements DefaultApiInterface { requestParameters: ApiTestComplexUnionRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling testComplexUnionRaw().', diff --git a/tests/golden/typescript/typescript-fetch/type-aliases-discriminated-union-inline-discriminator-only/apis/DefaultApi.ts.golden b/tests/golden/typescript/typescript-fetch/type-aliases-discriminated-union-inline-discriminator-only/apis/DefaultApi.ts.golden index bdf570e0d..5ecfc3789 100644 --- a/tests/golden/typescript/typescript-fetch/type-aliases-discriminated-union-inline-discriminator-only/apis/DefaultApi.ts.golden +++ b/tests/golden/typescript/typescript-fetch/type-aliases-discriminated-union-inline-discriminator-only/apis/DefaultApi.ts.golden @@ -79,7 +79,7 @@ export class DefaultApi extends BaseAPI implements DefaultApiInterface { requestParameters: ApiCreateEventRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling createEventRaw().', diff --git a/tests/golden/typescript/typescript-fetch/type-aliases-discriminated-union-inline-discriminator-only/models/Event.ts.golden b/tests/golden/typescript/typescript-fetch/type-aliases-discriminated-union-inline-discriminator-only/models/Event.ts.golden index 3e49a979c..1eaac2f02 100644 --- a/tests/golden/typescript/typescript-fetch/type-aliases-discriminated-union-inline-discriminator-only/models/Event.ts.golden +++ b/tests/golden/typescript/typescript-fetch/type-aliases-discriminated-union-inline-discriminator-only/models/Event.ts.golden @@ -17,6 +17,6 @@ import type { EventEventCreated } from './EventEventCreated'; import type { EventEventUpdated } from './EventEventUpdated'; export type Event = - | { kind: 'EVENT_UNSPECIFIED' } - | ({ kind: 'EVENT_CREATED' } & EventEventCreated) - | ({ kind: 'EVENT_UPDATED' } & EventEventUpdated); + | Record<'kind', 'EVENT_UNSPECIFIED'> + | (Record<'kind', 'EVENT_CREATED'> & EventEventCreated) + | (Record<'kind', 'EVENT_UPDATED'> & EventEventUpdated); diff --git a/tests/golden/typescript/typescript-fetch/type-aliases-discriminated-union-internally-tagged/apis/DefaultApi.ts.golden b/tests/golden/typescript/typescript-fetch/type-aliases-discriminated-union-internally-tagged/apis/DefaultApi.ts.golden index d85cefb57..52f4456f3 100644 --- a/tests/golden/typescript/typescript-fetch/type-aliases-discriminated-union-internally-tagged/apis/DefaultApi.ts.golden +++ b/tests/golden/typescript/typescript-fetch/type-aliases-discriminated-union-internally-tagged/apis/DefaultApi.ts.golden @@ -72,7 +72,7 @@ export class DefaultApi extends BaseAPI implements DefaultApiInterface { requestParameters: ApiCreateResourceRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling createResourceRaw().', diff --git a/tests/golden/typescript/typescript-fetch/type-aliases-discriminated-union-internally-tagged/models/Resource.ts.golden b/tests/golden/typescript/typescript-fetch/type-aliases-discriminated-union-internally-tagged/models/Resource.ts.golden index 14f0fa36e..95ba145b9 100644 --- a/tests/golden/typescript/typescript-fetch/type-aliases-discriminated-union-internally-tagged/models/Resource.ts.golden +++ b/tests/golden/typescript/typescript-fetch/type-aliases-discriminated-union-internally-tagged/models/Resource.ts.golden @@ -11,6 +11,6 @@ import type { ResourceQuick } from './ResourceQuick'; import type { ResourceUnspecified } from './ResourceUnspecified'; export type Resource = - | ({ kind: 'RESOURCE_UNSPECIFIED' } & ResourceUnspecified) - | ({ kind: 'RESOURCE_QUICK' } & ResourceQuick) - | ({ kind: 'RESOURCE_CUSTOM' } & ResourceCustom); + | (Record<'kind', 'RESOURCE_UNSPECIFIED'> & ResourceUnspecified) + | (Record<'kind', 'RESOURCE_QUICK'> & ResourceQuick) + | (Record<'kind', 'RESOURCE_CUSTOM'> & ResourceCustom); diff --git a/tests/golden/typescript/typescript-fetch/type-aliases-discriminated-union-long-names/apis/DefaultApi.ts.golden b/tests/golden/typescript/typescript-fetch/type-aliases-discriminated-union-long-names/apis/DefaultApi.ts.golden index 680be8add..8646776e2 100644 --- a/tests/golden/typescript/typescript-fetch/type-aliases-discriminated-union-long-names/apis/DefaultApi.ts.golden +++ b/tests/golden/typescript/typescript-fetch/type-aliases-discriminated-union-long-names/apis/DefaultApi.ts.golden @@ -72,7 +72,7 @@ export class DefaultApi extends BaseAPI implements DefaultApiInterface { requestParameters: ApiCreateResourceRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling createResourceRaw().', diff --git a/tests/golden/typescript/typescript-fetch/type-aliases-discriminated-union-long-names/models/VeryLongDiscriminatedUnionTypeNameForTesting20260630.ts.golden b/tests/golden/typescript/typescript-fetch/type-aliases-discriminated-union-long-names/models/VeryLongDiscriminatedUnionTypeNameForTesting20260630.ts.golden index ff7b5b51f..d9d609252 100644 --- a/tests/golden/typescript/typescript-fetch/type-aliases-discriminated-union-long-names/models/VeryLongDiscriminatedUnionTypeNameForTesting20260630.ts.golden +++ b/tests/golden/typescript/typescript-fetch/type-aliases-discriminated-union-long-names/models/VeryLongDiscriminatedUnionTypeNameForTesting20260630.ts.golden @@ -11,6 +11,6 @@ import type { VeryLongDiscriminatedUnionTypeNameBeta20260630 } from './VeryLongD import type { VeryLongDiscriminatedUnionTypeNameGamma20260630 } from './VeryLongDiscriminatedUnionTypeNameGamma20260630'; export type VeryLongDiscriminatedUnionTypeNameForTesting20260630 = - | ({ kind: 'ALPHA' } & VeryLongDiscriminatedUnionTypeNameAlpha20260630) - | ({ kind: 'BETA' } & VeryLongDiscriminatedUnionTypeNameBeta20260630) - | ({ kind: 'GAMMA' } & VeryLongDiscriminatedUnionTypeNameGamma20260630); + | (Record<'kind', 'ALPHA'> & VeryLongDiscriminatedUnionTypeNameAlpha20260630) + | (Record<'kind', 'BETA'> & VeryLongDiscriminatedUnionTypeNameBeta20260630) + | (Record<'kind', 'GAMMA'> & VeryLongDiscriminatedUnionTypeNameGamma20260630); diff --git a/tests/golden/typescript/typescript-fetch/type-aliases-discriminated-union-mixed-unit-and-allof/apis/DefaultApi.ts.golden b/tests/golden/typescript/typescript-fetch/type-aliases-discriminated-union-mixed-unit-and-allof/apis/DefaultApi.ts.golden index 8dc506699..b749b173a 100644 --- a/tests/golden/typescript/typescript-fetch/type-aliases-discriminated-union-mixed-unit-and-allof/apis/DefaultApi.ts.golden +++ b/tests/golden/typescript/typescript-fetch/type-aliases-discriminated-union-mixed-unit-and-allof/apis/DefaultApi.ts.golden @@ -75,7 +75,7 @@ export class DefaultApi extends BaseAPI implements DefaultApiInterface { requestParameters: ApiCreateResourceRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling createResourceRaw().', diff --git a/tests/golden/typescript/typescript-fetch/type-aliases-discriminated-union-mixed-unit-and-allof/models/Setup.ts.golden b/tests/golden/typescript/typescript-fetch/type-aliases-discriminated-union-mixed-unit-and-allof/models/Setup.ts.golden index 14cbae06c..c59eacb26 100644 --- a/tests/golden/typescript/typescript-fetch/type-aliases-discriminated-union-mixed-unit-and-allof/models/Setup.ts.golden +++ b/tests/golden/typescript/typescript-fetch/type-aliases-discriminated-union-mixed-unit-and-allof/models/Setup.ts.golden @@ -12,9 +12,10 @@ import type { SetupCustom } from './SetupCustom'; import type { SetupQuick } from './SetupQuick'; -/** Setup: quick (managed), custom, or unspecified. */ - +/** + * Setup: quick (managed), custom, or unspecified. + */ export type Setup = - | { kind: 'SETUP_UNSPECIFIED' } - | ({ kind: 'SETUP_QUICK' } & SetupQuick) - | ({ kind: 'SETUP_CUSTOM' } & SetupCustom); + | Record<'kind', 'SETUP_UNSPECIFIED'> + | (Record<'kind', 'SETUP_QUICK'> & SetupQuick) + | (Record<'kind', 'SETUP_CUSTOM'> & SetupCustom); diff --git a/tests/golden/typescript/typescript-fetch/type-aliases-discriminated-union-multiple/apis/DefaultApi.ts.golden b/tests/golden/typescript/typescript-fetch/type-aliases-discriminated-union-multiple/apis/DefaultApi.ts.golden index 628e1bee6..30ad63dd0 100644 --- a/tests/golden/typescript/typescript-fetch/type-aliases-discriminated-union-multiple/apis/DefaultApi.ts.golden +++ b/tests/golden/typescript/typescript-fetch/type-aliases-discriminated-union-multiple/apis/DefaultApi.ts.golden @@ -109,7 +109,7 @@ export class DefaultApi extends BaseAPI implements DefaultApiInterface { requestParameters: ApiCreateContainerRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling createContainerRaw().', @@ -173,7 +173,7 @@ export class DefaultApi extends BaseAPI implements DefaultApiInterface { requestParameters: ApiCreateVolumeRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling createVolumeRaw().', diff --git a/tests/golden/typescript/typescript-fetch/type-aliases-discriminated-union-multiple/models/ContainerKind.ts.golden b/tests/golden/typescript/typescript-fetch/type-aliases-discriminated-union-multiple/models/ContainerKind.ts.golden index 8b6a2c6c2..bd644f217 100644 --- a/tests/golden/typescript/typescript-fetch/type-aliases-discriminated-union-multiple/models/ContainerKind.ts.golden +++ b/tests/golden/typescript/typescript-fetch/type-aliases-discriminated-union-multiple/models/ContainerKind.ts.golden @@ -11,5 +11,5 @@ import type { ContainerKindCustom } from './ContainerKindCustom'; import type { ContainerKindManaged } from './ContainerKindManaged'; export type ContainerKind = - | ({ kind: 'CONTAINER_KIND_MANAGED' } & ContainerKindManaged) - | ({ kind: 'CONTAINER_KIND_CUSTOM' } & ContainerKindCustom); + | (Record<'kind', 'CONTAINER_KIND_MANAGED'> & ContainerKindManaged) + | (Record<'kind', 'CONTAINER_KIND_CUSTOM'> & ContainerKindCustom); diff --git a/tests/golden/typescript/typescript-fetch/type-aliases-discriminated-union-multiple/models/VolumeKind.ts.golden b/tests/golden/typescript/typescript-fetch/type-aliases-discriminated-union-multiple/models/VolumeKind.ts.golden index 8c9cb06fe..c2abb5db3 100644 --- a/tests/golden/typescript/typescript-fetch/type-aliases-discriminated-union-multiple/models/VolumeKind.ts.golden +++ b/tests/golden/typescript/typescript-fetch/type-aliases-discriminated-union-multiple/models/VolumeKind.ts.golden @@ -11,5 +11,5 @@ import type { VolumeKindLocal } from './VolumeKindLocal'; import type { VolumeKindRemote } from './VolumeKindRemote'; export type VolumeKind = - | ({ kind: 'VOLUME_KIND_LOCAL' } & VolumeKindLocal) - | ({ kind: 'VOLUME_KIND_REMOTE' } & VolumeKindRemote); + | (Record<'kind', 'VOLUME_KIND_LOCAL'> & VolumeKindLocal) + | (Record<'kind', 'VOLUME_KIND_REMOTE'> & VolumeKindRemote); diff --git a/tests/golden/typescript/typescript-fetch/type-aliases-discriminated-union-with-refs/apis/DefaultApi.ts.golden b/tests/golden/typescript/typescript-fetch/type-aliases-discriminated-union-with-refs/apis/DefaultApi.ts.golden index 3a613f89d..6597a4326 100644 --- a/tests/golden/typescript/typescript-fetch/type-aliases-discriminated-union-with-refs/apis/DefaultApi.ts.golden +++ b/tests/golden/typescript/typescript-fetch/type-aliases-discriminated-union-with-refs/apis/DefaultApi.ts.golden @@ -71,7 +71,7 @@ export class DefaultApi extends BaseAPI implements DefaultApiInterface { requestParameters: ApiCreateContainerRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling createContainerRaw().', diff --git a/tests/golden/typescript/typescript-fetch/type-aliases-discriminated-union-with-refs/models/ContainerImage.ts.golden b/tests/golden/typescript/typescript-fetch/type-aliases-discriminated-union-with-refs/models/ContainerImage.ts.golden index 13d686f13..bf5a1fe15 100644 --- a/tests/golden/typescript/typescript-fetch/type-aliases-discriminated-union-with-refs/models/ContainerImage.ts.golden +++ b/tests/golden/typescript/typescript-fetch/type-aliases-discriminated-union-with-refs/models/ContainerImage.ts.golden @@ -9,5 +9,5 @@ import type { ContainerImageCustom } from './ContainerImageCustom'; import type { ContainerImageManaged } from './ContainerImageManaged'; export type ContainerImage = - | ({ kind: 'CONTAINER_IMAGE_MANAGED' } & ContainerImageManaged) - | ({ kind: 'CONTAINER_IMAGE_CUSTOM' } & ContainerImageCustom); + | (Record<'kind', 'CONTAINER_IMAGE_MANAGED'> & ContainerImageManaged) + | (Record<'kind', 'CONTAINER_IMAGE_CUSTOM'> & ContainerImageCustom); diff --git a/tests/golden/typescript/typescript-fetch/type-aliases-intersection-allof/apis/DefaultApi.ts.golden b/tests/golden/typescript/typescript-fetch/type-aliases-intersection-allof/apis/DefaultApi.ts.golden index 2985e232c..03b99f490 100644 --- a/tests/golden/typescript/typescript-fetch/type-aliases-intersection-allof/apis/DefaultApi.ts.golden +++ b/tests/golden/typescript/typescript-fetch/type-aliases-intersection-allof/apis/DefaultApi.ts.golden @@ -70,7 +70,7 @@ export class DefaultApi extends BaseAPI implements DefaultApiInterface { requestParameters: ApiTestIntersectionRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling testIntersectionRaw().', diff --git a/tests/golden/typescript/typescript-fetch/type-aliases-intersection-with-nullable-reference/apis/DefaultApi.ts.golden b/tests/golden/typescript/typescript-fetch/type-aliases-intersection-with-nullable-reference/apis/DefaultApi.ts.golden index cfc4380ae..9d0a1e892 100644 --- a/tests/golden/typescript/typescript-fetch/type-aliases-intersection-with-nullable-reference/apis/DefaultApi.ts.golden +++ b/tests/golden/typescript/typescript-fetch/type-aliases-intersection-with-nullable-reference/apis/DefaultApi.ts.golden @@ -105,7 +105,7 @@ export class DefaultApi extends BaseAPI implements DefaultApiInterface { requestParameters: ApiCreateTestRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling createTestRaw().', @@ -169,7 +169,7 @@ export class DefaultApi extends BaseAPI implements DefaultApiInterface { requestParameters: ApiGetTestRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.id === undefined || requestParameters.id === null) { + if (requestParameters['id'] === undefined || requestParameters['id'] === null) { throw new RequiredError( 'id', 'Required parameter "id" was null or undefined when calling getTestRaw().', diff --git a/tests/golden/typescript/typescript-fetch/type-aliases-nested-union/apis/DefaultApi.ts.golden b/tests/golden/typescript/typescript-fetch/type-aliases-nested-union/apis/DefaultApi.ts.golden index 030f28f6a..e7d569a24 100644 --- a/tests/golden/typescript/typescript-fetch/type-aliases-nested-union/apis/DefaultApi.ts.golden +++ b/tests/golden/typescript/typescript-fetch/type-aliases-nested-union/apis/DefaultApi.ts.golden @@ -70,7 +70,7 @@ export class DefaultApi extends BaseAPI implements DefaultApiInterface { requestParameters: ApiTestNestedUnionRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling testNestedUnionRaw().', diff --git a/tests/golden/typescript/typescript-fetch/type-aliases-simple-type-alias/apis/DefaultApi.ts.golden b/tests/golden/typescript/typescript-fetch/type-aliases-simple-type-alias/apis/DefaultApi.ts.golden index b41980f80..c03ee42ba 100644 --- a/tests/golden/typescript/typescript-fetch/type-aliases-simple-type-alias/apis/DefaultApi.ts.golden +++ b/tests/golden/typescript/typescript-fetch/type-aliases-simple-type-alias/apis/DefaultApi.ts.golden @@ -70,7 +70,7 @@ export class DefaultApi extends BaseAPI implements DefaultApiInterface { requestParameters: ApiTestSimpleAliasRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling testSimpleAliasRaw().', diff --git a/tests/golden/typescript/typescript-fetch/type-aliases-simple-type-alias/models/Bar.ts.golden b/tests/golden/typescript/typescript-fetch/type-aliases-simple-type-alias/models/Bar.ts.golden index 75f379d02..d11a9c841 100644 --- a/tests/golden/typescript/typescript-fetch/type-aliases-simple-type-alias/models/Bar.ts.golden +++ b/tests/golden/typescript/typescript-fetch/type-aliases-simple-type-alias/models/Bar.ts.golden @@ -4,6 +4,7 @@ * Simple Type Alias Test — 1.0.0 * Test fixture for simple type aliases (not unions or intersections) */ -/** A simple number type alias */ - +/** + * A simple number type alias + */ export type Bar = number; diff --git a/tests/golden/typescript/typescript-fetch/type-aliases-simple-type-alias/models/Baz.ts.golden b/tests/golden/typescript/typescript-fetch/type-aliases-simple-type-alias/models/Baz.ts.golden index aa8f1b23f..a8a588b77 100644 --- a/tests/golden/typescript/typescript-fetch/type-aliases-simple-type-alias/models/Baz.ts.golden +++ b/tests/golden/typescript/typescript-fetch/type-aliases-simple-type-alias/models/Baz.ts.golden @@ -4,6 +4,7 @@ * Simple Type Alias Test — 1.0.0 * Test fixture for simple type aliases (not unions or intersections) */ -/** A simple array type alias */ - +/** + * A simple array type alias + */ export type Baz = readonly string[]; diff --git a/tests/golden/typescript/typescript-fetch/type-aliases-simple-type-alias/models/Foo.ts.golden b/tests/golden/typescript/typescript-fetch/type-aliases-simple-type-alias/models/Foo.ts.golden index 5b6ed7ee7..8b8c77b87 100644 --- a/tests/golden/typescript/typescript-fetch/type-aliases-simple-type-alias/models/Foo.ts.golden +++ b/tests/golden/typescript/typescript-fetch/type-aliases-simple-type-alias/models/Foo.ts.golden @@ -4,6 +4,7 @@ * Simple Type Alias Test — 1.0.0 * Test fixture for simple type aliases (not unions or intersections) */ -/** A simple string type alias */ - +/** + * A simple string type alias + */ export type Foo = string; diff --git a/tests/golden/typescript/typescript-fetch/type-aliases-union-mixed/apis/DefaultApi.ts.golden b/tests/golden/typescript/typescript-fetch/type-aliases-union-mixed/apis/DefaultApi.ts.golden index 0bdeb8a1e..2776752ff 100644 --- a/tests/golden/typescript/typescript-fetch/type-aliases-union-mixed/apis/DefaultApi.ts.golden +++ b/tests/golden/typescript/typescript-fetch/type-aliases-union-mixed/apis/DefaultApi.ts.golden @@ -70,7 +70,7 @@ export class DefaultApi extends BaseAPI implements DefaultApiInterface { requestParameters: ApiTestMixedUnionRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling testMixedUnionRaw().', diff --git a/tests/golden/typescript/typescript-fetch/type-aliases-union-with-any/apis/DefaultApi.ts.golden b/tests/golden/typescript/typescript-fetch/type-aliases-union-with-any/apis/DefaultApi.ts.golden index 3f9b65bbd..49e4d80db 100644 --- a/tests/golden/typescript/typescript-fetch/type-aliases-union-with-any/apis/DefaultApi.ts.golden +++ b/tests/golden/typescript/typescript-fetch/type-aliases-union-with-any/apis/DefaultApi.ts.golden @@ -70,10 +70,10 @@ export class DefaultApi extends BaseAPI implements DefaultApiInterface { requestParameters: ApiTestUnionWithAnyRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined) { throw new RequiredError( 'body', - 'Required parameter "body" was null or undefined when calling testUnionWithAnyRaw().', + 'Required parameter "body" was undefined when calling testUnionWithAnyRaw().', ); } // Build path with path parameters diff --git a/tests/golden/typescript/typescript-fetch/type-aliases-union-with-inline-objects/apis/DefaultApi.ts.golden b/tests/golden/typescript/typescript-fetch/type-aliases-union-with-inline-objects/apis/DefaultApi.ts.golden index 5b509271d..2eca6c1cd 100644 --- a/tests/golden/typescript/typescript-fetch/type-aliases-union-with-inline-objects/apis/DefaultApi.ts.golden +++ b/tests/golden/typescript/typescript-fetch/type-aliases-union-with-inline-objects/apis/DefaultApi.ts.golden @@ -70,7 +70,7 @@ export class DefaultApi extends BaseAPI implements DefaultApiInterface { requestParameters: ApiTestUnionWithInlineObjectsRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling testUnionWithInlineObjectsRaw().', diff --git a/tests/golden/typescript/typescript-fetch/type-aliases-union-with-inline-objects/models/Foo.ts.golden b/tests/golden/typescript/typescript-fetch/type-aliases-union-with-inline-objects/models/Foo.ts.golden index 765aee582..c17e65a84 100644 --- a/tests/golden/typescript/typescript-fetch/type-aliases-union-with-inline-objects/models/Foo.ts.golden +++ b/tests/golden/typescript/typescript-fetch/type-aliases-union-with-inline-objects/models/Foo.ts.golden @@ -8,6 +8,7 @@ import type { FooMember1 } from './FooMember1'; import type { FooMember2 } from './FooMember2'; import type { FooMember3 } from './FooMember3'; -/** Union type with inline object schemas */ - +/** + * Union type with inline object schemas + */ export type Foo = FooMember1 | FooMember2 | FooMember3; diff --git a/tests/golden/typescript/typescript-fetch/type-aliases-union-with-interfaces/apis/DefaultApi.ts.golden b/tests/golden/typescript/typescript-fetch/type-aliases-union-with-interfaces/apis/DefaultApi.ts.golden index b53e5250f..75d2dc011 100644 --- a/tests/golden/typescript/typescript-fetch/type-aliases-union-with-interfaces/apis/DefaultApi.ts.golden +++ b/tests/golden/typescript/typescript-fetch/type-aliases-union-with-interfaces/apis/DefaultApi.ts.golden @@ -70,7 +70,7 @@ export class DefaultApi extends BaseAPI implements DefaultApiInterface { requestParameters: ApiTestUnionRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling testUnionRaw().', diff --git a/tests/golden/typescript/typescript-fetch/type-aliases-union-with-primitives/apis/DefaultApi.ts.golden b/tests/golden/typescript/typescript-fetch/type-aliases-union-with-primitives/apis/DefaultApi.ts.golden index 68650c858..bd35d5732 100644 --- a/tests/golden/typescript/typescript-fetch/type-aliases-union-with-primitives/apis/DefaultApi.ts.golden +++ b/tests/golden/typescript/typescript-fetch/type-aliases-union-with-primitives/apis/DefaultApi.ts.golden @@ -70,10 +70,10 @@ export class DefaultApi extends BaseAPI implements DefaultApiInterface { requestParameters: ApiTestUnionPrimitivesRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined) { throw new RequiredError( 'body', - 'Required parameter "body" was null or undefined when calling testUnionPrimitivesRaw().', + 'Required parameter "body" was undefined when calling testUnionPrimitivesRaw().', ); } // Build path with path parameters diff --git a/tests/golden/typescript/typescript-fetch/typed-error-responses/apis/ResourcesApi.ts.golden b/tests/golden/typescript/typescript-fetch/typed-error-responses/apis/ResourcesApi.ts.golden index 55746058e..39ca6fb9e 100644 --- a/tests/golden/typescript/typescript-fetch/typed-error-responses/apis/ResourcesApi.ts.golden +++ b/tests/golden/typescript/typescript-fetch/typed-error-responses/apis/ResourcesApi.ts.golden @@ -333,7 +333,7 @@ export class ResourcesApi extends BaseAPI implements ResourcesApiInterface { requestParameters: ApiCreateResourceRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling createResourceRaw().', @@ -474,7 +474,7 @@ export class ResourcesApi extends BaseAPI implements ResourcesApiInterface { requestParameters: ApiCreateResourceWithWildcardSuccessRequest, initOverrides?: RequestInit | InitOverrideFunction, ): Promise { - if (requestParameters.body === undefined || requestParameters.body === null) { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { throw new RequiredError( 'body', 'Required parameter "body" was null or undefined when calling createResourceWithWildcardSuccessRaw().', diff --git a/tests/golden/typescript/typescript-fetch/typescript-additional-properties/models/AllOfRenamedMap.ts.golden b/tests/golden/typescript/typescript-fetch/typescript-additional-properties/models/AllOfRenamedMap.ts.golden index 43b4af0b6..34f224090 100644 --- a/tests/golden/typescript/typescript-fetch/typescript-additional-properties/models/AllOfRenamedMap.ts.golden +++ b/tests/golden/typescript/typescript-fetch/typescript-additional-properties/models/AllOfRenamedMap.ts.golden @@ -6,6 +6,7 @@ import type { AllOfRenamedMapAllOf2 } from './AllOfRenamedMapAllOf2'; import type { RenamedMap } from './RenamedMap'; -/** Composed map with overlapping declared fields. */ - +/** + * Composed map with overlapping declared fields. + */ export type AllOfRenamedMap = Readonly; diff --git a/tests/golden/typescript/typescript-fetch/typescript-additional-properties/models/ClosedExternalEvent.ts.golden b/tests/golden/typescript/typescript-fetch/typescript-additional-properties/models/ClosedExternalEvent.ts.golden index fd9897860..8620090c0 100644 --- a/tests/golden/typescript/typescript-fetch/typescript-additional-properties/models/ClosedExternalEvent.ts.golden +++ b/tests/golden/typescript/typescript-fetch/typescript-additional-properties/models/ClosedExternalEvent.ts.golden @@ -5,4 +5,4 @@ */ import type { Detail } from './Detail'; -export type ClosedExternalEvent = { started: Detail } | { finished: Detail }; +export type ClosedExternalEvent = Record<'started', Detail> | Record<'finished', Detail>; diff --git a/tests/golden/typescript/typescript-fetch/typescript-additional-properties/models/SearchStreamEvent.ts.golden b/tests/golden/typescript/typescript-fetch/typescript-additional-properties/models/SearchStreamEvent.ts.golden index ab5bf10f3..248c8579b 100644 --- a/tests/golden/typescript/typescript-fetch/typescript-additional-properties/models/SearchStreamEvent.ts.golden +++ b/tests/golden/typescript/typescript-fetch/typescript-additional-properties/models/SearchStreamEvent.ts.golden @@ -6,7 +6,6 @@ import type { SearchStreamEventResponseWebSearchCallSearching } from './SearchStreamEventResponseWebSearchCallSearching'; export type SearchStreamEvent = - | ({ - type: 'response.web_search_call.searching'; - } & SearchStreamEventResponseWebSearchCallSearching) - | { type: 'response.completed' }; + | (Record<'type', 'response.web_search_call.searching'> & + SearchStreamEventResponseWebSearchCallSearching) + | Record<'type', 'response.completed'>; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/README.md.golden b/tests/golden/typescript/typescript-fetch/untagged-union/README.md.golden new file mode 100644 index 000000000..d0af3cc7d --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/README.md.golden @@ -0,0 +1,248 @@ +# untagged-union-conversion + +Generated API client + +**Version:** 1.0.0 + +## Overview + +This package provides a TypeScript/JavaScript client for the Untagged union conversion API. It uses the native [Fetch API](https://fetch.spec.whatwg.org/) for HTTP requests and works in both Node.js and browser environments. + +## Features + +- ✨ **Type-safe** - Full TypeScript support with generated types +- 🚀 **Modern** - Uses native Fetch API, no external HTTP dependencies +- 🔧 **Configurable** - Flexible configuration options +- 🎯 **Middleware** - Support for request/response interceptors +- 📦 **Tree-shakeable** - Import only what you need +- 🌐 **Universal** - Works in Node.js and browsers + +## Installation + +### From npm (published package) + +```bash +npm install untagged-union-conversion +``` + +### From local path (development) + +Add the package to your `package.json` using the `file:` protocol: + +```json +{ + "dependencies": { + "untagged-union-conversion": "file:../../path/to/generated/package" + } +} +``` + +Then run: + +```bash +npm install +``` + +## Quick Start + +```typescript +import { Configuration, ConversionApi } from 'untagged-union-conversion'; + +// Create a configuration +const config = new Configuration({ + basePath: 'https://api.example.com', + headers: { + Authorization: 'Bearer YOUR_TOKEN', + }, +}); + +// Initialize the API client +const api = new ConversionApi(config); + +// Make API calls +try { + const result = await api.someMethod(); + console.log(result); +} catch (error) { + console.error('API Error:', error); +} +``` + +## Configuration + +The `Configuration` class accepts the following options: + +```typescript +interface ConfigurationParameters { + /** Base URL for API requests */ + basePath?: string; + + /** Custom fetch implementation */ + fetchApi?: typeof fetch; + + /** Request/response middleware */ + middleware?: Middleware[]; + + /** Custom query string serializer */ + queryParamsStringify?: (params: HTTPQuery) => string; + + /** Default headers for all requests */ + headers?: Record; + + /** Credentials mode for requests */ + credentials?: RequestCredentials; +} +``` + +### Example with custom configuration + +```typescript +const config = new Configuration({ + basePath: 'https://api.example.com', + headers: { + 'X-API-Key': 'your-api-key', + 'Content-Type': 'application/json', + }, + credentials: 'include', +}); +``` + +## Middleware + +Add custom middleware to intercept requests and responses: + +```typescript +import { Configuration, Middleware } from 'untagged-union-conversion'; + +const loggingMiddleware: Middleware = { + pre: async (context) => { + console.log('Request:', context.url); + return context; + }, + post: async (context) => { + console.log('Response:', context.response.status); + return context.response; + }, + onError: async (context) => { + console.error('Error:', context.error); + return undefined; + }, +}; + +const config = new Configuration({ + basePath: 'https://api.example.com', + middleware: [loggingMiddleware], +}); +``` + +## Error Handling + +The client throws typed errors for different failure scenarios: + +```typescript +import { ResponseError, FetchError, RequiredError } from 'untagged-union-conversion'; + +try { + const result = await api.someMethod(); +} catch (error) { + if (error instanceof ResponseError) { + // HTTP error response (4xx, 5xx) + console.error('HTTP Error:', error.response.status); + } else if (error instanceof FetchError) { + // Network or fetch error + console.error('Network Error:', error.cause); + } else if (error instanceof RequiredError) { + // Missing required parameter + console.error('Missing field:', error.field); + } +} +``` + +## API Reference + +This package exports the following: + +- **Configuration** - Client configuration class +- **BaseAPI** - Base class for all API clients +- **API Classes** - Generated API client classes (e.g., `UserApi`, `PostApi`) +- **Models** - Generated TypeScript interfaces for request/response types +- **Errors** - `ResponseError`, `FetchError`, `RequiredError` +- **Types** - TypeScript type definitions + +## Development + +### Building + +To build the package: + +```bash +npm install +npm run build +``` + +This will compile TypeScript to JavaScript in the `dist/` directory. + +### Building for ESM + +To build ES modules: + +```bash +npm run build +``` + +## TypeScript Support + +This package includes TypeScript type definitions. No additional `@types` package is needed. + +### TypeScript Configuration + +This package works with standard TypeScript configurations. If you're using a bundler-based setup, you may want to configure: + +```json +{ + "compilerOptions": { + "moduleResolution": "bundler" + } +} +``` + +### Type Imports + +```typescript +import type { User, CreateUserRequest } from 'untagged-union-conversion'; + +const user: User = { + id: 1, + name: 'John Doe', + email: 'john@example.com', +}; +``` + +## Browser Support + +This package uses the native Fetch API, which is supported in: + +- Chrome 42+ +- Firefox 39+ +- Safari 10.1+ +- Edge 14+ +- Node.js 18+ (native fetch) +- Node.js <18 (with `node-fetch` polyfill) + +For older browsers, you may need to include a fetch polyfill. + +## License + +This is an auto-generated API client. Please refer to your API documentation for license information. + +## Support + +For issues related to the API itself, please contact the API provider. + +For issues with this generated client, please check the OpenAPI specification used to generate it. + +--- + +**Generated by OpenAPI Generator** + +API Version: 1.0.0 diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/apis/ConversionApi.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/apis/ConversionApi.ts.golden new file mode 100644 index 000000000..241122791 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/apis/ConversionApi.ts.golden @@ -0,0 +1,757 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { ExchangeMultipartMultipartRequestBody } from '../models/ExchangeMultipartMultipartRequestBody'; +import type { NullableDetail } from '../models/NullableDetail'; +import type { NullableScalarInner } from '../models/NullableScalarInner'; +import type { SendOptionalInlineScalarRequest } from '../models/SendOptionalInlineScalarRequest'; +import type { Tool } from '../models/Tool'; +import type { + Configuration, + HTTPQuery, + HttpSuccessResponse, + InitOverrideFunction, +} from '../runtime/runtime'; +import { + BaseAPI, + DefaultConfig, + JSONApiResponse, + RequiredError, + ResponseError, + VoidApiResponse, + multipartHeaderValue, + uploadFileData, + uploadFileFilename, +} from '../runtime/runtime'; + +export interface ApiExchangeMatrixRequest { + body: Tool[][]; +} + +export interface ApiExchangeMultipartRequest { + body: ExchangeMultipartMultipartRequestBody; +} + +export interface ApiExchangeNullableRequest { + body: Tool[] | null; +} + +export interface ApiSendOptionalDetailRequest { + body?: NullableDetail; +} + +export interface ApiSendOptionalInlineScalarRequest { + body?: SendOptionalInlineScalarRequest; +} + +export interface ApiSendOptionalScalarRequest { + body?: NullableScalarInner; +} + +export interface ApiSendRequiredScalarRequest { + body: NullableScalarInner; +} + +export type ExchangeMatrixRawResponse = + | (JSONApiResponse & { status: 200 }) + | (JSONApiResponse & HttpSuccessResponse); +export type ExchangeMultipartRawResponse = + | (VoidApiResponse & { status: 204 }) + | (VoidApiResponse & HttpSuccessResponse); +export type ExchangeNullableRawResponse = + | (JSONApiResponse & { status: 200 }) + | (JSONApiResponse & HttpSuccessResponse); +export type SendOptionalDetailRawResponse = + | (VoidApiResponse & { status: 204 }) + | (VoidApiResponse & HttpSuccessResponse); +export type SendOptionalInlineScalarRawResponse = + | (VoidApiResponse & { status: 204 }) + | (VoidApiResponse & HttpSuccessResponse); +export type SendOptionalScalarRawResponse = + | (VoidApiResponse & { status: 204 }) + | (VoidApiResponse & HttpSuccessResponse); +export type SendRequiredScalarRawResponse = + | (VoidApiResponse & { status: 204 }) + | (VoidApiResponse & HttpSuccessResponse); + +export type ExchangeMatrixErrorDetail = + | { + kind: 'badRequest'; + status: 400; + raw: Response; + value(): Promise; + } + | { + kind: 'unexpected'; + status: number; + raw: Response; + value(): Promise; + }; + +export class ExchangeMatrixError extends ResponseError { + readonly detail: ExchangeMatrixErrorDetail; + + constructor( + response: Response, + detail: ExchangeMatrixErrorDetail, + msg = 'Response returned an error code', + ) { + super(response, msg); + this.detail = detail; + } +} + +export type ExchangeMultipartErrorDetail = { + kind: 'unexpected'; + status: number; + raw: Response; + value(): Promise; +}; + +export class ExchangeMultipartError extends ResponseError { + readonly detail: ExchangeMultipartErrorDetail; + + constructor( + response: Response, + detail: ExchangeMultipartErrorDetail, + msg = 'Response returned an error code', + ) { + super(response, msg); + this.detail = detail; + } +} + +export type ExchangeNullableErrorDetail = { + kind: 'unexpected'; + status: number; + raw: Response; + value(): Promise; +}; + +export class ExchangeNullableError extends ResponseError { + readonly detail: ExchangeNullableErrorDetail; + + constructor( + response: Response, + detail: ExchangeNullableErrorDetail, + msg = 'Response returned an error code', + ) { + super(response, msg); + this.detail = detail; + } +} + +export type SendOptionalDetailErrorDetail = { + kind: 'unexpected'; + status: number; + raw: Response; + value(): Promise; +}; + +export class SendOptionalDetailError extends ResponseError { + readonly detail: SendOptionalDetailErrorDetail; + + constructor( + response: Response, + detail: SendOptionalDetailErrorDetail, + msg = 'Response returned an error code', + ) { + super(response, msg); + this.detail = detail; + } +} + +export type SendOptionalInlineScalarErrorDetail = { + kind: 'unexpected'; + status: number; + raw: Response; + value(): Promise; +}; + +export class SendOptionalInlineScalarError extends ResponseError { + readonly detail: SendOptionalInlineScalarErrorDetail; + + constructor( + response: Response, + detail: SendOptionalInlineScalarErrorDetail, + msg = 'Response returned an error code', + ) { + super(response, msg); + this.detail = detail; + } +} + +export type SendOptionalScalarErrorDetail = { + kind: 'unexpected'; + status: number; + raw: Response; + value(): Promise; +}; + +export class SendOptionalScalarError extends ResponseError { + readonly detail: SendOptionalScalarErrorDetail; + + constructor( + response: Response, + detail: SendOptionalScalarErrorDetail, + msg = 'Response returned an error code', + ) { + super(response, msg); + this.detail = detail; + } +} + +export type SendRequiredScalarErrorDetail = { + kind: 'unexpected'; + status: number; + raw: Response; + value(): Promise; +}; + +export class SendRequiredScalarError extends ResponseError { + readonly detail: SendRequiredScalarErrorDetail; + + constructor( + response: Response, + detail: SendRequiredScalarErrorDetail, + msg = 'Response returned an error code', + ) { + super(response, msg); + this.detail = detail; + } +} + +export interface ConversionApiInterface { + exchangeMatrixRaw: ( + requestParameters: ApiExchangeMatrixRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ) => Promise; + exchangeMatrix: ( + requestParameters: ApiExchangeMatrixRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ) => Promise; + exchangeMultipartRaw: ( + requestParameters: ApiExchangeMultipartRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ) => Promise; + exchangeMultipart: ( + requestParameters: ApiExchangeMultipartRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ) => Promise; + exchangeNullableRaw: ( + requestParameters: ApiExchangeNullableRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ) => Promise; + exchangeNullable: ( + requestParameters: ApiExchangeNullableRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ) => Promise; + sendOptionalDetailRaw: ( + requestParameters: ApiSendOptionalDetailRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ) => Promise; + sendOptionalDetail: ( + requestParameters: ApiSendOptionalDetailRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ) => Promise; + sendOptionalInlineScalarRaw: ( + requestParameters: ApiSendOptionalInlineScalarRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ) => Promise; + sendOptionalInlineScalar: ( + requestParameters: ApiSendOptionalInlineScalarRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ) => Promise; + sendOptionalScalarRaw: ( + requestParameters: ApiSendOptionalScalarRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ) => Promise; + sendOptionalScalar: ( + requestParameters: ApiSendOptionalScalarRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ) => Promise; + sendRequiredScalarRaw: ( + requestParameters: ApiSendRequiredScalarRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ) => Promise; + sendRequiredScalar: ( + requestParameters: ApiSendRequiredScalarRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ) => Promise; +} + +export class ConversionApi extends BaseAPI implements ConversionApiInterface { + /** + * Initialize the API client + */ + constructor(configuration?: Configuration) { + super(configuration ?? DefaultConfig); + } + + async exchangeMatrixRaw( + requestParameters: ApiExchangeMatrixRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ): Promise { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { + throw new RequiredError( + 'body', + 'Required parameter "body" was null or undefined when calling exchangeMatrixRaw().', + ); + } + // Build path with path parameters + const urlPath = `/matrix`; + // Build query parameters + const queryParameters: HTTPQuery = {}; + // Build headers + const headerParameters: Record = { + 'Content-Type': 'application/json', + }; + + // Prepare request body + const requestBody = requestParameters.body; + // Make request + const response = await this.request( + { + path: urlPath, + method: 'POST', + headers: headerParameters, + query: queryParameters, + body: requestBody, + }, + initOverrides, + ); + + // Handle responses + if (!(response.status >= 200 && response.status < 300)) { + const errorRaw = response.clone(); + if (response.status === 400) { + let errorValue: Promise | undefined; + throw new ExchangeMatrixError(response, { + kind: 'badRequest', + status: response.status as 400, + raw: response, + value: () => { + errorValue ??= new JSONApiResponse(errorRaw.clone()).value(); + return errorValue; + }, + }); + } else { + let errorValue: Promise | undefined; + throw new ExchangeMatrixError(response, { + kind: 'unexpected', + status: response.status, + raw: response, + value: () => { + errorValue ??= errorRaw.clone().blob(); + return errorValue; + }, + }); + } + } + if (response.status === 200) { + return new JSONApiResponse(response) as JSONApiResponse & { status: 200 }; + } else { + return new JSONApiResponse(response) as JSONApiResponse & HttpSuccessResponse; + } + } + + async exchangeMatrix( + requestParameters: ApiExchangeMatrixRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ): Promise { + const response = await this.exchangeMatrixRaw(requestParameters, initOverrides); + return (await response.value()) as Tool[][]; + } + + async exchangeMultipartRaw( + requestParameters: ApiExchangeMultipartRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ): Promise { + if (requestParameters['body'] === undefined || requestParameters['body'] === null) { + throw new RequiredError( + 'body', + 'Required parameter "body" was null or undefined when calling exchangeMultipartRaw().', + ); + } + // Build path with path parameters + const urlPath = `/multipart`; + // Build query parameters + const queryParameters: HTTPQuery = {}; + // Build headers + const headerParameters: Record = {}; + + // Prepare request body + const multipartBody = requestParameters['body']; + const multipartBoundary = '----openapi-nexus-' + Math.random().toString(16).slice(2); + const multipartChunks: Array = []; + headerParameters['Content-Type'] = 'multipart/form-data; boundary=' + multipartBoundary; + const multipartValue$0 = multipartBody['payload_values']; + multipartChunks.push( + '--', + multipartBoundary, + '\r\nContent-Disposition: form-data; name="payload_values"\r\nContent-Type: application/json\r\n\r\n', + ); + multipartChunks.push(JSON.stringify(multipartValue$0)); + multipartChunks.push('\r\n'); + const multipartValue$1 = multipartBody['upload_file']; + if (multipartValue$1 !== undefined && multipartValue$1 !== null) { + const multipartFilename$1 = uploadFileFilename(multipartValue$1, 'upload_file'); + multipartChunks.push( + '--', + multipartBoundary, + '\r\nContent-Disposition: form-data; name="upload_file"; filename="', + multipartHeaderValue(multipartFilename$1), + '"\r\nContent-Type: application/octet-stream\r\n\r\n', + ); + multipartChunks.push(uploadFileData(multipartValue$1)); + multipartChunks.push('\r\n'); + } + multipartChunks.push('--' + multipartBoundary + '--\r\n'); + const requestBody = new Blob(multipartChunks); + // Make request + const response = await this.request( + { + path: urlPath, + method: 'POST', + headers: headerParameters, + query: queryParameters, + body: requestBody, + }, + initOverrides, + ); + + // Handle responses + if (!(response.status >= 200 && response.status < 300)) { + const errorRaw = response.clone(); + { + let errorValue: Promise | undefined; + throw new ExchangeMultipartError(response, { + kind: 'unexpected', + status: response.status, + raw: response, + value: () => { + errorValue ??= errorRaw.clone().blob(); + return errorValue; + }, + }); + } + } + if (response.status === 204) { + return new VoidApiResponse(response) as VoidApiResponse & { status: 204 }; + } else { + return new VoidApiResponse(response) as VoidApiResponse & HttpSuccessResponse; + } + } + + async exchangeMultipart( + requestParameters: ApiExchangeMultipartRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ): Promise { + const response = await this.exchangeMultipartRaw(requestParameters, initOverrides); + return await response.value(); + } + + async exchangeNullableRaw( + requestParameters: ApiExchangeNullableRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ): Promise { + if (requestParameters['body'] === undefined) { + throw new RequiredError( + 'body', + 'Required parameter "body" was undefined when calling exchangeNullableRaw().', + ); + } + // Build path with path parameters + const urlPath = `/nullable`; + // Build query parameters + const queryParameters: HTTPQuery = {}; + // Build headers + const headerParameters: Record = { + 'Content-Type': 'application/json', + }; + + // Prepare request body + const requestBody = requestParameters.body; + // Make request + const response = await this.request( + { + path: urlPath, + method: 'POST', + headers: headerParameters, + query: queryParameters, + body: requestBody, + }, + initOverrides, + ); + + // Handle responses + if (!(response.status >= 200 && response.status < 300)) { + const errorRaw = response.clone(); + { + let errorValue: Promise | undefined; + throw new ExchangeNullableError(response, { + kind: 'unexpected', + status: response.status, + raw: response, + value: () => { + errorValue ??= errorRaw.clone().blob(); + return errorValue; + }, + }); + } + } + if (response.status === 200) { + return new JSONApiResponse(response) as JSONApiResponse & { status: 200 }; + } else { + return new JSONApiResponse(response) as JSONApiResponse & HttpSuccessResponse; + } + } + + async exchangeNullable( + requestParameters: ApiExchangeNullableRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ): Promise { + const response = await this.exchangeNullableRaw(requestParameters, initOverrides); + return (await response.value()) as Tool[] | null; + } + + async sendOptionalDetailRaw( + requestParameters: ApiSendOptionalDetailRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ): Promise { + // Build path with path parameters + const urlPath = `/optional-detail`; + // Build query parameters + const queryParameters: HTTPQuery = {}; + // Build headers + const headerParameters: Record = { + 'Content-Type': 'application/json', + }; + + // Prepare request body + const requestBody = requestParameters.body; + // Make request + const response = await this.request( + { + path: urlPath, + method: 'POST', + headers: headerParameters, + query: queryParameters, + body: requestBody, + }, + initOverrides, + ); + + // Handle responses + if (!(response.status >= 200 && response.status < 300)) { + const errorRaw = response.clone(); + { + let errorValue: Promise | undefined; + throw new SendOptionalDetailError(response, { + kind: 'unexpected', + status: response.status, + raw: response, + value: () => { + errorValue ??= errorRaw.clone().blob(); + return errorValue; + }, + }); + } + } + if (response.status === 204) { + return new VoidApiResponse(response) as VoidApiResponse & { status: 204 }; + } else { + return new VoidApiResponse(response) as VoidApiResponse & HttpSuccessResponse; + } + } + + async sendOptionalDetail( + requestParameters: ApiSendOptionalDetailRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ): Promise { + const response = await this.sendOptionalDetailRaw(requestParameters, initOverrides); + return await response.value(); + } + + async sendOptionalInlineScalarRaw( + requestParameters: ApiSendOptionalInlineScalarRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ): Promise { + // Build path with path parameters + const urlPath = `/optional-inline-scalar`; + // Build query parameters + const queryParameters: HTTPQuery = {}; + // Build headers + const headerParameters: Record = { + 'Content-Type': 'application/json', + }; + + // Prepare request body + const requestBody = requestParameters.body; + // Make request + const response = await this.request( + { + path: urlPath, + method: 'POST', + headers: headerParameters, + query: queryParameters, + body: requestBody, + }, + initOverrides, + ); + + // Handle responses + if (!(response.status >= 200 && response.status < 300)) { + const errorRaw = response.clone(); + { + let errorValue: Promise | undefined; + throw new SendOptionalInlineScalarError(response, { + kind: 'unexpected', + status: response.status, + raw: response, + value: () => { + errorValue ??= errorRaw.clone().blob(); + return errorValue; + }, + }); + } + } + if (response.status === 204) { + return new VoidApiResponse(response) as VoidApiResponse & { status: 204 }; + } else { + return new VoidApiResponse(response) as VoidApiResponse & HttpSuccessResponse; + } + } + + async sendOptionalInlineScalar( + requestParameters: ApiSendOptionalInlineScalarRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ): Promise { + const response = await this.sendOptionalInlineScalarRaw(requestParameters, initOverrides); + return await response.value(); + } + + async sendOptionalScalarRaw( + requestParameters: ApiSendOptionalScalarRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ): Promise { + // Build path with path parameters + const urlPath = `/optional-scalar`; + // Build query parameters + const queryParameters: HTTPQuery = {}; + // Build headers + const headerParameters: Record = { + 'Content-Type': 'application/json', + }; + + // Prepare request body + const requestBody = requestParameters.body; + // Make request + const response = await this.request( + { + path: urlPath, + method: 'POST', + headers: headerParameters, + query: queryParameters, + body: requestBody, + }, + initOverrides, + ); + + // Handle responses + if (!(response.status >= 200 && response.status < 300)) { + const errorRaw = response.clone(); + { + let errorValue: Promise | undefined; + throw new SendOptionalScalarError(response, { + kind: 'unexpected', + status: response.status, + raw: response, + value: () => { + errorValue ??= errorRaw.clone().blob(); + return errorValue; + }, + }); + } + } + if (response.status === 204) { + return new VoidApiResponse(response) as VoidApiResponse & { status: 204 }; + } else { + return new VoidApiResponse(response) as VoidApiResponse & HttpSuccessResponse; + } + } + + async sendOptionalScalar( + requestParameters: ApiSendOptionalScalarRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ): Promise { + const response = await this.sendOptionalScalarRaw(requestParameters, initOverrides); + return await response.value(); + } + + async sendRequiredScalarRaw( + requestParameters: ApiSendRequiredScalarRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ): Promise { + if (requestParameters['body'] === undefined) { + throw new RequiredError( + 'body', + 'Required parameter "body" was undefined when calling sendRequiredScalarRaw().', + ); + } + // Build path with path parameters + const urlPath = `/required-scalar`; + // Build query parameters + const queryParameters: HTTPQuery = {}; + // Build headers + const headerParameters: Record = { + 'Content-Type': 'application/json', + }; + + // Prepare request body + const requestBody = requestParameters.body; + // Make request + const response = await this.request( + { + path: urlPath, + method: 'POST', + headers: headerParameters, + query: queryParameters, + body: requestBody, + }, + initOverrides, + ); + + // Handle responses + if (!(response.status >= 200 && response.status < 300)) { + const errorRaw = response.clone(); + { + let errorValue: Promise | undefined; + throw new SendRequiredScalarError(response, { + kind: 'unexpected', + status: response.status, + raw: response, + value: () => { + errorValue ??= errorRaw.clone().blob(); + return errorValue; + }, + }); + } + } + if (response.status === 204) { + return new VoidApiResponse(response) as VoidApiResponse & { status: 204 }; + } else { + return new VoidApiResponse(response) as VoidApiResponse & HttpSuccessResponse; + } + } + + async sendRequiredScalar( + requestParameters: ApiSendRequiredScalarRequest, + initOverrides?: RequestInit | InitOverrideFunction, + ): Promise { + const response = await this.sendRequiredScalarRaw(requestParameters, initOverrides); + return await response.value(); + } +} diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/apis/index.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/apis/index.ts.golden new file mode 100644 index 000000000..24b35e481 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/apis/index.ts.golden @@ -0,0 +1,39 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export type { + ApiExchangeMatrixRequest, + ExchangeMatrixRawResponse, + ExchangeMatrixErrorDetail, + ApiExchangeMultipartRequest, + ExchangeMultipartRawResponse, + ExchangeMultipartErrorDetail, + ApiExchangeNullableRequest, + ExchangeNullableRawResponse, + ExchangeNullableErrorDetail, + ApiSendOptionalDetailRequest, + SendOptionalDetailRawResponse, + SendOptionalDetailErrorDetail, + ApiSendOptionalInlineScalarRequest, + SendOptionalInlineScalarRawResponse, + SendOptionalInlineScalarErrorDetail, + ApiSendOptionalScalarRequest, + SendOptionalScalarRawResponse, + SendOptionalScalarErrorDetail, + ApiSendRequiredScalarRequest, + SendRequiredScalarRawResponse, + SendRequiredScalarErrorDetail, + ConversionApiInterface, +} from './ConversionApi'; +export { + ConversionApi, + ExchangeMatrixError, + ExchangeMultipartError, + ExchangeNullableError, + SendOptionalDetailError, + SendOptionalInlineScalarError, + SendOptionalScalarError, + SendRequiredScalarError, +} from './ConversionApi'; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/index.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/index.ts.golden new file mode 100644 index 000000000..8ba5e6eb9 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/index.ts.golden @@ -0,0 +1,8 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export * from './runtime/runtime'; +export * from './apis'; +export * from './models'; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/Adjacent.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/Adjacent.ts.golden new file mode 100644 index 000000000..8e913d738 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/Adjacent.ts.golden @@ -0,0 +1,10 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail } from './Detail'; + +export type Adjacent = + | globalThis.Record<'event_type', 'empty'> + | (globalThis.Record<'event_type', 'data'> & globalThis.Record<'event_data', Detail>); diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/AdjacentContainer.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/AdjacentContainer.ts.golden new file mode 100644 index 000000000..aa9852e7c --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/AdjacentContainer.ts.golden @@ -0,0 +1,11 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail } from './Detail'; + +export type AdjacentContainer = + | globalThis.Record<'event_type', 'empty'> + | (globalThis.Record<'event_type', 'data'> & + globalThis.Record<'event_data', readonly (globalThis.Record | string)[]>); diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/AdjacentContainerEmpty.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/AdjacentContainerEmpty.ts.golden new file mode 100644 index 000000000..3eb26328e --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/AdjacentContainerEmpty.ts.golden @@ -0,0 +1,8 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface AdjacentContainerEmpty { + readonly event_type: 'empty'; +} diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/AdjacentEmpty.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/AdjacentEmpty.ts.golden new file mode 100644 index 000000000..4a0a57b2d --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/AdjacentEmpty.ts.golden @@ -0,0 +1,8 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface AdjacentEmpty { + readonly event_type: 'empty'; +} diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/AdjacentOrNumber.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/AdjacentOrNumber.ts.golden new file mode 100644 index 000000000..6e65800a1 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/AdjacentOrNumber.ts.golden @@ -0,0 +1,8 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Adjacent } from './Adjacent'; + +export type AdjacentOrNumber = Adjacent | number; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/AdjacentPrimitive.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/AdjacentPrimitive.ts.golden new file mode 100644 index 000000000..e513af304 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/AdjacentPrimitive.ts.golden @@ -0,0 +1,8 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export type AdjacentPrimitive = + | globalThis.Record<'event_type', 'empty'> + | (globalThis.Record<'event_type', 'data'> & globalThis.Record<'event_data', readonly number[]>); diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/AdjacentPrimitiveEmpty.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/AdjacentPrimitiveEmpty.ts.golden new file mode 100644 index 000000000..d5715510d --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/AdjacentPrimitiveEmpty.ts.golden @@ -0,0 +1,8 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface AdjacentPrimitiveEmpty { + readonly event_type: 'empty'; +} diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/AliasChoice.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/AliasChoice.ts.golden new file mode 100644 index 000000000..203715981 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/AliasChoice.ts.golden @@ -0,0 +1,9 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail } from './Detail'; +import type { ValueAlias } from './ValueAlias'; + +export type AliasChoice = ValueAlias | Detail; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/AliasOuter.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/AliasOuter.ts.golden new file mode 100644 index 000000000..1915f952e --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/AliasOuter.ts.golden @@ -0,0 +1,9 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { OpenAlias } from './OpenAlias'; +import type { SearchTool } from './SearchTool'; + +export type AliasOuter = OpenAlias | SearchTool; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/AlreadyNullableEnumParent.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/AlreadyNullableEnumParent.ts.golden new file mode 100644 index 000000000..b03671687 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/AlreadyNullableEnumParent.ts.golden @@ -0,0 +1,10 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { NullableScalarInner } from './NullableScalarInner'; + +export interface AlreadyNullableEnumParent { + readonly scalar_value: NullableScalarInner | null; +} diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/AlreadyNullableOuterEnumParent.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/AlreadyNullableOuterEnumParent.ts.golden new file mode 100644 index 000000000..838d7df05 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/AlreadyNullableOuterEnumParent.ts.golden @@ -0,0 +1,10 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { NullableScalarInner } from './NullableScalarInner'; + +export interface AlreadyNullableOuterEnumParent { + readonly scalar_value: NullableScalarInner | null; +} diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/AlternativeTool.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/AlternativeTool.ts.golden new file mode 100644 index 000000000..3ce32b91c --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/AlternativeTool.ts.golden @@ -0,0 +1,10 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface AlternativeTool { + readonly input_schema: globalThis.Record; + readonly name: string; + readonly second_option?: boolean; +} diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/AmbiguousInner.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/AmbiguousInner.ts.golden new file mode 100644 index 000000000..19aaa2c19 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/AmbiguousInner.ts.golden @@ -0,0 +1,6 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export type AmbiguousInner = unknown; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/AmbiguousMap.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/AmbiguousMap.ts.golden new file mode 100644 index 000000000..204da4c63 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/AmbiguousMap.ts.golden @@ -0,0 +1,6 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface AmbiguousMap extends Readonly> {} diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/AmbiguousMapOuter.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/AmbiguousMapOuter.ts.golden new file mode 100644 index 000000000..05834f583 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/AmbiguousMapOuter.ts.golden @@ -0,0 +1,6 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export type AmbiguousMapOuter = unknown; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/AmbiguousOuter.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/AmbiguousOuter.ts.golden new file mode 100644 index 000000000..cac1198df --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/AmbiguousOuter.ts.golden @@ -0,0 +1,8 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { AmbiguousInner } from './AmbiguousInner'; + +export type AmbiguousOuter = AmbiguousInner | string; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/AmbiguousTool.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/AmbiguousTool.ts.golden new file mode 100644 index 000000000..4519f9058 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/AmbiguousTool.ts.golden @@ -0,0 +1,9 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { AlternativeTool } from './AlternativeTool'; +import type { ClientTool } from './ClientTool'; + +export type AmbiguousTool = ClientTool | AlternativeTool; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/ArrayPayload.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/ArrayPayload.ts.golden new file mode 100644 index 000000000..bd4513def --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/ArrayPayload.ts.golden @@ -0,0 +1,8 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface ArrayPayload { + readonly wire_items: readonly string[]; +} diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/ArrayPayloadOrBoolean.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/ArrayPayloadOrBoolean.ts.golden new file mode 100644 index 000000000..65bee1557 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/ArrayPayloadOrBoolean.ts.golden @@ -0,0 +1,8 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { ArrayPayload } from './ArrayPayload'; + +export type ArrayPayloadOrBoolean = ArrayPayload | boolean; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/ArrayWrapped.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/ArrayWrapped.ts.golden new file mode 100644 index 000000000..7df6de42b --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/ArrayWrapped.ts.golden @@ -0,0 +1,8 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { DetailArrayOrObject } from './DetailArrayOrObject'; + +export type ArrayWrapped = DetailArrayOrObject; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/BinaryModel.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/BinaryModel.ts.golden new file mode 100644 index 000000000..430543750 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/BinaryModel.ts.golden @@ -0,0 +1,9 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface BinaryModel { + readonly binary_data: Blob | File; + readonly field_name: string; +} diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/BinaryUnion.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/BinaryUnion.ts.golden new file mode 100644 index 000000000..e5ba994a8 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/BinaryUnion.ts.golden @@ -0,0 +1,8 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { BinaryModel } from './BinaryModel'; + +export type BinaryUnion = BinaryModel | boolean; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/ClientTool.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/ClientTool.ts.golden new file mode 100644 index 000000000..8a57f60f6 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/ClientTool.ts.golden @@ -0,0 +1,9 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface ClientTool { + readonly input_schema: globalThis.Record; + readonly name: string; +} diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/ConstrainedOuterEnumParent.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/ConstrainedOuterEnumParent.ts.golden new file mode 100644 index 000000000..45083ae37 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/ConstrainedOuterEnumParent.ts.golden @@ -0,0 +1,10 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { NullableScalarInner } from './NullableScalarInner'; + +export interface ConstrainedOuterEnumParent { + readonly scalar_value: NullableScalarInner | null; +} diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/ContainerOuterEnum.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/ContainerOuterEnum.ts.golden new file mode 100644 index 000000000..13384eec4 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/ContainerOuterEnum.ts.golden @@ -0,0 +1,8 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { NullableScalarInner } from './NullableScalarInner'; + +export type ContainerOuterEnum = readonly (NullableScalarInner | null)[]; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/Detail.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/Detail.ts.golden new file mode 100644 index 000000000..5ddc5530c --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/Detail.ts.golden @@ -0,0 +1,8 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface Detail { + readonly wire_name: string; +} diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/DetailArrayOrObject.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/DetailArrayOrObject.ts.golden new file mode 100644 index 000000000..3379f80cc --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/DetailArrayOrObject.ts.golden @@ -0,0 +1,8 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail } from './Detail'; + +export type DetailArrayOrObject = readonly Detail[] | Detail; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/DictionaryAlias.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/DictionaryAlias.ts.golden new file mode 100644 index 000000000..0b403dfd8 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/DictionaryAlias.ts.golden @@ -0,0 +1,8 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail } from './Detail'; + +export type DictionaryAlias = Readonly>; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/EnumFreeOuterEnumParent.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/EnumFreeOuterEnumParent.ts.golden new file mode 100644 index 000000000..2b88d683e --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/EnumFreeOuterEnumParent.ts.golden @@ -0,0 +1,10 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { NullableScalarInner } from './NullableScalarInner'; + +export interface EnumFreeOuterEnumParent { + readonly scalar_value: NullableScalarInner | null; +} diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/ExchangeMultipartMultipartRequestBody.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/ExchangeMultipartMultipartRequestBody.ts.golden new file mode 100644 index 000000000..871b76edd --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/ExchangeMultipartMultipartRequestBody.ts.golden @@ -0,0 +1,12 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { UploadFileInput } from '../runtime/runtime'; +import type { Detail } from './Detail'; + +export interface ExchangeMultipartMultipartRequestBody { + payload_values: readonly (globalThis.Record | string)[]; + upload_file?: UploadFileInput; +} diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/ExchangeMultipartRequest.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/ExchangeMultipartRequest.ts.golden new file mode 100644 index 000000000..f149a271e --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/ExchangeMultipartRequest.ts.golden @@ -0,0 +1,11 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail } from './Detail'; + +export interface ExchangeMultipartRequest { + readonly payload_values: readonly (globalThis.Record | string)[]; + readonly upload_file?: Blob | File; +} diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/ExplicitNullOverlap.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/ExplicitNullOverlap.ts.golden new file mode 100644 index 000000000..f96bead31 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/ExplicitNullOverlap.ts.golden @@ -0,0 +1,9 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { NullableDetail } from './NullableDetail'; +import type { NullableTool } from './NullableTool'; + +export type ExplicitNullOverlap = NullableDetail | NullableTool | null; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/ExplicitNullableEnumChoice.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/ExplicitNullableEnumChoice.ts.golden new file mode 100644 index 000000000..30510b4cc --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/ExplicitNullableEnumChoice.ts.golden @@ -0,0 +1,8 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail } from './Detail'; + +export type ExplicitNullableEnumChoice = readonly (number | null)[] | Detail; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/InlineDictionaryValues.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/InlineDictionaryValues.ts.golden new file mode 100644 index 000000000..5e87ff9c5 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/InlineDictionaryValues.ts.golden @@ -0,0 +1,10 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail } from './Detail'; + +export type InlineDictionaryValues = + | globalThis.Record | string> + | boolean; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/InlineMixedValues.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/InlineMixedValues.ts.golden new file mode 100644 index 000000000..ee86ef69b --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/InlineMixedValues.ts.golden @@ -0,0 +1,8 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail } from './Detail'; + +export type InlineMixedValues = readonly (globalThis.Record | string)[] | Detail; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/InlineNumericValues.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/InlineNumericValues.ts.golden new file mode 100644 index 000000000..738f71ba8 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/InlineNumericValues.ts.golden @@ -0,0 +1,8 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail } from './Detail'; + +export type InlineNumericValues = readonly (globalThis.Record | number)[]; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/InlineProperty.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/InlineProperty.ts.golden new file mode 100644 index 000000000..ecc08cc0b --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/InlineProperty.ts.golden @@ -0,0 +1,10 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail } from './Detail'; + +export interface InlineProperty { + readonly entries: readonly (globalThis.Record | string)[]; +} diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/MapOuter.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/MapOuter.ts.golden new file mode 100644 index 000000000..961d209f2 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/MapOuter.ts.golden @@ -0,0 +1,6 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export type MapOuter = unknown; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/MapWrapped.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/MapWrapped.ts.golden new file mode 100644 index 000000000..c50d2bbf7 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/MapWrapped.ts.golden @@ -0,0 +1,8 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { NullableMapFallback } from './NullableMapFallback'; + +export type MapWrapped = NullableMapFallback; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/MixedMapA.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/MixedMapA.ts.golden new file mode 100644 index 000000000..80accadaf --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/MixedMapA.ts.golden @@ -0,0 +1,10 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail } from './Detail'; + +export interface MixedMapA extends Readonly> { + readonly marker: string; +} diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/MixedMapB.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/MixedMapB.ts.golden new file mode 100644 index 000000000..913e9653d --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/MixedMapB.ts.golden @@ -0,0 +1,10 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail } from './Detail'; + +export interface MixedMapB extends Readonly> { + readonly marker: string; +} diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/MixedMapChoice.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/MixedMapChoice.ts.golden new file mode 100644 index 000000000..6a34d97c8 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/MixedMapChoice.ts.golden @@ -0,0 +1,9 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { MixedMapA } from './MixedMapA'; +import type { MixedMapB } from './MixedMapB'; + +export type MixedMapChoice = MixedMapA | MixedMapB; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/NamedContainer.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/NamedContainer.ts.golden new file mode 100644 index 000000000..35149f4e6 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/NamedContainer.ts.golden @@ -0,0 +1,9 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail } from './Detail'; +import type { NamedValue } from './NamedValue'; + +export type NamedContainer = readonly NamedValue[] | Detail; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/NamedValue.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/NamedValue.ts.golden new file mode 100644 index 000000000..9efaaeb95 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/NamedValue.ts.golden @@ -0,0 +1,8 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail } from './Detail'; + +export type NamedValue = globalThis.Record | string; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/NestedDetailLists.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/NestedDetailLists.ts.golden new file mode 100644 index 000000000..1f03e4174 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/NestedDetailLists.ts.golden @@ -0,0 +1,8 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail } from './Detail'; + +export type NestedDetailLists = readonly Detail[][]; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/NestedEnumChoice.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/NestedEnumChoice.ts.golden new file mode 100644 index 000000000..6a43ae1e3 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/NestedEnumChoice.ts.golden @@ -0,0 +1,9 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { NestedEnumNarrow } from './NestedEnumNarrow'; +import type { NumericTwo } from './NumericTwo'; + +export type NestedEnumChoice = NestedEnumNarrow | NumericTwo; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/NestedEnumNarrow.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/NestedEnumNarrow.ts.golden new file mode 100644 index 000000000..5c8f3aebb --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/NestedEnumNarrow.ts.golden @@ -0,0 +1,9 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface NestedEnumNarrow { + readonly first_value?: string; + readonly revision: number | null; +} diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/NestedExplicitNullOverlap.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/NestedExplicitNullOverlap.ts.golden new file mode 100644 index 000000000..764447d69 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/NestedExplicitNullOverlap.ts.golden @@ -0,0 +1,8 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { NullableOverlap } from './NullableOverlap'; + +export type NestedExplicitNullOverlap = NullableOverlap | boolean | null; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/NestedNullableEnumChoice.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/NestedNullableEnumChoice.ts.golden new file mode 100644 index 000000000..84dc77011 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/NestedNullableEnumChoice.ts.golden @@ -0,0 +1,9 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { NestedNullableEnumNarrow } from './NestedNullableEnumNarrow'; +import type { NumericTwo } from './NumericTwo'; + +export type NestedNullableEnumChoice = NestedNullableEnumNarrow | NumericTwo; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/NestedNullableEnumNarrow.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/NestedNullableEnumNarrow.ts.golden new file mode 100644 index 000000000..74f8734be --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/NestedNullableEnumNarrow.ts.golden @@ -0,0 +1,9 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface NestedNullableEnumNarrow { + readonly first_value?: string; + readonly revision: number | null; +} diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/NullableDetail.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/NullableDetail.ts.golden new file mode 100644 index 000000000..d0b7f6b4b --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/NullableDetail.ts.golden @@ -0,0 +1,8 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail } from './Detail'; + +export type NullableDetail = Detail | null; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/NullableEnumParent.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/NullableEnumParent.ts.golden new file mode 100644 index 000000000..3f5c5d9f1 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/NullableEnumParent.ts.golden @@ -0,0 +1,10 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { ScalarEnumChoice } from './ScalarEnumChoice'; + +export interface NullableEnumParent { + readonly scalar_value: ScalarEnumChoice | null; +} diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/NullableInlineEnumParent.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/NullableInlineEnumParent.ts.golden new file mode 100644 index 000000000..cda196e07 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/NullableInlineEnumParent.ts.golden @@ -0,0 +1,8 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface NullableInlineEnumParent { + readonly scalar_value: number | boolean | null; +} diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/NullableMapFallback.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/NullableMapFallback.ts.golden new file mode 100644 index 000000000..f6d5243cf --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/NullableMapFallback.ts.golden @@ -0,0 +1,8 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail } from './Detail'; + +export type NullableMapFallback = globalThis.Record | null | Detail; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/NullableNonNullScalars.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/NullableNonNullScalars.ts.golden new file mode 100644 index 000000000..6dbed257b --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/NullableNonNullScalars.ts.golden @@ -0,0 +1,8 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { NullableNonNullScalarsItems } from './NullableNonNullScalarsItems'; + +export type NullableNonNullScalars = readonly (NullableNonNullScalarsItems | null)[]; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/NullableNonNullScalarsItems.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/NullableNonNullScalarsItems.ts.golden new file mode 100644 index 000000000..1cceae1f5 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/NullableNonNullScalarsItems.ts.golden @@ -0,0 +1,6 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export type NullableNonNullScalarsItems = number; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/NullableNumericChoice.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/NullableNumericChoice.ts.golden new file mode 100644 index 000000000..4f140e20b --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/NullableNumericChoice.ts.golden @@ -0,0 +1,9 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { NullableNumericOne } from './NullableNumericOne'; +import type { NullableNumericTwo } from './NullableNumericTwo'; + +export type NullableNumericChoice = NullableNumericOne | NullableNumericTwo; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/NullableNumericOne.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/NullableNumericOne.ts.golden new file mode 100644 index 000000000..84e5952cb --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/NullableNumericOne.ts.golden @@ -0,0 +1,9 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface NullableNumericOne { + readonly first_value?: string; + readonly revision: number | null; +} diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/NullableNumericTwo.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/NullableNumericTwo.ts.golden new file mode 100644 index 000000000..89480e9f4 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/NullableNumericTwo.ts.golden @@ -0,0 +1,9 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface NullableNumericTwo { + readonly revision: number | null; + readonly second_value?: string; +} diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/NullableOpaque.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/NullableOpaque.ts.golden new file mode 100644 index 000000000..ba3dbc006 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/NullableOpaque.ts.golden @@ -0,0 +1,6 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export type NullableOpaque = unknown; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/NullableOpaqueSiblingOuter.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/NullableOpaqueSiblingOuter.ts.golden new file mode 100644 index 000000000..f76711e1f --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/NullableOpaqueSiblingOuter.ts.golden @@ -0,0 +1,8 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { NullableOpaque } from './NullableOpaque'; + +export type NullableOpaqueSiblingOuter = NullableOpaque | boolean | null; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/NullableOpenExplicitOuter.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/NullableOpenExplicitOuter.ts.golden new file mode 100644 index 000000000..a6d7bf06e --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/NullableOpenExplicitOuter.ts.golden @@ -0,0 +1,8 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { NullableOpenInner } from './NullableOpenInner'; + +export type NullableOpenExplicitOuter = NullableOpenInner | null; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/NullableOpenInner.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/NullableOpenInner.ts.golden new file mode 100644 index 000000000..48cca3850 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/NullableOpenInner.ts.golden @@ -0,0 +1,6 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export type NullableOpenInner = unknown; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/NullableOpenSiblingOuter.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/NullableOpenSiblingOuter.ts.golden new file mode 100644 index 000000000..913fdb26b --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/NullableOpenSiblingOuter.ts.golden @@ -0,0 +1,8 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { NullableOpenInner } from './NullableOpenInner'; + +export type NullableOpenSiblingOuter = NullableOpenInner | boolean | null; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/NullableOverlap.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/NullableOverlap.ts.golden new file mode 100644 index 000000000..32341b499 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/NullableOverlap.ts.golden @@ -0,0 +1,9 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { NullableDetail } from './NullableDetail'; +import type { NullableTool } from './NullableTool'; + +export type NullableOverlap = NullableDetail | NullableTool; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/NullablePlainEnumParent.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/NullablePlainEnumParent.ts.golden new file mode 100644 index 000000000..8954e3d16 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/NullablePlainEnumParent.ts.golden @@ -0,0 +1,10 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { ScalarEnumChoice } from './ScalarEnumChoice'; + +export interface NullablePlainEnumParent { + readonly scalar_value: ScalarEnumChoice | null; +} diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/NullableScalarArray.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/NullableScalarArray.ts.golden new file mode 100644 index 000000000..b9dec5f15 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/NullableScalarArray.ts.golden @@ -0,0 +1,8 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { NullableScalarInner } from './NullableScalarInner'; + +export type NullableScalarArray = readonly (NullableScalarInner | null)[]; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/NullableScalarInner.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/NullableScalarInner.ts.golden new file mode 100644 index 000000000..3fe178b8a --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/NullableScalarInner.ts.golden @@ -0,0 +1,6 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export type NullableScalarInner = string | null | number; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/NullableScalarMap.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/NullableScalarMap.ts.golden new file mode 100644 index 000000000..de2c0ebbc --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/NullableScalarMap.ts.golden @@ -0,0 +1,8 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { NullableScalarInner } from './NullableScalarInner'; + +export interface NullableScalarMap extends Readonly> {} diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/NullableScalarParent.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/NullableScalarParent.ts.golden new file mode 100644 index 000000000..148499d1c --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/NullableScalarParent.ts.golden @@ -0,0 +1,10 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { NullableScalarInner } from './NullableScalarInner'; + +export interface NullableScalarParent { + readonly inner_value: NullableScalarInner | null; +} diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/NullableTool.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/NullableTool.ts.golden new file mode 100644 index 000000000..9fdca3812 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/NullableTool.ts.golden @@ -0,0 +1,8 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Tool } from './Tool'; + +export type NullableTool = Tool | null; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/NumericEnumChoice.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/NumericEnumChoice.ts.golden new file mode 100644 index 000000000..52ac45957 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/NumericEnumChoice.ts.golden @@ -0,0 +1,9 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { NumericOne } from './NumericOne'; +import type { NumericTwo } from './NumericTwo'; + +export type NumericEnumChoice = NumericOne | NumericTwo; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/NumericOne.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/NumericOne.ts.golden new file mode 100644 index 000000000..84bd79a86 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/NumericOne.ts.golden @@ -0,0 +1,9 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface NumericOne { + readonly first_value?: string; + readonly revision: number; +} diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/NumericTwo.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/NumericTwo.ts.golden new file mode 100644 index 000000000..f9f93994a --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/NumericTwo.ts.golden @@ -0,0 +1,9 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface NumericTwo { + readonly revision: number; + readonly second_value?: string; +} diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/OnceNullableOuter.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/OnceNullableOuter.ts.golden new file mode 100644 index 000000000..46183f0c4 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/OnceNullableOuter.ts.golden @@ -0,0 +1,9 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { NullableDetail } from './NullableDetail'; +import type { NullableOpaque } from './NullableOpaque'; + +export type OnceNullableOuter = NullableOpaque | NullableDetail; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/OpenAlias.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/OpenAlias.ts.golden new file mode 100644 index 000000000..9e7eac352 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/OpenAlias.ts.golden @@ -0,0 +1,8 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { OpenInner } from './OpenInner'; + +export type OpenAlias = OpenInner; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/OpenInner.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/OpenInner.ts.golden new file mode 100644 index 000000000..63792fba4 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/OpenInner.ts.golden @@ -0,0 +1,6 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export type OpenInner = unknown; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/OpenMap.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/OpenMap.ts.golden new file mode 100644 index 000000000..b2780480b --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/OpenMap.ts.golden @@ -0,0 +1,6 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface OpenMap extends Readonly> {} diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/OpenTool.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/OpenTool.ts.golden new file mode 100644 index 000000000..1270bc2d4 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/OpenTool.ts.golden @@ -0,0 +1,6 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export type OpenTool = unknown; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/OtherDetail.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/OtherDetail.ts.golden new file mode 100644 index 000000000..27a72079b --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/OtherDetail.ts.golden @@ -0,0 +1,9 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface OtherDetail { + readonly optional_flag?: boolean; + readonly wire_name: string; +} diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/Outer.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/Outer.ts.golden new file mode 100644 index 000000000..24681460c --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/Outer.ts.golden @@ -0,0 +1,8 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { OpenInner } from './OpenInner'; + +export type Outer = OpenInner | string; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/PrimitiveArrayChoice.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/PrimitiveArrayChoice.ts.golden new file mode 100644 index 000000000..2cb288590 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/PrimitiveArrayChoice.ts.golden @@ -0,0 +1,8 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail } from './Detail'; + +export type PrimitiveArrayChoice = readonly number[] | Detail; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/PrimitiveMapChoice.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/PrimitiveMapChoice.ts.golden new file mode 100644 index 000000000..98dd5ad31 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/PrimitiveMapChoice.ts.golden @@ -0,0 +1,8 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail } from './Detail'; + +export type PrimitiveMapChoice = globalThis.Record | Detail; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/RawEnvelope.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/RawEnvelope.ts.golden new file mode 100644 index 000000000..fe4ffa319 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/RawEnvelope.ts.golden @@ -0,0 +1,13 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { AliasChoice } from './AliasChoice'; +import type { Detail } from './Detail'; + +export interface RawEnvelope { + readonly plain_detail: Detail; + readonly raw_choice: AliasChoice; + readonly request_id: string; +} diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/Readonly.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/Readonly.ts.golden new file mode 100644 index 000000000..c61c2459c --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/Readonly.ts.golden @@ -0,0 +1,9 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail } from './Detail'; + +export type Readonly = globalThis.Readonly> & + globalThis.Readonly>; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/Record.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/Record.ts.golden new file mode 100644 index 000000000..d71f8477f --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/Record.ts.golden @@ -0,0 +1,10 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail } from './Detail'; + +export type Record = + | globalThis.Record<'event_type', 'empty'> + | (globalThis.Record<'event_type', 'data'> & globalThis.Record<'event_data', Detail>); diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/RecordEmpty.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/RecordEmpty.ts.golden new file mode 100644 index 000000000..6d6f93c56 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/RecordEmpty.ts.golden @@ -0,0 +1,8 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface RecordEmpty { + readonly event_type: 'empty'; +} diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/RecordOrBoolean.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/RecordOrBoolean.ts.golden new file mode 100644 index 000000000..b4cc821a7 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/RecordOrBoolean.ts.golden @@ -0,0 +1,8 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Record } from './Record'; + +export type RecordOrBoolean = Record | boolean; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/RecursiveDictionaryChoice.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/RecursiveDictionaryChoice.ts.golden new file mode 100644 index 000000000..ce6258f59 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/RecursiveDictionaryChoice.ts.golden @@ -0,0 +1,6 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export type RecursiveDictionaryChoice = unknown; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/RecursiveList.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/RecursiveList.ts.golden new file mode 100644 index 000000000..feab8eb94 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/RecursiveList.ts.golden @@ -0,0 +1,8 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail } from './Detail'; + +export type RecursiveList = readonly RecursiveList[] | Detail; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/RecursiveOpenDictionary.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/RecursiveOpenDictionary.ts.golden new file mode 100644 index 000000000..6f579c660 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/RecursiveOpenDictionary.ts.golden @@ -0,0 +1,6 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export type RecursiveOpenDictionary = unknown; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/ScalarEnumChoice.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/ScalarEnumChoice.ts.golden new file mode 100644 index 000000000..d60ccd168 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/ScalarEnumChoice.ts.golden @@ -0,0 +1,6 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export type ScalarEnumChoice = number | boolean; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/SearchTool.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/SearchTool.ts.golden new file mode 100644 index 000000000..a08767564 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/SearchTool.ts.golden @@ -0,0 +1,10 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface SearchTool { + readonly max_uses?: number; + readonly name: 'web_search'; + readonly type: 'web_search_20250305'; +} diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/SendOptionalInlineScalarRequest.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/SendOptionalInlineScalarRequest.ts.golden new file mode 100644 index 000000000..da7196e78 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/SendOptionalInlineScalarRequest.ts.golden @@ -0,0 +1,6 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export type SendOptionalInlineScalarRequest = string | null | number; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/TaggedMapA.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/TaggedMapA.ts.golden new file mode 100644 index 000000000..cca099b34 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/TaggedMapA.ts.golden @@ -0,0 +1,9 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail } from './Detail'; + +export type TaggedMapA = globalThis.Readonly> & + globalThis.Readonly>; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/TaggedMapB.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/TaggedMapB.ts.golden new file mode 100644 index 000000000..57429cd77 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/TaggedMapB.ts.golden @@ -0,0 +1,9 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail } from './Detail'; + +export type TaggedMapB = globalThis.Readonly> & + globalThis.Readonly>; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/TaggedMapChoice.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/TaggedMapChoice.ts.golden new file mode 100644 index 000000000..d5f4a5083 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/TaggedMapChoice.ts.golden @@ -0,0 +1,9 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { TaggedMapA } from './TaggedMapA'; +import type { TaggedMapB } from './TaggedMapB'; + +export type TaggedMapChoice = TaggedMapA | TaggedMapB; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/Tool.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/Tool.ts.golden new file mode 100644 index 000000000..0a7a33942 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/Tool.ts.golden @@ -0,0 +1,9 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { ClientTool } from './ClientTool'; +import type { SearchTool } from './SearchTool'; + +export type Tool = ClientTool | SearchTool; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/ToolAlias.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/ToolAlias.ts.golden new file mode 100644 index 000000000..59a34e3c1 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/ToolAlias.ts.golden @@ -0,0 +1,8 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Tool } from './Tool'; + +export type ToolAlias = Tool; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/ToolList.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/ToolList.ts.golden new file mode 100644 index 000000000..534f27920 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/ToolList.ts.golden @@ -0,0 +1,9 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { ClientTool } from './ClientTool'; +import type { SearchTool } from './SearchTool'; + +export type ToolList = readonly ClientTool[] | readonly SearchTool[]; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/ToolMap.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/ToolMap.ts.golden new file mode 100644 index 000000000..f46e06c17 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/ToolMap.ts.golden @@ -0,0 +1,9 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { ClientTool } from './ClientTool'; +import type { SearchTool } from './SearchTool'; + +export type ToolMap = globalThis.Record | globalThis.Record; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/ToolRequest.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/ToolRequest.ts.golden new file mode 100644 index 000000000..971313467 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/ToolRequest.ts.golden @@ -0,0 +1,10 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Tool } from './Tool'; + +export interface ToolRequest { + readonly tools: readonly Tool[]; +} diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/TwiceNullableOpaque.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/TwiceNullableOpaque.ts.golden new file mode 100644 index 000000000..6a83123a4 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/TwiceNullableOpaque.ts.golden @@ -0,0 +1,8 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { NullableOpaque } from './NullableOpaque'; + +export type TwiceNullableOpaque = NullableOpaque | null; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/TwiceNullableOuter.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/TwiceNullableOuter.ts.golden new file mode 100644 index 000000000..3c0fda851 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/TwiceNullableOuter.ts.golden @@ -0,0 +1,9 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { NullableDetail } from './NullableDetail'; +import type { TwiceNullableOpaque } from './TwiceNullableOpaque'; + +export type TwiceNullableOuter = TwiceNullableOpaque | NullableDetail; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/UntypedNumericChoice.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/UntypedNumericChoice.ts.golden new file mode 100644 index 000000000..a4f32b611 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/UntypedNumericChoice.ts.golden @@ -0,0 +1,9 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { UntypedNumericOne } from './UntypedNumericOne'; +import type { UntypedNumericTwo } from './UntypedNumericTwo'; + +export type UntypedNumericChoice = UntypedNumericOne | UntypedNumericTwo; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/UntypedNumericOne.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/UntypedNumericOne.ts.golden new file mode 100644 index 000000000..e5d510941 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/UntypedNumericOne.ts.golden @@ -0,0 +1,9 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface UntypedNumericOne { + readonly first_value?: string; + readonly revision: string; +} diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/UntypedNumericTwo.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/UntypedNumericTwo.ts.golden new file mode 100644 index 000000000..c7717ed1d --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/UntypedNumericTwo.ts.golden @@ -0,0 +1,9 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface UntypedNumericTwo { + readonly revision: string; + readonly second_value?: string; +} diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/ValueAlias.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/ValueAlias.ts.golden new file mode 100644 index 000000000..e65cfb019 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/ValueAlias.ts.golden @@ -0,0 +1,8 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { Detail } from './Detail'; + +export type ValueAlias = readonly (globalThis.Record | string)[]; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/WrappedArrayOrBoolean.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/WrappedArrayOrBoolean.ts.golden new file mode 100644 index 000000000..2d75be2ff --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/WrappedArrayOrBoolean.ts.golden @@ -0,0 +1,8 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { ArrayWrapped } from './ArrayWrapped'; + +export type WrappedArrayOrBoolean = ArrayWrapped | boolean; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/WrappedMapOrBoolean.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/WrappedMapOrBoolean.ts.golden new file mode 100644 index 000000000..b10a832a4 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/WrappedMapOrBoolean.ts.golden @@ -0,0 +1,8 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { MapWrapped } from './MapWrapped'; + +export type WrappedMapOrBoolean = MapWrapped | boolean; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/WrappedScalarInner.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/WrappedScalarInner.ts.golden new file mode 100644 index 000000000..f85e678d4 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/WrappedScalarInner.ts.golden @@ -0,0 +1,8 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { NullableScalarInner } from './NullableScalarInner'; + +export type WrappedScalarInner = NullableScalarInner | null; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/WrapperChoice.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/WrapperChoice.ts.golden new file mode 100644 index 000000000..29e1973c5 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/WrapperChoice.ts.golden @@ -0,0 +1,9 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +import type { WrapperOne } from './WrapperOne'; +import type { WrapperTwo } from './WrapperTwo'; + +export type WrapperChoice = WrapperOne | WrapperTwo; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/WrapperOne.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/WrapperOne.ts.golden new file mode 100644 index 000000000..b89985e5e --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/WrapperOne.ts.golden @@ -0,0 +1,9 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface WrapperOne { + readonly first_value?: string; + readonly revision: number | null; +} diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/WrapperTwo.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/WrapperTwo.ts.golden new file mode 100644 index 000000000..705c458d2 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/WrapperTwo.ts.golden @@ -0,0 +1,9 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export interface WrapperTwo { + readonly revision: number | null; + readonly second_value?: string; +} diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/models/index.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/models/index.ts.golden new file mode 100644 index 000000000..a11edb0ae --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/models/index.ts.golden @@ -0,0 +1,119 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export type { Adjacent } from './Adjacent'; +export type { AdjacentContainer } from './AdjacentContainer'; +export type { AdjacentContainerEmpty } from './AdjacentContainerEmpty'; +export type { AdjacentEmpty } from './AdjacentEmpty'; +export type { AdjacentOrNumber } from './AdjacentOrNumber'; +export type { AdjacentPrimitive } from './AdjacentPrimitive'; +export type { AdjacentPrimitiveEmpty } from './AdjacentPrimitiveEmpty'; +export type { AliasChoice } from './AliasChoice'; +export type { AliasOuter } from './AliasOuter'; +export type { AlreadyNullableEnumParent } from './AlreadyNullableEnumParent'; +export type { AlreadyNullableOuterEnumParent } from './AlreadyNullableOuterEnumParent'; +export type { AlternativeTool } from './AlternativeTool'; +export type { AmbiguousInner } from './AmbiguousInner'; +export type { AmbiguousMap } from './AmbiguousMap'; +export type { AmbiguousMapOuter } from './AmbiguousMapOuter'; +export type { AmbiguousOuter } from './AmbiguousOuter'; +export type { AmbiguousTool } from './AmbiguousTool'; +export type { ArrayPayload } from './ArrayPayload'; +export type { ArrayPayloadOrBoolean } from './ArrayPayloadOrBoolean'; +export type { ArrayWrapped } from './ArrayWrapped'; +export type { BinaryModel } from './BinaryModel'; +export type { BinaryUnion } from './BinaryUnion'; +export type { ClientTool } from './ClientTool'; +export type { ConstrainedOuterEnumParent } from './ConstrainedOuterEnumParent'; +export type { ContainerOuterEnum } from './ContainerOuterEnum'; +export type { Detail } from './Detail'; +export type { DetailArrayOrObject } from './DetailArrayOrObject'; +export type { DictionaryAlias } from './DictionaryAlias'; +export type { EnumFreeOuterEnumParent } from './EnumFreeOuterEnumParent'; +export type { ExplicitNullOverlap } from './ExplicitNullOverlap'; +export type { ExplicitNullableEnumChoice } from './ExplicitNullableEnumChoice'; +export type { InlineDictionaryValues } from './InlineDictionaryValues'; +export type { InlineMixedValues } from './InlineMixedValues'; +export type { InlineNumericValues } from './InlineNumericValues'; +export type { InlineProperty } from './InlineProperty'; +export type { MapOuter } from './MapOuter'; +export type { MapWrapped } from './MapWrapped'; +export type { MixedMapA } from './MixedMapA'; +export type { MixedMapB } from './MixedMapB'; +export type { MixedMapChoice } from './MixedMapChoice'; +export type { NamedContainer } from './NamedContainer'; +export type { NamedValue } from './NamedValue'; +export type { NestedDetailLists } from './NestedDetailLists'; +export type { NestedEnumChoice } from './NestedEnumChoice'; +export type { NestedEnumNarrow } from './NestedEnumNarrow'; +export type { NestedExplicitNullOverlap } from './NestedExplicitNullOverlap'; +export type { NestedNullableEnumChoice } from './NestedNullableEnumChoice'; +export type { NestedNullableEnumNarrow } from './NestedNullableEnumNarrow'; +export type { NullableDetail } from './NullableDetail'; +export type { NullableEnumParent } from './NullableEnumParent'; +export type { NullableInlineEnumParent } from './NullableInlineEnumParent'; +export type { NullableMapFallback } from './NullableMapFallback'; +export type { NullableNonNullScalars } from './NullableNonNullScalars'; +export type { NullableNonNullScalarsItems } from './NullableNonNullScalarsItems'; +export type { NullableNumericChoice } from './NullableNumericChoice'; +export type { NullableNumericOne } from './NullableNumericOne'; +export type { NullableNumericTwo } from './NullableNumericTwo'; +export type { NullableOpaque } from './NullableOpaque'; +export type { NullableOpaqueSiblingOuter } from './NullableOpaqueSiblingOuter'; +export type { NullableOpenExplicitOuter } from './NullableOpenExplicitOuter'; +export type { NullableOpenInner } from './NullableOpenInner'; +export type { NullableOpenSiblingOuter } from './NullableOpenSiblingOuter'; +export type { NullableOverlap } from './NullableOverlap'; +export type { NullablePlainEnumParent } from './NullablePlainEnumParent'; +export type { NullableScalarArray } from './NullableScalarArray'; +export type { NullableScalarInner } from './NullableScalarInner'; +export type { NullableScalarMap } from './NullableScalarMap'; +export type { NullableScalarParent } from './NullableScalarParent'; +export type { NullableTool } from './NullableTool'; +export type { NumericEnumChoice } from './NumericEnumChoice'; +export type { NumericOne } from './NumericOne'; +export type { NumericTwo } from './NumericTwo'; +export type { OnceNullableOuter } from './OnceNullableOuter'; +export type { OpenAlias } from './OpenAlias'; +export type { OpenInner } from './OpenInner'; +export type { OpenMap } from './OpenMap'; +export type { OpenTool } from './OpenTool'; +export type { OtherDetail } from './OtherDetail'; +export type { Outer } from './Outer'; +export type { PrimitiveArrayChoice } from './PrimitiveArrayChoice'; +export type { PrimitiveMapChoice } from './PrimitiveMapChoice'; +export type { RawEnvelope } from './RawEnvelope'; +export type { Readonly } from './Readonly'; +export type { Record } from './Record'; +export type { RecordEmpty } from './RecordEmpty'; +export type { RecordOrBoolean } from './RecordOrBoolean'; +export type { RecursiveDictionaryChoice } from './RecursiveDictionaryChoice'; +export type { RecursiveList } from './RecursiveList'; +export type { RecursiveOpenDictionary } from './RecursiveOpenDictionary'; +export type { ScalarEnumChoice } from './ScalarEnumChoice'; +export type { SearchTool } from './SearchTool'; +export type { TaggedMapA } from './TaggedMapA'; +export type { TaggedMapB } from './TaggedMapB'; +export type { TaggedMapChoice } from './TaggedMapChoice'; +export type { Tool } from './Tool'; +export type { ToolAlias } from './ToolAlias'; +export type { ToolList } from './ToolList'; +export type { ToolMap } from './ToolMap'; +export type { ToolRequest } from './ToolRequest'; +export type { TwiceNullableOpaque } from './TwiceNullableOpaque'; +export type { TwiceNullableOuter } from './TwiceNullableOuter'; +export type { UntypedNumericChoice } from './UntypedNumericChoice'; +export type { UntypedNumericOne } from './UntypedNumericOne'; +export type { UntypedNumericTwo } from './UntypedNumericTwo'; +export type { ValueAlias } from './ValueAlias'; +export type { WrappedArrayOrBoolean } from './WrappedArrayOrBoolean'; +export type { WrappedMapOrBoolean } from './WrappedMapOrBoolean'; +export type { WrappedScalarInner } from './WrappedScalarInner'; +export type { WrapperChoice } from './WrapperChoice'; +export type { WrapperOne } from './WrapperOne'; +export type { WrapperTwo } from './WrapperTwo'; +export type { ExchangeMultipartRequest } from './ExchangeMultipartRequest'; +export type { SendOptionalInlineScalarRequest } from './SendOptionalInlineScalarRequest'; +export type { ExchangeMultipartMultipartRequestBody } from './ExchangeMultipartMultipartRequestBody'; diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/package.json.golden b/tests/golden/typescript/typescript-fetch/untagged-union/package.json.golden new file mode 100644 index 000000000..3edb55e9f --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/package.json.golden @@ -0,0 +1,26 @@ +{ + "name": "untagged-union-conversion", + "version": "1.0.0", + "description": "TypeScript client for Untagged union conversion", + "keywords": [ + "api-client", + "generated", + "openapi", + "typescript" + ], + "files": [ + "dist" + ], + "type": "module", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "default": "./dist/index.js", + "types": "./dist/index.d.ts" + } + }, + "scripts": { + "build": "tsc" + } +} diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/runtime/runtime.ts.golden b/tests/golden/typescript/typescript-fetch/untagged-union/runtime/runtime.ts.golden new file mode 100644 index 000000000..084a11e06 --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/runtime/runtime.ts.golden @@ -0,0 +1,667 @@ +/** + * @generated by openapi-nexus. Do not edit. + * + * Untagged union conversion — 1.0.0 + */ +export const BASE_PATH = 'http://localhost'.replace(/\/+$/, ''); + +export interface ConfigurationParameters { + basePath?: string; // override base path + fetchApi?: FetchAPI; // override for fetch implementation + middleware?: Middleware[]; // middleware to apply before/after fetch requests + queryParamsStringify?: (params: HTTPQuery) => string; // stringify function for query strings + username?: string | (() => string | Promise); // parameter for basic security + password?: string | (() => string | Promise); // parameter for basic security + apiKey?: string | Promise | ((name: string) => string | Promise); // parameter for apiKey security + accessToken?: + | string + | Promise + | ((name?: string, scopes?: string[]) => string | Promise); // parameter for oauth2 security + headers?: HTTPHeaders; //header params we want to use on every request + credentials?: RequestCredentials; //value for the credentials param we want to use on each request +} + +export class Configuration { + private configuration: ConfigurationParameters; + + constructor(configuration: ConfigurationParameters = {}) { + this.configuration = configuration; + } + + set config(configuration: ConfigurationParameters) { + this.configuration = configuration; + } + + get basePath(): string { + return this.configuration.basePath != null ? this.configuration.basePath : BASE_PATH; + } + + get fetchApi(): FetchAPI | undefined { + return this.configuration.fetchApi; + } + + get middleware(): Middleware[] { + return this.configuration.middleware || []; + } + + get queryParamsStringify(): (params: HTTPQuery) => string { + return this.configuration.queryParamsStringify || querystring; + } + + get username(): (() => string | Promise) | undefined { + const username = this.configuration.username; + if (username) { + return typeof username === 'function' ? username : async () => username; + } + return undefined; + } + + get password(): (() => string | Promise) | undefined { + const password = this.configuration.password; + if (password) { + return typeof password === 'function' ? password : async () => password; + } + return undefined; + } + + get apiKey(): ((name: string) => string | Promise) | undefined { + const apiKey = this.configuration.apiKey; + if (apiKey) { + return typeof apiKey === 'function' ? apiKey : () => apiKey; + } + return undefined; + } + + get accessToken(): ((name?: string, scopes?: string[]) => string | Promise) | undefined { + const accessToken = this.configuration.accessToken; + if (accessToken) { + return typeof accessToken === 'function' ? accessToken : async () => accessToken; + } + return undefined; + } + + get headers(): HTTPHeaders | undefined { + return this.configuration.headers; + } + + get credentials(): RequestCredentials | undefined { + return this.configuration.credentials; + } +} + +export const DefaultConfig = new Configuration(); + +export interface UploadFile { + data: Blob; + filename?: string; +} + +export type UploadFileInput = Blob | File | UploadFile; + +export function uploadFileData(value: UploadFileInput): Blob { + return isUploadFile(value) ? value.data : value; +} + +export function uploadFileFilename(value: UploadFileInput, fallback: string): string { + if (isFile(value) && value.name) { + return value.name; + } + if (isUploadFile(value) && value.filename) { + return value.filename; + } + return fallback; +} + +export function multipartHeaderValue(value: string): string { + return value + .replace(/[\r\n]/g, '') + .replace(/\\/g, '\\\\') + .replace(/"/g, '\\"'); +} + +function isUploadFile(value: UploadFileInput): value is UploadFile { + return typeof value === 'object' && value !== null && 'data' in value; +} + +function isFile(value: UploadFileInput): value is File { + return typeof File !== 'undefined' && value instanceof File; +} + +/** + * This is the base class for all generated API classes. + */ +export class BaseAPI { + private static readonly jsonRegex = new RegExp( + '^(:?application/json|[^;/ \t]+/[^;/ \t]+[+]json)[ \t]*(:?;.*)?$', + 'i', + ); + protected configuration: Configuration; + private middleware: Middleware[]; + + constructor(configuration: Configuration = DefaultConfig) { + this.configuration = configuration; + this.middleware = configuration.middleware; + } + + withMiddleware(this: T, ...middlewares: Middleware[]) { + const next = this.clone(); + next.middleware = next.middleware.concat(...middlewares); + return next; + } + + withPreMiddleware(this: T, ...preMiddlewares: Array) { + const middlewares = preMiddlewares.map((pre) => ({ pre })); + return this.withMiddleware(...middlewares); + } + + withPostMiddleware(this: T, ...postMiddlewares: Array) { + const middlewares = postMiddlewares.map((post) => ({ post })); + return this.withMiddleware(...middlewares); + } + + /** + * Check if the given MIME is a JSON MIME. + * JSON MIME examples: + * application/json + * application/json; charset=UTF8 + * APPLICATION/JSON + * application/vnd.company+json + * @param mime - MIME (Multipurpose Internet Mail Extensions) + * @return True if the given MIME is JSON, false otherwise. + */ + protected isJsonMime(mime: string | null | undefined): boolean { + if (!mime) { + return false; + } + return BaseAPI.jsonRegex.test(mime); + } + + protected async request( + context: RequestOpts, + initOverrides?: RequestInit | InitOverrideFunction, + ): Promise { + const { url, init } = await this.createFetchParams(context, initOverrides); + return await this.fetchApi(url, init); + } + + private async createFetchParams( + context: RequestOpts, + initOverrides?: RequestInit | InitOverrideFunction, + ) { + let url = this.configuration.basePath + context.path; + if (context.query !== undefined && Object.keys(context.query).length !== 0) { + // only add the querystring to the URL if there are query parameters. + // this is done to avoid urls ending with a "?" character which buggy webservers + // do not handle correctly sometimes. + url += '?' + this.configuration.queryParamsStringify(context.query); + } + + const headers = Object.assign({}, this.configuration.headers, context.headers); + Object.keys(headers).forEach((key) => (headers[key] === undefined ? delete headers[key] : {})); + + const token = await this.configuration.accessToken?.(); + if (token) { + headers['Authorization'] = `Bearer ${token}`; + } + + if (!headers['Authorization']) { + const username = await this.configuration.username?.(); + const password = await this.configuration.password?.(); + if (username && password) { + headers['Authorization'] = `Basic ${btoa(`${username}:${password}`)}`; + } + } + + const initOverrideFn = + typeof initOverrides === 'function' ? initOverrides : async () => initOverrides; + + const initParams: HTTPRequestInit = { + method: context.method, + headers, + body: context.body, + credentials: this.configuration.credentials, + signal: context.signal, + }; + + const overriddenInit = { + ...initParams, + ...(await initOverrideFn({ + init: initParams, + context, + })), + }; + + let body: BodyInit | null | undefined; + if ( + isFormData(overriddenInit.body) || + overriddenInit.body instanceof URLSearchParams || + isBlob(overriddenInit.body) + ) { + body = overriddenInit.body; + } else if (this.isJsonMime(headers['Content-Type'])) { + body = JSON.stringify(overriddenInit.body); + } else { + body = overriddenInit.body as BodyInit | null | undefined; + } + + const init: RequestInit = { + ...overriddenInit, + body, + }; + + return { url, init }; + } + + private fetchApi = async (url: string, init: RequestInit) => { + let fetchParams = { url, init }; + for (const middleware of this.middleware) { + if (middleware.pre) { + fetchParams = + (await middleware.pre({ + fetch: this.fetchApi, + ...fetchParams, + })) || fetchParams; + } + } + let response: Response | undefined = undefined; + try { + response = await (this.configuration.fetchApi || fetch)(fetchParams.url, fetchParams.init); + } catch (e) { + for (const middleware of this.middleware) { + if (middleware.onError) { + response = + (await middleware.onError({ + fetch: this.fetchApi, + url: fetchParams.url, + init: fetchParams.init, + error: e, + response: response ? response.clone() : undefined, + })) || response; + } + } + if (response === undefined) { + if (e instanceof Error) { + throw new FetchError( + e, + 'The request failed and the interceptors did not return an alternative response', + ); + } else { + throw e; + } + } + } + for (const middleware of this.middleware) { + if (middleware.post) { + response = + (await middleware.post({ + fetch: this.fetchApi, + url: fetchParams.url, + init: fetchParams.init, + response: response.clone(), + })) || response; + } + } + return response; + }; + + /** + * Create a shallow clone of `this` by constructing a new instance + * and then shallow cloning data members. + */ + private clone(this: T): T { + const constructor = this.constructor as new (configuration: Configuration) => T; + const next = new constructor(this.configuration); + next.middleware = this.middleware.slice(); + return next; + } +} + +function isBlob(value: unknown): value is Blob { + return typeof Blob !== 'undefined' && value instanceof Blob; +} + +function isFormData(value: unknown): value is FormData { + return typeof FormData !== 'undefined' && value instanceof FormData; +} + +export class ResponseError extends Error { + override name = 'ResponseError' as const; + public response: Response; + + constructor(response: Response, msg?: string) { + super(msg); + this.response = response; + } +} + +export class FetchError extends Error { + override name = 'FetchError' as const; + public cause: Error; + + constructor(cause: Error, msg?: string) { + super(msg); + this.cause = cause; + } +} + +export class RequiredError extends Error { + override name = 'RequiredError' as const; + public field: string; + + constructor(field: string, msg?: string) { + super(msg); + this.field = field; + } +} + +export type FetchAPI = WindowOrWorkerGlobalScope['fetch']; + +export type Json = unknown; +export type HTTPMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS' | 'HEAD'; +export type HTTPHeaders = { [key: string]: string }; +export type HTTPQuery = { + [key: string]: + | string + | number + | null + | boolean + | Array + | Set + | HTTPQuery; +}; +export type HTTPBody = Json | Blob | FormData | URLSearchParams; // eslint-disable-line @typescript-eslint/no-redundant-type-constituents +export type HTTPRequestInit = { + headers?: HTTPHeaders; + method: HTTPMethod; + credentials?: RequestCredentials; + body?: HTTPBody; + signal?: AbortSignal; +}; +export type ModelPropertyNaming = 'camelCase' | 'snake_case' | 'PascalCase' | 'original'; + +export type InitOverrideFunction = (requestContext: { + init: HTTPRequestInit; + context: RequestOpts; +}) => Promise; + +export interface FetchParams { + url: string; + init: RequestInit; +} + +export interface RequestOpts { + path: string; + method: HTTPMethod; + headers: HTTPHeaders; + query?: HTTPQuery; + body?: HTTPBody; + signal?: AbortSignal; +} + +export function querystring(params: HTTPQuery, prefix: string = ''): string { + return Object.keys(params) + .map((key) => querystringSingleKey(key, params[key], prefix)) + .filter((part) => part.length > 0) + .join('&'); +} + +function querystringSingleKey( + key: string, + value: + | string + | number + | null + | undefined + | boolean + | Array + | Set + | HTTPQuery, + keyPrefix: string = '', +): string { + const fullKey = keyPrefix + (keyPrefix.length ? `[${key}]` : key); + if (value instanceof Array) { + const multiValue = value + .map((singleValue) => encodeURIComponent(String(singleValue))) + .join(`&${encodeURIComponent(fullKey)}=`); + return `${encodeURIComponent(fullKey)}=${multiValue}`; + } + if (value instanceof Set) { + const valueAsArray = Array.from(value); + return querystringSingleKey(key, valueAsArray, keyPrefix); + } + if (value instanceof Date) { + return `${encodeURIComponent(fullKey)}=${encodeURIComponent(value.toISOString())}`; + } + if (value instanceof Object) { + return querystring(value as HTTPQuery, fullKey); + } + return `${encodeURIComponent(fullKey)}=${encodeURIComponent(String(value))}`; +} + +export function exists(json: Record, key: string): boolean { + const value = json[key]; + return value !== null && value !== undefined; +} + +export function mapValues(data: Record, fn: (item: T) => U): Record { + const result: Record = {}; + for (const key of Object.keys(data)) { + result[key] = fn(data[key]); + } + return result; +} + +export function canConsumeForm(consumes: Consume[]): boolean { + for (const consume of consumes) { + if ('multipart/form-data' === consume.contentType) { + return true; + } + } + return false; +} + +export interface Consume { + contentType: string; +} + +export interface RequestContext { + fetch: FetchAPI; + url: string; + init: RequestInit; +} + +export interface ResponseContext { + fetch: FetchAPI; + url: string; + init: RequestInit; + response: Response; +} + +export interface ErrorContext { + fetch: FetchAPI; + url: string; + init: RequestInit; + error: unknown; + response?: Response; +} + +export interface Middleware { + pre?(context: RequestContext): Promise; + post?(context: ResponseContext): Promise; + onError?(context: ErrorContext): Promise; +} + +export interface ApiResponse { + raw: Response; + value(): Promise; +} + +export type HttpSuccessStatus = + | 200 + | 201 + | 202 + | 203 + | 204 + | 205 + | 206 + | 207 + | 208 + | 209 + | 210 + | 211 + | 212 + | 213 + | 214 + | 215 + | 216 + | 217 + | 218 + | 219 + | 220 + | 221 + | 222 + | 223 + | 224 + | 225 + | 226 + | 227 + | 228 + | 229 + | 230 + | 231 + | 232 + | 233 + | 234 + | 235 + | 236 + | 237 + | 238 + | 239 + | 240 + | 241 + | 242 + | 243 + | 244 + | 245 + | 246 + | 247 + | 248 + | 249 + | 250 + | 251 + | 252 + | 253 + | 254 + | 255 + | 256 + | 257 + | 258 + | 259 + | 260 + | 261 + | 262 + | 263 + | 264 + | 265 + | 266 + | 267 + | 268 + | 269 + | 270 + | 271 + | 272 + | 273 + | 274 + | 275 + | 276 + | 277 + | 278 + | 279 + | 280 + | 281 + | 282 + | 283 + | 284 + | 285 + | 286 + | 287 + | 288 + | 289 + | 290 + | 291 + | 292 + | 293 + | 294 + | 295 + | 296 + | 297 + | 298 + | 299; + +export type HttpSuccessResponse = { status: HttpSuccessStatus }; + +export interface ResponseTransformer { + (json: unknown): T; +} + +class ApiResponseBase { + public readonly status: number; + public readonly ok: boolean; + public readonly statusText: string; + public readonly headers: Headers; + public readonly raw: Response; + + constructor(raw: Response) { + this.raw = raw; + this.status = raw.status; + this.ok = raw.ok; + this.statusText = raw.statusText; + this.headers = raw.headers; + } +} + +export class JSONApiResponse extends ApiResponseBase { + private transformer: ResponseTransformer; + + constructor( + raw: Response, + transformer: ResponseTransformer = (jsonValue: unknown) => jsonValue as T, + ) { + super(raw); + this.transformer = transformer; + } + + async value(): Promise { + return this.transformer(await this.raw.json()); + } +} + +export class VoidApiResponse extends ApiResponseBase { + constructor(raw: Response) { + super(raw); + } + + async value(): Promise { + return undefined; + } +} + +export class BlobApiResponse extends ApiResponseBase { + constructor(raw: Response) { + super(raw); + } + + async value(): Promise { + return await this.raw.blob(); + } +} + +export class TextApiResponse extends ApiResponseBase { + constructor(raw: Response) { + super(raw); + } + + async value(): Promise { + return await this.raw.text(); + } +} diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/tsconfig.esm.json.golden b/tests/golden/typescript/typescript-fetch/untagged-union/tsconfig.esm.json.golden new file mode 100644 index 000000000..769c716fd --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/tsconfig.esm.json.golden @@ -0,0 +1,7 @@ +{ + "compilerOptions": { + "module": "ES2020", + "outDir": "dist/esm" + }, + "extends": "./tsconfig.json" +} diff --git a/tests/golden/typescript/typescript-fetch/untagged-union/tsconfig.json.golden b/tests/golden/typescript/typescript-fetch/untagged-union/tsconfig.json.golden new file mode 100644 index 000000000..9f125842c --- /dev/null +++ b/tests/golden/typescript/typescript-fetch/untagged-union/tsconfig.json.golden @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "declaration": true, + "declarationMap": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "lib": ["ES2020", "DOM"], + "module": "ES2020", + "moduleResolution": "bundler", + "outDir": "./dist", + "resolveJsonModule": true, + "rootDir": "./", + "skipLibCheck": true, + "sourceMap": true, + "strict": true, + "target": "ES2020", + "typeRoots": ["node_modules/@types"] + }, + "exclude": ["dist", "node_modules"], + "include": ["**/*.ts"] +} diff --git a/tests/golden_tests_typescript_fetch.rs b/tests/golden_tests_typescript_fetch.rs index 3db9acdd3..b88671008 100644 --- a/tests/golden_tests_typescript_fetch.rs +++ b/tests/golden_tests_typescript_fetch.rs @@ -88,6 +88,7 @@ fn get_golden_test_cases() -> HashMap<&'static str, &'static str> { ("additional-properties", "valid/additional-properties/additional-properties.yaml"), ("open-tagged-additional-properties", "valid/open-tagged-additional-properties.yaml"), ("typescript-additional-properties", "valid/typescript-additional-properties.yaml"), + ("untagged-union", "valid/typescript-untagged-union.yaml"), ("enum-repr", "valid/enum-repr/enum-repr.yaml"), ("query-param-enum", "valid/query/query-param-enum.yaml"), ("server-path-prefix", "valid/server-path-prefix.yaml"), @@ -183,6 +184,7 @@ generate_golden_tests! { test_additional_properties_golden: "additional-properties", test_open_tagged_additional_properties_golden: "open-tagged-additional-properties", test_typescript_additional_properties_golden: "typescript-additional-properties", + test_untagged_union_golden: "untagged-union", test_query_param_enum_golden: "query-param-enum", test_server_path_prefix_golden: "server-path-prefix", @@ -282,6 +284,72 @@ fn test_property_naming_camel_case_additional_properties_golden() { ); } +#[test] +fn test_camel_case_untagged_union_delegates_to_member_converters() { + use openapi_nexus::codegen::traits::code_generator::CodeGenerator; + + let config = toml::from_str("property_naming = 'camelCase'").unwrap(); + let generator = TypeScriptFetchCodeGenerator::new(config); + let parsed = openapi_nexus::parser::parse_content_yaml(include_str!( + "fixtures/valid/typescript-untagged-union.yaml" + )) + .unwrap(); + let ir = openapi_nexus::ir::lower::lower(parsed).unwrap(); + let files = generator.generate(&ir).unwrap(); + let union = files + .iter() + .find(|file| file.filename == "Tool.ts") + .unwrap(); + assert!(union.content.contains("clientToolFromJSON(")); + assert!(union.content.contains("searchToolToJSON(")); +} + +#[test] +fn test_invalid_untagged_union_member_retains_model_emission_context() { + use openapi_nexus::codegen::traits::code_generator::CodeGenerator; + use openapi_nexus::generators::{ArtifactKind, GeneratorError}; + use openapi_nexus::ir::types::{IrSchemaKind, IrTypeExpr}; + let parsed = openapi_nexus::parser::parse_content_yaml(include_str!( + "fixtures/valid/typescript-untagged-union.yaml" + )) + .unwrap(); + let mut ir = openapi_nexus::ir::lower::lower(parsed).unwrap(); + let IrSchemaKind::Union(union) = &mut ir.schemas.get_mut("Tool").unwrap().kind else { + panic!("untagged union") + }; + union.members.push(IrTypeExpr::Named(String::new())); + let generator = + TypeScriptFetchCodeGenerator::new(toml::from_str("property_naming = 'camelCase'").unwrap()); + let error = generator + .generate(&ir) + .expect_err("invalid type must retain its error, not panic"); + assert!( + matches!(error, GeneratorError::Emission { artifact: ArtifactKind::Model, name, .. } if name == "Tool") + ); +} + +#[test] +fn test_camel_case_untagged_union_golden() { + for (strategy, suffix) in [ + ("firstMatch", "first-match"), + ("strict", "strict"), + ("raw", "raw"), + ] { + let config = toml::from_str(&format!( + "property_naming = 'camelCase'\ntoolchain = 'vp'\nuntagged_union_strategy = '{strategy}'" + )) + .unwrap(); + let generator = TypeScriptFetchCodeGenerator::new(config); + run_golden_test( + &generator, + golden_dir(), + &format!("ts-untagged-union-{suffix}"), + "valid/typescript-untagged-union.yaml", + UPDATE_HINT, + ); + } +} + #[test] #[traced_test] fn test_property_naming_camel_case_response_only_types_golden() { @@ -669,6 +737,12 @@ fn test_self_referential_object_keeps_self_references_local() { foo_file.content ); if camel_case { + assert!( + !files + .iter() + .any(|file| file.filename.ends_with("unionMatch.ts")), + "a union-free recursive model must not emit unused matchers" + ); assert!( foo_file .content @@ -679,17 +753,39 @@ fn test_self_referential_object_keeps_self_references_local() { assert!( foo_file .content - .contains("json.children.map((item) => fooFromJSON(item))"), - "Foo's decoder must call itself locally:\n{}", + .contains("convert$0FromJSON(json.children)"), + "Foo's decoder must delegate its array through the planned helper:\n{}", foo_file.content ); assert!( - foo_file - .content - .contains("value.children.map((item) => fooToJSON(item))"), - "Foo's encoder must call itself locally:\n{}", + foo_file.content.contains("convert$0ToJSON(value.children)"), + "Foo's encoder must delegate its array through the planned helper:\n{}", foo_file.content ); + let helpers = files + .iter() + .find(|file| file.filename.ends_with("valueConversion$0.ts")) + .unwrap(); + assert!( + helpers + .content + .contains("import type { Foo, Foo$Wire } from '../models/Foo';") + ); + assert!( + helpers + .content + .contains("import { fooFromJSON, fooToJSON } from '../models/Foo';") + ); + assert!( + helpers.content.contains("return json.map(fooFromJSON);"), + "the helper must retain the original recursive decoder: {}", + helpers.content + ); + assert!( + helpers.content.contains("return value.map(fooToJSON);"), + "the helper must retain the original recursive encoder: {}", + helpers.content + ); } } } diff --git a/tests/ir_scalar_enums.rs b/tests/ir_scalar_enums.rs new file mode 100644 index 000000000..07794eba2 --- /dev/null +++ b/tests/ir_scalar_enums.rs @@ -0,0 +1,315 @@ +use openapi_nexus::ir::{lower::lower, types::IrSchemaKind}; +use openapi_nexus::parser::parse_content_yaml; +use serde_json::json; + +use openapi_nexus::codegen::traits::code_generator::CodeGenerator; +use openapi_nexus::generators::{ + go::http::GoHttpCodeGenerator, + java::okhttp::JavaOkhttpCodeGenerator, + kotlin::okhttp::KotlinOkhttpCodeGenerator, + python::{httpx::PythonHttpxCodeGenerator, requests::PythonRequestsCodeGenerator}, + rust::{ + aioduct::RustAioductCodeGenerator, reqwest::RustReqwestCodeGenerator, + ureq::RustUreqCodeGenerator, + }, +}; + +#[test] +fn untyped_scalar_enum_values_do_not_inherit_a_synthetic_type_constraint() { + for version in ["3.0.3", "3.1.0", "3.2.0"] { + let source = format!( + "openapi: {version}\ninfo: {{title: Untyped finite values, version: '1'}}\npaths: {{}}\ncomponents:\n schemas:\n Payload:\n type: object\n properties:\n revision: {{enum: [1, 2]}}\n typed_revision: {{type: integer, enum: [1, 2]}}\n flags: {{type: array, items: {{enum: [true]}}}}\n ratios: {{type: object, additionalProperties: {{enum: [1.5]}}}}\n" + ); + let ir = lower(parse_content_yaml(&source).unwrap()).unwrap(); + let IrSchemaKind::Object(object) = &ir.schemas["Payload"].kind else { + panic!("payload is an object"); + }; + for (name, path) in [ + ("revision", "/ScalarEnum"), + ("flags", "/Array/ScalarEnum"), + ("ratios", "/Map/ScalarEnum"), + ] { + let expr = serde_json::to_value(&object.properties[name].type_expr).unwrap(); + let metadata = expr.pointer(path).unwrap(); + assert_eq!( + metadata["representation_is_constraint"], + json!(false), + "{version} {name}" + ); + assert_eq!( + metadata["representation"], + json!({"Primitive": "String"}), + "{version} {name}" + ); + } + let typed = serde_json::to_value(&object.properties["typed_revision"].type_expr).unwrap(); + assert_eq!( + typed["ScalarEnum"]["representation_is_constraint"], + json!(true), + "{version}" + ); + } +} + +#[test] +fn inline_scalar_enum_facts_survive_each_supported_lowerer_and_container() { + for version in ["3.0.3", "3.1.0", "3.2.0"] { + let nullable_type = if version == "3.0.3" { + "type: integer, nullable: true" + } else { + "type: [integer, 'null']" + }; + let source = format!( + "openapi: {version}\ninfo: {{title: Scalar facts, version: '1'}}\npaths: {{}}\ncomponents:\n schemas:\n Payload:\n type: object\n properties:\n revision: {{type: integer, enum: [1, 2]}}\n nullable_revision: {{{nullable_type}, enum: [1, 2]}}\n values:\n type: array\n items: {{type: number, enum: [1.5, 2.5]}}\n flags:\n type: object\n additionalProperties: {{type: boolean, enum: [true]}}\n" + ); + let ir = lower(parse_content_yaml(&source).unwrap()).unwrap(); + let IrSchemaKind::Object(object) = &ir.schemas["Payload"].kind else { + panic!("payload is an object") + }; + let revision = serde_json::to_value(&object.properties["revision"].type_expr).unwrap(); + let values = serde_json::to_value(&object.properties["values"].type_expr).unwrap(); + let flags = serde_json::to_value(&object.properties["flags"].type_expr).unwrap(); + let nullable = &object.properties["nullable_revision"]; + assert!( + nullable.nullable, + "{version}: property nullability remains structural" + ); + let nullable = serde_json::to_value(&nullable.type_expr).unwrap(); + assert_eq!(nullable["ScalarEnum"]["values"], json!([1, 2]), "{version}"); + assert_eq!( + nullable["ScalarEnum"]["representation_nullable"], + json!(true), + "{version}" + ); + assert_eq!(revision["ScalarEnum"]["values"], json!([1, 2]), "{version}"); + assert_eq!( + revision["ScalarEnum"]["representation_nullable"], + json!(false), + "{version}" + ); + assert_eq!( + values["Array"]["ScalarEnum"]["values"], + json!([1.5, 2.5]), + "{version}" + ); + assert_eq!( + flags["Map"]["ScalarEnum"]["values"], + json!([true]), + "{version}" + ); + } +} + +#[test] +fn nullable_wrapper_enums_retain_constraints_at_each_lowering_boundary() { + for version in ["3.1.0", "3.2.0"] { + let source = format!( + r#"openapi: {version} +info: {{title: Nullable wrapper facts, version: '1'}} +paths: {{}} +components: + schemas: + Payload: + type: object + properties: + revision: + anyOf: [{{type: integer}}, {{type: 'null'}}] + enum: [1] + nested_revision: + anyOf: [{{type: integer, enum: [1]}}, {{type: 'null'}}] + enum: [1, null] + values: + type: array + items: + anyOf: [{{type: integer}}, {{type: 'null'}}] + enum: [1, null] + flags: + type: object + additionalProperties: + anyOf: [{{type: integer}}, {{type: 'null'}}] + enum: [1, null] + rows: + type: array + items: + type: array + items: + anyOf: [{{type: integer}}, {{type: 'null'}}] + enum: [1, null] +"# + ); + let ir = lower(parse_content_yaml(&source).unwrap()).unwrap(); + let IrSchemaKind::Object(object) = &ir.schemas["Payload"].kind else { + panic!("payload is an object") + }; + assert!(object.properties["revision"].nullable, "{version}"); + let nested = serde_json::to_value(&object.properties["nested_revision"].type_expr).unwrap(); + assert!(object.properties["nested_revision"].nullable, "{version}"); + assert_eq!( + nested["ScalarEnum"]["representation_nullable"], + json!(true), + "{version}" + ); + assert_eq!( + nested["ScalarEnum"]["representation"]["ScalarEnum"]["representation_nullable"], + json!(false), + "{version}" + ); + for (name, path, expected) in [ + ("revision", "/ScalarEnum/values", json!([1])), + ("values", "/Array/ScalarEnum/values", json!([1, null])), + ("flags", "/Map/ScalarEnum/values", json!([1, null])), + ("rows", "/Array/Array/ScalarEnum/values", json!([1, null])), + ] { + let expr = serde_json::to_value(&object.properties[name].type_expr).unwrap(); + assert_eq!(expr.pointer(path), Some(&expected), "{version}: {name}"); + } + } +} + +#[test] +fn scalar_enum_metadata_keeps_other_generators_on_the_legacy_representation() { + let source = r#"openapi: 3.1.0 +info: {title: Scalar representations, version: '1'} +paths: + /parts: + post: + operationId: sendParts + parameters: + - name: enabled + in: query + schema: {type: boolean, $BOOL default: true} + - name: limit + in: query + schema: {type: [integer, 'null'], $INT format: int64} + - name: numbers + in: query + required: true + schema: + type: array + items: {type: [integer, 'null'], $INT format: int32} + - name: status + in: query + schema: + anyOf: [{$ref: '#/components/schemas/Status'}, {type: 'null'}] + $WRAPPER + requestBody: + content: + multipart/form-data: + schema: + type: object + properties: + revision: {type: integer, $INT format: int32} + enabled: {type: boolean, $BOOL default: true} + status: + anyOf: [{$ref: '#/components/schemas/Status'}, {type: 'null'}] + $WRAPPER + values: + type: array + items: {type: number, $NUMBER format: double} + responses: + '200': + description: Values + headers: + X-Revision: + schema: {type: integer, $INT format: int32} + content: + application/json: + schema: + type: array + items: {type: number, $NUMBER format: double} +components: + schemas: + Status: + type: integer + enum: [1, 2] + Choice: + anyOf: + - {type: boolean, $BOOL default: true} + - {type: integer, $INT format: int32} + Payload: + type: object + properties: + revision: {type: integer, $INT format: int64} + optional_revision: {type: [integer, 'null'], $INT format: int64} + wrapped_revision: + anyOf: [{type: integer}, {type: 'null'}] + $WRAPPER + nested_revision: + anyOf: [{type: integer, $INT format: int32}, {type: 'null'}] + $WRAPPER + status: + anyOf: [{$ref: '#/components/schemas/Status'}, {type: 'null'}] + $WRAPPER + statuses: + type: array + items: + anyOf: [{$ref: '#/components/schemas/Status'}, {type: 'null'}] + $WRAPPER + status_map: + type: object + additionalProperties: + anyOf: [{$ref: '#/components/schemas/Status'}, {type: 'null'}] + $WRAPPER + enabled: {type: boolean, $BOOL default: true} + flags: + type: object + additionalProperties: {type: boolean, $BOOL default: true} + timestamps: + type: array + items: {type: string, enum: ['2026-01-01T00:00:00Z'], format: date-time} +"#; + let decorated = source + .replace("$INT", "enum: [1, 2],") + .replace("$NUMBER", "enum: [1.5, 2.5],") + .replace("$WRAPPER", "enum: [1, null]") + .replace("$BOOL", "enum: [true],"); + let legacy = source + .replace("$INT", "") + .replace("$NUMBER", "") + .replace("$WRAPPER", "") + .replace("$BOOL", "") + .replace("enum: ['2026-01-01T00:00:00Z'], ", ""); + let decorated = lower(parse_content_yaml(&decorated).unwrap()).unwrap(); + let legacy = lower(parse_content_yaml(&legacy).unwrap()).unwrap(); + let generators: Vec> = vec![ + Box::new(GoHttpCodeGenerator::new(Default::default())), + Box::new(JavaOkhttpCodeGenerator::new(Default::default())), + Box::new(KotlinOkhttpCodeGenerator::new(Default::default())), + Box::new(PythonHttpxCodeGenerator::new(Default::default())), + Box::new(PythonRequestsCodeGenerator::new(Default::default())), + Box::new(RustAioductCodeGenerator::new(Default::default())), + Box::new(RustReqwestCodeGenerator::new(Default::default())), + Box::new(RustUreqCodeGenerator::new(Default::default())), + ]; + for generator in generators { + let files = |ir| { + generator + .generate(ir) + .unwrap() + .into_iter() + .map(|file| { + ( + format!("{:?}/{}", file.category, file.filename), + file.content, + ) + }) + .collect::>() + }; + let actual = files(&decorated); + let expected = files(&legacy); + for (filename, expected) in &expected { + assert_eq!( + actual.get(filename), + Some(expected), + "{} {filename}", + generator.generator_type() + ); + } + assert_eq!( + actual.len(), + expected.len(), + "{} file count", + generator.generator_type() + ); + } +} diff --git a/tests/multipart_runtime_smoke.rs b/tests/multipart_runtime_smoke.rs index 84a350bbd..a0a99fefe 100644 --- a/tests/multipart_runtime_smoke.rs +++ b/tests/multipart_runtime_smoke.rs @@ -121,21 +121,22 @@ fn multipart_wire_construction_is_pinned_across_clients() { let ts_api = generated_file(&ts_files, "apis/MultipartApi.ts"); let ts_input = generated_file(&ts_files, "models/SendOptionalPartsMultipartRequestBody.ts"); assert!(ts_api.contains("let requestBody: Blob | undefined = undefined;")); - assert!( - ts_api.contains( - "if (requestParameters.body !== undefined && requestParameters.body !== null)" - ) - ); + assert!(ts_api.contains("const multipartBody = requestParameters['body'];")); + assert!(ts_api.contains("if (multipartBody !== undefined && multipartBody !== null)")); assert!(ts_api.contains("const multipartChunks: Array = [];")); assert!( ts_api.contains( "Content-Disposition: form-data; name=\"note\"\\r\\nContent-Type: text/plain" ) ); - assert!(ts_api.contains("uploadFileFilename(requestParameters.body.file, 'file')")); - assert!(ts_api.contains("uploadFileData(requestParameters.body.file)")); + assert!(ts_api.contains("const multipartValue$2 = multipartBody['file'];")); + assert!(ts_api.contains("if (multipartValue$2 !== undefined && multipartValue$2 !== null)")); + assert!(ts_api.contains("uploadFileFilename(multipartValue$2, 'file')")); + assert!(ts_api.contains("multipartHeaderValue(multipartFilename$2)")); + assert!(ts_api.contains("uploadFileData(multipartValue$2)")); assert!(ts_input.contains("file?: UploadFileInput;")); - assert!(ts_api.contains("multipartChunks.push(String(requestParameters.body.note));")); + assert!(ts_api.contains("const multipartValue$1 = multipartBody['note'];")); + assert!(ts_api.contains("multipartChunks.push(String(multipartValue$1));")); assert!(!ts_api.contains("filename=\"file\"")); let reqwest_files = @@ -356,7 +357,10 @@ property_naming = "camelCase" let api = generated_file(&files, "apis/MultipartApi.ts"); assert!(api.contains("itemConfigToJSON")); - assert!(api.contains( - "multipartChunks.push(JSON.stringify(itemConfigToJSON(requestParameters.body.itemConfig)));" - )); + assert!(api.contains("const multipartBody = requestParameters['body'];")); + assert!(api.contains("const multipartValue$1 = multipartBody['itemConfig'];")); + assert!( + api.contains("multipartChunks.push(JSON.stringify(itemConfigToJSON(multipartValue$1)));") + ); + assert!(!api.contains("multipartChunks.push(JSON.stringify(multipartValue$1));")); } diff --git a/tests/sigil_kinds_stage_c.rs b/tests/typescript_tagged_emission.rs similarity index 84% rename from tests/sigil_kinds_stage_c.rs rename to tests/typescript_tagged_emission.rs index 1b72eab22..25fcfbdda 100644 --- a/tests/sigil_kinds_stage_c.rs +++ b/tests/typescript_tagged_emission.rs @@ -1,4 +1,4 @@ -//! Stage C: prove `sigil_emit::generate_model_files` handles TaggedUnion across +//! Prove `sigil_emit::generate_model_files` handles tagged unions across //! all three tagging styles (Internal / Adjacent / External). //! //! Fixtures available today mostly exercise Internal (the common OpenAPI @@ -50,7 +50,7 @@ fn find<'a>(files: &'a [(String, String)], filename: &str) -> &'a str { }) } -/// Internal tagging: `({ kind: 'VAL' } & ContentType)` per variant. Variants +/// Internal tagging: `Record<'kind', 'VAL'> & ContentType` per variant. Variants /// here are inferred from oneOf-of-allOf pairs — the IR lowerer promotes each /// variant to its own named schema, so the content type is a Named ref. #[test] @@ -58,10 +58,10 @@ fn internally_tagged_union_emits_discriminator_intersection() { let files = render_models("valid/type-aliases/discriminated-union-internally-tagged.yaml"); let resource = find(&files, "Resource.ts"); - // Each variant should appear as `({ kind: 'VAL' } & SomeType)`. + // Each variant retains its discriminator literal and content intersection. for value in ["RESOURCE_UNSPECIFIED", "RESOURCE_QUICK", "RESOURCE_CUSTOM"] { assert!( - resource.contains(&format!("kind: '{value}'")), + resource.contains(&format!("Record<'kind', '{value}'>")), "expected discriminator literal {value} in:\n{resource}" ); } @@ -76,7 +76,7 @@ fn internally_tagged_union_emits_discriminator_intersection() { ); // Pipe separator between variants. assert!( - resource.contains(" | "), + resource.contains("export type Resource =\n | (Record<'kind', 'RESOURCE_UNSPECIFIED'> & ResourceUnspecified)\n | (Record<'kind', 'RESOURCE_QUICK'> & ResourceQuick)\n | (Record<'kind', 'RESOURCE_CUSTOM'> & ResourceCustom);\n"), "expected `|` between variants, got:\n{resource}" ); } @@ -92,7 +92,7 @@ fn discriminated_union_with_refs_imports_each_variant() { container.contains("export type ContainerImage ="), "expected type alias, got:\n{container}" ); - // At least one `{ kind: '...' } & Something` variant should render. + // At least one discriminator Record intersected with a payload must render. assert!( container.contains(" & "), "expected intersection operator, got:\n{container}"