Skip to content

fix(plugin-dashboard): declare the DOM pass-through the metric props accept (#4426) - #4435

Merged
yinlianghui merged 3 commits into
mainfrom
claude/issue-4426-metric-props-passthrough
Aug 12, 2026
Merged

fix(plugin-dashboard): declare the DOM pass-through the metric props accept (#4426)#4435
yinlianghui merged 3 commits into
mainfrom
claude/issue-4426-metric-props-passthrough

Conversation

@yinlianghui

@yinlianghui yinlianghui commented Aug 12, 2026

Copy link
Copy Markdown
Collaborator

Fixes #4426

MetricWidget and MetricCard end their prop list with a ...domProps spread onto the Shadcn Card. PR #4428 kept that spread deliberately — it is the components' only accessibility pass-through, and removing it would delete the only way a host can put an id, a role or an aria-label on a KPI card. Neither props interface declared any of it, so the type refused what the runtime accepted. This is the mirror image of #4357: that issue was renderer metadata reaching the DOM because the spread is open; this is DOM attributes being unreachable because the type was closed.

Zero runtime change — no component body was touched, and the added pins are type-only, so they emit zero runtime bytes.

The convention, measured

The ruling asked for the repo's own spelling, not a third one. Counted over packages/*/src, an exported *Props interface that spreads onto a host element extends React.HTMLAttributes< HTMLDivElement >7 occurrences, 6 first-party plus shadcn's own:

interface package
PageHeaderComponentProps layout
ChatbotProps plugin-chatbot
ChatbotEnhancedProps plugin-chatbot
TypingIndicatorProps plugin-chatbot
RefreshIndicatorProps components/custom
FieldProps components/custom
BadgeProps components/ui (upstream shadcn, no-touch zone)

PageHeaderComponentProps is the closest analogue and states the convention in its own doc comment: "it extends HTMLAttributes so every DOM prop rides along."

The alternative the issue named — an explicit block mirroring FieldWidgetDomProps (#3221) — is 1 occurrence, and it is not a competing style: it exists to be bound key-by-key to a runtime whitelist (toDomProps) in both directions, so that a declared key which is never forwarded is a compile error. These components have no such whitelist. Writing half of one here would declare a set nothing enforces — the exact "declared but not delivered" failure that type was built to prevent. Whether plugin widgets should get that whitelist is #4425, and if they do, the declared DOM set becomes that whitelist and the two are one change.

So:

  • MetricWidgetProps extends React.HTMLAttributes< HTMLDivElement >
  • MetricCardProps extends Omit< React.HTMLAttributes< HTMLDivElement >, 'title' >

The one carve-out, and why it is the accurate contract rather than a workaround

MetricCard.title is the card's heading, in the I18nLabel vocabulary; HTML's title is a tooltip string. The types are incompatible, so extending whole does not merely mis-describe the component — it does not compile. But Omit is right on the merits regardless: the component destructures title out and renders it into CardTitle, so no title attribute has ever reached this element. Declaring the inherited DOM title would type-check, read as a supported tooltip, and silently do nothing — objectui#3290 / objectui#3222's first-class failure mode. Pinned from the runtime side by case (c), which asserts the attribute is absent after a render.

MetricWidget has no such collision (its heading is label) and extends the DOM attributes whole.

The repo's spelling for this carve-out is ComboboxPropsextends Omit< React.ButtonHTMLAttributes< HTMLButtonElement >, "value" | "onChange" > — omitted there for the same reason: the component's own contract owns the name.

Two smaller notes, both documented at the declaration: MetricWidget.onClick stays zero-arg (narrower than the inherited MouseEventHandler< HTMLDivElement >, because the same handler is wired to Enter/Space where there is no mouse event to hand over; a zero-arg function is assignable to the inherited signature, so existing callers keep compiling), and prefix keeps its value-prefix meaning over the inherited RDFa attribute of the same name and same string type.

What is deliberately NOT declared

The seven schema-shaped keys SchemaRenderer injects — schema / bind / events / props / ariaLabel / ariaDescribedBy / dataSource. None is an HTML attribute name, all seven are destructured out before the spread, and declaring them would re-assert as public contract exactly what #4428 stripped from the DOM. They stay in SchemaHostProps, intersected in at each component's own signature — #4428's shape, kept.

A compile-time pin holds this from the type side: Extract< keyof MetricWidgetProps, keyof SchemaHostProps > must be never, and likewise for the card. Written against keyof SchemaHostProps rather than a copied list, so a key added there is covered automatically.

Correction 1: accepted is not declared

My first draft of the probe put a @ts-expect-error on a consumer passing schema={…} directly. That directive is unused (TS2578), and the probe was wrong. Both components are declared MetricWidgetProps & SchemaHostProps at their own signature, so the renderer's keys are accepted by the component — deliberately, because SchemaRenderer has to be able to inject them. The true and narrower claim is the Extract/never pin above: they are not on the exported props interface, so they never become documented authoring surface, and they are still stripped before the spread. Accepted-and-dropped, not declared.

Correction 2: where compile-time pins are allowed to live — CI caught this one

The defect was type-only (id / role / aria-label reached the card the whole time), so nothing vitest runs can observe the fix's direction. The assertions had to be compiled by something, and this package's tests are compiled by nothing: tsconfig.json excludes **/*.test.tsx, @object-ui/plugin-dashboard is the sole remaining TEST_DEBT entry in scripts/check-type-check-coverage.mjs (6 errors, #4118), and vitest erases types. That is objectui#3181 — assertions in an uncompiled test file read as coverage and are decoration.

My first push reached for the narrow tsconfig.typetests.json rescue hatch, whose gate rules explicitly describe it as the escape for a package still in TEST_DEBT. That was wrong, and CI said soscripts/__tests__/check-type-check-coverage.test.ts pins #4291's ratchet as a repository-state test:

FAIL scripts/__tests__/check-type-check-coverage.test.ts
  > every surviving narrow project is a package still in debt, in this repository
  > is empty — the rescue hatch has no users left
AssertionError: expected [ '@object-ui/plugin-dashboard' ] to deeply equal []

The gate script permits the shape; the ratchet forbids a new user. It is stated there in as many words: "a tsconfig.typetests.json reappearing ANYWHERE turns this red."

The narrow project is gone and type-check is back to plain tsc --noEmit. The assertions moved to src/domPassthroughPins.ts, a source module the package's own tsc --noEmit already compiles — widgets/toDomProps.ts's shape in @object-ui/fields, which binds its DOM whitelist to its declaration the same way. This is strictly better than the project I first added: no extra config, no ratchet to violate, and the pins are enforced by the default type-check that CI's Type Check job runs. Unlike toDomProps.ts they are type-only, so they emit nothing at all — which matters, because this change must stay types-only.

The test file correspondingly keeps only its runtime assertions. Its @ts-expect-error cases were deleted rather than left behind in a file nothing compiles, where they would have read as a negative pin while checking nothing.

Verification

  • Repo-root pnpm exec vitest run scripts/__tests__/check-type-check-coverage.test.ts packages/plugin-dashboard/ --maxWorkers=245 files, 393 tests passed, including the test that failed on the first push. fix(plugin-dashboard): keep schema-shaped props off the KPI card DOM (#4357) #4428's MetricWidget.domProps.test.tsx is untouched and green.
  • tsc --noEmit in packages/plugin-dashboard — exit 0, and it now compiles the pins.
  • Build closure first (--filter '@object-ui/plugin-dashboard^...' build), then a package rebuild, before judging any type.
  • Repo-wide turbo run type-check --concurrency=278 successful, 78 total (run against the first push; the second push only moves assertions between files that the same run compiles). This is the no-downstream-red proof for an exported-type widening; the 5 downstream consumers (console, site, console-starter, app-shell, byo-backend-console) were enumerated with the prefix filter ...@object-ui/plugin-dashboard (the downstream direction — the suffix form walks upstream instead) and all are inside that run.
  • eslint on the four touched source files: 0 errors. The 11 warnings are no-unused-vars on the compile-time assertion aliases — the same shape as the established idiom in flow-designer-edge.types.test.ts, and lint.yml deliberately does not set --max-warnings. Package-wide lint is exit 0.
  • check:control-bytes, check:phantom-deps, changeset:check, check-changeset-presence, type-check:coverage: all green — the last now back to reporting 0 with a narrow type-assertion project. Plus a self-scan of every touched file for control bytes beyond the gate's surface: no hits.

Reverse verification — direction predicted before each run

Method: git checkout origin/main -- the two component files (never git stash), rebuild dist, re-measure, restore. Consumer probes compiled against the rebuilt dist/*.d.ts from a real consumer package (apps/console), never sibling src/, per #4428's method.

probe predicted before after
positive — id / role / aria-label / aria-describedby / tabIndex red then green RED (exit 2) GREEN (exit 0)
negative — a bogus prop, no suppression red in BOTH RED (3 errors) RED (same 3)
control — data-* only green in both GREEN GREEN
src/domPassthroughPins.ts under plain tsc --noEmit red then green RED (2 errors) GREEN

The before-state positive probe reproduces the issue's own report:

probe-positive.tsx(11,5): error TS2322: Type '{ label: string; value: number; id: string;
  role: string; "aria-label": string; "aria-describedby": string; tabIndex: number;
  "data-testid": string; }' is not assignable to type
  'IntrinsicAttributes & MetricWidgetProps & SchemaHostProps'.
    Property 'id' does not exist on type 'IntrinsicAttributes & MetricWidgetProps & SchemaHostProps'.

The negative probe is red on both sides with byte-identical errors — bogusProp rejected on both components, and colorVariant: "chartreuse" still rejected against the closed vocabulary. That is the assertion that the widening opened no [key: string]: any; its committed counterpart is the RejectsBogus pin, green on both sides for the same reason.

The pins file failed in exactly the two predicted places and nowhere else, under the package's ordinary type-check:

src/domPassthroughPins.ts(75,41): error TS2344: Type 'false' does not satisfy the constraint 'true'.   # _WidgetAcceptsDomIdentity
src/domPassthroughPins.ts(76,39): error TS2344: Type 'false' does not satisfy the constraint 'true'.   # _CardAcceptsDomIdentity

The negative pins and the Extract/never renderer-key pins reported nothing in either state, as predicted. That is what makes the pins load-bearing rather than decorative.

One measured correction to the fix's framing

The ruling named id / role / aria-label / data-* as what must type-check. data-* was never blocked. The control probe — data-testid and data-obj-id with no other DOM prop — is green before the change as well as after, because TypeScript does not type-check a JSX attribute whose name is not a valid identifier. So the widening genuinely fixes the first three; data-* is reported as already working rather than claimed as newly fixed. It is still exercised in the runtime test, since it is part of the pass-through a consumer will write.

Changeset

minor, per the #4403 precedent — two exported interfaces widen. Never major, per the fixed-group rule. The widening is purely additive for existing callers: every prop that compiled before still compiles, nothing narrows, and no source change is required to upgrade. Both semantic decisions (title stays the heading, onClick stays zero-arg) are written into the changeset body rather than left to the diff.


Generated by Claude Code

claude added 2 commits August 12, 2026 06:04
…accept (#4426)

`MetricWidgetProps` / `MetricCardProps` end their prop list with a
`...domProps` spread onto the Shadcn `Card`, kept deliberately by #4357, but
declared none of it — so `id` / `role` / `aria-label` were a TS error for a
direct consumer while working at runtime.

`MetricWidgetProps` now extends `React.HTMLAttributes<HTMLDivElement>` and
`MetricCardProps` extends the same minus `title` (its heading, an `I18nLabel`,
which never reached the DOM). The seven schema-shaped keys `SchemaRenderer`
injects stay undeclared in `SchemaHostProps`. Zero runtime change.

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

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-B7R9OEpn.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.20KB 108.43KB
core (index.js) 2.99KB 1.14KB
create-plugin (index.js) 10.08KB 3.26KB
data-objectstack (index.js) 153.42KB 41.19KB
fields (index.js) 228.99KB 56.82KB
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.71KB 3.79KB
plugin-calendar (index.js) 45.23KB 12.45KB
plugin-charts (index.js) 62.01KB 17.63KB
plugin-chatbot (index.js) 181.17KB 43.03KB
plugin-dashboard (index.js) 120.75KB 31.38KB
plugin-designer (index.js) 211.16KB 42.76KB
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) 187.99KB 49.92KB
plugin-kanban (index.js) 48.60KB 13.41KB
plugin-list (index.js) 110.21KB 26.79KB
plugin-map (index.js) 18.05KB 5.80KB
plugin-markdown (index.js) 13.72KB 4.69KB
plugin-report (index.js) 40.99KB 10.74KB
plugin-timeline (index.js) 26.21KB 7.52KB
plugin-tree (index.js) 8.50KB 2.88KB
plugin-view (index.js) 84.03KB 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.71KB 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

…ow typetests project

CI caught the first attempt: `scripts/__tests__/check-type-check-coverage.test.ts`
pins objectui#4291's ratchet as a repository-state test — a
`tsconfig.typetests.json` reappearing ANYWHERE turns it red. The gate script
permits the shape; the ratchet forbids a new user. The narrow project is removed
and `type-check` goes back to plain `tsc --noEmit`.

The compile-time assertions move to `src/domPassthroughPins.ts`, which the
package's own `tsc --noEmit` already compiles — `widgets/toDomProps.ts`'s shape
in @object-ui/fields. They are `type`-only, so they emit zero runtime bytes.
Reverting either `extends` turns them red under the ordinary type-check.

The test file keeps only its RUNTIME assertions; its `@ts-expect-error` cases are
dropped rather than left in a file nothing compiles, which would have read as
coverage while checking nothing (objectui#3181).

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

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Main entry (gzip) 24.7 KB 350 KB
Entry file index-B7R9OEpn.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.20KB 108.43KB
core (index.js) 2.99KB 1.14KB
create-plugin (index.js) 10.08KB 3.26KB
data-objectstack (index.js) 153.42KB 41.19KB
fields (index.js) 228.99KB 56.82KB
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.71KB 3.79KB
plugin-calendar (index.js) 45.23KB 12.45KB
plugin-charts (index.js) 62.01KB 17.63KB
plugin-chatbot (index.js) 181.17KB 43.03KB
plugin-dashboard (index.js) 120.75KB 31.38KB
plugin-designer (index.js) 211.16KB 42.76KB
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) 187.99KB 49.92KB
plugin-kanban (index.js) 48.60KB 13.41KB
plugin-list (index.js) 110.21KB 26.79KB
plugin-map (index.js) 18.05KB 5.80KB
plugin-markdown (index.js) 13.72KB 4.69KB
plugin-report (index.js) 40.99KB 10.74KB
plugin-timeline (index.js) 26.21KB 7.52KB
plugin-tree (index.js) 8.50KB 2.88KB
plugin-view (index.js) 84.03KB 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.71KB 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

Copy link
Copy Markdown
Collaborator Author

ACCEPT — PM 复核 (session session_017Qqyix2QcnpUC9XeYVDzx3), closes #4426.

  • Convention measured (7 exemplars, closest analogue self-documenting, the whitelist-bound alternative correctly ruled out as not-a-style), and the Omit<'title'> carve-out is the declared-but-not-delivered discipline applied at the type level.
  • The CI-caught deviation and its in-task fix is the loop working: the typetests project passed the gate SCRIPT locally but violated the Retire the narrow per-file tsconfig.typetests.json projects in packages that have graduated out of TEST_DEBT #4291 repo-state RATCHET (shape permitted, new user forbidden) — and the recovery (src pins module compiled by the job CI already runs, no extra config) is strictly better than the first push. The stated lesson — a package-scoped local farm excludes scripts/ repo-state tests — is the accepted cost of scoped verification, now on the record.
  • Both framing corrections accepted as measured: data-* was never blocked (non-identifier JSX attributes aren't type-checked) and is reported as already-working, not newly-fixed; "accepted is not declared" resolved the unused @ts-expect-error into the true Extract/never claim. Reporting the measured direction over the expected assertion is the standard.
  • The probe set (positive reproducing the issue's own error, negative byte-identical both sides proving no index signature opened, control green both) plus the onClick narrower-in-assignable-direction pin and the documented prefix shadowing — all to standard. 78/78, CI converged.

Flipping ready + arming auto-merge.


Generated by Claude Code

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

Projects

None yet

2 participants