fix(build): give each exports condition its own types target in 27 dual-build packages - #13299
Draft
os-elon wants to merge 8 commits into
Draft
fix(build): give each exports condition its own types target in 27 dual-build packages#13299os-elon wants to merge 8 commits into
os-elon wants to merge 8 commits into
Conversation
…8 dual-build packages
… resolves a CommonJS-flavoured types file that exists
…packages (beyond the ruled scope; see PR body)
… green (see PR body)
…ting its declarations splits ObjectKernel's identity
Contributor
📓 Docs Drift Check
What this run could not see
Coarse fallback — 56 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of #13112
Implements the maintainer ruling of 2026-08-29 (decision batch #3, verbatim
「同意」): declare the files, do not delete them. 27 of the 28 dual-build
packages now give each
exportscondition its owntypestarget. The 28th,@objectstack/core, is held back at a measured boundary described below —that is the one place this PR does not discharge the ruling, and it is the
reason this needs contract review rather than a rubber stamp.
This PR changes the published surface of 27 packages. Concretely, per
package: the
exportsmap is rewritten soimportandrequireeach carrytheir own nested
types. What that changes for a consumer:node16/nodenext/bundlerdist/index.d.tsdist/index.d.ts— unchanged, byte for bytenode16/nodenextdist/index.d.ts(an ES-module declaration) → TS1479dist/index.d.cts— the CommonJS declarationnode10/moduleResolution: nodetypesfieldtypesfield — untouchedimport/requireof JS)Nothing is removed and no path that resolved before stops resolving. No package
version, entry point, or runtime artifact changes. 33 entry points across 27
packages; 34
.d.ctsfiles totalling 5,296,847 B move from unreachable toreachable. This is a widening of what the manifests name, not of what the
tarballs contain.
The defect is not the wasted bytes
The card measured 5.2 MiB of
.d.ctsfiles that notypescondition points at.Re-derived on this branch's build at
74049254d4: 28 packages · 36 files ·5,517,701 B (5.26 MiB) — the card's figures reproduce (it measured 5,497,247 B
at
2fde235c84; the drift is intervening source changes).But the bytes are the symptom. Measured with
tsc --traceResolutionagainst areal
"type": "commonjs"fixture atmoduleResolution: node16, resolving@objectstack/metadata-core:So the published
requireentry point was unusable from TypeScript, whilethe JavaScript behind it loaded perfectly —
check:dual-build-cjs-loadshasasserted that for months. The types said the supported entry point could not be
required. The ESM leg is the positive control: same harness, same run, verdict
unchanged, which is what makes the CJS leg a reading rather than a coincidence.
The triage comment's confidence gap — "does any consumer reach
.d.ctsby fileadjacency?" — is answered and the answer is no: TypeScript matches the
sibling
typescondition first, so it never falls back to adjacency. Thesibling
typeswas answering for both conditions.⛔
@objectstack/coreis deliberately NOT changed — the measured boundarySplitting one declaration into two makes TypeScript compare it nominally
when it carries a
privatemember.ObjectKernelhasprivate pluginsandtravels into every plugin's
initviaPluginContext.getKernel(). Threewhole-repo
pnpm buildruns on this branch:@objectstack/verify, 5 × TS2345@objectstack/plugin-dev, TS2345The third row is the informative one. It moves the failure instead of
removing it: splitting
@objectstack/objectqlis what fixesverifyand whatbreaks
plugin-dev, because a split dependency resolves core one way from its.d.mtsand the other way from its.d.ts. This is TypeScript's dual-packagehazard on the type axis, and it is a property of the dependency, never of the
consumer that trips over it.
⇒ Core cannot be split until
ObjectKernel's identity stops being nominal.That is a decision about core's public types, not about an exports map, so it is
not taken here. Core keeps the sibling-
typesshape; its two.d.ctsfiles(220,854 B) stay unreachable, declared as such in the gate with the
measurement above attached, never silently skipped.
The gate (ruling point 2)
check:dual-build-cjs-loadsgains a fourth invariant beside PARSES / LOADS /AGREES:
TYPED — the declaration a
requireconsumer resolves is CommonJS-flavouredand is really on disk.
Three properties worth reviewing:
.d.ctsextension.dist/index.d.tsis thecorrect CommonJS declaration in a CJS-first package and the wrong one in a
"type": "module"package. An extension rule would red 50 correctly-spelledentries in this repo. The self-test carries that case as an explicit green
control.
typesat any depth, sibling included — otherwise thedefective shape and a legitimately CJS-first package both report "no types"
and cannot be told apart.
ablation: with the floor on the clean count, restoring the 28 defective
manifests drove it 102 → 67 and the gate refused (exit 2, "nothing was
read") instead of reporting its 35 findings — a real regression rendered as a
broken instrument. Pinned.
Ablation, on the committed fix, mutation confirmed on disk before the reading
and restoration proven after (
git diff HEADempty, blob hash matches HEAD):35 findings at the base commit, 0 at HEAD, with 67 entries already correct at
base as the built-in control that the invariant is not vacuously true.
Verification (local; CI has a large queue backlog, so this is the evidence)
At
23fc0d875c, everything under the shared verify lock:All 20 gate families derived by
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstackon the final HEAD are green, pluscheck:nul-bytesand
check:empty-changeset. Re-deriving after the changeset landed added threefamilies the first derivation did not name (
changeset-gate-self-tests,objectui-changeset,pm-half-states); those are included.Out of scope, deliberately
manifest") does not ride along, per the ruling.
CJS-first packages ship 97 undeclared
.d.mtsfiles, 15,357,733 B (14.65MiB), because their sibling
typeshands ESM consumers a CJS-flavoured.d.ts. It is measured here and not fixed here — it is a published-surfacechange in 38 more packages, and the third build row above shows it is not
independently safe.
ObjectKernel's nominal identity, which is what actually blocks core.The dedupe channel refused on both routes while this ran (repo-scoped REST 403,
ghabsent, MCPsearch_issuesrate-limited), so ⛔ nothing was filed blind;the three findings are handed to the PM in the dev report for filing.
Needs
needs:contract-review: 27 published manifests move at once, and the@objectstack/coreboundary is a maintainer call.Generated by Claude Code
Generated by Claude Code