Skip to content

feat(filter-builder): the dropdown speaks the protocol's operator ids; camelCase is read as a deprecated alias (objectui#9306) - #10534

Merged
objectstack-fleet[bot] merged 5 commits into
mainfrom
claude/issue-9306-filter-builder-protocol-ids
Sep 25, 2026
Merged

objectstack-fleet[bot] merged 5 commits into
mainfrom
claude/issue-9306-filter-builder-protocol-ids

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

Part of #9306. ⛔ This PR does not close it: ruling item 4's components half stays open on the card (see "Why objectui#9306 stays open" below).
Clause-②: yes

The FilterBuilder dropdown now emits the protocol's operator ids: the twenty members of @objectstack/spec's VIEW_FILTER_OPERATORS, spelled the way the spec spells them, plus the opt-in exists / notExists, which stay unfolded under objectui#9559 ruling B. The old camelCase ids are the spec's deprecated alias form (objectui#7993). They are accepted on read and rewritten on write. The builder folds a stored spelling at its read boundary, through the spec's normalizeFilterOperator plus one local row the spec's alias table lacks: containsCaseInsensitive → icontains, tracked as objectstack-ai/objectstack#20092. The author's next edit then writes the canonical id. Opening a stored filter writes nothing.

This is the dropdown half. The mirror half landed as objectui#10465 (1779e8dee).

Why objectui#9306 stays open (seat note)

The domain:ui seat #1 (session_01BA3nKVUwKQJf8DBxrSVtNC) changed line 1 from a closing keyword to a plain reference. The rest of this body is the dev's, unchanged.

Ruling 5651809976 item 4 is an acceptance criterion of objectui#9306: 「objectui#6349's two parked name-authority rows converge on the @object-ui/types declaration with operator typed as the protocol set」. objectui#10465 delivered the types half. This PR does not touch the components half:

  • packages/components/src/custom/filter-builder.tsx still declares its own FilterBuilderCondition { operator: string } and FilterGroup;
  • two FilterBuilderOperator exports remain: @object-ui/types with the protocol's 20, and @object-ui/components with those 20 plus the opt-in exists / notExists.

Converging the components declaration on the types one "with operator typed as the protocol set" would refuse the opt-in pair that the later, ratified ruling B keeps in the dropdown. The seat records that on the card instead of choosing here. Everything else this card asks for (items 1–3 and 5) is in this PR.

What changed, by consumer

package table now
components defaultOperators, the buckets, VALUELESS_FILTER_BUILDER_OPERATORS, the label defaults protocol ids. The buckets are typed as the id union, so a stale spelling fails to compile.
components read boundary normalizeFilterBuilderOperator (new export) folds each row when a group arrives. It feeds the arity, reconcile, mounted-trigger and value-input folds.
components OPT_IN_OPERATORS icontains is no longer opt-in (seat decision on the card; see below). exists / notExists stay opt-in.
fields condToMongo, kvToCondition, TEXT_COMPARAND_OPERATORS, FILTER_CONDITION_EXTRA_OPERATORS keyed on protocol ids. The stored $-tokens are unchanged.
app-shell dataset bridge OP_TO_MONGO / MONGO_TO_OP / VALUELESS_TO_MONGO keyed on protocol ids. New row: icontains ↔ $icontains.
app-shell widgets.tsx SPEC_TO_FB removed. Every key was already a row of the spec's fold, and the builder now applies that fold itself.
app-shell drillUrlFilters.ts NULL_FILTER label keys filterBuilder.operators.is_null / is_not_null
app-shell ObjectView.tsx sanitizeViewOverride folds the row's operator through normalizeFilterOperator before the value-less lookup, the same both-sides fold objectui#9302 / #9359 used. A stored { operator: 'isEmpty', value: '' } override row is kept.
plugin-view CANONICAL_TO_BUILDER removed. It had become the identity over the twenty. specToBuilderOperator returns the canonical member.
i18n filterBuilder.operators.* re-keyed to the protocol ids in all ten packs. Every translated value is untouched.

icontains leaves OPT_IN_OPERATORS. Seat decision on the card, executing the plan that docblock records. Once the dropdown's id was icontains, plugin-list's offered-equals-expressible spine measured it as expressible on both list dialects but still withheld: VALID_AST_OPERATORS has it, ViewFilterRuleSchema accepts it, and mapOperator has an arm for it. It is now offered on the text bucket to every consumer. FilterConditionField keeps writing $icontains, and contains / icontains stay two operators (objectui#7379).

The 22-id census: before and after, every consumer

I measured once, the same way for both trees. On the base tree (bc4efba73) each consumer ran over the 22 camelCase rows; on this branch it ran over the 22 ids those rows became. Consumers: condToMongo and its kvToCondition read-back, the dataset groupToCondition and its conditionToGroup read-back, the saved-view fold, the live grid's convertFilterGroupToAST, the override recovery pass, and the view-config reader. That is 22 × 8 cells.

  • Every cell matched except four, all on one row. Base containsCaseInsensitive against branch icontains, on the dataset bridge (write and read-back), the saved-view fold and the live grid. Those are the consumers that never offered the case-insensitive contains.
  • In those four cells the old id produced nothing storable. The dataset bridge dropped it, the fold wrote a rule the server enum refuses, and the grid emitted a node isFilterAST rejects. The new id produces $icontains / a valid icontains rule / an icontains node. These are new rows, not changed ones.
  • Stored predicates: condToMongo stores the identical predicate for all 22. So does the override pass (keep / drop).

The permanent pins hold this:

  • app-shell filter-builder-protocol-ids-census-9306.test.ts: every id's stored predicate in condToMongo and the dataset bridge, read-backs included, plus fold and override equivalence for both spellings.
  • plugin-list and plugin-view each get a 22-row leg beside their readers.
  • components filter-builder-protocol-ids-9306.test.tsx: the vocabulary equals the protocol set, and a type-level pin FilterBuilderOperator = ViewFilterOperator | 'exists' | 'notExists'. It also covers the 22-row read-side fold, and a stored camelCase group that loads with no write and saves back canonical on the next edit.

Ablations

All three ran under ablation-replace (WRAP mode) at f5134f30a. In each, the anchor went ×1 → ×0 on disk, the blob changed, and the restore was checked as blob == HEAD with an empty git diff HEAD. The subjects load through the vitest alias to src, so no dist leg is owed.

  • (a) condToMongo's greater_than_or_equal arm deleted: Tests 3 failed | 192 passed. The census reported greater_than_or_equal as storing a different predicate (the row fell to the default equality), the census read-back failed, and so did the fields round trip.
  • (b) the read-side fold removed (normalizeGroupOperators returns its input): Tests 1 failed | 30 passed. The failing test is "saves back: the author's next edit writes every row's CANONICAL id".
  • (c) the sanitizeViewOverride fold removed: Tests 11 failed | 143 passed. Every stored camelCase value-less row (isEmpty, isNotEmpty, isNull, isNotNull, isnull, and the legacy triple) was dropped.

Tests and gates

All results are from the repo root through the shared verify lock, at 2f87c03ec (this branch with origin/main merged in). Every run exited 0.

Test suites:

  • vitest run packages/components/: 300 passed | 1 skipped (301)
  • vitest run packages/fields/ packages/plugin-view/ packages/plugin-list/: 334 passed | 1 skipped (335)
  • vitest run --shard=N/3 packages/app-shell/src/views/, N = 1..3: 166 / 166 / 166 files passed
  • vitest run packages/i18n/ packages/types/src/__tests__/ packages/core/src/utils/ examples/schema-catalog/test/ plus the i18n call-site, dead-key, en-drift, designer-table, defaults-population and placeholder-spelling script suites: 433 passed (433)

Type-check after building the app-shell closure (turbo run build --filter=@object-ui/app-shell^... --concurrency=2, 28 tasks): run type-check (tsc --noEmit && tsc -p tsconfig.test.json) exited 0 on types, core, i18n, components, fields, plugin-list, plugin-view and app-shell. --listFilesOnly shows the new pins inside the test programs.

Reverse check against the rebuilt dist: in a scratch file in fields, FilterBuilderOperator = 'greaterOrEqual' gives TS2322. The control, with that line removed, exits 0. The file was deleted.

ESLint over the 45 touched lintable files, per rule, base against head (at f5134f30a; the merge touched none of the 45):

  • no-explicit-any: 259 → 259
  • no-unused-vars: 3 → 3
  • exhaustive-deps: 10 → 10
  • set-state-in-effect: 5 → 5
  • react-refresh/only-export-components: 39 → 40. The new normalizeFilterBuilderOperator export sits in a component file that already exports ten helpers.
  • 0 errors either side. The root config enables no type-aware linting and no custom rule reads other files, so this diff cannot move the verdict on an untouched file.

Gates, all exit 0:

  • check-changeset-presence: 1 changeset for 8 released packages
  • check-changeset-no-major
  • check-changeset-overwrite: 0 modified
  • check:new-line-citations: 0 new
  • check:i18n-keys: the dynamic family is read from defaultOperators, so the re-key is seen
  • check:control-bytes
  • check:changeset-claims (report-only): I read all 9 flagged paragraphs, and none is falsified by this change.

Declared deviations

  1. ObjectView.tsx: besides sanitizeViewOverride, the only edit is one added specifier, normalizeFilterOperator, on the file's existing @objectstack/spec/ui import line. The fold needs it. It is hunk-disjoint from objectui#10035's region, and that card's PR objectui#10494 has already merged.
  2. normalizeFilterBuilderOperator guards the spec's answer. If the spec returns a non-string, the builder keeps the original spelling. Measured: normalizeFilterOperator('constructor') returns the Object function, and toString / hasOwnProperty / valueOf return their prototype members, although the function is declared to return a string. The builder now STORES the fold's answer, so without the guard such a row would lose its operator on serialisation. The defect itself is upstream and reported, not patched here in any other way.
  3. SPEC_TO_FB and CANONICAL_TO_BUILDER are removed rather than re-keyed. Re-keyed, they would have been a hand-kept copy of the spec's fold and an identity table.
  4. Naming mismatch: the dispatch's FILTER_BUILDER_IDS literal in the types 9559 test is named DROPDOWN_IDS. It is relabelled as the pre-objectui#9306 corpus rather than rewritten, because those ids are exactly what old stored filters carry.

Acceptance notes (outside this change's surface, not edited)

  • carrier: seat. plugin-list ListView.tsx, held by objectui#10326. Two stale docblock sentences:

    • The LIST_VIEW_EXTRA_OPERATORS docblock says neither list dialect has a case-insensitive contains. That was already false since spec 17.1.0, and the toolbar now offers icontains.
    • The VALUELESS_FILTER_BUILDER_OPERATORS_CANONICAL docblock calls the builder's set camelCase.

    Both are still correct in behaviour.

  • carrier: seat. content/docs/components/complex/filter-builder.mdx (draft PR objectui#10515) lists operators by English label only. It could state the protocol-id vocabulary and the read-side alias rule.

  • carrier: seat. These pending changesets name the builder's ids in their camelCase spelling in the present tense, and publish into the same release: 7561-filter-builder-operator-trigger-label, 9302-filter-builder-valueless-canonical-fold, 9359-list-ast-valueless-canonical-fold, 9559-filter-operator-schema-protocol-set and 8748-icontains-empty-comparand. An in-release supersession note is the precedent objectui#10465 used.

  • For the seat to file in objectstack (class a): normalizeFilterOperator returns non-strings for Object.prototype keys. The probe is in deviation 2.


Generated by Claude Code

…— checkpoint, stopped on a file-surface breach

CHECKPOINT, NOT A DELIVERABLE. Source half of the dropdown change only; the
pins that read these tables are NOT updated yet, so 19 test files are red on
this commit by design. Committed so the work survives the stop and a takeover
starts from the remote sha rather than from nothing.

- components: `defaultOperators` emits the 20 `VIEW_FILTER_OPERATORS` ids plus
  the opt-in `exists` / `notExists`; a read boundary folds stored spellings
  through the spec's `normalizeFilterOperator` plus ONE local row
  (`containsCaseInsensitive` -> `icontains`, which the spec's alias table
  lacks); buckets typed as the id union; `icontains` leaves OPT_IN_OPERATORS
  (the list parity spine measures it expressible on both list dialects).
- fields: `condToMongo` / `kvToCondition` / `TEXT_COMPARAND_OPERATORS` /
  `FILTER_CONDITION_EXTRA_OPERATORS` keyed on the protocol ids; stored `$`-tokens
  unchanged.
- app-shell: dataset bridge tables keyed on the protocol ids, plus
  `icontains` <-> `$icontains`; the Studio widget's local SPEC_TO_FB table is
  removed (the builder folds at its read boundary).
- plugin-view: CANONICAL_TO_BUILDER (now an identity) removed.
- i18n: `filterBuilder.operators.*` re-keyed in all ten packs, values untouched.

Stopped because two consumers OUTSIDE the claim's file surface need edits:
app-shell `drillUrlFilters.ts` (it reuses the `isNull` / `isNotNull` label
keys) and app-shell `ObjectView.tsx` `sanitizeViewOverride` (raw value-less
lookup against a set whose builder half no longer carries the deprecated ids).

Claude-Session: https://claude.ai/code/session_01BA3nKVUwKQJf8DBxrSVtNC
…r the protocol operator ids

Completes the dropdown half of objectui#9306 on top of the checkpoint.

- app-shell drillUrlFilters: NULL_FILTER label keys follow the re-keyed
  `filterBuilder.operators.is_null` / `is_not_null` (the drill chip rendered
  the raw key otherwise).
- app-shell ObjectView sanitizeViewOverride ONLY: the row's operator is folded
  through the spec's normalizeFilterOperator before the value-less lookup, so
  a stored `{ operator: 'isEmpty', value: '' }` override row is kept on read.
- components: normalizeFilterBuilderOperator keeps the spelling when the
  spec's normalizer answers a non-string (Object.prototype keys).
- Stale prose: viewFilterFold VALUELESS_FILTER_OPERATORS comments (set
  unchanged), core filter-converter + 8976 test, types 9559 corpus label,
  schema-catalog 7561 CORRECTION arm.
- Pins: in-surface parity pins re-keyed to the protocol ids; new
  filter-builder-protocol-ids-9306 (vocabulary, type-level pin, read-side
  fold, camelCase load-and-save-back) and the app-shell 22-id census; live
  grid and view-config census legs beside their readers.

Claude-Session: https://claude.ai/code/session_01BA3nKVUwKQJf8DBxrSVtNC
@github-actions

github-actions Bot commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

changeset-claim-re-read

⚠️ 9 pending changeset(s) describe a file this change touches

Their bodies publish verbatim into the CHANGELOG at the next release, so this is a request to re-read them against your diff — addressed here because you are the one seat that can answer it without re-deriving anything.

⛔ Nothing here blocks, and nothing here is a verdict on your change. This gate exits 0, is not a required context, and judges name resolution, never meaning: it asked whether a pending body names a file you touched. "Is this sentence still true?" is the one question it will not answer, and the one you are being asked to answer.

.changeset/5293-view-sort-order-spelling.md

  • names packages/plugin-view/src/config/view-config-utils.ts → packages/plugin-view/src/config/view-config-utils.ts — edited by this change

    Scope — at this change one other published export still accepted direction, and this release did not retire it. toSortItems (packages/plugin-view/src/config/view-config-utils.ts, re-exported from the package root and listed in the README) folded s.order || s.direction || 'asc'. It serves a different surface — the studio inspector-draft that feeds SortBuilder — and it is not reachable from the views prop, so it neither affected nor was affected by this rename: dormant (nothing in this repo called it outside a test), and removing it would be a separate break on a separate public export, tracked as objectui#6011. It was not a partial retirement of this one.

.changeset/6661-app-launcher-nav-menu-renderers.md

  • names en.ts → packages/i18n/src/locales/en.ts — edited by this change

    Three new strings — the launcher's and the menu's accessible names, and the menu's empty state — are declared under console.nav in en.ts and its nine sibling packs. An inline defaultValue alone is not a fix: it renders English at one call site and leaves the string untranslatable everywhere (objectui#3517).

.changeset/6939-filter-builder-mirror.md

  • names packages/components/src/custom/filter-builder.tsx → packages/components/src/custom/filter-builder.tsx — edited by this change

    1. FilterFieldSchema required name. Every read site matches an entry by value — fields.find((f) => f.value === …) in getOperatorsForField, changeField, getInputType and renderValueInput, fields[0]?.value in addCondition, and ANGLE-BRACKETS(SelectItem value={field.value}) in the field dropdown (packages/components/src/custom/filter-builder.tsx). name had zero read sites, and FilterBuilderProps.fields in that same file already declares ArrayANGLE-BRACKETS({ value, label, type? }). @object-ui/fields' deriveFilterFields, the real producer that builds this list from an object schema, emits value too, and the published doc (content/docs/components/complex/filter-builder.mdx) has declared value: string all along. 2. Its type enum was string | number | date | boolean | select. string is a phantom, and text, datetime and time — three of the six FilterValueFamily members the component folds a column into — were all refused. 3. FilterGroupSchema was { operator, conditions }. The gate is isValidGroup, which tests Array.isArray(v.conditions) and v.logic === "and" || v.logic === "or" and nothing else.

.changeset/7070-no-invented-gantt-date-fields.md

  • names app-shell/src/views/ObjectView.tsx → packages/app-shell/src/views/ObjectView.tsx — edited by this change

    • app-shell/src/views/ObjectView.tsx — the console object page. The inline branch becomes ganttViewOptions, the sibling of calendarViewOptions and timelineViewOptions: the declared block spread whole, title floored at 'name', no date field invented. - plugin-list/src/ListView.tsx — the render branch AND the capability gate. - plugin-view/src/ObjectView.tsx — generateViewSchema, the authored object-view element route, which bypasses ListView entirely.

.changeset/7125-dashboard-empty-state-keys-retired.md

  • names widgets.tsx → packages/app-shell/src/views/metadata-admin/widgets.tsx — edited by this change

    Not touched: table.noRows ('No rows to display') and engine.form.noRows (packages/app-shell/src/views/metadata-admin/i18n.ts, read at widgets.tsx) — two different, same-named keys in different namespaces. Nor the comments in WidgetEmptyState.tsx, DatasetWidget.tsx, ObjectDataTable.tsx and PivotTable.tsx that record WHY three widgets with three strings became one shared empty state; the packs' own comment keeps that rationale and now names the retirement instead of a row that is gone.

.changeset/8218-studio-property-panel-de-developerize.md

  • names widgets.tsx → packages/app-shell/src/views/metadata-admin/widgets.tsx — edited by this change

    SchemaForm + widgets.tsx were written for an administrator editing metadata. Studio's interface panel renders the very same form for someone who has never seen a JSON Schema, so four developer habits landed inside an otherwise fully Chinese surface. Same cause, one pass:

.changeset/8415-filter-builder-condition-id.md

  • names packages/components/src/custom/filter-builder.tsx → packages/components/src/custom/filter-builder.tsx — edited by this change

    Re-derived from packages/components/src/custom/filter-builder.tsx rather than inherited, and stated as a property rather than as a tally of read sites, since that file keeps gaining condition.id reads and a count goes stale with the next one: which row an edit lands on is decided by id and by nothing else. Each mutator that acts on a single row picks it with a MATCH on id (removeCondition's c.id !== conditionId, updateCondition's and changeOperator's c.id === conditionId, changeField's c.id !== conditionId), every call to one of them hands it condition.id, the row's React key is condition.id, and nothing in the file matches a row by its index. The file's other reads of condition.id pick no row; one of them builds the element id that a half-filled range's blank bound names in aria-describedby. The MATCH sites and the key are re-derived from the component source by the re-derived from the reader block of packages/types/src/__tests__/filter-builder-condition-id-8415.test.ts; the rest of the property is read off the source, and no test pins it.

.changeset/9159-drill-url-isnull-operator.md

  • names drillEmptyBucketNavHost-9085.test.ts → packages/app-shell/src/views/drillEmptyBucketNavHost-9085.test.ts — edited by this change

    The range maps are deliberately untouched: is_null is already a canonical ViewFilterRule word, and ObjectDataPage inverts URL_FILTER_OPS to bridge a triple's operator to the spec's alias spelling. An entry there would have sent it through that bridge as the alias null, which the rule schema refuses — a "Save as view" that silently loses the condition. ObjectDataPage.saveAsViewFilterFold.test.ts pins the fold for the new operator, and drillEmptyBucketNavHost-9085.test.ts — which recorded this boundary as open — is updated to record it closed.

.changeset/formfieldspec-dependson-5040.md

  • names widgets.tsx → packages/app-shell/src/views/metadata-admin/widgets.tsx — edited by this change

    FormFieldSpec — the authoring type for a metadata-admin form layout, the element type of FormSectionSpec.fields[] — did not declare dependsOn. widgets.tsx held a second, inline description of the same object as WidgetProps.fieldSpec, and that one did, because two registered widgets read it as their primary configuration: field-selector resolves dependsOn || reference || 'objectName' to decide whose field catalog to offer, and dynamic-config uses it to pick a sub-schema out of WidgetContext.dynamicSchemas. One value travelling down one channel, described twice, disagreeing on the one key that decides what those widgets show — so

Read the paragraph, not the line: both false halves of the objectui#8617 claim sat in one paragraph, and correcting either alone would have left it asserting the same wrong thing.

If a claim did go false, correct the body. That is precedented and prose-only, frontmatter untouched; check-changeset-overwrite.mjs will report the correction as its own case 2 ("correcting a declaration on purpose … legitimate"), which is the intended shape — one gate asks for the read, the other records the write.

Not covered, stated so nobody reads this as more: a born-false claim that spells no line address at all (objectui#9495 coordinated one by ORDINAL — "a grep finds that member first" — and deciding that means reading what the sentence means), a claim spelled as a symbol or a package rather than a backticked file name, and a file named ambiguously.

Angle-bracketed names in the quoted prose above are rewritten as ANGLE-BRACKETS(name): GitHub deletes tag-shaped fragments from a stored body, and a quote that silently loses the identifier it is about is worse than a visible repair.

Compared the checked-out tree with d461b0de8 (merge-base with origin/main): 47 file(s) changed outside .changeset/, read against 1425 pending declaration(s) that publish a body (2007 pending in total). · run

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 329 chunks) 3044.6 KB 3104.5 KB
Main entry chunk (gzip) 147.8 KB 350 KB
Entry file index-BZzGSZ73.js —
Status PASS —

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 16.68KB 6.20KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.52KB 3.45KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.13KB 7.95KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 545.50KB 130.49KB
core (index.js) 9.22KB 3.71KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 223.91KB 62.28KB
fields (index.js) 256.66KB 65.00KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.24KB 2.27KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.40KB 12.91KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 39.28KB 11.09KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.52KB 4.88KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.01KB 3.93KB
plugin-calendar (index.js) 51.38KB 14.60KB
plugin-charts (index.js) 74.95KB 20.88KB
plugin-chatbot (index.js) 198.36KB 47.20KB
plugin-dashboard (index.js) 133.59KB 35.39KB
plugin-designer (index.js) 216.25KB 44.39KB
plugin-detail (index.js) 232.25KB 61.40KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 146.50KB 37.51KB
plugin-gantt (index.js) 169.37KB 41.83KB
plugin-grid (index.js) 215.49KB 58.91KB
plugin-kanban (index.js) 49.30KB 15.39KB
plugin-list (index.js) 114.55KB 28.29KB
plugin-map (index.js) 23.82KB 7.42KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.55KB 11.99KB
plugin-timeline (index.js) 30.64KB 8.94KB
plugin-tree (index.js) 10.52KB 3.69KB
plugin-view (index.js) 87.46KB 21.79KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.66KB 3.50KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 114.58KB 37.60KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.03KB 1.86KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.78KB 2.09KB
sdui-parser (codegen.js) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.78KB 2.56KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 18.27KB 6.20KB
types (ai.js) 4.11KB 2.06KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.25KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 17.15KB 6.32KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

…ocol ids (objectui#9306)

Four pending changesets publish in the same release and state present-tense
facts about the builder's ids that objectui#9306 makes false: 7561, 9302,
9359 and 8748. Each gains one "Superseded in this release by objectui#9306"
paragraph after its history; frontmatter and history sentences untouched,
following the objectui#10465 precedent.

Also: the objectui#9306 changeset no longer lists containsCaseInsensitive
among the spec's deprecated aliases (the spec's table has no row for it), and
two test docblocks stop calling it an opt-in builder id. Prose only.

Claude-Session: https://claude.ai/code/session_01BA3nKVUwKQJf8DBxrSVtNC
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 329 chunks) 3045.7 KB 3104.5 KB
Main entry chunk (gzip) 147.8 KB 350 KB
Entry file index-WFrGiwx-.js —
Status PASS —

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 16.68KB 6.20KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.52KB 3.45KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.13KB 7.95KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 545.50KB 130.49KB
core (index.js) 9.22KB 3.71KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 223.91KB 62.28KB
fields (index.js) 257.00KB 65.06KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.24KB 2.27KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.40KB 12.91KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 39.28KB 11.09KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.52KB 4.88KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.01KB 3.93KB
plugin-calendar (index.js) 51.38KB 14.60KB
plugin-charts (index.js) 74.95KB 20.88KB
plugin-chatbot (index.js) 198.36KB 47.20KB
plugin-dashboard (index.js) 133.56KB 35.38KB
plugin-designer (index.js) 216.25KB 44.39KB
plugin-detail (index.js) 232.25KB 61.40KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 146.50KB 37.51KB
plugin-gantt (index.js) 169.37KB 41.83KB
plugin-grid (index.js) 215.49KB 58.91KB
plugin-kanban (index.js) 49.30KB 15.39KB
plugin-list (index.js) 114.55KB 28.29KB
plugin-map (index.js) 23.82KB 7.42KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.55KB 11.99KB
plugin-timeline (index.js) 30.64KB 8.94KB
plugin-tree (index.js) 10.52KB 3.69KB
plugin-view (index.js) 87.46KB 21.79KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.66KB 3.50KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 114.58KB 37.60KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.03KB 1.86KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.78KB 2.09KB
sdui-parser (codegen.js) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.78KB 2.56KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 18.27KB 6.20KB
types (ai.js) 4.11KB 2.06KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.25KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 17.15KB 6.32KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review September 25, 2026 08:09
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Sep 25, 2026
Merged via the queue into main with commit 641fb55 Sep 25, 2026
45 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-9306-filter-builder-protocol-ids branch September 25, 2026 08:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant