Skip to content

fix: admit empty-object allOf members instead of crashing#214

Open
nottmey wants to merge 1 commit into
eseidel:mainfrom
nottmey:fix/allof-empty-object-member
Open

fix: admit empty-object allOf members instead of crashing#214
nottmey wants to merge 1 commit into
eseidel:mainfrom
nottmey:fix/allof-empty-object-member

Conversation

@nottmey

@nottmey nottmey commented Jun 26, 2026

Copy link
Copy Markdown

Summary

allOf: [{$ref: JsonObject}, {type: object, properties: {...}}] crashes with FormatException: allOf only supports objects because JsonObject (an open empty object: type: object, properties: {}, additionalProperties: {}) resolves to ResolvedEmptyObject, not ResolvedObject, and the resolver rejected any non-ResolvedObject allOf member.

Fix

Allow ResolvedEmptyObject as an allOf member (alongside ResolvedObject) in the type-check at resolver.dart. An empty open object intersects trivially — every JSON object satisfies it — so it is a valid allOf member that simply contributes no properties to the merged object the renderer builds.

The render pass already only merges RenderObject members (case ResolvedAllOf in render_tree.dart), so a ResolvedEmptyObject member is already a no-op there; this just makes the resolver stop crashing on the way there. The allOf is preserved (not collapsed), so the parent schema keeps its name — important for test emission and downstream naming.

This mirrors the spirit of PR #138's comment_references handling: spec prose / spec shapes that trip Dart's tooling should be sanitized at the generator boundary rather than crashing.

Spec that surfaced this

Backstage's catalog OpenAPI spec uses this exact shape for RecursivePartialEntityMeta:

https://github.com/backstage/backstage/blob/master/plugins/catalog-backend/src/schema/openapi.yaml

Schema excerpt:

JsonObject:
  type: object
  properties: {}
  additionalProperties: {}
  description: A type representing all allowed JSON object values.
RecursivePartialEntityMeta:
  allOf:
    - $ref: '#/components/schemas/JsonObject'
    - type: object
      properties: { ... }
      additionalProperties: false

With this fix, space_gen generates the full Backstage catalog client from the remote spec with no patches or vendoring required.

Test plan

  • Added allOf admits empty-object members to test/resolver_test.dart — verifies an allOf containing an open empty object plus a real object resolves to a ResolvedAllOf with both members preserved (no crash, no collapse).
  • dart test test/resolver_test.dart — all resolver tests pass.
  • dart test (full suite) — 548 tests pass, no regressions.
  • End-to-end: ran this branch's bin/space_gen.dart against the remote Backstage catalog spec URL; full client generated successfully (name: devhub_client, all lib/ emitted, RecursivePartialEntityMeta model present, 138 generated round-trip tests pass).

@nottmey nottmey force-pushed the fix/allof-empty-object-member branch from cd373a1 to 44b4a77 Compare June 26, 2026 14:45
@nottmey nottmey changed the title fix: elide empty-object allOf members instead of crashing fix: admit empty-object allOf members instead of crashing Jun 26, 2026
`allOf: [$ref JsonObject, {type: object, properties: {...}}]` crashed with
`FormatException: allOf only supports objects` because `JsonObject` (an open
empty object: `type: object, properties: {}, additionalProperties: {}`)
resolves to `ResolvedEmptyObject`, not `ResolvedObject`, and the resolver
rejected any non-`ResolvedObject` allOf member.

An empty open object intersects trivially — every JSON object satisfies it —
so it is a valid allOf member that simply contributes no properties to the
merged object the renderer builds. The render pass already only merges
`RenderObject` members (`case ResolvedAllOf` in render_tree.dart), so a
`ResolvedEmptyObject` member is already a no-op there; this just makes the
resolver stop crashing on the way there.

Backstage's catalog spec uses exactly this shape for
`RecursivePartialEntityMeta`:
https://github.com/backstage/backstage/blob/master/plugins/catalog-backend/src/schema/openapi.yaml

After this fix, space_gen generates the full Backstage catalog client from
the remote spec without patches.

Co-authored-by: Cursor <cursoragent@cursor.com>
@nottmey nottmey force-pushed the fix/allof-empty-object-member branch from 44b4a77 to 7e20da6 Compare June 26, 2026 15:06
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.

1 participant