fix(spec): validate all three runtime view shapes in the view type-schema (#3095)#3159
Merged
Merged
Conversation
…schema (#3095) The `view` metadata type mapped to the aggregate container `ViewSchema` (`{ list, form, listViews, formViews }`, all slots optional). Zod strips unknown keys, so the two non-container shapes a `view` body carries at runtime — a standalone ViewItem record (`{ name, object, viewKind, config }`) and a console personalization overlay (raw config + inherited identity, #2555) — both strip-parsed to `{}`. That made the 422 check in `saveMetaItem` and read-time `computeMetadataDiagnostics` a no-op for them: a broken `config` (e.g. a kanban missing `groupByField`) saved with a false 200 and badged valid. Map `view` to a new `ViewMetadataSchema` — a union over the three shapes, each validated genuinely: 1. non-empty defineView container (empty container rejected), 2. ViewItem record (nested config validated against ListView/FormView), 3. flattened personalization overlay (inline config + identity), with structural guards pinning config/list/form/listViews/formViews to undefined so a malformed record/container is never rescued through the lenient branch with its payload stripped. All members strip-parse (no .strict()) so aux Studio keys still ride along without a false 422; saveMetaItem keeps persisting verbatim. toJSONSchema emits an anyOf of the four members for /api/v1/meta/types/view. Adds regression tests: schema-level (all three shapes + JSON Schema), save-path 422 for a bad ViewItem, and computeMetadataDiagnostics badges. Fixes #3095. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LFmMsWLC3CVULh3Ke7rUdd
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Contributor
📓 Docs Drift CheckThis PR changes 2 package(s): 107 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
…n schema Two CI gates broke on the #3095 change that maps `view` to a union: - Spec property liveness: `check-liveness.mts` walked each metadata type's top-level OBJECT shape; a union has none, so it threw "view is not an object schema". Teach `shapeOf` to resolve a union to its first object-typed member — the canonical authorable container (list/form/listViews/formViews) the ledger governs. Discriminated-union members (ViewItem) are skipped; their inner config is the same ListView/FormView surface already governed under the container's list/form children. - API-surface snapshot: record the two intended new public exports (`ViewMetadata` type, `ViewMetadataSchema` const) via gen:api-surface. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LFmMsWLC3CVULh3Ke7rUdd
os-zhuang
marked this pull request as ready for review
July 18, 2026 02:29
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.
Fixes #3095.
Problem
metadata-type-schemas.tsmapped theviewmetadata type to the aggregate containerViewSchema({ list, form, listViews, formViews }, every slot optional). Zod strips unknown keys, so the two non-container shapes aviewbody actually carries at runtime both strip-parsed to{}:{ name, object, viewKind, config }(frommetadata-create-seeds.ts)normalizeViewMetadata(Runtime view overlay drops viewKind/object — one grid sort permanently removes the view from the switcher #2555)Because they parsed to
{}and succeeded, the schema was a no-op for them:422spec-validation insaveMetaItemnever fired for ViewItem/personalization writes,computeMetadataDiagnosticsreturnedvalid: truefor enumerated ViewItems,A broken
config— e.g. a kanban missinggroupByField— saved with a false200and was badged valid.Fix
viewnow maps to a newViewMetadataSchema(packages/spec/src/ui/view.zod.ts) — az.unionover the three runtime shapes, each validated genuinely:ViewSchemarefined to require at least one oflist/form/listViews/formViews(an empty container is rejected, mirroringdefineView).ViewItemSchema; the nestedconfigis validated against ListView/FormView.config/list/form/listViews/formViewstoundefined, so a malformed record or container can never be rescued through this lenient branch with its real payload silently stripped.Design notes:
.strict()), so auxiliary Studio round-trip keys (isPinned,sortOrder, …) still ride along without a false422;saveMetaItemkeeps persisting the body verbatim.columnsand the ListView/FormViewtypeenums are disjoint — so a bad list config cannot silently pass as a form.z.toJSONSchema()emits the schema as ananyOfof the four members, which/api/v1/meta/types/viewserves to Studio's SchemaForm (verified in tests).No change to
normalizeViewMetadata/ persisted shapes — the #2555 personalization overlay is validated in place, so console saves and switcher badges do not regress.Acceptance criteria
config(e.g. kanban missinggroupByField) returns422on save and an error in diagnostics.view./api/v1/meta/types/viewJSON Schema output (union →anyOf) verified.Tests
packages/spec/src/ui/view-metadata-schema.test.ts(new) — all three shapes accepted/rejected genuinely, member exclusivity, JSON-SchemaanyOfemission.packages/objectql/src/metadata-diagnostics.test.ts—computeMetadataDiagnostics('view', …)for bad ViewItem, good ViewItem, personalization overlay, and container.packages/objectql/src/protocol-view-identity-overlay.test.ts—saveMetaItemreturns422for a broken ViewItem record and200for a well-formed one.Full
@objectstack/spec(6911),@objectstack/objectql(911),@objectstack/metadata-protocol(34), and@objectstack/metadata(276) suites pass. A changeset is included.🤖 Generated with Claude Code
https://claude.ai/code/session_01LFmMsWLC3CVULh3Ke7rUdd
Generated by Claude Code