Repository navigation
finding: @object-ui/types exports["."].require points at dist/index.cjs, which its bare-tsc build never emits #4896
Description
Activity
- addedbugSomething isn't workingSomething isn't workingand removed
on Aug 19, 2026 os-support-ai commented
on Aug 19, 2026 CollaboratorMore actionsTriage (concentrated finding round): promoted
finding→pm:queue+bug— the shippedexports["."].requirecondition resolves to a file the bare-tscbuild structurally never emits, so the published artifact lies to any CJS consumer. Route suggestion (dev verifies): drop therequirecondition rather than adding an emit step — the package declares"type": "module"and is types-first, so ESM-only is the contract-honest shape; removing a dead condition beats maintaining a second build format nobody consumes. The 22-package exports-vs-dist sweep noted in the card stays out of scope here; file it separately only if this fix confirms the pattern is worth a gate.本评论来自分诊座位 Routine (session
session_018euHiF12oV8iSfYdjVioUF).
Generated by Claude Code
os-support-ai commented
on Aug 19, 2026 CollaboratorMore actionsClaim: PM loop round 15
Session:
session_01RV6yuVCxymHYE16PL9vQkE
Branch:claude/issue-4896-types-exports-require-condition
Worktree:objectui-issue-4896
Domain:repo:objectui
File surface:packages/types/package.json(+ whatever test/gate pins the exports map) — small and precise; declare the real surface in the PR (stop on breach; explain in the report)
Container & model: S,mode:subagent,model: sonnet
Clause-②: yes — this removes a declared entry point from the published surface. Declared, not argued away. The mitigating fact (and it must be stated in the PR body, not assumed): that entry point has never resolved to anything — the bare-tscbuild structurally never emitsdist/index.cjs, so no working consumer can exist. You are removing a lie, not a capability.
Serial constraints cleared:packages/typesis free — PR #5298 (#5006), which heldpackages/types/src/base.ts, merged at 12:11. Only other in-flight cards are #4815 (plugin-grid+core) and #4889 (packages/components) — both disjoint.The defect
packages/types' shippedexports["."].requirecondition points atdist/index.cjs, which its bare-tscbuild never emits. The published artifact therefore lies to any CJS consumer.Route (triage suggestion — verify it, don't just take it)
Drop the
requirecondition rather than adding an emit step. The reasoning: the package declares"type": "module"and is types-first, so ESM-only is the contract-honest shape; removing a dead condition beats maintaining a second build format nobody consumes.Verify the two premises yourself before acting — that
"type": "module"is actually declared, and that nothing in-repo consumes the package viarequire. Counter-probe any zero with a known-present neighbouring term.⛔ Out of scope: the 22-package exports-vs-dist sweep the card mentions. File it separately only if this fix confirms the pattern is worth a gate — and file, don't fix.
⚠️ The changeset call, and why it is not obviousRemoving a published export condition looks like a breaking change. Decide deliberately and write your reasoning in the changeset body:
- If you judge it breaking →
minor, with the breaking semantics spelled out in the body. ⛔ NEVERmajor— objectui's major is pinned to@objectstack's major, and objectui's own breaking changes ship asminor. AmajorreddensChangeset Bump Policyand blocks the PR. (This exact mistake cost this lane sixteen hours earlier today.) - If you judge it non-breaking because the condition never resolved →
patch, and say so explicitly.
Either answer is defensible; an unstated one is not.
Verification
- Prove the emit gap rather than asserting it: show that the build does not produce
dist/index.cjs. - Reverse-verify where meaningful: if you add a gate/test pinning the exports map against real build output, revert the fix with it kept, predict what goes red, and report predicted-vs-observed. If a reverse leg genuinely does not apply to a manifest-only change, say that plainly instead of inventing a ceremonial one.
- State whether a build artifact sits between your edit and the thing under test.
⛔ Do not edit
content/docs/releases/,docs/adr/**,.claude/**,skills/**,AGENTS.md,CLAUDE.md.
Generated by Claude Code
- If you judge it breaking →
os-support-ai commented
on Aug 19, 2026 CollaboratorMore actions<!-- os-dev-report -->
{ "issue": 4896, "status": "done", "branch": "claude/issue-4896-types-exports-require-condition", "pr": "https://github.com/objectstack-ai/objectui/pull/5315", "premise_still_valid": true, "summary": "Dropped the dead require condition from @object-ui/types' exports[\".\"] map (it pointed at dist/index.cjs, which the package's bare-tsc build never emits). Verified both route premises directly: \"type\": \"module\" is declared, and nothing in-repo consumes the package via require() (zero grep hits; the one real CJS-bundling consumer, packages/vscode-extension via tsup, builds byte-identical with the condition present or absent, since esbuild resolves its import statements via the import condition regardless of output format). Added a manifest-level pin test and a changeset judged patch (non-breaking), backed by measuring require('@object-ui/types') through the real workspace symlink both before and after: it throws in both states (MODULE_NOT_FOUND before, ERR_PACKAGE_PATH_NOT_EXPORTED after), so no working call regresses.", "tests": "pnpm exec vitest run packages/types/ -> 39 files / 447 tests passed (includes new pin packages/types/src/__tests__/package-exports-manifest.test.ts, run in both pre-fix and post-fix states). pnpm --filter '@object-ui/types' type-check -> passed. node scripts/check-changeset-no-major.mjs, check-changeset-presence.mjs, check-phantom-dependencies.mjs, check-node-esm-load.mjs --specifiers-only, check-control-bytes.mjs -> all passed. Emit-gap proof: rm -rf packages/types/dist tsconfig.tsbuildinfo && tsc then find dist -name '*.cjs' -> 0 results; require.resolve('.../dist/index.cjs') -> MODULE_NOT_FOUND. Reverse leg (manifest pin, not artifact-level, and stated as such): fix committed first (38217cd54), then reverted only packages/types/package.json to origin/main content with the test kept -> predicted 2 of 4 tests red (the {types,import}-exact and no-.cjs-target assertions); observed exactly 'Tests 2 failed | 2 passed (4)' naming require: ./dist/index.cjs; restored fix -> 4/4 green again. A real build artifact (dist/) sat between the edit and the manual emit-gap proof; the automated pin itself is manifest-only by design (repo's own test job runs with no pre-build step, so a dist-dependent per-PR test would be vacuously red/absent on a cold cache -- documented in the PR body). All checks re-run at final commit 0097d2752.", "open_questions": [], "out_of_scope_findings": [] }
Generated by Claude Code
os-support-ai commented
on Aug 19, 2026 CollaboratorMore actions(supersedes the malformed marker in the previous comment — same report, correct marker)
{ "issue": 4896, "status": "done", "branch": "claude/issue-4896-types-exports-require-condition", "pr": "https://github.com/objectstack-ai/objectui/pull/5315", "premise_still_valid": true, "summary": "Dropped the dead require condition from @object-ui/types' exports[\".\"] map (it pointed at dist/index.cjs, which the package's bare-tsc build never emits). Verified both route premises directly: \"type\": \"module\" is declared, and nothing in-repo consumes the package via require() (zero grep hits; the one real CJS-bundling consumer, packages/vscode-extension via tsup, builds byte-identical with the condition present or absent, since esbuild resolves its import statements via the import condition regardless of output format). Added a manifest-level pin test and a changeset judged patch (non-breaking), backed by measuring require('@object-ui/types') through the real workspace symlink both before and after: it throws in both states (MODULE_NOT_FOUND before, ERR_PACKAGE_PATH_NOT_EXPORTED after), so no working call regresses.", "tests": "pnpm exec vitest run packages/types/ -> 39 files / 447 tests passed (includes new pin packages/types/src/__tests__/package-exports-manifest.test.ts, run in both pre-fix and post-fix states). pnpm --filter '@object-ui/types' type-check -> passed. node scripts/check-changeset-no-major.mjs, check-changeset-presence.mjs, check-phantom-dependencies.mjs, check-node-esm-load.mjs --specifiers-only, check-control-bytes.mjs -> all passed. Emit-gap proof: rm -rf packages/types/dist tsconfig.tsbuildinfo && tsc then find dist -name '*.cjs' -> 0 results; require.resolve('.../dist/index.cjs') -> MODULE_NOT_FOUND. Reverse leg (manifest pin, not artifact-level, and stated as such): fix committed first (38217cd54), then reverted only packages/types/package.json to origin/main content with the test kept -> predicted 2 of 4 tests red (the {types,import}-exact and no-.cjs-target assertions); observed exactly 'Tests 2 failed | 2 passed (4)' naming require: ./dist/index.cjs; restored fix -> 4/4 green again. A real build artifact (dist/) sat between the edit and the manual emit-gap proof; the automated pin itself is manifest-only by design (repo's own test job runs with no pre-build step, so a dist-dependent per-PR test would be vacuously red/absent on a cold cache -- documented in the PR body). All checks re-run at final commit 0097d2752.", "open_questions": [], "out_of_scope_findings": [] }
Generated by Claude Code
Fact (measured on
origin/main, unrelated to any in-flight PR)packages/types/package.json'sexportsmap:packages/types's"build"script is bare"tsc"— no bundler, no dual-format step.A clean rebuild (
rm -rf dist tsconfig.tsbuildinfo && tsc) produces.js+.d.ts(+ previously
.d.ts.map, see #4851) and never anindex.cjs. Therequirecondition of the root
.export therefore resolves to a file the tarball nevercontains — measured directly:
How it was found
While implementing #4851 (the
declarationMap/filestrim for@object-ui/types),the pre-existing-vs-introduced check for that PR walked every
exportsconditionagainst the freshly rebuilt
distand found this one missing. Confirmed pre-existingon
origin/main(git show origin/main:packages/types/package.jsonalready has it) —not introduced by #4851, and out of that PR's file surface (its constraint was
tsconfig.json+filesarray only).Scope note — possibly wider, not fully measured
A quick grep found 23 packages across the repo declaring a
"require": ".../index.cjs"export condition. A few were spot-checked:
@object-ui/data-objectstack(tsup) — tsup emits.cjsby default; almostcertainly fine, not directly verified in this pass.
@object-ui/fields(tsc && vite build && build-css,vite.config.tsexplicitlysets
formats: ['es', 'cjs']and a matchingfileNamemapping) — almost certainlyfine.
@object-ui/components,@object-ui/layout, and most@object-ui/plugin-*packages build via plain
vite build(vite-plugin-dts, no explicitbuild.lib.formats) — whether Vite's library-mode default actually emits a fileliterally named
index.cjsfor each of these was not verified in this pass; itdepends on each package's
vite.config.tslib.fileName/namesettings, whichdiffer from file to file (e.g.
layout'sfileName: 'index'static string vs. aformat-aware function).
@object-ui/typesis the only one confirmed broken (baretsc, structurallycannot emit
.cjs). The other 22 are unverified — this issue is scoped narrowly tothe confirmed case; a follow-up sweep across the other 22 would need a clean rebuild
of each and an exports-vs-dist walk like the one that found this.
Why it matters, and why it's observation-class today
No consumer changes in this repo import
@object-ui/typesviarequire()— the repois ESM (
"type": "module"everywhere), so this deadrequirecondition is invisibleto every workspace consumer and to
check-phantom-dependencies.mjs(which asks "is itdeclared", not "does the exports map resolve"). It would only bite an external consumer
that
require()s this package directly (or a bundler resolving therequireconditionunder
--conditions require) — a real but currently unmeasured audience. Cheap fixonce confirmed in scope: either drop the
requirecondition (types-only package, ESMdeclared) or add the missing emit step.
Observation-class, unassigned.