Skip to content

fix(list): OBJECT_API_DISABLED renders an honest cannot-work state instead of the empty state (#4408) - #4495

Merged
yinlianghui merged 3 commits into
mainfrom
claude/issue-4408-api-disabled-honest-state
Aug 12, 2026
Merged

fix(list): OBJECT_API_DISABLED renders an honest cannot-work state instead of the empty state (#4408)#4495
yinlianghui merged 3 commits into
mainfrom
claude/issue-4408-api-disabled-honest-state

Conversation

@yinlianghui

Copy link
Copy Markdown
Collaborator

Closes #4408

A list pointed at an object whose enable block withholds the API rendered its ordinary empty state, so "this page cannot work, and never could" reached the user as "you have no records". The reported instance — Setup › Advanced › Signing Keys, whose sys_jwks declares enable.apiEnabled: false — could not load for any persona and said so to nobody. That is also why the upstream defect objectstack#7544 survived review for its whole life: a merely unpopulated page invites nobody to click through.

Measurement first — the card's mechanism section was a declared assumption

1. Where the 404 is swallowed. ObjectStackDataSource.find(), packages/data-objectstack/src/index.ts. Its catch ran is404Error(err) and returned { data: [], total: 0 }, then memoised the resource in missingResources so later calls short-circuited to empty without touching the network. An error state does exist — ListView's list-error-state panel, with classifyLoadError behind it — and this path never reached it: a resolved promise carrying zero rows is indistinguishable from a genuinely empty object. So this was a swallow, not a missing state.

2. Does the ADR-0112 code survive the transport? YES — on the primary path. @objectstack/client's fetch wrapper stamps error.code (top-level code, else the nested error.code) and error.httpStatus on every non-2xx, and client.data.find goes through it. Both denial spellings are declared members of the spec's StandardErrorCode in the vendored @objectstack/spec@17.0.0-rc.6. So no heuristic on status was needed — and none was written.

But there was a real propagation gap in this repo, on the same call: find()'s raw $expand/$search branch (rawFindWithPopulate) bypasses the client and hand-rolled its own error, stamping only status. That made the branch a list takes whenever it expands a lookup or runs a search the one path where a denial arrived anonymous. Fixed at that producer rather than worked around downstream.

3. The 405 sibling — confirmed same class, but it did NOT flow the same path. is404Error matches only 404, so OBJECT_API_METHOD_NOT_ALLOWED was never swallowed: it already reached the error panel, and was classified network"check your connection and try again" for a condition no retry can change. Same class, two different wrong outcomes, one repair.

Both denials are pure functions of the object's metadata (apiAccessDenialFromEnable) — no user, no permission, no context — so neither is transient or per-user, which is exactly the case where a silent empty state is most misleading.

The fix

  • @object-ui/data-objectstackfind() no longer degrades the two enable-block denials to an empty result, and does not memoise them either (absorbing one would pin the object to "empty" for the rest of the session). Discrimination is on the code, never the status: a missing collection, a missing record and a disabled object are all 404. New exports isApiAccessDeniedError() / API_ACCESS_DENIED_CODES. The raw branch now carries code + httpStatus.
  • @object-ui/plugin-listclassifyLoadError gains an api-disabled kind, checked first and on the code alone. Its panel says the object is not exposed through the API and that this is a setting on the object rather than a permission, and it offers no Retry — every retry re-fetches the identical refusal, which is the same wrong advice as "check your connection", just spelled as a control.
  • New strings through the i18n channel: defaults-map rows + en mirrors (the fix(i18n): the drifted detail row mirrors the en pack, and the three defaults maps get a gate (#4401) #4409 map-mirror gate is live), translated in all ten locale packs for all-locales-key-parity.

plugin-list is deliberately backend-agnostic and does not depend on any adapter, so neither package's suite can see the composition. It is pinned once in app-shell, which already depends on both.

Red-first — both directions, measured

Taking both halves out (git checkout of the sources, never git stash) and re-running:

Defect pin, verbatim. A real adapter over a stubbed transport answering HTTP 404 + code: 'OBJECT_API_DISABLED', feeding a real ListView carrying the Setup page's managedBy empty-state override:

BEFORE  testid=empty-state
        text=No identity recordsThese records are created by the authentication
             provider - through sign-in, provisioning, and security flows - not
             added by hand here.

AFTER   testid=list-error-state
        kind=api-disabled
        text=This object isn't available through the APIThis page can't load its
             records because the object is not exposed through the API. That is a
             setting on the object itself, not a permission - an administrator has
             to enable API access for it before this page can work.

The composition test failed pre-fix with AssertionError: expected null not to be null — there was no error state in the DOM at all.

The 405 failed pre-fix differently, as measured: AssertionError: expected 'network' to be 'api-disabled'. It reached the panel and was called a network outage.

At the adapter, pre-fix: AssertionError: promise resolved "{ data: [], total: +0 }" instead of rejecting (8 failed / 7 passed of 15).

Controls — green on BOTH sides, never red:

control verdict
2xx with zero rows renders the ORDINARY empty state (override copy intact) green both sides
bare 404, no code (collection absent) still degrades to empty green both sides
OBJECT_NOT_FOUND still degrades to empty and is still memoised green both sides
RECORD_NOT_FOUND is not an enable-block denial green both sides
403 still classifies forbidden, 401 unauthorized, 400 rejected green both sides

The card's binding control holds: a genuinely empty object still renders the ordinary empty state. Empty is the overwhelmingly common case and the one that state exists for.

Verification

  • pnpm exec vitest run packages/data-objectstack/ packages/plugin-list/ packages/i18n/110 files, 1779 tests passed
  • pnpm exec vitest run packages/app-shell/361 files, 3466 passed, 1 skipped (hosts the fix(i18n): the drifted detail row mirrors the en pack, and the three defaults maps get a gate (#4401) #4409 map-mirror gate and the composition pin)
  • type-check (both tsc commands where the package defines them) for data-objectstack, plugin-list, i18n, app-shell → all exit 0, after building each one's dependency closure first
  • check:control-bytes, check:i18n-keys, check:i18n-drift, changeset:check, check-changeset-presence → all green
  • eslint over the touched trees → 0 errors

Note for review

One assertion of mine was wrong on the first run and was corrected rather than the copy: I had asserted the panel text contains no /permission/i at all, but the copy deliberately says "not a permission" — that disclaimer is the honest distinction between this state and the 403 panel. The test now asserts the disclaimer and the absence of the forbidden panel's own wording.


Generated by Claude Code

@vercel

vercel Bot commented Aug 12, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectui Ignored Ignored Aug 12, 2026 9:17pm

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Main entry (gzip) 24.7 KB 350 KB
Entry file index-BQWUM69s.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) 22.10KB 4.37KB
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) 35.76KB 9.11KB
auth (createAuthenticatedFetch.js) 4.37KB 1.69KB
auth (index.js) 2.35KB 1.07KB
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) 4.91KB 0.87KB
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) 489.32KB 108.45KB
core (index.js) 3.37KB 1.34KB
create-plugin (index.js) 10.08KB 3.26KB
data-objectstack (index.js) 153.79KB 41.35KB
fields (index.js) 230.07KB 57.07KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (currency.js) 1.22KB 0.64KB
i18n (i18n.js) 4.32KB 1.77KB
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.33KB 1.20KB
i18n (useObjectLabel.js) 27.59KB 6.63KB
i18n (useSafeTranslation.js) 7.77KB 3.13KB
layout (index.js) 38.98KB 10.85KB
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) 8.75KB 3.06KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 3.67KB 1.12KB
permissions (evaluator.js) 4.41KB 1.44KB
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.55KB 0.71KB
plugin-ai (index.js) 15.75KB 3.80KB
plugin-calendar (index.js) 45.46KB 12.52KB
plugin-charts (index.js) 62.01KB 17.63KB
plugin-chatbot (index.js) 181.21KB 43.14KB
plugin-dashboard (index.js) 120.85KB 31.41KB
plugin-designer (index.js) 212.58KB 42.83KB
plugin-detail (index.js) 239.03KB 59.77KB
plugin-editor (index.js) 2.46KB 1.10KB
plugin-form (index.js) 114.58KB 27.68KB
plugin-gantt (index.js) 164.14KB 39.98KB
plugin-grid (index.js) 188.13KB 50.00KB
plugin-kanban (index.js) 48.62KB 13.42KB
plugin-list (index.js) 111.07KB 27.08KB
plugin-map (index.js) 18.16KB 5.81KB
plugin-markdown (index.js) 13.72KB 4.69KB
plugin-report (index.js) 41.16KB 10.96KB
plugin-timeline (index.js) 26.21KB 7.52KB
plugin-tree (index.js) 8.50KB 2.88KB
plugin-view (index.js) 84.08KB 20.55KB
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.71KB 3.53KB
providers (index.js) 0.44KB 0.22KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.67KB 2.37KB
react (LazyPluginLoader.js) 3.77KB 1.33KB
react (SchemaRenderer.js) 23.73KB 7.96KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 1.23KB 0.66KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 4.09KB 1.74KB
sdui-parser (index.js) 4.47KB 2.03KB
sdui-parser (parse.js) 10.04KB 2.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

…orts + find() resolve→reject on the two denial codes)
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Main entry (gzip) 24.7 KB 350 KB
Entry file index-unjGY551.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) 22.10KB 4.37KB
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) 35.76KB 9.11KB
auth (createAuthenticatedFetch.js) 4.37KB 1.69KB
auth (index.js) 2.35KB 1.07KB
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) 4.91KB 0.87KB
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) 489.32KB 108.45KB
core (index.js) 3.37KB 1.34KB
create-plugin (index.js) 10.08KB 3.26KB
data-objectstack (index.js) 153.79KB 41.35KB
fields (index.js) 230.07KB 57.07KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (currency.js) 1.22KB 0.64KB
i18n (i18n.js) 4.32KB 1.77KB
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.33KB 1.20KB
i18n (useObjectLabel.js) 27.59KB 6.63KB
i18n (useSafeTranslation.js) 7.77KB 3.13KB
layout (index.js) 38.98KB 10.85KB
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) 8.75KB 3.06KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 3.67KB 1.12KB
permissions (evaluator.js) 4.41KB 1.44KB
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.55KB 0.71KB
plugin-ai (index.js) 15.75KB 3.80KB
plugin-calendar (index.js) 46.13KB 12.76KB
plugin-charts (index.js) 62.01KB 17.63KB
plugin-chatbot (index.js) 181.21KB 43.14KB
plugin-dashboard (index.js) 120.85KB 31.41KB
plugin-designer (index.js) 212.58KB 42.83KB
plugin-detail (index.js) 239.03KB 59.77KB
plugin-editor (index.js) 2.46KB 1.10KB
plugin-form (index.js) 114.58KB 27.68KB
plugin-gantt (index.js) 164.14KB 39.98KB
plugin-grid (index.js) 188.13KB 50.00KB
plugin-kanban (index.js) 48.62KB 13.42KB
plugin-list (index.js) 111.07KB 27.08KB
plugin-map (index.js) 18.16KB 5.81KB
plugin-markdown (index.js) 13.72KB 4.69KB
plugin-report (index.js) 41.16KB 10.96KB
plugin-timeline (index.js) 26.21KB 7.52KB
plugin-tree (index.js) 8.50KB 2.88KB
plugin-view (index.js) 84.08KB 20.55KB
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.71KB 3.53KB
providers (index.js) 0.44KB 0.22KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.67KB 2.37KB
react (LazyPluginLoader.js) 3.77KB 1.33KB
react (SchemaRenderer.js) 23.73KB 7.96KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 1.23KB 0.66KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 4.09KB 1.74KB
sdui-parser (index.js) 4.47KB 2.03KB
sdui-parser (parse.js) 10.04KB 2.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

@yinlianghui
yinlianghui marked this pull request as ready for review August 12, 2026 21:25
@yinlianghui
yinlianghui added this pull request to the merge queue Aug 12, 2026
Merged via the queue into main with commit 2e3b0c0 Aug 12, 2026
21 checks passed
@yinlianghui
yinlianghui deleted the claude/issue-4408-api-disabled-honest-state branch August 12, 2026 21:26
yinlianghui pushed a commit that referenced this pull request Aug 13, 2026
`generateTimeScaleHeaders` is reachable from the package entry (`index.tsx`
does `export * from './renderer'`) and its published declaration grew an
optional trailing `locale?: string`. Entry-reachable additive API growth is
minor, not patch — patch is for changes with no API-surface movement at all.

`dist/index.d.ts` being byte-identical does not argue for patch: the entry
re-exports by reference, so the resolved public surface moved even though the
entry file's bytes did not. The contrapositive of #4496, which was graded patch
precisely because its .d.ts additions were NOT re-exported from the entry;
#4403 / #4177 / #4485 / #4495-regrade are the line this follows.

Additive and back-compatible is what minor means — no consumer breaks, existing
three-argument callers keep compiling and keep producing byte-identical output.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017Qqyix2QcnpUC9XeYVDzx3
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.

A list request that 404s with OBJECT_API_DISABLED renders as the generic empty state — "this page cannot work" reads as "you have no records"

2 participants