feat(ack): add Ack.lazy for recursive schemas#110
Merged
Conversation
Introduces LazySchema with a memoized builder for schema graphs that reference themselves (e.g. tree-shaped objects). Discriminated unions reject lazy branches with a lazy-specific error since the discriminator property cannot be statically analyzed through a deferred reference. JSON Schema export raises a clear UnsupportedError until $ref/$defs emission lands.
|
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
Adds a new lazy/deferred schema reference to the Ack runtime so recursive schema graphs (e.g., tree-like objects) can be expressed without requiring immediate construction of the full graph.
Changes:
- Introduces
Ack.lazy(name, builder)/LazySchemawith memoized target resolution for recursive graphs. - Prevents
Ack.lazy(including wrapped variants) from being used as a discriminated-union branch and adds tests for this behavior. - Makes JSON Schema export fail fast with a clear
UnsupportedErrorwhenAck.lazyappears in the schema graph, and adds regression tests.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/ack/lib/src/ack.dart | Adds the public Ack.lazy(...) factory and docs. |
| packages/ack/lib/src/schemas/schema.dart | Registers the new lazy_schema.dart part. |
| packages/ack/lib/src/schemas/schema_type.dart | Adds SchemaType.lazy for introspection/debug output. |
| packages/ack/lib/src/schemas/lazy_schema.dart | Implements LazySchema with memoized builder resolution and fluent support. |
| packages/ack/lib/src/schemas/discriminated_object_schema.dart | Rejects LazySchema as a discriminated branch with a dedicated ArgumentError. |
| packages/ack/lib/src/schema_model/ack_schema_model_builder.dart | Throws UnsupportedError when encountering LazySchema during model export. |
| packages/ack/test/schemas/lazy_schema_test.dart | Adds tests for recursive parse/encode, export failure, memoization, and equality semantics. |
| packages/ack/test/schemas/discriminated_object_schema_test.dart | Adds coverage ensuring lazy branches (bare and wrapped) are rejected with the lazy-specific error. |
Comment on lines
+68
to
+71
| final validated = validateRuntimeWithContext(value, context); | ||
| if (validated.isFail) return SchemaResult.fail(validated.getError()); | ||
|
|
||
| final runtime = validated.getOrNull(); |
Comment on lines
+77
to
+78
| 'JSON Schema export of recursive schemas (Ack.lazy) is not supported. ' | ||
| 'Use a non-recursive schema, or wait for \$ref/\$defs export support.', |
Comment on lines
+48
to
+49
| 'Discriminated branches cannot be Ack.lazy(...) - recursive ' | ||
| 'discriminator property references cannot be analyzed.', |
Copilot review feedback on PR #110: - LazySchema.encodeWithContext no longer re-validates the entire subtree before delegating to the target; it applies own constraints/refinements and lets the target's own encode handle target-side validation. Avoids O(N^2) work on deeply nested recursive graphs and prevents duplicate refinement side effects. - Reworded the JSON Schema export UnsupportedError and the discriminated branch ArgumentError so they describe "schemas containing Ack.lazy" / "deferred reference" instead of implying recursion specifically. Union-owned discriminator (caught review-time, ships with PR #110): - effectiveDiscriminatedObjectBranch now attaches withDefault to the synthesized literal so encode emits the discriminator even when the branch's runtime encoder omits it (the behavior flutter_codec needs for keyCodec, widgetCodec, constraintsCodec, ...). - ack_schema_model_builder._discriminated wraps each exported branch with _withRequiredDiscriminator so the JSON Schema marks the discriminator as required and strips the synthetic encode-time default from the output. Tests: - Regression test locking in single-pass lazy refinement counts. - Codec-backed branch whose encoder omits the discriminator. - Runtime omits the discriminator. - JSON Schema export keeps discriminator required and default-free.
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.lazy<Boundary, Runtime>(name, builder)for recursive schema graphs; the builder result is resolved on first use and memoized.Ack.lazythrough Draft-7definitions/$ref, including recursive refs, nullable ref metadata, duplicate-name deduping, collision detection for reused names with different targets, JSON Pointer escaping for local refs, and safe merging with existing rootdefinitions.allOfso Draft-7 consumers do not ignore$refsiblings.Ack.lazyschemas as discriminated-union branches with a lazy-specificArgumentErrorsince the discriminator literal cannot be analyzed through a deferred reference.ack_json_schema_builderand Firebase AI responseJsonSchema fixtures.Test plan
dart analyzeinpackages/ackdart test test/schemas/lazy_schema_test.dart test/schema_model/ack_schema_model_builder_test.dartinpackages/ack(31 tests passing)dart test test/schemas/comprehensive_json_schema_test.dart test/json_schema_conformance_test.dart test/schema_model/ack_schema_model_test.dartinpackages/ack(141 tests passing)dart testinpackages/ack(912 tests passing)dart analyze && dart testinpackages/ack_json_schema_builder(49 tests passing)flutter analyze && flutter testinpackages/ack_firebase_ai(33 tests passing, 4 live tests skipped without Firebase env)rg -n -F '$defs' docs packages --glob '!**/.dart_tool/**' --glob '!**/build/**'(no matches)rg -n "directly as (a )?discriminated|throws.*export|UnsupportedError.*lazy|\\$defs|#\\/\\$defs" docs packages --glob '!**/.dart_tool/**' --glob '!**/build/**'(no matches)git diff --check