Skip to content

feat(ack): add Ack.lazy for recursive schemas#110

Merged
leoafarias merged 10 commits into
mainfrom
feat/lazy-schema
May 28, 2026
Merged

feat(ack): add Ack.lazy for recursive schemas#110
leoafarias merged 10 commits into
mainfrom
feat/lazy-schema

Conversation

@leoafarias
Copy link
Copy Markdown
Collaborator

@leoafarias leoafarias commented May 27, 2026

Summary

  • Adds Ack.lazy<Boundary, Runtime>(name, builder) for recursive schema graphs; the builder result is resolved on first use and memoized.
  • Exports schema graphs containing Ack.lazy through Draft-7 definitions / $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 root definitions.
  • Wraps non-null metadata-bearing lazy refs with allOf so Draft-7 consumers do not ignore $ref siblings.
  • Documents recursive schemas and JSON Schema export behavior across the API reference, schema concepts, and JSON Schema integration guide.
  • Rejects bare or wrapped Ack.lazy schemas as discriminated-union branches with a lazy-specific ArgumentError since the discriminator literal cannot be analyzed through a deferred reference.
  • Adds adapter coverage for recursive lazy refs in ack_json_schema_builder and Firebase AI responseJsonSchema fixtures.

Test plan

  • dart analyze in packages/ack
  • dart test test/schemas/lazy_schema_test.dart test/schema_model/ack_schema_model_builder_test.dart in packages/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.dart in packages/ack (141 tests passing)
  • dart test in packages/ack (912 tests passing)
  • dart analyze && dart test in packages/ack_json_schema_builder (49 tests passing)
  • flutter analyze && flutter test in packages/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

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.
@docs-page
Copy link
Copy Markdown

docs-page Bot commented May 27, 2026

To view this pull requests documentation preview, visit the following URL:

docs.page/btwld/ack~110

Documentation is deployed and generated using docs.page.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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) / LazySchema with 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 UnsupportedError when Ack.lazy appears 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.
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 17 out of 17 changed files in this pull request and generated no new comments.

@leoafarias leoafarias merged commit 5e122e3 into main May 28, 2026
3 checks passed
@leoafarias leoafarias deleted the feat/lazy-schema branch May 28, 2026 18:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants