fix(publish): declarations must name only what a consumer will have (TML-3125) - #29862
Conversation
|
|
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yml Review profile: CHILL Plan: Pro Plus Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (12)
📝 WalkthroughWalkthroughThe publish check validates dependencies referenced by published declaration files. Operation registries support ChangesDeclaration dependency validation
List-targeted operation self specifications
Runtime test client decoupling
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant PackageTarball
participant runCheck
participant DeclarationParser
participant DependencyResolver
participant CheckOutput
PackageTarball->>runCheck: provide packed declaration files
runCheck->>DeclarationParser: extract module specifiers
DeclarationParser->>DependencyResolver: resolve package and type dependencies
DependencyResolver-->>runCheck: return declaration violations
runCheck->>CheckOutput: include violations in diagnostics and JSON output
Possibly related PRs
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
size-limit report 📦
|
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/1-framework/1-core/operations/src/index.ts (1)
57-75: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winValidate mutual exclusivity between
manyandcodecId/traitsat runtime.The
SelfSpectype enforces thatmanyis mutually exclusive withcodecIdandtraitsthroughneverfields. The runtime check only rejectshasCodecId && hasTraits. It does not rejecthasCodecId && targetsManyorhasTraits && targetsMany.A descriptor built from untyped JS, or from a generic
Tthat widensself, can pass{ codecId: 'x', many: true }without triggering a contract error. Add the missing pairwise checks so the runtime validation matches the type contract.🛠️ Proposed fix
if (hasCodecId && hasTraits) { throw contractError( 'CONTRACT.PACK_CONTRIBUTION_INVALID', `Operation "${name}" self has both codecId and traits`, { meta: { operation: name } }, ); } + if ((hasCodecId || hasTraits) && targetsMany) { + throw contractError( + 'CONTRACT.PACK_CONTRIBUTION_INVALID', + `Operation "${name}" self has many together with codecId or traits`, + { meta: { operation: name } }, + ); + }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/1-framework/1-core/operations/src/index.ts` around lines 57 - 75, Update the runtime validation in the descriptor.self block to reject every pairwise conflict among hasCodecId, hasTraits, and targetsMany: codecId with traits, codecId with many, and traits with many. Preserve the existing CONTRACT.PACK_CONTRIBUTION_INVALID error behavior and operation metadata for each invalid combination.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@scripts/check-publish-deps.mjs`:
- Around line 240-245: Update publishedEntryRoots() to retain tarball-root entry
points such as ./index.d.ts instead of filtering them out, using an explicit
root representation. Adjust findDeclarationDepViolations() so its
entryRoots.has(file.split('/')[0]) lookup recognizes declaration files at the
package root, while preserving existing subdirectory-root handling.
---
Outside diff comments:
In `@packages/1-framework/1-core/operations/src/index.ts`:
- Around line 57-75: Update the runtime validation in the descriptor.self block
to reject every pairwise conflict among hasCodecId, hasTraits, and targetsMany:
codecId with traits, codecId with many, and traits with many. Preserve the
existing CONTRACT.PACK_CONTRIBUTION_INVALID error behavior and operation
metadata for each invalid combination.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro Plus
Run ID: 5a3691c1-f950-4c96-8869-1c520d322bc9
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (9)
package.jsonpackages/1-framework/1-core/operations/src/index.tspackages/1-framework/1-core/operations/test/operations-registry.test.tspackages/2-sql/4-lanes/query-builder/package.jsonpackages/3-extensions/postgres/package.jsonpackages/3-extensions/supabase/package.jsonpackages/3-targets/7-drivers/postgres/package.jsonscripts/check-publish-deps-declarations.test.mjsscripts/check-publish-deps.mjs
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@skills/extension-author/prisma-next-extension-upgrade/upgrades/0.16-to-0.17/instructions.md`:
- Line 524: Update the fenced diagnostic block at the documented location to
specify the text language by adding the text fence tag, while preserving the
block’s existing contents.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro Plus
Run ID: 846e187e-a197-48aa-a68f-c9b012f386e4
📒 Files selected for processing (1)
skills/extension-author/prisma-next-extension-upgrade/upgrades/0.16-to-0.17/instructions.md
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/2-sql/5-runtime/test/utils.ts (1)
537-538: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick winRemove the remaining re-export from this module.
packages/2-sql/5-runtime/test/utils.tsretains a decode-helper re-export outside anexports/folder. Import the helper at its use sites or move the barrel to anexports/folder.As per coding guidelines: “Do not re-export from one file in another, except in
exports/folders.”🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/2-sql/5-runtime/test/utils.ts` around lines 537 - 538, Remove the decode-helper re-export from the test utilities module and update its use sites to import the helper from its defining module, or relocate the barrel into an exports/ folder. Ensure no re-export remains outside an exports/ directory.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@packages/2-sql/5-runtime/test/utils.ts`:
- Around line 537-538: Remove the decode-helper re-export from the test
utilities module and update its use sites to import the helper from its defining
module, or relocate the barrel into an exports/ folder. Ensure no re-export
remains outside an exports/ directory.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro Plus
Run ID: 901dd6b5-7ac8-478d-b1ad-ea1fd27227be
📒 Files selected for processing (4)
packages/2-sql/5-runtime/test/utils.tspackages/2-sql/5-runtime/tsdown.config.tsscripts/check-publish-deps-declarations.test.mjsscripts/check-publish-deps.mjs
💤 Files with no reviewable changes (1)
- packages/2-sql/5-runtime/tsdown.config.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- scripts/check-publish-deps-declarations.test.mjs
`@prisma-next/sql-operations` declares `SqlOperationEntry = QueryOperationTypeEntry` and passes it to the framework generics `OperationDescriptor<T extends OperationEntry>`, `OperationDescriptors<T>` and `OperationRegistry<T>`. The SQL self spec has three variants — codec identity, codec traits, and list-typed (`many`) fields — while the framework `SelfSpec` had only the first two, so the type argument did not satisfy its constraint and the emitted `dist/index.d.mts` failed to compile for anyone building with `exactOptionalPropertyTypes` off (the default) and `skipLibCheck: false`. In the repo the mismatch is invisible because `packages/0-config/tsconfig/base.json` sets `exactOptionalPropertyTypes: true`, which keeps `traits?: never` as `never` (assignable to `readonly string[]`) instead of widening it to `undefined`. The type mismatch mirrored a real runtime one: `createOperationRegistry().register()` rejected any `self` without `codecId` or `traits`, so registering a list-targeted operation threw `CONTRACT.PACK_CONTRIBUTION_INVALID`. Adds the list variant to the framework `SelfSpec` and accepts it in `register()`. `many` is already family-blind framework vocabulary (`domain-types.ts`, the emitter); families refine the variant structurally, which is how SQL carries `elementTraits`. Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
`pg` carries no types of its own — they come from `@types/pg`. Three
publishable packages re-export `pg` types from their `.d.mts` files:
@prisma-next/driver-postgres dist/control.d.mts, dist/runtime.d.mts
@prisma-next/postgres dist/postgres-*.d.mts
@prisma-next/extension-supabase dist/runtime.d.mts
All three had `pg` in `dependencies` but `@types/pg` only in
`devDependencies`, which consumers never install. Anyone compiling against
the published declarations with `skipLibCheck: false` got TS7016
("Could not find a declaration file for module 'pg'").
Moves `@types/pg` into `dependencies` for those three. `@types/pg-cursor`
stays a devDependency: `pg-cursor` is only referenced from `dist/*.mjs`,
never from a declaration, so consumers never need its types.
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>
Sweep of the class the `@types/pg` fix belongs to: a publishable package whose shipped declarations name a module the consumer will not have. Checked all 65 publishable packages by extracting the module specifiers from every declaration file in their published entry-point tree with TypeScript's own file preprocessor. `@prisma-next/sql-lane-query-builder` declared no `dependencies` at all. Its public API is written in `Contract` / `SqlStorage` types, but the packages that own those were devDependencies, and tsdown bundles devDependency types instead of importing them — so `dist/index.d.mts` inlined 1211 lines of foreign declarations and carried bare side-effect imports of `arktype` and `@standard-schema/spec`, neither of which a consumer installs. Moves `@prisma-next/contract` and `@prisma-next/sql-contract` into `dependencies` (the declaration now imports them, 1211 lines → 231, and the stray `arktype` import is gone) and declares `@standard-schema/spec`, which the emitter still names. Depending on the packages whose types it re-exposes also puts the package back under the `@prisma-next/*` exact-pin rule, so a consumer gets one identity for `Contract` rather than a structurally-similar inlined copy. One instance is not fixed here. `@prisma-next/sql-runtime` publishes a `./test/utils` subpath whose graph reaches `@prisma-next/test-utils`, a `private: true` package that is never published — so the subpath is already broken for external consumers at runtime, and no manifest edit fixes it. Removing it from the published surface is a public-surface decision; the next commit records it as a named exemption so it stays visible. Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
Both defects in this branch had the same shape: a package compiled in the workspace, where pnpm has every devDependency linked, and failed for a consumer who builds with `skipLibCheck: false`. Nothing caught either one. Adds a third rule to `check-publish-deps`. For every publishable package it already packs the exact tarball that ships; this reads the declaration files in that tarball's published entry-point tree and asserts every module specifier they name resolves for a consumer — the package is in `dependencies` / `peerDependencies` / `optionalDependencies`, and when it carries no types of its own, its `@types/*` companion is declared too. `check-publish-deps` is the right home: `publish.yml` runs `pnpm build` immediately before it, so `dist/` exists, and the tarball is what consumers actually get rather than a reconstruction of it. Specifiers come from TypeScript's own file preprocessor, not a regex — an earlier regex pass read `pg-pool` prose in a doc comment as an import of `pg`. Scope is the entry-point tree (`exports` / `types` targets), because tarballs also ship `src/` for declaration maps and nothing in a consumer's module graph reaches those files. Verified against both defects: reintroducing them makes the gate report `dist/control.d.mts names "pg" — declare "@types/pg"` and `dist/index.d.mts names "arktype" — declare "arktype"`. Carries one exemption, printed on every run so it cannot go quiet: `@prisma-next/sql-runtime`'s `./test/utils` subpath reaches the private, never-published `@prisma-next/test-utils`. Tests land in a `node --test` file wired into `test:scripts`. The existing `scripts/check-publish-deps.test.mjs` is vitest-style but no runner picks it up — the root vitest config only globs `packages/**/vitest.config.ts` — so tests added there would never execute. Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
The `@types/pg` fix touches `packages/3-extensions/`, so `check-upgrade-coverage`
requires this PR to record its intent in the 0.16 → 0.17 extension-author
instructions.
The change is not a no-op for extension authors, and the reason is the
workaround it removes. `@prisma-next/postgres` and `@prisma-next/extension-supabase`
re-export `pg` types from their declarations but only devDepended on `@types/pg`,
so authors had to add `@types/pg` themselves to compile. Now that those packages
ship it, an author who keeps their own entry at a different version has two
`@types/pg` copies in the tree — and `pg` has no types of its own, so `pg.Client`
and `pg.Pool` get two identities.
Verified against a scratch consumer built from the packed tarballs: with
`@types/pg@8.11.0` alongside the shipped `8.20.0`, passing a `pg.Client` into
`new PostgresControlDriver(client)` fails with
Argument of type 'Client' is not assignable to parameter of type 'Client'.
Type 'Client' is missing the following properties from type 'Client':
connection, setTypeParser, getTypeParser
and aligning the version compiles clean. So the entry states an action —
drop the entry and take it transitively, or pin it to what
`@prisma-next/postgres` depends on — rather than a courtesy note, which the
authoring skill treats as a defect.
Appends one `changes[]` entry and one body section; the existing entries are
untouched.
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>
The exemption was the wrong outcome — it made the guard document a defect
instead of catching it. Fixes the defect and deletes the carve-out.
`@prisma-next/sql-runtime/test/utils` is published, and its output named
`@prisma-next/test-utils`, which is `private: true` and never reaches the
registry. Two separate reasons, both removed:
Re-export block. It forwarded `collectAsync`, `createDevDatabase`,
`DevDatabase`, `teardownTestDatabase`, and `withClient`. Dead — all twelve
in-repo importers of this subpath already take those symbols straight from
`@prisma-next/test-utils` and pull only sql-runtime-specific ones
(`createTestRuntime`, `createStubAdapter`, `createTestContext`,
`seedTestMarker`, `decodeRow`) from here. Dropped; no call site changed.
Three internal helpers. Inlined `collectAsync` and `drainAsyncIterable`
(eleven lines, no dependencies) and a trimmed `applicationDomainOf` (the
`valueObjects` branch was unused here). I tried bundling first — dropping
`@prisma-next/test-utils` from tsdown `external` — and rejected it on
evidence: tree-shaking removed the values but left `import "@prisma/dev"`
in the emitted `.mjs`, trading one undeclared specifier for another. The
inline is deterministic and does not depend on tree-shaking behaviour.
That left `pg`, which the subpath had all along: `setupTestDatabase`,
`seedTestMarker`, and `writeTestContractMarker` were typed against
`pg.Client`, and `pg` is a devDependency here. Declaring it was not an
option — this is the target-agnostic SQL runtime, which must not depend on
a Postgres driver. All three only ever call `client.query(...)`, so they
now take a structural `TestSqlClient`; `setupTestDatabase` is generic over
it so callers passing `pg.Client`-typed callbacks still infer correctly.
The `external` list is now empty and gone.
Every specifier left in the packed `dist/test/utils.{mjs,d.mts}` is a
declared dependency: @prisma-next/{contract,framework-components,ids,
sql-contract,sql-relational-core,utils}, arktype, node:crypto.
Exemption plumbing fully removed, not emptied: the constant, the check-site
lookup, `reportExemptions` and both call sites, and the test that asserted
the carve-out — replaced by one asserting a private package is flagged like
any other undeclared module.
Also fixes a literal NUL byte I had left in a template string in
check-publish-deps.mjs. It made the file read as binary, so plain `grep`
silently returned no matches against it — which is how the leftover
`reportExemptions` call sites nearly survived this cleanup.
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>
`publishedEntryRoots()` derived each root by taking the leading path segment
of an `exports` / `types` / `main` / `module` target and keeping it only when
it looked like a directory. An entry with no directory component — a package
whose `types` is `./index.d.ts` — produced no root at all, and the lookup
`entryRoots.has(file.split("/")[0])` then matched nothing, so every
declaration in that tarball went unchecked and the gate reported OK having
inspected no files.
No package in the repo publishes that way today, which is precisely the
danger: the failure mode is silence, so the first package with root-level
entries would get a green check that read nothing. Same shape as the NUL
byte earlier in this branch — a verification that passes because it looked
at nothing.
A root-level entry is now represented explicitly as `PACKAGE_ROOT` ("."),
and `declarationRoot()` maps a declaration with no directory component to
the same value, so root and subdirectory entries compare the same way and
mixed packages keep both. The `package.json` self-reference is skipped: it
is a manifest, not a code entry, so it does not put the root in scope on
its own.
Three tests, each failing before the fix: `publishedEntryRoots` yields `.`
for a root-level entry; a fixture package with `types: "./index.d.ts"` and
an `index.d.ts` naming an undeclared `arktype` is flagged; and a package
publishing from both root and `dist/` reports violations in both while
still ignoring `src/`. The last one pins that the fix widens scope without
losing the subdirectory scoping.
Also tags the fenced error-message block in the 0.16-to-0.17 extension
instructions as `text` (markdownlint MD040).
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>
c022d8a to
9e36363
Compare
Fifteen conflicts. Five are files main deleted — the `functional-indexes` project write-ups, whose content became ADR 243 — and this branch had only renamed strings inside them, so the deletion stands. Seven took main's content and then the rename. Three needed more. `scripts/check-publish-deps.mjs` — main's declaration-dependency rule is taken whole. Its scope literals are renamed, and that matters more than it reads: the exact-pin check keys on the scope by name, so left as `@prisma-next/*` it would have matched nothing and passed vacuously on this tree — the same defect found in the dist scan a merge ago. The new rule 3 is scope-agnostic (it reads package names out of specifiers), so it needed nothing. `package.json` — taking main's wholesale dropped this branch's two checks and three of its script-test entries, and left main's `--filter @internal/e2e-tests` against packages this branch renamed to bare names. Reconciled key by key rather than by side. The extension-author upgrade notes — main appended `postgres-packages-now-ship- types-pg` under the old directory name while this branch renamed the directory and carries an entry of its own. Both survive in the renamed location, 27 entries total, and both keep their `@prisma-next` literals: each tells a reader what their pack depends on today, which is the name they still have. The `@types/pg` move from #29862 pulls three shells with it — `orm-target-postgres`, `orm-postgres`, `orm-extension-supabase` each carry a package that now has it as a runtime dependency, and the shell manifests validate against exactly that. ADR 243 names the old scope and is a dated decision record like ADR 234, so the legacy-name check's existing allowance covers it with no change. Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
Fifteen conflicts. Five are files main deleted — the `functional-indexes` project write-ups, whose content became ADR 243 — and this branch had only renamed strings inside them, so the deletion stands. Seven took main's content and then the rename. Three needed more. `scripts/check-publish-deps.mjs` — main's declaration-dependency rule is taken whole. Its scope literals are renamed, and that matters more than it reads: the exact-pin check keys on the scope by name, so left as `@prisma-next/*` it would have matched nothing and passed vacuously on this tree — the same defect found in the dist scan a merge ago. The new rule 3 is scope-agnostic (it reads package names out of specifiers), so it needed nothing. `package.json` — taking main's wholesale dropped this branch's two checks and three of its script-test entries, and left main's `--filter @internal/e2e-tests` against packages this branch renamed to bare names. Reconciled key by key rather than by side. The extension-author upgrade notes — main appended `postgres-packages-now-ship- types-pg` under the old directory name while this branch renamed the directory and carries an entry of its own. Both survive in the renamed location, 27 entries total, and both keep their `@prisma-next` literals: each tells a reader what their pack depends on today, which is the name they still have. The `@types/pg` move from #29862 pulls three shells with it — `orm-target-postgres`, `orm-postgres`, `orm-extension-supabase` each carry a package that now has it as a runtime dependency, and the shell manifests validate against exactly that. ADR 243 names the old scope and is a dated decision record like ADR 234, so the legacy-name check's existing allowance covers it with no change. Alongside the merge, the language server stops being published surface. Editors reach it by spawning `prisma-next lsp` and speaking the protocol over stdio, never by importing a module — the split from the CLI was for code organization, not API — and the published entrypoint's only consumer in the tree was a README mention. `@prisma/orm-toolchain` goes from 54 export subpaths to 53. The package is unchanged and still bundled: the CLI depends on it, so the shell carries the code. Expressing that took one addition to the shell map — a package can now be mapped to a shell without publishing entrypoints, which is what "bundled here, named nowhere" needs to say. ADR 242 listed language-server distribution as a deferred decision; that entry now records the resolution. Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
Overview
Two defects that are invisible in-repo but hit any consumer compiling against the published declarations, plus a sweep of the defect class each belongs to and a publish-time guard so neither can recur.
Based on
main, independent of the ADR 242 stack. Refs: TML-3125.Defect 1 — an operation's
selfcould not target list-typed fields@prisma-next/sql-operationspassesQueryOperationTypeEntryinto the framework generics constrained byOperationEntry. The SQL self spec has three variants — codec identity, codec traits, and list-typed (many) fields — while the frameworkSelfSpechad only the first two, so the type argument never satisfied its constraint (TS2344).What hides it in-repo is
exactOptionalPropertyTypes: true, notskipLibCheck. With the flag on,traits?: neverstaysnever, which is assignable toreadonly string[]. With it off — the default, so what every consumer gets — it widens toundefinedand the union member no longer matches. Compiling the package with--exactOptionalPropertyTypes falsereproduces all three errors in the source file, withskipLibCheckuntouched.It was also a runtime bug.
createOperationRegistry().register()threwCONTRACT.PACK_CONTRIBUTION_INVALIDfor anyselfwithoutcodecIdortraits, so registering a list-targeted operation failed outright. That is what the new test pins — an in-repo type test cannot fail here, because the flag masks the relation either way.The fix adds the list variant to the framework
SelfSpecand accepts it inregister(). This widens an exported type in@prisma-next/operations: additive, a new accepted input shape nothing currently produces, with two readers in the repo (both handled). The alternatives were worse — making the framework'sselfopaque would delete its early-error validation, and dropping the SQL variant would leave the framework unable to express list dispatch at all.A workspace-wide compile with
exactOptionalPropertyTypes: falseconfirms this has no siblings: 2 errors total, both stale-distTS2307, zeroTS2344.Defect 2 — declarations naming modules the consumer will not have
pgships no types of its own. Three publishable packages re-exportpgtypes from.d.mtswhile declaring@types/pgonly indevDependencies, which consumers never install.The class sweep
The class: a publishable package whose shipped declarations name a module the consumer will not have — either the package is absent from consumer-installed dependency fields, or it is present but ships no types and its
@types/*companion is missing.Swept all 65 publishable packages, extracting specifiers with TypeScript's
preProcessFilerather than a regex — the regex lied in both directions, reading a'release'event mentioned in a doc comment as an import ofpg, and an earlierdist/index.d.mts-only pass missed a real finding underdist/test/.driver-postgres@types/pg(devDep)dependenciespostgres@types/pg(devDep)dependenciesextension-supabase@types/pg(devDep)dependenciessql-lane-query-builderdependenciesblock at all; declarations namedarktypeand@standard-schema/specThe query-builder root cause is worth knowing: tsdown bundles devDependency types instead of importing them. With
@prisma-next/contractandsql-contractas devDeps, their declarations were inlined — 1211 lines of foreign types — dragging in bare side-effect imports the package never declared. Moving them todependenciesmade them external: the declaration dropped to 231 lines and the strayarktypeimport disappeared on its own. It also restores the exact-pin rule, so a consumer gets one identity forContractrather than a structurally-similar inlined copy.The guard
scripts/check-publish-deps.mjsgains a third rule beside its leak and pin checks: every module specifier named by a declaration in the tarball's published entry-point tree must resolve for a consumer. This is the right home —publish.ymlrunspnpm buildimmediately before it, and it already packs the exact tarball that ships.Scoped to the entry-point tree rather than the whole tarball, since tarballs also ship
src/for declaration maps; roots are derived fromexports/types/main/modulerather than hardcodingdist/. Proven non-vacuous by reintroducing both defects and confirming the gate names each one.23 tests in
check-publish-deps-declarations.test.mjs, wired intotest:scripts, including the doc-comment case that broke the first scanner.Known exemption, recorded not guessed
@prisma-next/sql-runtimepublishes a./test/utilssubpath whose module graph reaches@prisma-next/test-utils, which isprivate: trueand never published. That subpath is already broken for external consumers at runtime, not merely at type level, and no manifest edit fixes it — declaring a private package would put a nonexistent version into the shipped manifest and break installs for everyone. Every real fix either removes a published export or moves code across a package boundary. Recorded as a named exemption and filed separately with the options costed.Verification
pnpm build,typecheck:packages(130),test:scripts(251),lint,lint:deps,lint:manifests,check:publish-deps,check:clean-tree,lint:castsandlint:throws(delta 0) all green.test:packages13945 passed with one known 100ms-timeout flake that passes in isolation.Consumer compile from the final tree: 12 tarballs repacked, installed outside the workspace,
nodenext+skipLibCheck: false— 5 errors before, exit 0 after. Thepgfix was confirmed to resolve through the isolated dependency path rather than by hoisting accident.Incidental finding
scripts/check-publish-deps.test.mjsis dead code — vitest-style, but the root vitest config only globspackages/**/vitest.config.tsandtest:scriptsdoes not list it, so nothing runs it. Pre-existing; left alone, worth a separate cleanup.🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
many: true.Bug Fixes
Quality Improvements
Documentation