Repository navigation
feat: union-owned discriminator for discriminated schemas - #107
Merged
Merged
Conversation
Branches in Ack.discriminated(...) no longer need to redeclare the discriminator property. The union owns the discriminator and synthesizes an effective branch with the literal injected (first in the property order, marked required) for validation and JSON Schema export. Branches may still declare the discriminator as Ack.literal, Ack.enumString, or unconstrained Ack.string — the analyzer verifies compatibility. Adds DiscriminatedObjectSchema.effectiveBranch, effectiveDiscriminatedBranch util, TransformedSchema.copyWithSchema, and refactors the ack_generator analyzer + type_builder to emit subtype parse/safeParse via the union's effective branch. Docs, examples, and tests updated.
|
To view this pull requests documentation preview, visit the following URL: Documentation is deployed and generated using docs.page. |
There was a problem hiding this comment.
Pull request overview
This PR updates Ack.discriminated(...) so the union owns the discriminator property: branch schemas can omit the discriminator field, and the runtime/generator synthesize an “effective branch” schema with a required literal discriminator injected first (used for validation and JSON Schema export).
Changes:
- Add “effective branch” synthesis (
DiscriminatedObjectSchema.effectiveBranch,effectiveDiscriminatedBranch(...)) and use it for discriminated validation + JSON Schema export. - Add
TransformedSchema.copyWithSchemato support injecting discriminator literals through object-backed transforms. - Update
ack_generatoranalysis + emitted subtypeparse/safeParseto validate through the union’s effective branch; update docs/examples/tests accordingly.
Reviewed changes
Copilot reviewed 21 out of 23 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/ack/test/schemas/parse_result_immutability_test.dart | Updates discriminated branch test setup to omit discriminator property. |
| packages/ack/test/schemas/json_schema_default_emission_test.dart | Adjusts JSON Schema emission test for discriminator-owned behavior. |
| packages/ack/test/schemas/discriminated_object_schema_test.dart | Adds extensive runtime tests covering union-owned discriminator policy + effectiveBranch behavior. |
| packages/ack/test/schemas/comprehensive_json_schema_test.dart | Updates comprehensive JSON Schema tests to omit discriminator in branches. |
| packages/ack/test/schemas/composite_default_test.dart | Updates composite default tests for discriminator-owned branches. |
| packages/ack/test/converters/ack_to_json_schema_model_test.dart | Adds model-conversion tests for omitted/compatible discriminator properties and rejection cases. |
| packages/ack/lib/src/utils/discriminated_branch_utils.dart | Introduces structural, side-effect-free discriminator compatibility checking + effective-branch synthesis. |
| packages/ack/lib/src/schemas/transformed_schema.dart | Adds copyWithSchema to rebuild transforms with a different input schema. |
| packages/ack/lib/src/schemas/discriminated_object_schema.dart | Adds effectiveBranch and refactors parsing/JSON Schema export to use effective branches. |
| packages/ack/lib/src/converters/ack_to_json_schema_model.dart | Refactors discriminated conversion to convert effective branches and enforce discriminator-first ordering. |
| packages/ack_json_schema_builder/test/to_json_schema_builder_test.dart | Updates builder conversion tests for compatible discriminator handling and new failure mode. |
| packages/ack_generator/test/integration/ack_type_discriminated_test.dart | Expands integration coverage for omitted/compatible discriminators and rejection diagnostics. |
| packages/ack_generator/README.md | Documents union-owned discriminator rules for generation. |
| packages/ack_generator/lib/src/builders/type_builder.dart | Emits discriminated subtype factories using baseSchema.effectiveBranch(value); adjusts args filtering. |
| packages/ack_generator/lib/src/analyzer/schema_ast_analyzer.dart | Updates discriminated analysis to allow omitted/compatible discriminator properties and reject incompatible ones with clearer errors. |
| packages/ack_firebase_ai/test/to_firebase_ai_schema_test.dart | Updates Firebase AI schema conversion tests for compatible discriminator handling. |
| llms.txt | Updates canonical API/generation guidance for discriminator-owned discriminated unions. |
| example/test/schema_types_discriminated_test.dart | Adds example tests for generated discriminated types with omitted/explicit branch discriminators. |
| example/lib/schema_types_discriminated.g.dart | Updates generated example subtype parsing to validate via effectiveBranch. |
| example/lib/schema_types_discriminated.dart | Updates example source to omit discriminator fields in branches. |
| example/lib/pet.g.dart | Updates generated example subtype parsing to validate via effectiveBranch. |
| docs/core-concepts/typesafe-schemas.mdx | Updates guide docs to describe discriminator-owned discriminated unions and generation behavior. |
| docs/api-reference/index.mdx | Updates API reference with discriminator-owned semantics and effectiveBranch(...). |
Files not reviewed (2)
- example/lib/pet.g.dart: Language not supported
- example/lib/schema_types_discriminated.g.dart: Language not supported
Comment on lines
+189
to
193
| final AckSchema<T> effectiveSubSchema; | ||
| try { | ||
| effectiveSubSchema = effectiveBranch(discValueRaw); | ||
| } on ArgumentError catch (error) { | ||
| return SchemaResult.fail( |
Comment on lines
+100
to
103
| - branch schemas normally omit the discriminator field | ||
| - if a branch includes the discriminator field, it must be `Ack.literal(...)` | ||
| matching the branch key or `Ack.enumString(...)` containing the branch key | ||
|
|
leoafarias
added a commit
that referenced
this pull request
May 27, 2026
The synthesized discriminator schema produced by effectiveDiscriminatedObjectBranch now carries withDefault(discriminatorValue), so a discriminated branch whose runtime encode lambda omits the discriminator key still emits it via the default. Closes the encode side of PR #107's union-owned discriminator story (parse already synthesized the literal; encode previously required the branch to emit the key). ack_schema_model_builder.\_discriminated wraps each branch's exported model with _withRequiredDiscriminator so the JSON Schema marks the discriminator as required and strips the synthetic default from the output.
leoafarias
added a commit
that referenced
this pull request
May 28, 2026
The previous commit added `_matchesAnyBranchRuntime` plus a map-block in `validateRuntimeWithContext` so that calling validateRuntime directly stayed consistent with what encode would do for a map missing the discriminator. The consistency was edge-case and the cost was ~21 lines that most users never observe. Drop both. The encode fast path now calls `_validateDiscriminatorKey` inline to keep focused errors for non-string and unknown discriminator values (this used to fall out of the validateRuntime block). validateRuntime called directly is now stricter than encode for the specific case of "a JsonMap missing the discriminator key", which is a defensible invariant: a valid discriminated runtime carries its label. Behavior preserved: PR #107 union-owned discriminator (branches that omit the property) still works via `effectiveBranch`, and PR #109 codec branches that synthesize the discriminator at encode time still succeed through the slow path. Net: -33 lines on this PR.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Ack.discriminated(...)no longer need to redeclare the discriminator property; the union owns it and synthesizes an effective branch with the literal injected first for validation and JSON Schema export.Ack.literal(...)matching the branch key orAck.enumString(...)containing the branch key. BroadAck.string(), transformed/refined discriminator fields, and restrictive discriminator chains are rejected.DiscriminatedObjectSchema.effectiveBranch, theeffectiveDiscriminatedBranchutil, andTransformedSchema.copyWithSchema; refactorsack_generatorto emit subtypeparse/safeParsevia the union effective branch and updates docs, examples, and tests.Test plan
melos run build --no-selectmelos run analyze --no-selectmelos run test:dart --no-selectmelos run test:flutter --no-selectmelos run validate-jsonschema --no-selectgit diff --check