feat(schemas): x-adcp-hoist opt-in marker for canonically shared object schemas#4630
Merged
Conversation
…hemas (#4557) Opt-in companion to the pure-enum auto-hoist (`hoistDuplicateInlineEnums`, #3170). Spec authors set `x-hoist: true` on a source schema's root; the bundler moves it to root `$defs`, replaces every inline occurrence with a `$ref`, and strips the directive from bundled output. Why opt-in for complex objects: structural identity ≠ semantic identity (BriefAsset and VASTAsset share fields today but represent different lifecycle concepts). Auto-hoisting would lock in coupling that's hard to walk back. `x-hoist` is the deliberate exception, not the default — and per-type decisions stay separate from landing the mechanism. Behavior: - Hoists at any occurrence count (>=1). The marker declares intent; honoring it for single uses means adding a second reference later doesn't change the codegen surface. - `title` is required — missing/empty -> build error (the directive is meant to be deliberate). - Collision suffixing (`PriceBlock2`) when a `$defs` name already exists, matching the enum-hoist convention. - Different titles never collapse, even with structurally identical bodies — preserves the BriefAsset/VASTAsset distinction by default. - Recurses into hoisted defs so nested markers also collapse to refs. Dry-run validation: marking `core/duration.json` and rebuilding hoisted 6 inline copies in `create-media-buy-request` (and 5 other media-buy bundles) into a single `$defs.Duration` entry, with Ajv compiling cleanly and `x-hoist` absent from output. Reverted before commit — no source schemas opt in here. Per-type decisions ship in follow-ups. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Rename `x-hoist` → `x-adcp-hoist` to match the `x-adcp-*` namespacing convention documented in schema-extensions.mdx. Prevents collision with any future cross-vendor `x-hoist` keyword and matches the precedent set by `x-adcp-validation`. - Strip stray `x-adcp-hoist` markers from bundled output even when they live inside a pre-existing $defs block (which Pass 1 skips). The directive must never leak into bundled artifacts regardless of where it was authored. - Reject two distinct schemas marked with the same `title` — the collision-suffix path would silently rename one of them, defeating the directive's "canonical name" guarantee. Pre-existing $defs collisions still suffix (that case is a non-marker name collision, not a spec author bug). - Wire `test:build-schemas-hoist-marked` into the npm `test` chain so the unit tests actually run in CI. - Add changeset for the bundler mechanism. - Add `docs/reference/schema-extensions.mdx` entry covering directive contract, bundler behavior, SDK/codegen impact, and conformance — the canonical reference for source-tree consumers who dereference source schemas directly. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.
Closes #4557.
Summary
Opt-in companion to the pure-enum auto-hoist (
hoistDuplicateInlineEnums, #3170). Spec authors setx-adcp-hoist: trueon a source schema's root; the bundler moves it to root\$defs, replaces every inline occurrence with a\$ref, and strips the directive from bundled output.Why opt-in for complex objects: structural identity ≠ semantic identity (
BriefAssetandVASTAssetshare fields today but represent different lifecycle concepts). Auto-hoisting would lock in coupling that's hard to walk back.x-adcp-hoistis the deliberate exception, not the default — and per-type decisions stay separate from landing the mechanism.Naming follows the
x-adcp-*precedent set byx-adcp-validation; the schema-extensions reference documents the directive's contract for source-tree consumers.Behavior
titleis required — missing/empty → build error (the directive is meant to be deliberate).Foo2would defeat the "canonical name" guarantee.PriceBlock2) only for a pre-existing\$defskey (non-marker name collision), matching the enum-hoist convention.BriefAsset/VASTAssetdistinction by default.\$defsblock — the directive never appears in bundled output.Dry-run validation
Marked
core/duration.jsonwithx-adcp-hoist: trueand rebuilt locally:\$defs.Durationpresent in changed bundlestitle, nox-adcp-hoistx-adcp-hoistanywhere in bundled output\"title\": \"Duration\"outside\$defscreate-media-buy-requestcollapsed)Source change reverted before commit. No source schemas opt in here — per-type decisions ship in follow-ups.
Confirmed against this branch's full build: zero bundles change semantically vs. main (timestamp-only diffs only).
Review-driven changes
Two expert reviews flagged:
npm test→ fixed (test:build-schemas-hoist-markedadded to the chain)..changeset/4557-x-adcp-hoist-opt-in-marker.md).\$defsweren't stripped → fixed (final sweep + dedicated tests).Foo/Foo2→ fixed (build-time error + test).x-hoistnamespace → renamed tox-adcp-hoist(matchesx-adcp-validationprecedent).docs/reference/schema-extensions.mdxentry documents the directive contract, conformance, and SDK codegen impact.Out of scope
BriefAsset/VASTAsset/DAASTAsset/CatalogAsset(RFC default lean: keep-split).Test plan
tests/build-schemas-hoist-marked.test.cjs(basic hoist, single-occurrence, missing/empty title errors, same-title-different-shape error, PascalCase sanitization, pre-existing $defs collision, distinct-titles preservation, no-op, array items, nested markers, pre-existing $defs same-shape, stray-marker stripping in two configurations)hoist-enumsandpreserve-subschema-idstests still passnpm testchain now includestest:build-schemas-hoist-markednode scripts/build-schemas.cjsruns clean (81 bundled schemas, zero semantic diffs vs main)🤖 Generated with Claude Code