Skip to content

build(tsconfig): raise test-program lib to ES2022 across 31 packages - #9512

Merged
os-try-charles merged 1 commit into
mainfrom
claude/issue-8691-test-lib-floor
Sep 14, 2026
Merged

os-try-charles merged 1 commit into
mainfrom
claude/issue-8691-test-lib-floor

Conversation

@claude

@claude claude Bot commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Part of #8691 — the in-lane half, measured. The maintainer fork the card named is answered below: it dissolves.

What moved

31 packages/*/tsconfig.test.json files, one axis only: lib ES2020 to ES2022.

  • The root tsconfig.json is untouched. What every package SHIPS still compiles against the ES2020 baseline, so downlevel support is unchanged and this is not the maintainer's downlevel question.
  • Each file keeps its existing DOM set exactly. Three spellings existed and all three are preserved: ES2022+DOM+DOM.Iterable, ES2022+DOM, and ES2022 alone for vscode-extension.
  • All 31 projects are noEmit: true — measured, not assumed — so nothing here reaches an artifact. tsc lib is type-only; vitest never reads these configs. There is no runtime direction to this change.
  • packages/plugin-timeline/tsconfig.test.json is deliberately NOT touched; it belongs to another in-flight card and is left exactly as found.

The distribution that was actually there

The card described the divergence as binary. It is not. Measured at af4ff1d1e0 with TypeScript's own JSONC reader, and independently confirmed by the repo's own census:tsconfig-test-parity — two readers, same answer:

39 tsconfig.test.json projects, five distinct lib spellings:

effective lib count packages
es2020 + dom + dom.iterable 29 the inheriting majority, plus core fields plugin-dashboard plugin-editor react which RESTATE the inherited value
es2022 + dom + dom.iterable 4 app-shell components mobile plugin-list
es2020 + dom 3 i18n plugin-form types — a NARROWING, drops DOM.Iterable
es2022 + dom 2 plugin-grid, examples/schema-catalog
es2020 1 vscode-extension — narrowest, no DOM at all, and extends its own sibling rather than the root

The split that matters is not "sets lib" versus "inherits": 13 projects set lib and five of those still land on ES2020, restating the inherited value for no effect. Reading the file for the presence of a lib key tells you nothing about which level it compiles at.

The .at(-1) population, re-measured

Re-read at af4ff1d1e0; the card's figure is from six days earlier.

  • 79 tracked test files contain the literal, up from the card's 75.
  • 74 of those have at least one real call site; 127 call sites in total.
  • 5 files match on COMMENT TEXT ONLY — all in plugin-designer, all comments explaining why the author used index arithmetic instead. A naive file-count reader counts the workaround as an instance of the problem.
  • 0 occurrences in non-test package source. Control for that zero: the same command shape over the same file set returns 475 for a token known to be present.

What it cost — before and after, with controls

Dependency closure built first (43/43 turbo build tasks), because an unbuilt tree makes every program look broken identically.

run result
BEFORE — tsc -p tsconfig.test.json for all 37 package projects 37 exit 0, 0 errors
AFTER — same 37 37 exit 0, 0 errors
AFTER — turbo run type-check (CI's own job: source AND test programs) 81/81 tasks, exit 0

Cost of the raise: zero errors, zero overload-resolution changes, in either direction.

Every zero above carries a control proven able to return non-zero in the same command:

control result
.at(-1) on a number[] in permissions BEFORE exit 2 — TS2550, the card's exact signature
byte-identical probe in app-shell (already ES2022) exit 0
the same probe in permissions AFTER the raise exit 0 — the change is load-bearing, not vacuous
provably-false assignment in permissions AFTER exit 2 — TS2322
provably-false assignment through turbo run type-check exit 2 — the turbo reader is not serving a cached green

Every probe was proven on disk before reading a result, and every leg restored under a trap; git diff HEAD verified empty after each.

Gates, all exit 0: type-check:coverage, check:published-tsconfig-exclude, check:self-import, check:published-dist, check:control-bytes, check-changeset-presence (0 published files moved, so no changeset is owed), and the 4 script suites that pin these configs (77 tests).

gates_weakened: NONE. Nothing was lowered, loosened or excluded — raising a test program's lib only admits more to the checker, for files that are never shipped.

Why the divergence was invisible

All 37 test projects were green BEFORE this change, including every ES2020 one holding a real .at(-1). That is not the ES2020 floor being harmless — it is the receiver being any. Every surviving call site in an ES2020 project reaches .at through an as any mock factory or a readonly any[] annotation, so the checker never sees an array and never applies the floor. The rule only bites when someone types the receiver properly, which is exactly what happened on the card that filed this.

So the defect was latent, not absent, and it failed in the most expensive direction: green under vitest, green under tsc until the day your value is well-typed.

The remainder — the fork dissolves

After this change, nothing requires the root lib to move.

  • Non-test package source contains 0 uses of .at(, Object.hasOwn, .findLast, or class static blocks. The shipped code does not want ES2022.
  • The package source programs are untouched and still compile at ES2020 — 81/81 above includes them.
  • This is not a new pattern: packages/app-shell raised its test lib and no other, and its config already documents the reasoning verbatim — raised there "so the package SOURCE keeps compiling against the ES2020 baseline it ships to."

The downlevel-support question the card correctly declined to answer is a question this card never needed answered. It can stay closed.

After this lands, two projects remain at ES2020, both deliberately outside this PR's surface: packages/plugin-timeline (another card's file) and examples/console-starter (not packages/*).

Known consequence, NOT repaired here

Five plugin-designer test files carry comments justifying index arithmetic by the ES2020 floor. This change makes those comments false. These are test sources, outside this PR's declared file surface, so they are reported rather than edited.

One of the five is already false at HEAD, independently of this PR: it states that .at() "type-checks nowhere in this repo", which five packages have contradicted since before this card was filed. The control run above compiles that exact idiom in app-shell today.

Maintainer speed-read (draft)

改了什么 — 31 个包的测试专用 tsconfig,lib 从 ES2020 抬到 ES2022,只动这一个轴,DOM 集合原样保留。根 tsconfig.json 一个字节没动。

为什么改 — 同一个写法 calls.at(-1) 在五个包里能过类型检查,在其余包里报 TS2550,差别只取决于文件恰好放在哪个包。作者只能一个包一个包地撞出来这条规则。

风险与代价(含回滚) — 实测代价为零:改动前后 37 个测试程序全绿,CI 自己的 type-check 81/81 全绿。测试程序不发布、全部 noEmit,发布产物与降级支持不受影响。回滚就是 revert 这一个 commit,无迁移、无数据、无下游。已知副作用一项:plugin-designer 五个测试文件里解释「为什么不用 .at(-1)」的注释会变成过期描述,已在上面单独列出,未在本 PR 内修改。

席位意见 — (留空)

你要做的 — 确认一件事:卡片把「要不要抬根 lib」升级成了维护者决定,而实测结论是这个问题不需要回答 —— 发布代码里 .at( 出现 0 次,根配置不用动。若认可,这条升级可以就此撤销。


Generated by Claude Code

The test programs diverged on `lib`: five packages had raised it to ES2022,
the rest inherited ES2020 from the root config, so the same idiom
(`calls.at(-1)`) type-checked or raised TS2550 purely by which package the
file lived in.

Only `tsconfig.test.json` files move here. The package SOURCE configs and the
root `tsconfig.json` are untouched, so what every package SHIPS still compiles
against the ES2020 baseline — this is the pattern `packages/app-shell` already
documented in its own test config.

Each file keeps its existing DOM set exactly; the only axis changed is
ES2020 -> ES2022. Every one of the 31 projects is `noEmit`, so nothing here
reaches an artifact.

`packages/plugin-timeline/tsconfig.test.json` is deliberately NOT touched: it
belongs to another in-flight card.

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

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 329 chunks) 3061.9 KB 3104.5 KB
Main entry chunk (gzip) 145.7 KB 350 KB
Entry file index-DyUsifoC.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.69KB 6.21KB
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) 25.05KB 9.16KB
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.46KB 3.43KB
auth (index.js) 3.19KB 1.44KB
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) 26.08KB 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.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) 544.93KB 130.50KB
core (index.js) 8.52KB 3.41KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 213.54KB 59.33KB
fields (index.js) 249.20KB 62.88KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 1.22KB 0.64KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.22KB 2.26KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 32.15KB 10.49KB
i18n (useDisplayLocale.js) 2.85KB 1.45KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 38.83KB 10.95KB
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) 2.53KB 0.85KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 4.39KB 1.66KB
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.39KB 3.10KB
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) 14.81KB 3.63KB
plugin-calendar (index.js) 49.92KB 14.22KB
plugin-charts (index.js) 71.33KB 19.90KB
plugin-chatbot (index.js) 195.34KB 46.51KB
plugin-dashboard (index.js) 131.44KB 34.65KB
plugin-designer (index.js) 215.94KB 44.33KB
plugin-detail (index.js) 252.48KB 65.61KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 136.71KB 34.16KB
plugin-gantt (index.js) 167.62KB 41.26KB
plugin-grid (index.js) 212.83KB 57.97KB
plugin-kanban (index.js) 46.63KB 14.53KB
plugin-list (index.js) 112.67KB 27.68KB
plugin-map (index.js) 21.48KB 6.99KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.41KB 11.93KB
plugin-timeline (index.js) 30.07KB 8.74KB
plugin-tree (index.js) 10.58KB 3.72KB
plugin-view (index.js) 84.36KB 20.78KB
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) 99.04KB 32.62KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.63KB 2.18KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.66KB 2.50KB
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) 14.82KB 4.99KB
types (ai.js) 0.20KB 0.17KB
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) 4.73KB 2.28KB
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) 14.04KB 5.36KB
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

@os-try-charles
os-try-charles marked this pull request as ready for review September 14, 2026 14:18
@os-try-charles
os-try-charles added this pull request to the merge queue Sep 14, 2026
Merged via the queue into main with commit 4e96bec Sep 14, 2026
36 checks passed
@os-try-charles
os-try-charles deleted the claude/issue-8691-test-lib-floor branch September 14, 2026 14:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment