Skip to content

fix(components,i18n): between needs both bounds; label six operators in every locale pack - #4962

Draft
os-project-manager wants to merge 4 commits into
mainfrom
claude/issue-8815-filterbuilder-between-i18n
Draft

fix(components,i18n): between needs both bounds; label six operators in every locale pack#4962
os-project-manager wants to merge 4 commits into
mainfrom
claude/issue-8815-filterbuilder-between-i18n

Conversation

@os-project-manager

Copy link
Copy Markdown
Collaborator

Fixes objectstack-ai/objectstack#8815

Console list-filter panel, both halves of the card. Verified against origin/main before implementing — the card's premise held only partly, and the surviving part was not where the card pointed.

Half ① — between — I took the REAL FIX, not the "hide the operator" fallback

Because the pair render already exists. objectui#3958 (commit 2b50261, 2026-08-16 04:42Z) landed the two-bound range input the day after this card was filed. A date column's 「介于」 already draws two date inputs on current main. Hiding the operator would have removed a control that works.

What actually survived is the submission half. Typing only one bound produces ["2024-01-01", ""], and both write paths asked "is this row filled in?" with one shape-blind predicate:

value == null || value === '' || (Array.isArray(value) && value.length === 0)

Correct for scalar and list, wrong for pair. An array of length 2 passes it, so the empty bound went to the server, which refuses the query outright (400 INVALID_FILTER) — the list showed 「该视图的查询被拒绝」 and, because the refusal takes the whole view, the filters the user had already applied stopped applying too. foldFilterGroupToSpecRules persisted the same half-range, so the refusal returned on every later read of that view, for every user of it. That is the card's reported symptom, still live.

The spec cannot intercept it. Measured against ViewFilterRuleSchema on @objectstack/spec 17.0.0:

value verdict
['2024-01-01', '2024-03-01'] accepted
['2024-01-01', ''] accepted — it counts the two slots, not what is in them
'2024-01-01' rejected
['2024-01-01'] rejected

Authoring validation is green on exactly the shape that fails at query time, one layer too late to be actionable. That makes refusing to emit it the producer's job — contract-first, no consumer-side tolerance.

Fix. @object-ui/components exports isFilterValueComplete(operator, value), arity-aware via the same filterValueArity fold the rest of the file uses. It lives beside the render that creates the shape because this component is what decides when one of its rows is finished — exactly as it already owns VALUELESS_FILTER_BUILDER_OPERATORS, and for the same measured reason: each consumer keeping its own copy is how the two came to disagree. Both consumers (plugin-list's convertFilterGroupToAST, app-shell's foldFilterGroupToSpecRules) now read it. A half-filled range is dropped exactly as a half-typed equals row already was: no filter, rather than a filter the server rejects. Bounds of 0 and false stay real bounds (objectui#4873, one column over).

Half ② — operator labels: genuinely missing keys, and there are SIX, not four

Confirmed missing from all ten locale packs, not present-but-unreached: startsWith, endsWith, isNull, isNotNull, exists, notExists. i18next resolves a missing key to the key itself, so the dropdown showed filterBuilder.operators.isNull beside translated entries.

The component's own createSafeTranslation defaults table has all six in English and could not help: that table serves only the no-provider path, and the Console mounts a provider, so the pack's answer is the one that renders.

Why the card says four. A date column's bucket offers the four nullness operators and not startsWith/endsWith. The reporter opened a date field. A text column shows all six. The extra two are the same defect, same mechanism, same file surface, and the correct English form was already pinned by the component's defaults table — fixed here rather than filed, and named explicitly rather than slipped in.

Why no gate caught it. The label key is built dynamically:

{t(`filterBuilder.operators.${op.value}`)}
  • scripts/check-i18n-call-site-keys.mjs classifies a template key as missing-prefix and asks only whether the prefix resolves — it does, sixteen members deep. It cannot know which members a dynamic key needs.
  • all-locales-key-parity.test.ts asks whether the ten packs agree. Its own header already records why it is blind here: "Ten packs identically missing it is full parity."

So a new parity test pins the packs against FILTER_BUILDER_OPERATORS in both directions — every drawable operator is labelled, and no pack carries a label for an operator the dropdown cannot draw. An operator added to the dropdown now fails loudly until every pack labels it. It lives in packages/components because the dependency graph forces it: components depends on i18n, so the reverse import would be a cycle.

Carve-out clause — not triggered. The vocabulary half stayed in the render layer. defaultOperators / OPT_IN_OPERATORS / the shared operator registry are untouched; only locale packs gained entries.

Fixture triage

viewFilterFold.test.ts's spelling-normalization test fed the scalar 'x' to every builder operator including between, so the guard correctly dropped that row and the operator vanished from its output. Re-spelled to a real pair rather than exempted: ViewFilterRuleSchema refuses between: 'x' outright, so the rule that fixture used to produce was never spec-valid. The test still asserts what it is named for. Swept the rule's whole consumption radius for other non-pair between fixtures — none in app-shell, plugin-list, apps/, examples/ or e2e/.

Verification — all at 7521ae8 (final commit)

Run from the repo root per AGENTS.md §9 (never pnpm --filter test, which silently runs another package's files).

gate result
vitest run union (9 files: new + adjacent filter/i18n parity) 183 passed
packages/components/ 148 files, 1363 passed
packages/i18n/ + packages/plugin-list/ 87 files, 1436 passed
packages/app-shell/src/views/ 259 files, 2449 passed, 1 skipped
turbo run type-check (components, plugin-list, app-shell, i18n) 33 tasks successful
turbo run lint (same four) 0 errors (2380 pre-existing warnings)
check:i18n-keys / check:i18n-drift / check:i18n-dead-keys / check:control-bytes PASS
check-changeset-presence.mjs PASS — 18 source files, 4 released packages, 1 changeset

Reverse verification (fix committed first, then reverted, then restored — direction predicted before running):

  • Locale packs reverted ⇒ the parity test goes red on 10 packs, each failure naming all six operators.
  • Both consumers reverted (predicate kept exported) ⇒ 8 failures across the two write paths, on exactly the half-filled cases. The [] "nothing filled in" and [0, 100] cases stayed green before the fix, confirming the change is narrowly scoped to the half-filled shape and did not re-judge anything already handled.

A changeset is included (@object-ui/components, @object-ui/plugin-list, @object-ui/app-shell, @object-ui/i18n, all patch) — this is user-visible, so no empty-frontmatter declaration and no skip-changeset label, which does not exist in this repo.


Generated by Claude Code

claude added 2 commits August 17, 2026 07:33
…in every locale pack

Console list-filter panel, two defects (objectstack#8815).

A `between` row with one bound typed was `["2024-01-01", ""]`, and both write
paths asked "is this row filled in?" with one shape-blind predicate (`null` /
`''` / empty array). An array of length 2 passed it, so the empty bound reached
the server, which refuses the query outright — the whole view failed to load and
the filters already applied stopped applying. The saved-view fold persisted the
same half-range, returning the refusal on every later read.

`ViewFilterRuleSchema` cannot catch it: it accepts `["2024-01-01", ""]` because
it counts the two slots, not what is in them, so authoring validation is green on
exactly the shape that fails at query time. Refusing to emit it is therefore the
producer's job. `isFilterValueComplete(operator, value)` is exported from the
builder — the component that decides when one of its rows is finished, as it
already does for the value-less operators — and both consumers read it instead of
keeping copies. Bounds of `0` and `false` stay real bounds.

Six operator labels (`startsWith`, `endsWith`, `isNull`, `isNotNull`, `exists`,
`notExists`) were missing from all ten locale packs, so i18next resolved them to
the raw key. The component's defaults table serves only the no-provider path, and
the Console mounts a provider. The label key is built dynamically, so the
call-site checker sees only a prefix and cross-pack parity is satisfied when all
ten packs are missing a key together; a new parity test pins the packs against
`FILTER_BUILDER_OPERATORS` in both directions.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y26DJEHSBhhAQ6wwfsHNza
…ween`

The spelling-normalization test fed the scalar `'x'` to every builder operator,
including `between`. Since the fold drops a range that is not filled in, a bare
scalar is now correctly dropped and the operator vanished from the output.

Re-spelled rather than exempted: `ViewFilterRuleSchema` refuses `between: 'x'`
outright, so the rule this fixture used to produce was never spec-valid. The
test keeps asserting what it is named for — camelCase to canonical spelling.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y26DJEHSBhhAQ6wwfsHNza
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Main entry (gzip) 24.7 KB 350 KB
Entry file index-CaStrbIM.js
Status PASS

📦 Bundle Size Report

Package Size Gzipped
app-shell (index.js) 9.56KB 3.59KB
app-shell (runtime-config.js) 7.42KB 2.32KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 8.92KB 3.41KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 1.17KB 0.53KB
auth (AuthProvider.js) 25.13KB 5.40KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.13KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.64KB 2.21KB
auth (SocialSignInButtons.js) 9.60KB 3.89KB
auth (UserMenu.js) 3.40KB 1.22KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.79KB
auth (createAuthenticatedFetch.js) 6.34KB 2.43KB
auth (index.js) 2.71KB 1.22KB
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.02KB 0.88KB
auth (useIsWorkspaceAdmin.js) 1.61KB 0.85KB
collaboration (CommentThread.js) 26.07KB 7.56KB
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.65KB 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) 499.00KB 111.29KB
core (index.js) 4.06KB 1.61KB
create-plugin (index.js) 10.08KB 3.26KB
data-objectstack (index.js) 157.40KB 43.42KB
fields (index.js) 233.27KB 58.22KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (currency.js) 1.22KB 0.64KB
i18n (i18n.js) 4.28KB 1.75KB
i18n (index.js) 3.35KB 1.38KB
i18n (pickLocalized.js) 3.69KB 1.73KB
i18n (provider.js) 23.12KB 7.62KB
i18n (useDisplayLocale.js) 2.84KB 1.45KB
i18n (useObjectLabel.js) 27.59KB 6.63KB
i18n (useSafeTranslation.js) 7.77KB 3.13KB
layout (index.js) 39.16KB 10.97KB
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.74KB
mobile (index.js) 1.50KB 0.62KB
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) 2.53KB 0.85KB
mobile (useResponsive.js) 0.71KB 0.42KB
mobile (useResponsiveConfig.js) 1.36KB 0.63KB
mobile (useSpecGesture.js) 4.32KB 1.64KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 9.35KB 3.31KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 4.42KB 1.42KB
permissions (evaluator.js) 5.12KB 1.74KB
permissions (index.js) 0.91KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.52KB
permissions (usePermissions.js) 1.81KB 0.83KB
plugin-ai (index.js) 15.75KB 3.80KB
plugin-calendar (index.js) 46.62KB 12.83KB
plugin-charts (index.js) 64.75KB 18.37KB
plugin-chatbot (index.js) 181.21KB 43.14KB
plugin-dashboard (index.js) 127.85KB 32.73KB
plugin-designer (index.js) 212.39KB 42.83KB
plugin-detail (index.js) 239.90KB 60.01KB
plugin-editor (index.js) 2.46KB 1.10KB
plugin-form (index.js) 120.42KB 29.03KB
plugin-gantt (index.js) 164.10KB 39.87KB
plugin-grid (index.js) 197.58KB 53.00KB
plugin-kanban (index.js) 52.72KB 14.54KB
plugin-list (index.js) 111.18KB 26.98KB
plugin-map (index.js) 17.91KB 5.72KB
plugin-markdown (index.js) 13.72KB 4.69KB
plugin-report (index.js) 41.97KB 11.33KB
plugin-timeline (index.js) 26.68KB 7.66KB
plugin-tree (index.js) 8.50KB 2.88KB
plugin-view (index.js) 83.81KB 20.49KB
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.44KB 0.22KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 3.77KB 1.33KB
react (SchemaRenderer.js) 27.53KB 9.41KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 1.28KB 0.68KB
react (schema-input.js) 1.45KB 0.83KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 4.09KB 1.74KB
sdui-parser (index.js) 4.55KB 2.07KB
sdui-parser (parse.js) 10.76KB 3.17KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.29KB 0.24KB
sdui-parser (validate.js) 4.69KB 1.48KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 0.99KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 0.20KB 0.18KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 0.20KB 0.18KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.87KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-retry.js) 4.32KB 2.02KB
types (index.js) 3.05KB 1.52KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 2.59KB 1.31KB
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 (spec-report.js) 5.05KB 1.93KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 0.20KB 0.18KB
types (ui-action.js) 3.40KB 1.71KB
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

Copy link
Copy Markdown
Collaborator Author

CI red on Test (shard 3/4) is not from this diff — it reproduces on clean main

Merged current main into the branch and re-verified everything at 1563d47. The one red test is #4911, which is already open and already labelled needs-user-decision. I have deliberately not touched it.

The failure

FAIL packages/fields/src/widgets/__tests__/FilterConditionField.operators.test.ts
 > every spec field operator is reachable from the builder (#2942)
AssertionError: FieldOperatorsSchema accepts these but no builder
operator can author them: expected [ '$like', '$ilike' ] to deeply equal []

Proof it is not mine

Checked out origin/main at e609aac in a separate worktree, with none of this branch's changes present (grep -c isFilterValueComplete = 0), installed --frozen-lockfile, ran that one file:

Test Files  1 failed (1)
Tests  1 failed | 44 passed (45)
AssertionError: ... expected [ '$like', '$ilike' ] to deeply equal []

Identical failure, same two tokens, on clean main.

Root cause — a base bump, not a code change

main moved @objectstack/spec from 17.0.0-rc.6 to 17.0.0 GA (the bump chain ending at e609aac). GA widened FieldOperatorsSchema from 16 shape keys to 18:

version shape keys $like / $ilike
17.0.0-rc.6 16 absent
17.0.0 GA 18 present

The #2942 gate derives its vocabulary from that schema, so two new tokens with no builder route turn it red. The gate is behaving exactly as designed — this is a real product gap, not a broken test.

It is also the same shape the test's own header already documents: $icontains sat in KNOWN_UNREACHABLE "from objectui#3560 until objectui#4023: the spec gained it between 17.0.0-rc.2 and rc.5, and no builder operator could author it."

Why it looked like mine: this branch was cut from 21e4585, before the bump, so it pinned rc.6 and was green locally. CI tests the PR merge commit, which carries main's GA lockfile — so the PR inherited main's red.

Why I am not fixing it here

  • Already filed and already routed: FilterConditionField cannot author spec 17.0.0 GA's new $like / $ilike — the same shape as the closed $icontains gap (#4023) #4911 is open with needs-user-decision. Fixing it here would duplicate a claimed card and pre-empt a decision the maintainer has been asked to make (which builder operator should author $like/$ilike, and whether that is a new user-visible dropdown entry needing a name plus ten locale labels).
  • It lands in the shared operator vocabularydefaultOperators / condToMongo — which is exactly the carve-out this dispatch said becomes its own card rather than a silent widening of this diff.
  • Adding the tokens to KNOWN_UNREACHABLE would delete the alarm rather than fix anything, so that was never on the table.

Verification at 1563d47 (post-merge, against GA spec)

The measurement this PR rests on is unchanged under GA — ViewFilterRuleSchema still accepts ['2024-01-01', ''] while rejecting a scalar and a one-element array — and VIEW_FILTER_PAIR_VALUE_OPERATORS is still ['between'], so filterValueArity is unaffected by the bump.

suite result
packages/fields/ (+ this PR's suites) 108 files / 1832 passed — only red is #4911
packages/components/ + packages/i18n/ + packages/plugin-list/ 236 files, 2804 passed
packages/app-shell/src/views/ 259 files, 2449 passed, 1 skipped

Corrected sweep. The earlier sweep was scoped to app-shell, plugin-list, apps/, examples/, e2e/ and did not include packages/fields/. That boundary was the real miss, so this round ran the whole of packages/fields/ rather than grepping it: 108 files pass and the only failure is the pre-existing one. No non-pair between fixture was found anywhere in packages/.


Generated by Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Main entry (gzip) 24.8 KB 350 KB
Entry file index-Dg6RdHUE.js
Status PASS

📦 Bundle Size Report

Package Size Gzipped
app-shell (index.js) 9.56KB 3.59KB
app-shell (runtime-config.js) 7.42KB 2.32KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 8.92KB 3.41KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 1.17KB 0.53KB
auth (AuthProvider.js) 25.13KB 5.40KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.13KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.64KB 2.21KB
auth (SocialSignInButtons.js) 9.60KB 3.89KB
auth (UserMenu.js) 3.40KB 1.22KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.79KB
auth (createAuthenticatedFetch.js) 6.34KB 2.43KB
auth (index.js) 2.71KB 1.22KB
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.02KB 0.88KB
auth (useIsWorkspaceAdmin.js) 1.61KB 0.85KB
collaboration (CommentThread.js) 26.07KB 7.56KB
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.65KB 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) 499.00KB 111.29KB
core (index.js) 4.06KB 1.61KB
create-plugin (index.js) 10.08KB 3.26KB
data-objectstack (index.js) 157.40KB 43.42KB
fields (index.js) 233.27KB 58.22KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (currency.js) 1.22KB 0.64KB
i18n (i18n.js) 4.28KB 1.75KB
i18n (index.js) 3.35KB 1.38KB
i18n (pickLocalized.js) 3.69KB 1.73KB
i18n (provider.js) 23.12KB 7.62KB
i18n (useDisplayLocale.js) 2.84KB 1.45KB
i18n (useObjectLabel.js) 27.59KB 6.63KB
i18n (useSafeTranslation.js) 7.77KB 3.13KB
layout (index.js) 39.16KB 10.97KB
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.74KB
mobile (index.js) 1.50KB 0.62KB
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) 2.53KB 0.85KB
mobile (useResponsive.js) 0.71KB 0.42KB
mobile (useResponsiveConfig.js) 1.36KB 0.63KB
mobile (useSpecGesture.js) 4.32KB 1.64KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 9.35KB 3.31KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 4.42KB 1.42KB
permissions (evaluator.js) 5.12KB 1.74KB
permissions (index.js) 0.91KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.52KB
permissions (usePermissions.js) 1.81KB 0.83KB
plugin-ai (index.js) 15.75KB 3.80KB
plugin-calendar (index.js) 46.62KB 12.83KB
plugin-charts (index.js) 64.75KB 18.37KB
plugin-chatbot (index.js) 181.21KB 43.14KB
plugin-dashboard (index.js) 127.85KB 32.73KB
plugin-designer (index.js) 212.39KB 42.83KB
plugin-detail (index.js) 239.90KB 60.01KB
plugin-editor (index.js) 2.46KB 1.10KB
plugin-form (index.js) 120.42KB 29.03KB
plugin-gantt (index.js) 164.10KB 39.87KB
plugin-grid (index.js) 197.58KB 53.00KB
plugin-kanban (index.js) 52.72KB 14.54KB
plugin-list (index.js) 111.18KB 26.98KB
plugin-map (index.js) 17.91KB 5.72KB
plugin-markdown (index.js) 13.72KB 4.69KB
plugin-report (index.js) 41.97KB 11.33KB
plugin-timeline (index.js) 26.68KB 7.66KB
plugin-tree (index.js) 8.50KB 2.88KB
plugin-view (index.js) 83.81KB 20.49KB
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.44KB 0.22KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 3.77KB 1.33KB
react (SchemaRenderer.js) 27.53KB 9.41KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 1.28KB 0.68KB
react (schema-input.js) 1.45KB 0.83KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 4.09KB 1.74KB
sdui-parser (index.js) 4.55KB 2.07KB
sdui-parser (parse.js) 10.76KB 3.17KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.29KB 0.24KB
sdui-parser (validate.js) 4.69KB 1.48KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 0.99KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 0.20KB 0.18KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 0.20KB 0.18KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.87KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-retry.js) 4.32KB 2.02KB
types (index.js) 3.05KB 1.52KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 2.59KB 1.31KB
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 (spec-report.js) 5.05KB 1.93KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 0.20KB 0.18KB
types (ui-action.js) 3.40KB 1.71KB
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

Copy link
Copy Markdown
Collaborator Author

Merged current main — all three inherited failures relieved, everything re-verified at 6f7667f

The three main-side failures are gone: $like/$ilike via #4978's exemption, and the two generator-anchor assertions via #4974.

Merge result

origin/main at a34c0b2 merged clean — zero conflicts, so nothing was hand-merged and no generated artefact or lockfile needed regenerating. pnpm install --frozen-lockfile was re-run against the merged lockfile; @objectstack/spec resolves to 17.0.0.

The premise measurement still holds

Re-measured against the merged tree's spec, since this is what the PR rests on:

between value verdict on 17.0.0
['2024-01-01', '2024-03-01'] accepted
['2024-01-01', ''] accepted — still invisible to authoring validation
'2024-01-01' rejected
['2024-01-01'] rejected

VIEW_FILTER_PAIR_VALUE_OPERATORS is still ['between'] and VIEW_FILTER_LIST_VALUE_OPERATORS still ['in', 'not_in'], so filterValueArity is unchanged by the merge and the fix's basis is intact. The PR's premise did not move.

Composition with #4978 — no interaction, no overlap

KNOWN_UNREACHABLE on the merged branch is new Set(['$eq', '$between', '$like', '$ilike']), exactly as #4978 left it.

Full re-run at 6f7667f

suite result
packages/fields/ 104 files, 1720 passed — reachability gate now green
packages/components/ 149 files, 1368 passed
packages/i18n/ + packages/plugin-list/ 87 files, 1438 passed
packages/app-shell/src/views/ 259 files, 2449 passed, 1 skipped
turbo run type-check (those five packages) 34 tasks successful
turbo run lint (same five) 0 errors (2380 pre-existing warnings)
check:i18n-keys / -drift / -dead-keys / check:control-bytes / check:phantom-deps / check:self-import PASS
check-changeset-presence.mjs PASS — 18 source files, 4 released packages, 1 changeset

Counts moved slightly against the pre-merge run because main added files (components 148 to 149, i18n+plugin-list 1436 to 1438 tests); all additions pass. Nothing was weakened or exempted to reach green.


Generated by Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Main entry (gzip) 25.3 KB 350 KB
Entry file index-DNZ7LTPP.js
Status PASS

📦 Bundle Size Report

Package Size Gzipped
app-shell (index.js) 9.56KB 3.59KB
app-shell (runtime-config.js) 7.42KB 2.32KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 8.92KB 3.41KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 1.17KB 0.53KB
auth (AuthProvider.js) 25.13KB 5.40KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.13KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.64KB 2.21KB
auth (SocialSignInButtons.js) 9.60KB 3.89KB
auth (UserMenu.js) 3.40KB 1.22KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.79KB
auth (createAuthenticatedFetch.js) 6.34KB 2.43KB
auth (index.js) 2.71KB 1.22KB
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.02KB 0.88KB
auth (useIsWorkspaceAdmin.js) 1.61KB 0.85KB
collaboration (CommentThread.js) 26.07KB 7.56KB
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.65KB 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) 499.07KB 111.32KB
core (index.js) 4.11KB 1.62KB
create-plugin (index.js) 10.08KB 3.26KB
data-objectstack (index.js) 159.03KB 44.08KB
fields (index.js) 233.27KB 58.22KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (currency.js) 1.22KB 0.64KB
i18n (i18n.js) 4.28KB 1.75KB
i18n (index.js) 3.35KB 1.38KB
i18n (pickLocalized.js) 3.69KB 1.73KB
i18n (provider.js) 23.12KB 7.62KB
i18n (useDisplayLocale.js) 2.84KB 1.45KB
i18n (useObjectLabel.js) 27.59KB 6.63KB
i18n (useSafeTranslation.js) 7.77KB 3.13KB
layout (index.js) 39.16KB 10.97KB
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.74KB
mobile (index.js) 1.50KB 0.62KB
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) 2.53KB 0.85KB
mobile (useResponsive.js) 0.71KB 0.42KB
mobile (useResponsiveConfig.js) 1.36KB 0.63KB
mobile (useSpecGesture.js) 4.32KB 1.64KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 9.35KB 3.31KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 4.42KB 1.42KB
permissions (evaluator.js) 5.12KB 1.74KB
permissions (index.js) 0.91KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.52KB
permissions (usePermissions.js) 1.81KB 0.83KB
plugin-ai (index.js) 15.75KB 3.80KB
plugin-calendar (index.js) 46.62KB 12.83KB
plugin-charts (index.js) 64.75KB 18.37KB
plugin-chatbot (index.js) 181.21KB 43.14KB
plugin-dashboard (index.js) 127.85KB 32.73KB
plugin-designer (index.js) 212.39KB 42.83KB
plugin-detail (index.js) 240.05KB 60.05KB
plugin-editor (index.js) 2.46KB 1.10KB
plugin-form (index.js) 120.42KB 29.03KB
plugin-gantt (index.js) 164.10KB 39.87KB
plugin-grid (index.js) 197.61KB 53.03KB
plugin-kanban (index.js) 52.72KB 14.54KB
plugin-list (index.js) 111.17KB 26.99KB
plugin-map (index.js) 18.72KB 6.09KB
plugin-markdown (index.js) 13.72KB 4.69KB
plugin-report (index.js) 41.97KB 11.33KB
plugin-timeline (index.js) 26.68KB 7.66KB
plugin-tree (index.js) 8.50KB 2.88KB
plugin-view (index.js) 83.81KB 20.49KB
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.44KB 0.22KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 3.77KB 1.33KB
react (SchemaRenderer.js) 27.53KB 9.41KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 1.28KB 0.68KB
react (schema-input.js) 1.45KB 0.83KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 5.41KB 2.34KB
sdui-parser (index.js) 4.77KB 2.16KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 10.76KB 3.17KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.29KB 0.24KB
sdui-parser (validate.js) 6.92KB 2.40KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 0.99KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 0.20KB 0.18KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 0.20KB 0.18KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.87KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-retry.js) 4.32KB 2.02KB
types (index.js) 3.05KB 1.52KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 2.59KB 1.31KB
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 (spec-report.js) 5.05KB 1.93KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 0.20KB 0.18KB
types (ui-action.js) 3.40KB 1.71KB
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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

2 participants