Skip to content

v8.0.0-rc.19-dev.10

@wmadden-electric wmadden-electric tagged this 22 Oct 13:59
This PR moves each database's packages into one folder per database.
After it, everything Postgres needs is here:

```text
packages/3-targets/postgres/
  1-target/          @internal/target-postgres
  2-adapter/         @internal/adapter-postgres
  3-driver/          @internal/driver-postgres
  4-codec-testkit/   @internal/postgres-codec-testkit
  9-facade/          @internal/postgres
```

Before, the same five packages were spread over four folders:

| Package | Was in | Now in |
| --- | --- | --- |
| target pack | `packages/3-targets/3-targets/postgres/` |
`packages/3-targets/postgres/1-target/` |
| adapter | `packages/3-targets/6-adapters/postgres/` |
`packages/3-targets/postgres/2-adapter/` |
| driver | `packages/3-targets/7-drivers/postgres/` |
`packages/3-targets/postgres/3-driver/` |
| codec testkit |
`packages/3-targets/6-adapters/postgres-codec-testkit/` |
`packages/3-targets/postgres/4-codec-testkit/` |
| facade | `packages/3-extensions/postgres/` |
`packages/3-targets/postgres/9-facade/` |

SQLite moves the same way. Mongo moves from `packages/3-mongo-target/`
and `packages/3-extensions/mongo/` into `packages/3-targets/mongo/`. The
SQL family folder `packages/2-sql/` becomes `packages/2-sql-family/`, to
match `packages/2-mongo-family/`.

No package is renamed. `@internal/target-postgres` is still
`@internal/target-postgres`, so no import, published package or
generated file changes. Only folders move.

## Why we're doing this

Each database used to be spread over four folders. The target pack,
adapter and driver each sat in their own role folder (`3-targets/`,
`6-adapters/`, `7-drivers/`), and the facade sat in a fourth,
`3-extensions/`. Working on one database meant moving between all four.

The numbers 3, 6 and 7 looked like layers that the build checks, but
nothing checked them. `pnpm lint:deps` decides what each file may import
from globs in `architecture.config.json`, not from folders. The role
folders cost something and bought nothing.

Mongo already kept its packages together, but under a top-level folder
of its own, `3-mongo-target/`. Copying that for every database would add
a top-level folder per database.

So every database, in every family, now gets one folder under
`packages/3-targets/`. Inside it, the numbers give the dependency order:
the adapter imports the target pack, the facade imports the target pack,
adapter and driver, and the driver imports none of them. Adding a
database means adding one folder. MySQL and MariaDB are next, in the
MySQL proof-of-concept project
([`projects/mysql-poc/`](projects/mysql-poc/spec.md)). This PR lands
first so their PRs only add files.

ADR 278 records the decision.

## What changed, step by step

1. **The move.** Four commits do nothing but `git mv`: one for the
`2-sql` rename, then one each for Postgres, SQLite and Mongo. `git show
--stat -M <commit>` shows only renames.
2. **Paths.** One commit updates every file that names a folder:
`architecture.config.json`, the publish mapping in `shells.ts`,
`coverage.config.json`, `tsconfig.base.json`, scripts, tests, fixtures,
examples and READMEs. The facades and Mongo packages are one folder
deeper, so their relative paths gain a `../`. The publish mapping
produces exactly the same published entrypoints as before.
3. **Registration.** `architecture.config.json` must give every source
file a plane: shared, control (migration) or runtime. Postgres had
registered only six of its core files, so lint checked almost none of
its target. Every target now registers its files the same way. Doing
that showed some files filed as shared that are really control or
runtime code, so they moved:
- Control-only code went to `src/core/migrations/`. That covers the
SQLite control target, the schema verifiers, the control mutation
defaults and the Mongo control adapter.
- Runtime-only code went to a new `src/core/runtime/`. That covers the
SQLite runtime target, adapter and driver.
- The Postgres constraint-name helpers moved out of `migrations/`. They
are plain functions that shared code needs.
4. **Two new checks in `pnpm lint:deps`**, so the layout stays honest:
- Every source file under `packages/` must match exactly one glob
(`scripts/lint-architecture-registration.mjs`). It found 68 files that
matched none, and they are now registered.
- A package in a database folder may depend only on lower-numbered
packages of the same database (`scripts/lint-target-layering.mjs`). This
replaces the separate layering test each target had, so a new database
is covered without writing a test. I checked that it fails when a
forbidden import is added.
5. **Upgrade notes.** The upgrade-coverage check used to require
extension notes for changes under `packages/3-extensions/`, where the
facades lived. It now also covers `packages/3-targets/*/9-facade/`. Two
pending declarations say this PR needs no user action.
6. **Docs.** The layout docs, the directory-layout rule and the
onboarding recipe for adding a target are rewritten. ADR 278 is new, ADR
140 points to it, and other ADRs' paths are updated.

## A gap this found, which this PR does not fix

`pnpm lint:deps` has never checked an import from one package to
another. A workspace import such as `@internal/target-sqlite` resolves
to the package's `dist/` folder, which the config excludes, so the
import disappears. Only imports between files of the same package are
checked. That is true on `main` today.

Reading the imports by hand found seven plane violations the check
should have caught. One example: the adapter's `control-codecs.ts` is
control code that imports `@internal/sql-runtime`. Fixing the resolution
will surface more, so it is its own issue, TML-3700. Until then, the
ordering check in step 4 is what checks dependencies between one
database's packages.

## Effect on other open PRs

41 open PRs touch the moved folders. Git follows renames when it merges,
so a PR that only edits existing files merges as before. Trial merges
into six open PRs added 0 to 3 conflicts to typical ones. Most of those
are files a PR adds inside a moved folder, which need accepting git's
suggested new location and sometimes fixing an import. We resolve
conflicts on our own PRs as they come up.

## Verification

All of these pass locally:

- `pnpm build`, `pnpm typecheck`, `pnpm lint`, `pnpm lint:deps`
- `pnpm lint:publishability`, `pnpm lint:docs`, `pnpm fixtures:check`
- `pnpm test:scripts` (623 tests)
- `pnpm check:upgrade-coverage --mode pr`
- the tests of every moved package
- the one integration test that read the old folder

The full integration suite runs in the merge queue.

## Alternatives considered

- **Keep the role folders.** They check nothing and spread each database
over four folders.
- **Copy Mongo's layout for every database**, such as
`packages/3-postgres-target/1-postgres-target/`. It adds a top-level
folder per database and repeats the database's name in every folder.
- **Move only new databases, and leave Postgres, SQLite and Mongo for
later.** That avoids conflicts with open PRs now, but leaves two layouts
side by side. The move only gets more expensive as more code lands.
- **Move single-database extension packs, such as pgvector, under their
database.** They use the same extension points a third-party pack would,
and keeping them in `3-extensions/` shows that.

Refs: TML-3693

Agent: pangloss-22

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Assets 2
Loading