fix(spec): enforce ViewFilterRule operator enum with legacy-alias normalization#3373
Merged
Conversation
…malization Replace the free-form `operator: z.string()` on ViewFilterRuleSchema with a canonical `z.enum` (VIEW_FILTER_OPERATORS) wrapped in `z.preprocess` that folds legacy shorthand/camelCase spellings (eq, gt, isNull, notEquals, …) to canonical on parse. This surfaces a clean operator enum to JSON-Schema consumers (ObjectUI SchemaForm renders a dropdown) and rejects genuinely unknown operators, while already-stored metadata keeps validating and upgrades to one vocabulary. - Export VIEW_FILTER_OPERATORS, ViewFilterOperator, VIEW_FILTER_OPERATOR_ALIASES and a normalizeFilterOperator() helper as the single source of truth. - Pin `widget: 'filter-builder'` on the list view `filter` field in view.form.ts, matching dataset/page forms so the visual builder renders instead of a repeater. - Canonicalize plugin-sharing sys_share_link filters (isNull -> is_null, isNotNull -> is_not_null), preserving null semantics. - Keep the legacy string `sort` form (deprecation note only); retain the objectui#2601 regression fixture. - Relative-date operators (this_quarter, …) are not filter-rule operators; tests updated to canonical operators and alias-normalization / unknown-rejection coverage added. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PwbhoMeqh33xFWq5M77QTJ
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Merged
2 tasks
Contributor
📓 Docs Drift CheckThis PR changes 2 package(s): 104 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
Auto-generated reference docs reflect the ViewFilterRule operator enum (replacing the free-form `string`). Regenerated via gen:schema && gen:docs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PwbhoMeqh33xFWq5M77QTJ
Additive-only: VIEW_FILTER_OPERATORS, VIEW_FILTER_OPERATOR_ALIASES, ViewFilterOperator, normalizeFilterOperator (0 breaking, 4 added). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PwbhoMeqh33xFWq5M77QTJ
os-zhuang
marked this pull request as ready for review
July 21, 2026 00:56
This was referenced Jul 21, 2026
os-zhuang
added a commit
that referenced
this pull request
Jul 21, 2026
…ty dir (#3376) The `Check Changeset` gate counted every `.md` in `.changeset/` (`find | wc -l`), so it only asserted the directory was non-empty — never that THIS PR contributed a changeset. In pre-release (RC) mode `changeset version` retains consumed changeset files, so the directory is permanently non-empty and the gate can never go red: #3373 merged a real spec / api-surface fix with no changeset while the step reported "Found 104 changeset(s)". Diff the base commit instead and count only changesets the PR adds. An empty-frontmatter changeset still counts — it is the sanctioned "releases nothing" declaration. Proven to go RED on #3373's diff (0 added) and GREEN on #3360's (1 added). Also backfills the missing #3373 changeset so the ViewFilterRule operator enum fix actually ships in the next release. Co-authored-by: Jack Zhuang <277994282+os-zhuang@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 <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 #3368.
Summary
ViewFilterRuleSchema.operatorwas a free-formz.string(), so JSON-Schema consumers (notably ObjectUI's SchemaForm) could not render an operator dropdown and invalid operators passed validation silently. This replaces it with a canonical enum + on-parse alias normalization, chosen over a bare strict enum so already-stored view metadata does not break.operator→z.preprocess(normalizeFilterOperator, z.enum(VIEW_FILTER_OPERATORS)).z.enumsurfaces the canonical operator list toz.toJSONSchemainio: 'input'mode (verified), so SchemaForm auto-renders a clean dropdown.z.preprocessfolds legacy shorthand/camelCase spellings (eq,gt,gte,isNull,notEquals,nin, …) to canonical on parse, so consumers see exactly one vocabulary and stored data keeps validating.VIEW_FILTER_OPERATORS,ViewFilterOperator,VIEW_FILTER_OPERATOR_ALIASES, andnormalizeFilterOperator().widget: 'filter-builder'on the list viewfilterfield inview.form.ts— matching the existingdataset.form.ts/page.form.tsand the schema's own JSDoc example — so the Studio editor renders the field-type-aware visual builder instead of raw repeater rows (contract-first: the fix lives in the producer/metadata, not the renderer).plugin-sharingsys_share_linkfilters (isNull→is_null,isNotNull→is_not_null), preserving null semantics (not theis_emptyrewrite).sortform (deprecation note only) and retains the objectui#2601 regression fixture — thesortremoval is intentionally left to its own deprecation cycle.Decision: relative-date operators
this_quarteret al. are not filter-rule operators — they are date-range presets (DashboardFilterSchema.defaultRange) and were unimplemented at the filter-rule runtime level. They are dropped from the operator vocabulary; JSDoc and fixtures updated accordingly.Why not the previous attempt (#3363)
That PR used a strict enum with no aliases, which breaks stored data and ObjectUI's own
FilterBuilderWidgetwrites (gt/gte), bundled the unrelated breakingsortremoval, deleted the objectui#2601 regression fixture, and rewroteisNull→is_empty(changing semantics). Its "all tests pass" came from editing the tests themselves.Test plan
pnpm --filter @objectstack/spec test— 256 files, 6809 tests pass.pnpm --filter @objectstack/spec exec tsc --noEmit— clean.gte→greater_than_or_equal,eq→equals,isNull→is_null,nin→not_in, …) and unknown-operator rejection.Paired with objectstack-ai/objectui#2764.
🤖 Generated with Claude Code
https://claude.ai/code/session_01PwbhoMeqh33xFWq5M77QTJ
Generated by Claude Code