| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
chore(release): bump to 8.0.0-rc.12 (#30395) ## Release: 8.0.0-rc.11 → 8.0.0-rc.12 This is the release PR described in [docs/oss/versioning.md](https://github.com/prisma/orm/blob/main/docs/oss/versioning.md). It bumps every workspace package to 8.0.0-rc.12 and moves the Prisma dependencies to their latest versions. **Merging this PR ships the release.** The push to `main` carries the new root `version`. The `Publish to npm` workflow then publishes 8.0.0-rc.12 under `latest` and creates a pre-release GitHub Release from the notes file. ## Review these first - [docs/releases/v8.0.0-rc.12.md](https://github.com/prisma/orm/blob/release/8.0.0-rc.12/docs/releases/v8.0.0-rc.12.md): the release notes, which become the GitHub Release body. The same entry is at the top of `CHANGELOG.md`. - The upgrade guides for [apps](https://github.com/prisma/orm/blob/release/8.0.0-rc.12/skills/prisma-8/upgrading/app/upgrades/8.0.0-rc.11-to-8.0.0-rc.12/instructions.md) and [extensions](https://github.com/prisma/orm/blob/release/8.0.0-rc.12/skills/prisma-8/upgrading/extension/upgrades/8.0.0-rc.11-to-8.0.0-rc.12/instructions.md). They merge the 24 pending fragments. The original fragments are moved unchanged to `upgrade-instructions/releases/8.0.0-rc.11-to-8.0.0-rc.12/sources/`. - Four guide entries have no fragment behind them. The `migration new` default and its removed error codes (#30389) had no guide entry. Neither did the PSL parser API changes (#30312, #30344, #30335, #30379). I wrote those entries while preparing the release. - Where fragments contradicted later code, the guide follows the code. Examples: the Supabase storage hash, `voidParamsSchema`, and quoted defaults printed by `infer`. ## Dependency updates | Package | From | To | Where | | --- | --- | --- | --- | | `@prisma/cli-engine` | 0.4.0 | 0.6.1 | examples, test fixtures, apps (the toolchain packages were already on 0.6.1 from #30372) | | `@prisma/dev` | 0.25.1 | 0.25.2 | the workspace catalog | | `@prisma/compute-sdk` | ^0.39.0 | ^0.43.0 | `apps/telemetry-backend` | | `@prisma/management-api-sdk` | ^1.56.0 | ^1.76.0 | `apps/telemetry-backend` | compute-sdk 0.43 renames "service" to "app" and "version" to "deployment". The telemetry deploy script now uses the new names. Both SDK versions call `/v1/apps/{appId}`, so the ID stored in the existing `TELEMETRY_DEPLOY_SERVICE_ID` secret is still correct. The app's typecheck now includes `scripts/`, so it catches the next SDK rename. The repo does not depend on `@prisma/composer`. ## Fixes needed to publish - **The publish workflow has failed on `main` since #30372.** `check:conformance` called the `orm` config validator as `validate(value)`. Engine 0.6 always calls `validate(value, provenance)`, and the validator reads `provenance.files`, so it threw on every input. The check now passes the same provenance the engine would. The prisma-cli copy of this check already does this. - `set-version` rewrote `workspace:@internal/cli@<version>` to `workspace:<version>`, dropping the alias. The prisma7-adoption example uses that alias. This is the first bump since the alias was added. - `lint:legacy-name` and the `add-model-map` test pointed at the pending fragment paths. They now point at the archived sources. ## Verification Passed locally: - `pnpm build` - `pnpm typecheck` - `pnpm lint` - `pnpm test:scripts` (563 tests) - `pnpm check:conformance` - `pnpm check:publish-deps` - `pnpm check:upgrade-coverage`, in both publish and PR mode - `pnpm check:release-notes`, in both publish and PR mode - `pnpm lint:legacy-name` - `pnpm lint:skills` - `pnpm test:packages`: all 18,196 tests passed Not covered locally, left to CI: - Three `test:packages` suites install packed tarballs from the registry. This machine's pnpm refuses `@vercel/detect-agent@1.2.5` because it has no provenance. CI passed the same suites on #30390. - `prisma-8-cloudflare-worker` needs a local Hyperdrive database. - The telemetry backend tests need Node 24.16 with `Temporal`. This machine has 24.13. - `fixtures:check` needs Postgres. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added PostgreSQL full-text search, multi-file schemas, prepared ORM reads and aggregates, and conflict-skipping options for bulk creation. * Added support for using a Prisma 7 schema as the contract source, JavaScript `Date` timestamps on PostgreSQL, editor support for attribute arguments, and per-finding diagnostics. * **Breaking Changes** * Prisma 8 schema files now require `// use prisma-8` on the first line; unmapped models use their names verbatim for table names. * Replace `dbgenerated(...)` with SQL tagged literals. Defaults must be valid for their column types, creation timestamps use the application clock, and native PostgreSQL enums no longer support text operations. * Config naming and path resolution, migration starting points, and extension contracts have changed. * **Bug Fixes** * Improved migration checks and branching warnings, contract generation and inference, default verification, and type checking. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- 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> | 10 天前 | |
ci: combine package tests and coverage (#30082) ## Linked issue n/a — this infrastructure migration has no Linear ticket. ## At a glance ```json "coverage:packages": "turbo run build --filter='!./examples/**' --filter='!./test/**' && vitest run --coverage", "coverage:report": "node scripts/coverage-report.mjs" ``` One root Vitest invocation now runs package tests and collects coverage, replacing the duplicated package-test and coverage CI jobs. ## Decision This PR ships three related changes: 1. Package tests and package coverage run together in one root Vitest multi-project invocation on Vitest `5.0.0-rc.2`. 2. Each package owns its complete coverage policy in an adjacent `coverage.config.json`, while root composition and post-processing preserve package thresholds and time-limited warning-only exceptions. 3. The obsolete, type-test-only SQL lane query-builder package and its public facade export are removed instead of retaining a permanently unmeasurable 95% runtime-coverage policy. ## Reviewer notes - The broad config diff is mostly moving existing coverage include/exclude/threshold blocks from `vitest.config.ts` into adjacent JSON policies and removing now-redundant package coverage scripts. - Vitest 5 removes `describe.sequential`; affected suites now use `{ concurrent: false }`. Compile-only `.test-d.ts` suites also declare compile-time test cases so Vitest 5 recognizes them. - `examples/prisma-8-cloudflare-worker` intentionally remains on Vitest 4 because `@cloudflare/vitest-pool-workers@0.20.3` requires Vitest 4 peers. - Eight existing package coverage deficits remain visible as active, non-blocking warning-only entries. Expired warnings and ordinary threshold failures still block CI. ## How it fits together 1. `scripts/coverage-config.js` discovers and validates package policies deterministically, rebases package globs to the repository root, and composes process-wide V8 collection settings. 2. The root `vitest.config.ts` references every package project and applies the composed coverage settings to a single test process. 3. `scripts/coverage-report.mjs` reads the root `coverage/coverage-final.json`, attributes files to their owning package, calculates all four metrics, and enforces each package's policy and warning expiry. 4. `.github/workflows/ci.yml` runs `pnpm coverage:packages` in the test job, reports package coverage even when collection finds a test failure, and removes the standalone coverage job. Test failures remain blocking. 5. Vitest 5 compatibility updates keep type tests, sequential suites, and CLI module mocks deterministic under the new runner behavior. ## Behavior changes & evidence - **Package tests execute once in CI while still producing coverage.** The combined command and workflow live in [`package.json`](package.json) and [`.github/workflows/ci.yml`](.github/workflows/ci.yml); [`scripts/coverage-config.test.mjs`](scripts/coverage-config.test.mjs) guards the single-run workflow shape. - **Coverage ownership remains package-local and threshold enforcement remains package-aware.** Composition is implemented in [`scripts/coverage-config.js`](scripts/coverage-config.js), reporting in [`scripts/coverage-report.mjs`](scripts/coverage-report.mjs), and exercised by [`scripts/coverage-report.test.mjs`](scripts/coverage-report.test.mjs). - **Vitest 5 runs the workspace without the previous V8 merge bottleneck.** The workspace pins are in [`package.json`](package.json) and [`pnpm-lock.yaml`](pnpm-lock.yaml); representative compatibility fixes are covered by [`packages/1-framework/3-tooling/cli/test/migration-cli.test.ts`](packages/1-framework/3-tooling/cli/test/migration-cli.test.ts) and the migrated type-test suites. - **The obsolete SQL lane query-builder is no longer published.** Its package is removed, along with the facade dependency/export in [`packages/9-public/@prisma/orm-family-sql/package.json`](packages/9-public/@prisma/orm-family-sql/package.json) and publish-surface mapping in [`packages/0-shared/publish-surface/src/shells.ts`](packages/0-shared/publish-surface/src/shells.ts). ## Compatibility / migration / risk This is a pre-1.0 breaking cleanup: `@internal/sql-lane-query-builder` and `@prisma/orm-family-sql/lane-query-builder` are removed. Repository references and generated facade wiring were removed together, and the public SQL family shell rebuilds without them. Coverage semantics remain package-specific; only orchestration and report aggregation change. ## Testing performed - `CI=true TEST_TIMEOUT_MULTIPLIER=2 pnpm coverage:packages` — 1,155 files passed; 15,311 tests passed, 3 expected failures, no type errors - `pnpm coverage:report` — 69 package policies, 0 blocking failures, 8 active warnings, 0 expired warnings - `pnpm test:scripts` — 476 tests passed - `pnpm lint:deps` - `pnpm lint:manifests` - `pnpm build --filter=@prisma/orm-family-sql...` - Publish-surface tests and typecheck — 56 tests passed - Focused package tests/typechecks for CLI, Mongo runtime, SQL ORM client, SQLite codec testkit, integration tests, examples, and shell tarballs - `pnpm install --frozen-lockfile --ignore-scripts` - Targeted Biome checks and `git diff --check` ## Skill update n/a — the removed prototype query-builder export was not referenced by any user-facing skill; its package, public README, architecture docs, and publish surface were updated directly. ## Alternatives considered - **Keep Vitest 4 and optimize around it:** the single V8 run remained CPU-bound for more than 37 minutes because the relevant V8 merge optimization is only available in Vitest 5; the Vitest 4 backport was not merged. - **Switch to Istanbul coverage:** benchmarking was slower and introduced CLI language-server instrumentation timeouts, so V8 remains the provider. - **Run packages sequentially:** this preserves policy isolation but repeats runner startup and cannot eliminate duplicate test execution in CI; root collection plus package-aware post-processing keeps policy ownership without that cost. ## Checklist - [x] All commits are signed off (`git commit -s`) per the DCO. - [x] I read `CONTRIBUTING.md` and the change is scoped to one logical concern. - [x] Tests are updated. - [ ] The PR title is in `TML-NNNN: <sentence-case title>` form — no Linear ticket exists, so this uses the conventional commit title required by `CONTRIBUTING.md`. - [x] The **Skill update** section is filled in. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **Breaking Changes** - Removed the SQL lane query-builder package and its public package export. - Updated SQL documentation and package entrypoint references. - **Testing & Quality** - Centralized package coverage reporting with package-specific thresholds, exclusions, and warning policies. - Improved coverage validation, threshold reporting, and CI integration. - Updated serialized integration-test execution for compatibility with the current test runner. - **Documentation** - Expanded testing guidance for package coverage workflows and CI behavior. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: Steven McClankerton <tatarintsev@prisma.io> Co-authored-by: Steven McClankerton <tatarintsev@prisma.io> | 1 个月前 | |
fix(audit-notice): match workspace package paths on Windows separators The workspace-dependency filter used a POSIX-only `/packages/` regex when scanning `pnpm list` output. On Windows, `info.path` uses `\\`, so workspace packages would not be excluded and would be classified as redistributed runtime deps in the §4(d) NOTICE audit, skewing the result. Match either separator. The audit script is normally run from CI on Linux, but contributors may run it locally on any platform. | 4 个月前 | |
TML-3170: publish releases as 8.0.0-rc.N (latest tracks the RC line) (#29899) ## Linked issue Refs [TML-3170](https://linear.app/prisma-company/issue/TML-3170). Follow-up (not in this PR): the release PR that bumps the root version `0.17.0` → `8.0.0-rc.1` via the `publish-npm-version` skill, which is what actually starts the RC line. ## At a glance ```ts // scripts/determine-version-utils.ts export function computeNextReleaseVersion(current: string): string { assertCanonicalBase(current); const rcMatch = current.match(RC_BASE_PATTERN); if (rcMatch) { const [, major, minor, patch, rc] = rcMatch; return `${major}.${minor}.${patch}-rc.${Number(rc) + 1}`; } if (parseVersion(current).major < 8) { return '8.0.0-rc.1'; } return computeNextMinor(current); } ``` Before this PR, a release was always the next `0.x` minor. After it, the same merge-the-release-PR flow ships `8.0.0-rc.1`, `8.0.0-rc.2`, … — still under dist-tag `latest`, with the GitHub Release marked pre-release. ## Decision This PR moves the publish pipeline onto the v8 release-candidate line: 1. **Releases version as `8.0.0-rc.N`** — the counter advances on every release publish. "The v8 RC" is the product name; the number iterates freely underneath, so respins are cheap and there is no promise the final RC is literally `rc.1`. 2. **`latest` keeps tracking the newest release, RC included.** The package names this repo publishes (`@prisma/orm-*`, the platform packages, the `prisma-next` shim) have no pre-v8 stable audience to protect — a bare install is an early-access install. The frozen-`latest` concern belongs to the bare `prisma` package, which this repo does not publish — its v8 bin shim lives in [prisma/prisma-cli](https://github.com/prisma/prisma-cli), which publishes under `next` while v7 keeps `latest`. 3. **`pnpm bump-minor` becomes `pnpm bump-version`**, encoding the release policy: RC base → next RC; pre-8 stable base → `8.0.0-rc.1` (the one-time line transition); stable ≥ 8 → next minor. 4. **RC releases keep the full release ceremony**: committed release notes are required, and the GitHub Release is created with `--prerelease` whenever the version is on the RC line. The root version deliberately stays `0.17.0` in this PR. Until the transition release PR lands, the pipeline behaves exactly as before (verified by running the publish paths locally — see Testing performed). ## Notes for the reviewer - **No existing user is affected.** `latest` semantics are unchanged (newest release); lockfiles pin resolved versions, and existing `^0.x` ranges can never resolve to `8.0.0-rc.N` (pre-releases don't satisfy stable ranges), so `npm update` never moves anyone onto the RC line — only fresh installs get RCs once the transition PR lands. - **The canonical root-version shape widens** from "clean `X.Y.Z` only" to "clean `X.Y.Z` or `X.Y.Z-rc.N`" (`assertCanonicalBase`); anything else is still refused on `main`. - **`check-upgrade-coverage`'s publish baseline changed from "last stable tag" to "last release tag"** (`v*-rc.N` now counts; only `-dev.*`/`-beta.*` are excluded). Without this, every RC publish would diff against `v0.17.0` forever and the coverage diff would grow without bound. The `parseVersion`/`transitionLabel` machinery needed no changes — it already discards pre-release suffixes, so RC respins land in PR-mode steady-state semantics (in-flight directory `8.0-to-8.1`). - **`composeDevVersion` moved out of `determine-version.ts` into the pure utils module** so the dev-counter logic (including the new RC-base handling and counter reset across base changes) is unit-tested rather than only exercised in CI. Dev builds on the RC line are `8.0.0-rc.X-dev.N`. - The skill/docs sweep (`publish-npm-version`, `draft-release-notes`, `record-upgrade-instructions`, extension-upgrade skill, `docs/oss/versioning.md`) renames `bump-minor` → `bump-version` and updates the release procedures for the RC line. `draft-release-notes`' range lower bound now explicitly treats `-rc.N` tags as releases, so an RC respin's notes cover exactly what changed since the previous RC. ## How it fits together 1. **The version-shape vocabulary** ([scripts/determine-version-utils.ts](scripts/determine-version-utils.ts)): `assertCanonicalBase` admits the two release shapes; `computeNextReleaseVersion` and `composeDevVersion` are pure helpers over them. 2. **The publish decision** ([scripts/determine-version.ts](scripts/determine-version.ts)): unchanged trigger model — a release bump publishes `<base>` under `latest`, routine pushes compose `<base>-dev.N` via the shared helper. 3. **The maintainer entry point** ([scripts/bump-version.ts](scripts/bump-version.ts), `pnpm bump-version`): same idempotent read-from-HEAD design as before, now advancing to the next release version rather than the next minor. 4. **The workflow ceremony** ([.github/workflows/publish.yml](.github/workflows/publish.yml)): the GitHub Release step adds `--prerelease` when the published version matches `*-rc.*`; everything else (notes check, lightweight dev tags) keeps its `latest`-scoped conditions. 5. **The release-cycle baselines** ([scripts/check-upgrade-coverage.mjs](scripts/check-upgrade-coverage.mjs), [skills-contrib/draft-release-notes/SKILL.md](skills-contrib/draft-release-notes/SKILL.md)): "previous release" means the previous release tag — stable or RC — so upgrade-coverage diffs and release notes span exactly one release cycle on the RC line too. 6. **The policy documentation** ([docs/oss/versioning.md](docs/oss/versioning.md)): a new "The v8 RC line" section states the scheme, why `latest` tracks RCs for these packages, where the bare-`prisma` `next`-channel policy lives, and the one-time transition; the procedures are updated to match. ## Behavior changes & evidence - A release bump whose root version is `8.0.0-rc.N` publishes under `latest` with a pre-release GitHub Release; a stable bump behaves exactly as today. Implementation: [scripts/determine-version.ts](scripts/determine-version.ts), [.github/workflows/publish.yml](.github/workflows/publish.yml). Evidence: `computeNextReleaseVersion` and `assertCanonicalBase` suites in [scripts/determine-version-utils.test.ts](scripts/determine-version-utils.test.ts). - Dev builds on the RC line version as `8.0.0-rc.X-dev.N`, with the counter resetting whenever the base moves (new RC counter, stable→RC transition). Implementation: `composeDevVersion` in [scripts/determine-version-utils.ts](scripts/determine-version-utils.ts). Evidence: the `composeDevVersion` suite in [scripts/determine-version-utils.test.ts](scripts/determine-version-utils.test.ts). - `pnpm bump-version` from `0.17.0` produces `8.0.0-rc.1`; from `8.0.0-rc.1` produces `8.0.0-rc.2`; from a stable ≥ 8 produces the next minor. Implementation: [scripts/bump-version.ts](scripts/bump-version.ts). Evidence: the `computeNextReleaseVersion` suite in [scripts/determine-version-utils.test.ts](scripts/determine-version-utils.test.ts). - The publish-mode upgrade-coverage baseline resolves to the most recent release tag, including `v*-rc.N`. Implementation: [scripts/check-upgrade-coverage.mjs](scripts/check-upgrade-coverage.mjs). ## Summary Enables shipping the v8 RC early and iterating on it with frequent releases, using the exact publish flow that exists today — only the version shape and the pre-release marking change. ## Testing performed - `pnpm test:scripts` — 365 tests, 0 failures (includes the suites covering the new/changed version helpers). - `pnpm lint` (full repo), `pnpm lint:workflows`, `node scripts/validate-skills.mjs` — all green. - Local smoke-test of `determine-version.ts` against the real registry: dispatch resolves `0.17.0` → `latest`; push with unchanged version resolves `0.17.0-dev.N` → `dev` (continuing from the registry's actual counter). - `.github/workflows/publish.yml` parsed with the workspace `yaml` package to confirm validity after the edits. ## Skill update Updated in this PR: `skills-contrib/publish-npm-version` (RC-aware bump flow), `skills-contrib/draft-release-notes` (release-tag range bounds), `skills-contrib/record-upgrade-instructions` and `skills/extension-author/prisma-8-extension-upgrade` (`bump-version` rename). No end-user-facing CLI/API surface changes — the pipeline changes are maintainer-facing. ## Alternatives considered - **Publishing RCs under a `next` dist-tag and freezing `latest` at `0.17.0`** — rejected for these packages. Freezing `latest` protects a stable audience these package names don't have, npm publishes exactly one tag per publish so a second tag would need `npm dist-tag add` (which can't authenticate under OIDC trusted publishing — no long-lived token exists in this repo by design), and semver ranges already prevent any existing install from being moved onto an RC. The `next` channel remains the right design for the bare `prisma` package, whose `latest` genuinely must stay on v7; its v8 shim ships from [prisma/prisma-cli](https://github.com/prisma/prisma-cli). - **Nested pre-release identifiers (`8.0.0-rc.1.1`) for respins of a named RC** — rejected. Semver orders them correctly but no major ecosystem package does this (Drizzle, React, and TypeScript all use a flat counter), and the flat `rc.N` counter makes ordering and automation trivial. - **Keeping `bump-minor` and adding a separate RC bump script** — rejected; there is exactly one "advance to the next release version" operation and its meaning depends only on the current base's shape, so one script encoding the policy beats two scripts and a decision the maintainer must make each time. - **Bumping to `8.0.0-rc.1` in this same PR** — rejected to preserve the one-PR-per-release convention: merging a release bump is the publish trigger, and that merge should be its own reviewable event with its own release notes. ## Checklist - [x] All commits are signed off (`git commit -s`) per the [DCO](CONTRIBUTING.md#developer-certificate-of-origin-dco). The DCO status check will block merge if any commit is missing a `Signed-off-by:` trailer. - [x] I read [CONTRIBUTING.md](CONTRIBUTING.md) and the change is scoped to one logical concern. - [x] Tests are updated (or `n/a` if the change is doc-only / refactor with no behavioural delta). - [x] The PR title is in `TML-NNNN: <sentence-case title>` form (Linear ticket prefix + concise title naming the concrete deliverable). See `.claude/skills/create-pr/SKILL.md` for the full convention. - [x] The **Skill update** section above is filled in (or stated `n/a — internal only`). <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Added support for incremental release-candidate versions, including progression to the 8.0.0 release line. - Added development-version handling with consistent counter continuation and resets. - RC releases now use the `latest` channel and generate pre-release GitHub Releases. - **Documentation** - Updated versioning, publishing, release-note, and upgrade guidance for RC and stable releases. - Replaced the minor-bump workflow with the `pnpm bump-version` command. - Clarified tag, channel, and release-note requirements across publishing workflows. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> | 1 个月前 | |
chore: align biome.jsonc schema and apply 2.4.14 formatting Bump biome `$schema` to 2.4.14 across all package configs and apply the resulting formatting and `useOptionalChain` fixes so `lint` passes. Signed-off-by: Will Madden <madden@prisma.io> | 4 个月前 | |
ci: assert working tree is clean after build and test jobs (#558) ## Linked issue n/a — small change. ## At a glance What a developer sees when a build/test step leaves the tree dirty in CI: ``` Working tree is not clean after the previous step(s). A build or test step modified tracked files or created untracked files that are not in .gitignore. Either commit the regenerated artifacts, add the new paths to .gitignore, or fix the step that produced them. M packages/foo/contract.json ?? scripts/leftover.tmp ``` The job fails with a non-zero exit, the offending paths are listed in `git status --porcelain` form (status code + path), and the message points at the likely fix. ## Summary CI today builds and runs tests but does not assert that those steps leave the working tree untouched. If a build or test step accidentally regenerates a tracked file (e.g. a `contract.json` someone forgot to refresh locally) or writes a new file in an un-gitignored location (e.g. a stray snapshot, log, or generated artifact), CI passes and the divergence ships. The only existing diff guard is `pnpm fixtures:check` (`package.json`), scoped specifically to `**/contract.*`. This PR adds a generic equivalent and wires it into every CI job that builds or tests. ## How it works 1. **New script — [`scripts/check-clean-tree.mjs`](scripts/check-clean-tree.mjs).** Runs `git status --porcelain` from the repo root. Exits 0 if the output is empty; exits 1 and prints the porcelain output verbatim (covers modified tracked files **and** untracked files outside `.gitignore`) plus a short hint. 2. **Wired into CI — [`.github/workflows/ci.yml`](.github/workflows/ci.yml).** Added `Check working tree is clean` as the **last** step of `build`, `test`, `test-e2e`, `test-integration`, and `coverage`. Not added to `typecheck` or `lint` (they don't run build/tests) or `fixtures` (already has its own scoped `fixtures:check`). 3. **Exposed as a pnpm script — [`package.json`](package.json).** Added `check:clean-tree` alongside the other `check:*` entries; appended the new test path to `test:scripts` so it runs in the existing `Test scripts/` lint job. 4. **Unit-tested — [`scripts/check-clean-tree.test.mjs`](scripts/check-clean-tree.test.mjs).** Four `node --test` cases exercise the pure `formatDirtyReport` function (empty input → `null`; modified/untracked/multi-entry inputs included verbatim). Follows the same export-and-test pattern as `scripts/lint-workflow-triggers.{mjs,test.mjs}`. ## Notes for the reviewer - **Why porcelain, not `git diff --exit-code`.** The existing `fixtures:check` uses `git diff --exit-code` because it's scoped to known tracked paths. For a generic "did anything appear that shouldn't have?" check, untracked files are the more interesting failure mode — a build step writing `foo.generated.json` in an un-gitignored directory will silently pass `git diff` but is exactly what we want to flag. `git status --porcelain` covers both. - **Why these five jobs, not all of them.** Scope per the brief: catch diff produced by build/tests. `typecheck` and `lint` don't run `pnpm build` or any test runner, so they're outside scope. `fixtures` already has its own narrower assertion; adding the generic check there would be redundant. - **Five identical YAML insertions.** The five `Check working tree is clean` steps are intentionally copy-paste-identical. A composite action would shrink the diff but adds a layer for almost no payoff at this size. ## Testing performed - `pnpm test:scripts` — 87/87 pass (includes the 4 new cases in `scripts/check-clean-tree.test.mjs`). - Negative-path smoke test: ran `pnpm check:clean-tree` against an in-progress working tree containing the four files in this PR — output matched the format above (` M` for modified, `??` for untracked), exit code 1. - Positive path will be exercised on the PR itself: the five new CI steps must pass for this PR to merge, which proves the check is green on a clean post-build / post-test tree. ## Skill update n/a — internal only. No user-facing surface changes (no CLI flag, public API, config field, error code, or glossary term affected). ## Checklist - [x] All commits are signed off (`git commit -s`) per the [DCO](../CONTRIBUTING.md#developer-certificate-of-origin-dco). - [x] I read [CONTRIBUTING.md](../CONTRIBUTING.md) and the change is scoped to one logical concern. - [x] Tests are updated — `scripts/check-clean-tree.test.mjs` covers the new script. - [ ] The PR title is in `TML-NNNN: <sentence-case title>` form — n/a, no Linear ticket; using the `ci:` conventional-commit prefix per recent precedent on this repo. - [x] The **Skill update** section above is filled in. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Chores** * CI pipeline enhanced with automated working tree cleanliness verification. The validation now runs during build and test phases to detect unintended file modifications and ensure repository integrity throughout the development process. <!-- review_stack_entry_start --> [](https://app.coderabbit.ai/change-stack/prisma/prisma-next/pull/558?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack) <!-- review_stack_entry_end --> <!-- end of auto-generated comment: release notes by coderabbit.ai --> Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io> | 4 个月前 | |
ci: assert working tree is clean after build and test jobs (#558) ## Linked issue n/a — small change. ## At a glance What a developer sees when a build/test step leaves the tree dirty in CI: ``` Working tree is not clean after the previous step(s). A build or test step modified tracked files or created untracked files that are not in .gitignore. Either commit the regenerated artifacts, add the new paths to .gitignore, or fix the step that produced them. M packages/foo/contract.json ?? scripts/leftover.tmp ``` The job fails with a non-zero exit, the offending paths are listed in `git status --porcelain` form (status code + path), and the message points at the likely fix. ## Summary CI today builds and runs tests but does not assert that those steps leave the working tree untouched. If a build or test step accidentally regenerates a tracked file (e.g. a `contract.json` someone forgot to refresh locally) or writes a new file in an un-gitignored location (e.g. a stray snapshot, log, or generated artifact), CI passes and the divergence ships. The only existing diff guard is `pnpm fixtures:check` (`package.json`), scoped specifically to `**/contract.*`. This PR adds a generic equivalent and wires it into every CI job that builds or tests. ## How it works 1. **New script — [`scripts/check-clean-tree.mjs`](scripts/check-clean-tree.mjs).** Runs `git status --porcelain` from the repo root. Exits 0 if the output is empty; exits 1 and prints the porcelain output verbatim (covers modified tracked files **and** untracked files outside `.gitignore`) plus a short hint. 2. **Wired into CI — [`.github/workflows/ci.yml`](.github/workflows/ci.yml).** Added `Check working tree is clean` as the **last** step of `build`, `test`, `test-e2e`, `test-integration`, and `coverage`. Not added to `typecheck` or `lint` (they don't run build/tests) or `fixtures` (already has its own scoped `fixtures:check`). 3. **Exposed as a pnpm script — [`package.json`](package.json).** Added `check:clean-tree` alongside the other `check:*` entries; appended the new test path to `test:scripts` so it runs in the existing `Test scripts/` lint job. 4. **Unit-tested — [`scripts/check-clean-tree.test.mjs`](scripts/check-clean-tree.test.mjs).** Four `node --test` cases exercise the pure `formatDirtyReport` function (empty input → `null`; modified/untracked/multi-entry inputs included verbatim). Follows the same export-and-test pattern as `scripts/lint-workflow-triggers.{mjs,test.mjs}`. ## Notes for the reviewer - **Why porcelain, not `git diff --exit-code`.** The existing `fixtures:check` uses `git diff --exit-code` because it's scoped to known tracked paths. For a generic "did anything appear that shouldn't have?" check, untracked files are the more interesting failure mode — a build step writing `foo.generated.json` in an un-gitignored directory will silently pass `git diff` but is exactly what we want to flag. `git status --porcelain` covers both. - **Why these five jobs, not all of them.** Scope per the brief: catch diff produced by build/tests. `typecheck` and `lint` don't run `pnpm build` or any test runner, so they're outside scope. `fixtures` already has its own narrower assertion; adding the generic check there would be redundant. - **Five identical YAML insertions.** The five `Check working tree is clean` steps are intentionally copy-paste-identical. A composite action would shrink the diff but adds a layer for almost no payoff at this size. ## Testing performed - `pnpm test:scripts` — 87/87 pass (includes the 4 new cases in `scripts/check-clean-tree.test.mjs`). - Negative-path smoke test: ran `pnpm check:clean-tree` against an in-progress working tree containing the four files in this PR — output matched the format above (` M` for modified, `??` for untracked), exit code 1. - Positive path will be exercised on the PR itself: the five new CI steps must pass for this PR to merge, which proves the check is green on a clean post-build / post-test tree. ## Skill update n/a — internal only. No user-facing surface changes (no CLI flag, public API, config field, error code, or glossary term affected). ## Checklist - [x] All commits are signed off (`git commit -s`) per the [DCO](../CONTRIBUTING.md#developer-certificate-of-origin-dco). - [x] I read [CONTRIBUTING.md](../CONTRIBUTING.md) and the change is scoped to one logical concern. - [x] Tests are updated — `scripts/check-clean-tree.test.mjs` covers the new script. - [ ] The PR title is in `TML-NNNN: <sentence-case title>` form — n/a, no Linear ticket; using the `ci:` conventional-commit prefix per recent precedent on this repo. - [x] The **Skill update** section above is filled in. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Chores** * CI pipeline enhanced with automated working tree cleanliness verification. The validation now runs during build and test phases to detect unintended file modifications and ensure repository integrity throughout the development process. <!-- review_stack_entry_start --> [](https://app.coderabbit.ai/change-stack/prisma/prisma-next/pull/558?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack) <!-- review_stack_entry_end --> <!-- end of auto-generated comment: release notes by coderabbit.ai --> Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io> | 4 个月前 | |
Address review: use import.meta.main + commentChar-aware scissors - Switch the ESM entry-point guard to `import.meta.main` (Node >=24.2, satisfied by the repo's `engines.node` and pinned 24.13 in .tool-versions). The previous `file://${process.argv[1]}` comparison could mismatch on paths needing URL encoding, silently skipping the hook. - Derive the scissors marker from the active `core.commentChar` rather than hardcoding the `#` prefix, so `stripCommentsAndScissors` still cuts the message correctly when users have configured a different comment character. Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io> | 4 个月前 | |
chore(release): bump to 8.0.0-rc.12 (#30395) ## Release: 8.0.0-rc.11 → 8.0.0-rc.12 This is the release PR described in [docs/oss/versioning.md](https://github.com/prisma/orm/blob/main/docs/oss/versioning.md). It bumps every workspace package to 8.0.0-rc.12 and moves the Prisma dependencies to their latest versions. **Merging this PR ships the release.** The push to `main` carries the new root `version`. The `Publish to npm` workflow then publishes 8.0.0-rc.12 under `latest` and creates a pre-release GitHub Release from the notes file. ## Review these first - [docs/releases/v8.0.0-rc.12.md](https://github.com/prisma/orm/blob/release/8.0.0-rc.12/docs/releases/v8.0.0-rc.12.md): the release notes, which become the GitHub Release body. The same entry is at the top of `CHANGELOG.md`. - The upgrade guides for [apps](https://github.com/prisma/orm/blob/release/8.0.0-rc.12/skills/prisma-8/upgrading/app/upgrades/8.0.0-rc.11-to-8.0.0-rc.12/instructions.md) and [extensions](https://github.com/prisma/orm/blob/release/8.0.0-rc.12/skills/prisma-8/upgrading/extension/upgrades/8.0.0-rc.11-to-8.0.0-rc.12/instructions.md). They merge the 24 pending fragments. The original fragments are moved unchanged to `upgrade-instructions/releases/8.0.0-rc.11-to-8.0.0-rc.12/sources/`. - Four guide entries have no fragment behind them. The `migration new` default and its removed error codes (#30389) had no guide entry. Neither did the PSL parser API changes (#30312, #30344, #30335, #30379). I wrote those entries while preparing the release. - Where fragments contradicted later code, the guide follows the code. Examples: the Supabase storage hash, `voidParamsSchema`, and quoted defaults printed by `infer`. ## Dependency updates | Package | From | To | Where | | --- | --- | --- | --- | | `@prisma/cli-engine` | 0.4.0 | 0.6.1 | examples, test fixtures, apps (the toolchain packages were already on 0.6.1 from #30372) | | `@prisma/dev` | 0.25.1 | 0.25.2 | the workspace catalog | | `@prisma/compute-sdk` | ^0.39.0 | ^0.43.0 | `apps/telemetry-backend` | | `@prisma/management-api-sdk` | ^1.56.0 | ^1.76.0 | `apps/telemetry-backend` | compute-sdk 0.43 renames "service" to "app" and "version" to "deployment". The telemetry deploy script now uses the new names. Both SDK versions call `/v1/apps/{appId}`, so the ID stored in the existing `TELEMETRY_DEPLOY_SERVICE_ID` secret is still correct. The app's typecheck now includes `scripts/`, so it catches the next SDK rename. The repo does not depend on `@prisma/composer`. ## Fixes needed to publish - **The publish workflow has failed on `main` since #30372.** `check:conformance` called the `orm` config validator as `validate(value)`. Engine 0.6 always calls `validate(value, provenance)`, and the validator reads `provenance.files`, so it threw on every input. The check now passes the same provenance the engine would. The prisma-cli copy of this check already does this. - `set-version` rewrote `workspace:@internal/cli@<version>` to `workspace:<version>`, dropping the alias. The prisma7-adoption example uses that alias. This is the first bump since the alias was added. - `lint:legacy-name` and the `add-model-map` test pointed at the pending fragment paths. They now point at the archived sources. ## Verification Passed locally: - `pnpm build` - `pnpm typecheck` - `pnpm lint` - `pnpm test:scripts` (563 tests) - `pnpm check:conformance` - `pnpm check:publish-deps` - `pnpm check:upgrade-coverage`, in both publish and PR mode - `pnpm check:release-notes`, in both publish and PR mode - `pnpm lint:legacy-name` - `pnpm lint:skills` - `pnpm test:packages`: all 18,196 tests passed Not covered locally, left to CI: - Three `test:packages` suites install packed tarballs from the registry. This machine's pnpm refuses `@vercel/detect-agent@1.2.5` because it has no provenance. CI passed the same suites on #30390. - `prisma-8-cloudflare-worker` needs a local Hyperdrive database. - The telemetry backend tests need Node 24.16 with `Temporal`. This machine has 24.13. - `fixtures:check` needs Postgres. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added PostgreSQL full-text search, multi-file schemas, prepared ORM reads and aggregates, and conflict-skipping options for bulk creation. * Added support for using a Prisma 7 schema as the contract source, JavaScript `Date` timestamps on PostgreSQL, editor support for attribute arguments, and per-finding diagnostics. * **Breaking Changes** * Prisma 8 schema files now require `// use prisma-8` on the first line; unmapped models use their names verbatim for table names. * Replace `dbgenerated(...)` with SQL tagged literals. Defaults must be valid for their column types, creation timestamps use the application clock, and native PostgreSQL enums no longer support text operations. * Config naming and path resolution, migration starting points, and extension contracts have changed. * **Bug Fixes** * Improved migration checks and branching warnings, contract generation and inference, default verification, and type checking. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- 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> | 10 天前 | |
chore(release): bump to 8.0.0-rc.12 (#30395) ## Release: 8.0.0-rc.11 → 8.0.0-rc.12 This is the release PR described in [docs/oss/versioning.md](https://github.com/prisma/orm/blob/main/docs/oss/versioning.md). It bumps every workspace package to 8.0.0-rc.12 and moves the Prisma dependencies to their latest versions. **Merging this PR ships the release.** The push to `main` carries the new root `version`. The `Publish to npm` workflow then publishes 8.0.0-rc.12 under `latest` and creates a pre-release GitHub Release from the notes file. ## Review these first - [docs/releases/v8.0.0-rc.12.md](https://github.com/prisma/orm/blob/release/8.0.0-rc.12/docs/releases/v8.0.0-rc.12.md): the release notes, which become the GitHub Release body. The same entry is at the top of `CHANGELOG.md`. - The upgrade guides for [apps](https://github.com/prisma/orm/blob/release/8.0.0-rc.12/skills/prisma-8/upgrading/app/upgrades/8.0.0-rc.11-to-8.0.0-rc.12/instructions.md) and [extensions](https://github.com/prisma/orm/blob/release/8.0.0-rc.12/skills/prisma-8/upgrading/extension/upgrades/8.0.0-rc.11-to-8.0.0-rc.12/instructions.md). They merge the 24 pending fragments. The original fragments are moved unchanged to `upgrade-instructions/releases/8.0.0-rc.11-to-8.0.0-rc.12/sources/`. - Four guide entries have no fragment behind them. The `migration new` default and its removed error codes (#30389) had no guide entry. Neither did the PSL parser API changes (#30312, #30344, #30335, #30379). I wrote those entries while preparing the release. - Where fragments contradicted later code, the guide follows the code. Examples: the Supabase storage hash, `voidParamsSchema`, and quoted defaults printed by `infer`. ## Dependency updates | Package | From | To | Where | | --- | --- | --- | --- | | `@prisma/cli-engine` | 0.4.0 | 0.6.1 | examples, test fixtures, apps (the toolchain packages were already on 0.6.1 from #30372) | | `@prisma/dev` | 0.25.1 | 0.25.2 | the workspace catalog | | `@prisma/compute-sdk` | ^0.39.0 | ^0.43.0 | `apps/telemetry-backend` | | `@prisma/management-api-sdk` | ^1.56.0 | ^1.76.0 | `apps/telemetry-backend` | compute-sdk 0.43 renames "service" to "app" and "version" to "deployment". The telemetry deploy script now uses the new names. Both SDK versions call `/v1/apps/{appId}`, so the ID stored in the existing `TELEMETRY_DEPLOY_SERVICE_ID` secret is still correct. The app's typecheck now includes `scripts/`, so it catches the next SDK rename. The repo does not depend on `@prisma/composer`. ## Fixes needed to publish - **The publish workflow has failed on `main` since #30372.** `check:conformance` called the `orm` config validator as `validate(value)`. Engine 0.6 always calls `validate(value, provenance)`, and the validator reads `provenance.files`, so it threw on every input. The check now passes the same provenance the engine would. The prisma-cli copy of this check already does this. - `set-version` rewrote `workspace:@internal/cli@<version>` to `workspace:<version>`, dropping the alias. The prisma7-adoption example uses that alias. This is the first bump since the alias was added. - `lint:legacy-name` and the `add-model-map` test pointed at the pending fragment paths. They now point at the archived sources. ## Verification Passed locally: - `pnpm build` - `pnpm typecheck` - `pnpm lint` - `pnpm test:scripts` (563 tests) - `pnpm check:conformance` - `pnpm check:publish-deps` - `pnpm check:upgrade-coverage`, in both publish and PR mode - `pnpm check:release-notes`, in both publish and PR mode - `pnpm lint:legacy-name` - `pnpm lint:skills` - `pnpm test:packages`: all 18,196 tests passed Not covered locally, left to CI: - Three `test:packages` suites install packed tarballs from the registry. This machine's pnpm refuses `@vercel/detect-agent@1.2.5` because it has no provenance. CI passed the same suites on #30390. - `prisma-8-cloudflare-worker` needs a local Hyperdrive database. - The telemetry backend tests need Node 24.16 with `Temporal`. This machine has 24.13. - `fixtures:check` needs Postgres. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added PostgreSQL full-text search, multi-file schemas, prepared ORM reads and aggregates, and conflict-skipping options for bulk creation. * Added support for using a Prisma 7 schema as the contract source, JavaScript `Date` timestamps on PostgreSQL, editor support for attribute arguments, and per-finding diagnostics. * **Breaking Changes** * Prisma 8 schema files now require `// use prisma-8` on the first line; unmapped models use their names verbatim for table names. * Replace `dbgenerated(...)` with SQL tagged literals. Defaults must be valid for their column types, creation timestamps use the application clock, and native PostgreSQL enums no longer support text operations. * Config naming and path resolution, migration starting points, and extension contracts have changed. * **Bug Fixes** * Improved migration checks and branching warnings, contract generation and inference, default verification, and type checking. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- 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> | 10 天前 | |
feat: an application depends on one Prisma package (ADR 242) (#29864) ## What changes for someone using Prisma Today, an application that talks to Postgres installs a long list of our packages: ```jsonc { "dependencies": { "@prisma-next/postgres": "...", "@prisma-next/sql-runtime": "...", "@prisma-next/sql-orm-client": "...", "@prisma-next/target-postgres": "...", "@prisma-next/adapter-postgres": "...", "@prisma-next/sql-contract": "..." // ...a dozen more } } ``` After this PR, it installs one: ```jsonc { "dependencies": { "@prisma/orm-postgres": "0.16.0" } } ``` Everything else arrives as that package's own dependencies. Three of our example apps are converted in this PR to prove it — one per database — and each has exactly one Prisma package in its `dependencies`. This implements [ADR 242](https://github.com/prisma/prisma/pull/29852), which is already merged. ## What gets published 17 packages, all under the `@prisma` scope: - **3 database packages** — `@prisma/orm-postgres`, `orm-sqlite`, `orm-mongo`. An application installs exactly one. We call these *facades*: each is a small package that wires its database together and re-exports everything an application needs. - **6 extension packs** — PostGIS, pgvector, ParadeDB, Supabase, arktype-json, middleware-cache. Optional, installed alongside a database package. - **7 platform packages** — the framework, the toolchain, one per database family, one per database target. Applications never install these directly; they arrive as dependencies. Extension authors do install them. - **the `prisma` command**, as a bin-only package. Every other workspace package — around 50 of them — stops being published. They still exist in the repo as the unit we organise code in; they just stop having a life on the registry. **This PR does not make that switch yet.** It builds and proves the new surface while leaving today's publish list exactly as it is. Flipping it is a separate change. ## The problem this design has to avoid A published package can't depend on packages that won't exist on the registry. So each published package *contains a compiled copy* of the internal packages it covers. That creates a trap. If one application ends up with the same code twice — once inside a published package, once as its own package — then classes, registries, and anything compared by reference exist twice too. An `instanceof` check quietly returns false. Nothing crashes, nothing fails to compile, and both copies behave identically in isolation. You find out much later, somewhere unrelated. So the rule the whole design follows is: **every piece of internal code is published from exactly one package.** Concretely, that means: - Each published package is built in one pass, so code shared between its own entry points exists once. Verified from the build's source maps: no module appears in more than one chunk, in any published package. - When one published package needs code from another, it imports it as a real dependency rather than compiling in a second copy. - A facade re-exports from the platform packages; it never carries its own copy. `@prisma/orm-postgres/orm-client` and `@prisma/orm-family-sql/orm-client` are two names for the same object, and there's a test that asserts exactly that from installed tarballs. - One table in `packages/0-shared/publish-surface` maps every internal package to where it's published. The build, the code generator, and the lint checks all read it, so there's one answer to "where does this live" rather than three that can drift. ## Generated code follows the application Prisma writes imports into your project — contract types and migration files. Those imports have to name packages your project actually depends on, or they won't resolve. So the generator now reads the `package.json` next to the config it's generating for. A project that depends on `@prisma/orm-postgres` gets imports from that package. A project on today's names keeps today's names. Nothing to configure, because the manifest already says which it is. Contract hashes are unaffected, and that isn't an assumption — hashes are computed from a structure that import text never enters, and there's a test asserting the hash is identical across naming schemes *while* the emitted imports demonstrably differ. ## What stops the trap coming back Two checks, because the failure is silent and won't show up in a test suite: - Every example app and test project must use one naming scheme, not a mix. `lint-single-import-root` scans them and fails the build if any project imports from both, since that's the situation that loads code twice. - `lint-consumer-internal-imports` counts how many internal-package imports remain in those projects and compares against a committed number. It fails if the number goes up (someone added one) and also if it goes down without the number being updated (so improvements get locked in). Target is zero. The build itself also refuses to proceed if the published-package map would put one module in two places, or if a published package's `package.json` no longer matches what its code actually needs. ## Reading this PR It's large — 257 files — because it's a migration. The commits are grouped and meant to be read in order: 1. **Platform packages** — the build mechanism, and the seven platform packages it produces. 2. **Database packages, extension packs, the `prisma` command** — completes the set of 17. 3. **Generated imports become configurable** — one place decides which names get written, with today's names still the default. 4. **Database-family symmetry, publishing the map, the identity checks.** 5. **One package per application** — the three converted examples, the re-exports they proved necessary, and the counting check. 6. **Migration files follow the project too.** One thing worth knowing while reading: re-exporting a package republishes all of its sub-paths, not just the one that was needed. This PR adds 115 published sub-paths across the three database packages. Two candidates were dropped for exactly that reason — see below. ## Alternatives considered **Let an application install platform packages alongside its facade.** Nothing would need re-exporting and the facades would stay thinner. Rejected: an application would again juggle several Prisma dependencies whose correct combination it maintains by hand, and getting it wrong — upgrading one and not the other — produces the silent two-copies failure above. Re-exporting costs a generated line and nothing at runtime. **Re-export everything an application might plausibly want.** Rejected in review: because re-exporting brings a package's entire sub-path surface, generosity is expensive and hard to undo. Migration tooling (54 sub-paths) was dropped because its only users are extension packs, which install platform packages anyway; the SQL driver re-export was dropped because nothing imported it at all. What remains is what a converted example actually needed. **Flip the publish list in this same PR.** Rejected: it would mix "does the new surface work" with "is it safe to stop publishing 50 packages" in one review. The switch is mechanical once this lands, and gets its own change. ## Verification `build`, `typecheck` (156 tasks), `test:packages` (1077 files / 14087 tests), `test:e2e`, `lint`, `lint:deps`, `lint:docs`, `lint:manifests`, `check:publish-deps`, `check:clean-tree`, `lint:casts` and `lint:throws` (no new instances), `test:scripts`, coverage, the tarball-install suites, and regenerating every committed artifact leaves the tree unchanged. Known-unstable and unrelated to this change: the `relation-mode-gh-*` port suites (TML-3140), and several test timeouts that are too tight under load. ## Follow-ups TML-3124 switch the publish list · TML-3127 build cache can validate a stale published package on CI · TML-3140 unstable port suites · TML-3141 a test-helper sub-path reaches a package that is never published. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Added consolidated public ORM packages for PostgreSQL, MongoDB, SQLite, framework tooling, database targets, and extensions. - Generated contracts, migrations, and scaffolds now adapt imports to the consuming project’s package surface. - Added facade-provided `prisma-next` CLI access and consolidated migration entrypoints. - **Documentation** - Updated installation, package naming, public entrypoint, and migration scaffolding guidance. - **Tests** - Added coverage for package installation, exports, CLI behavior, module identity, and import compatibility. - **Chores** - Added checks preventing incompatible internal and public package imports. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> | 2 个月前 | |
feat: an application depends on one Prisma package (ADR 242) (#29864) ## What changes for someone using Prisma Today, an application that talks to Postgres installs a long list of our packages: ```jsonc { "dependencies": { "@prisma-next/postgres": "...", "@prisma-next/sql-runtime": "...", "@prisma-next/sql-orm-client": "...", "@prisma-next/target-postgres": "...", "@prisma-next/adapter-postgres": "...", "@prisma-next/sql-contract": "..." // ...a dozen more } } ``` After this PR, it installs one: ```jsonc { "dependencies": { "@prisma/orm-postgres": "0.16.0" } } ``` Everything else arrives as that package's own dependencies. Three of our example apps are converted in this PR to prove it — one per database — and each has exactly one Prisma package in its `dependencies`. This implements [ADR 242](https://github.com/prisma/prisma/pull/29852), which is already merged. ## What gets published 17 packages, all under the `@prisma` scope: - **3 database packages** — `@prisma/orm-postgres`, `orm-sqlite`, `orm-mongo`. An application installs exactly one. We call these *facades*: each is a small package that wires its database together and re-exports everything an application needs. - **6 extension packs** — PostGIS, pgvector, ParadeDB, Supabase, arktype-json, middleware-cache. Optional, installed alongside a database package. - **7 platform packages** — the framework, the toolchain, one per database family, one per database target. Applications never install these directly; they arrive as dependencies. Extension authors do install them. - **the `prisma` command**, as a bin-only package. Every other workspace package — around 50 of them — stops being published. They still exist in the repo as the unit we organise code in; they just stop having a life on the registry. **This PR does not make that switch yet.** It builds and proves the new surface while leaving today's publish list exactly as it is. Flipping it is a separate change. ## The problem this design has to avoid A published package can't depend on packages that won't exist on the registry. So each published package *contains a compiled copy* of the internal packages it covers. That creates a trap. If one application ends up with the same code twice — once inside a published package, once as its own package — then classes, registries, and anything compared by reference exist twice too. An `instanceof` check quietly returns false. Nothing crashes, nothing fails to compile, and both copies behave identically in isolation. You find out much later, somewhere unrelated. So the rule the whole design follows is: **every piece of internal code is published from exactly one package.** Concretely, that means: - Each published package is built in one pass, so code shared between its own entry points exists once. Verified from the build's source maps: no module appears in more than one chunk, in any published package. - When one published package needs code from another, it imports it as a real dependency rather than compiling in a second copy. - A facade re-exports from the platform packages; it never carries its own copy. `@prisma/orm-postgres/orm-client` and `@prisma/orm-family-sql/orm-client` are two names for the same object, and there's a test that asserts exactly that from installed tarballs. - One table in `packages/0-shared/publish-surface` maps every internal package to where it's published. The build, the code generator, and the lint checks all read it, so there's one answer to "where does this live" rather than three that can drift. ## Generated code follows the application Prisma writes imports into your project — contract types and migration files. Those imports have to name packages your project actually depends on, or they won't resolve. So the generator now reads the `package.json` next to the config it's generating for. A project that depends on `@prisma/orm-postgres` gets imports from that package. A project on today's names keeps today's names. Nothing to configure, because the manifest already says which it is. Contract hashes are unaffected, and that isn't an assumption — hashes are computed from a structure that import text never enters, and there's a test asserting the hash is identical across naming schemes *while* the emitted imports demonstrably differ. ## What stops the trap coming back Two checks, because the failure is silent and won't show up in a test suite: - Every example app and test project must use one naming scheme, not a mix. `lint-single-import-root` scans them and fails the build if any project imports from both, since that's the situation that loads code twice. - `lint-consumer-internal-imports` counts how many internal-package imports remain in those projects and compares against a committed number. It fails if the number goes up (someone added one) and also if it goes down without the number being updated (so improvements get locked in). Target is zero. The build itself also refuses to proceed if the published-package map would put one module in two places, or if a published package's `package.json` no longer matches what its code actually needs. ## Reading this PR It's large — 257 files — because it's a migration. The commits are grouped and meant to be read in order: 1. **Platform packages** — the build mechanism, and the seven platform packages it produces. 2. **Database packages, extension packs, the `prisma` command** — completes the set of 17. 3. **Generated imports become configurable** — one place decides which names get written, with today's names still the default. 4. **Database-family symmetry, publishing the map, the identity checks.** 5. **One package per application** — the three converted examples, the re-exports they proved necessary, and the counting check. 6. **Migration files follow the project too.** One thing worth knowing while reading: re-exporting a package republishes all of its sub-paths, not just the one that was needed. This PR adds 115 published sub-paths across the three database packages. Two candidates were dropped for exactly that reason — see below. ## Alternatives considered **Let an application install platform packages alongside its facade.** Nothing would need re-exporting and the facades would stay thinner. Rejected: an application would again juggle several Prisma dependencies whose correct combination it maintains by hand, and getting it wrong — upgrading one and not the other — produces the silent two-copies failure above. Re-exporting costs a generated line and nothing at runtime. **Re-export everything an application might plausibly want.** Rejected in review: because re-exporting brings a package's entire sub-path surface, generosity is expensive and hard to undo. Migration tooling (54 sub-paths) was dropped because its only users are extension packs, which install platform packages anyway; the SQL driver re-export was dropped because nothing imported it at all. What remains is what a converted example actually needed. **Flip the publish list in this same PR.** Rejected: it would mix "does the new surface work" with "is it safe to stop publishing 50 packages" in one review. The switch is mechanical once this lands, and gets its own change. ## Verification `build`, `typecheck` (156 tasks), `test:packages` (1077 files / 14087 tests), `test:e2e`, `lint`, `lint:deps`, `lint:docs`, `lint:manifests`, `check:publish-deps`, `check:clean-tree`, `lint:casts` and `lint:throws` (no new instances), `test:scripts`, coverage, the tarball-install suites, and regenerating every committed artifact leaves the tree unchanged. Known-unstable and unrelated to this change: the `relation-mode-gh-*` port suites (TML-3140), and several test timeouts that are too tight under load. ## Follow-ups TML-3124 switch the publish list · TML-3127 build cache can validate a stale published package on CI · TML-3140 unstable port suites · TML-3141 a test-helper sub-path reaches a package that is never published. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Added consolidated public ORM packages for PostgreSQL, MongoDB, SQLite, framework tooling, database targets, and extensions. - Generated contracts, migrations, and scaffolds now adapt imports to the consuming project’s package surface. - Added facade-provided `prisma-next` CLI access and consolidated migration entrypoints. - **Documentation** - Updated installation, package naming, public entrypoint, and migration scaffolding guidance. - **Tests** - Added coverage for package installation, exports, CLI behavior, module identity, and import compatibility. - **Chores** - Added checks preventing incompatible internal and public package imports. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> | 2 个月前 | |
feat: an application depends on one Prisma package (ADR 242) (#29864) ## What changes for someone using Prisma Today, an application that talks to Postgres installs a long list of our packages: ```jsonc { "dependencies": { "@prisma-next/postgres": "...", "@prisma-next/sql-runtime": "...", "@prisma-next/sql-orm-client": "...", "@prisma-next/target-postgres": "...", "@prisma-next/adapter-postgres": "...", "@prisma-next/sql-contract": "..." // ...a dozen more } } ``` After this PR, it installs one: ```jsonc { "dependencies": { "@prisma/orm-postgres": "0.16.0" } } ``` Everything else arrives as that package's own dependencies. Three of our example apps are converted in this PR to prove it — one per database — and each has exactly one Prisma package in its `dependencies`. This implements [ADR 242](https://github.com/prisma/prisma/pull/29852), which is already merged. ## What gets published 17 packages, all under the `@prisma` scope: - **3 database packages** — `@prisma/orm-postgres`, `orm-sqlite`, `orm-mongo`. An application installs exactly one. We call these *facades*: each is a small package that wires its database together and re-exports everything an application needs. - **6 extension packs** — PostGIS, pgvector, ParadeDB, Supabase, arktype-json, middleware-cache. Optional, installed alongside a database package. - **7 platform packages** — the framework, the toolchain, one per database family, one per database target. Applications never install these directly; they arrive as dependencies. Extension authors do install them. - **the `prisma` command**, as a bin-only package. Every other workspace package — around 50 of them — stops being published. They still exist in the repo as the unit we organise code in; they just stop having a life on the registry. **This PR does not make that switch yet.** It builds and proves the new surface while leaving today's publish list exactly as it is. Flipping it is a separate change. ## The problem this design has to avoid A published package can't depend on packages that won't exist on the registry. So each published package *contains a compiled copy* of the internal packages it covers. That creates a trap. If one application ends up with the same code twice — once inside a published package, once as its own package — then classes, registries, and anything compared by reference exist twice too. An `instanceof` check quietly returns false. Nothing crashes, nothing fails to compile, and both copies behave identically in isolation. You find out much later, somewhere unrelated. So the rule the whole design follows is: **every piece of internal code is published from exactly one package.** Concretely, that means: - Each published package is built in one pass, so code shared between its own entry points exists once. Verified from the build's source maps: no module appears in more than one chunk, in any published package. - When one published package needs code from another, it imports it as a real dependency rather than compiling in a second copy. - A facade re-exports from the platform packages; it never carries its own copy. `@prisma/orm-postgres/orm-client` and `@prisma/orm-family-sql/orm-client` are two names for the same object, and there's a test that asserts exactly that from installed tarballs. - One table in `packages/0-shared/publish-surface` maps every internal package to where it's published. The build, the code generator, and the lint checks all read it, so there's one answer to "where does this live" rather than three that can drift. ## Generated code follows the application Prisma writes imports into your project — contract types and migration files. Those imports have to name packages your project actually depends on, or they won't resolve. So the generator now reads the `package.json` next to the config it's generating for. A project that depends on `@prisma/orm-postgres` gets imports from that package. A project on today's names keeps today's names. Nothing to configure, because the manifest already says which it is. Contract hashes are unaffected, and that isn't an assumption — hashes are computed from a structure that import text never enters, and there's a test asserting the hash is identical across naming schemes *while* the emitted imports demonstrably differ. ## What stops the trap coming back Two checks, because the failure is silent and won't show up in a test suite: - Every example app and test project must use one naming scheme, not a mix. `lint-single-import-root` scans them and fails the build if any project imports from both, since that's the situation that loads code twice. - `lint-consumer-internal-imports` counts how many internal-package imports remain in those projects and compares against a committed number. It fails if the number goes up (someone added one) and also if it goes down without the number being updated (so improvements get locked in). Target is zero. The build itself also refuses to proceed if the published-package map would put one module in two places, or if a published package's `package.json` no longer matches what its code actually needs. ## Reading this PR It's large — 257 files — because it's a migration. The commits are grouped and meant to be read in order: 1. **Platform packages** — the build mechanism, and the seven platform packages it produces. 2. **Database packages, extension packs, the `prisma` command** — completes the set of 17. 3. **Generated imports become configurable** — one place decides which names get written, with today's names still the default. 4. **Database-family symmetry, publishing the map, the identity checks.** 5. **One package per application** — the three converted examples, the re-exports they proved necessary, and the counting check. 6. **Migration files follow the project too.** One thing worth knowing while reading: re-exporting a package republishes all of its sub-paths, not just the one that was needed. This PR adds 115 published sub-paths across the three database packages. Two candidates were dropped for exactly that reason — see below. ## Alternatives considered **Let an application install platform packages alongside its facade.** Nothing would need re-exporting and the facades would stay thinner. Rejected: an application would again juggle several Prisma dependencies whose correct combination it maintains by hand, and getting it wrong — upgrading one and not the other — produces the silent two-copies failure above. Re-exporting costs a generated line and nothing at runtime. **Re-export everything an application might plausibly want.** Rejected in review: because re-exporting brings a package's entire sub-path surface, generosity is expensive and hard to undo. Migration tooling (54 sub-paths) was dropped because its only users are extension packs, which install platform packages anyway; the SQL driver re-export was dropped because nothing imported it at all. What remains is what a converted example actually needed. **Flip the publish list in this same PR.** Rejected: it would mix "does the new surface work" with "is it safe to stop publishing 50 packages" in one review. The switch is mechanical once this lands, and gets its own change. ## Verification `build`, `typecheck` (156 tasks), `test:packages` (1077 files / 14087 tests), `test:e2e`, `lint`, `lint:deps`, `lint:docs`, `lint:manifests`, `check:publish-deps`, `check:clean-tree`, `lint:casts` and `lint:throws` (no new instances), `test:scripts`, coverage, the tarball-install suites, and regenerating every committed artifact leaves the tree unchanged. Known-unstable and unrelated to this change: the `relation-mode-gh-*` port suites (TML-3140), and several test timeouts that are too tight under load. ## Follow-ups TML-3124 switch the publish list · TML-3127 build cache can validate a stale published package on CI · TML-3140 unstable port suites · TML-3141 a test-helper sub-path reaches a package that is never published. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Added consolidated public ORM packages for PostgreSQL, MongoDB, SQLite, framework tooling, database targets, and extensions. - Generated contracts, migrations, and scaffolds now adapt imports to the consuming project’s package surface. - Added facade-provided `prisma-next` CLI access and consolidated migration entrypoints. - **Documentation** - Updated installation, package naming, public entrypoint, and migration scaffolding guidance. - **Tests** - Added coverage for package installation, exports, CLI behavior, module identity, and import compatibility. - **Chores** - Added checks preventing incompatible internal and public package imports. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> | 2 个月前 | |
fix: publishable manifests declare the canonical repository, enforced (#29884) ## Why After Trusted Publishing was configured, the publish run got past auth and failed with **E422 for all 16 `@prisma/*` packages**: provenance verification rejects a tarball whose `repository.url` does not match the repository the workflow runs in, and those manifests declared no `repository` field at all. `prisma-next` — the one package that declares it — published successfully and is live at `0.16.0-dev.35`. ## What - **Add `repository` `{ type, url, directory }` to the 16 manifests**, with each package's own workspace directory. - **Extend `lint:manifests`**: every publishable package must declare the canonical repository object — exact url, own directory, object form. Zero tolerance, no exemptions. Verified by stripping the field from one manifest and watching the lint exit 1. - **Revive two dead test files.** `validate-package-manifests.test.mjs` and `check-publish-deps.test.mjs` were written for vitest, but no suite ran them: the root vitest config only loads `packages/**` projects, and `test:scripts` (node --test) didn't list them. Both are converted to `node:test` and wired into `test:scripts`. The conversion immediately caught a stale stub in the check-publish-deps test (the script's io seam had grown two legs the test didn't cover) — exactly the failure mode dead tests hide. ## Verification - `test:scripts`: 347 tests, 0 failures — now including both revived files and the new repository rules. - `lint:manifests`, `check:publish-deps`: green. - Planted violation (repository stripped from `@prisma/orm-sqlite`): lint fails with exit 1 naming the package. Once merged, the push to main triggers the publish workflow; with auth and repository fields both in place it should publish all 17 packages at `0.16.0-dev.36`. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **Improvements** - Added standardized repository information to published packages, making package origins and locations clearer. - Enhanced package validation to check required repository details alongside licensing information. - Improved validation results with clearer human-readable and JSON reporting, including guidance related to package provenance. - **Tests** - Updated automated checks to use Node’s built-in test runner and expanded coverage for repository metadata validation. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io> | 2 个月前 | |
TML-3170: publish releases as 8.0.0-rc.N (latest tracks the RC line) (#29899) ## Linked issue Refs [TML-3170](https://linear.app/prisma-company/issue/TML-3170). Follow-up (not in this PR): the release PR that bumps the root version `0.17.0` → `8.0.0-rc.1` via the `publish-npm-version` skill, which is what actually starts the RC line. ## At a glance ```ts // scripts/determine-version-utils.ts export function computeNextReleaseVersion(current: string): string { assertCanonicalBase(current); const rcMatch = current.match(RC_BASE_PATTERN); if (rcMatch) { const [, major, minor, patch, rc] = rcMatch; return `${major}.${minor}.${patch}-rc.${Number(rc) + 1}`; } if (parseVersion(current).major < 8) { return '8.0.0-rc.1'; } return computeNextMinor(current); } ``` Before this PR, a release was always the next `0.x` minor. After it, the same merge-the-release-PR flow ships `8.0.0-rc.1`, `8.0.0-rc.2`, … — still under dist-tag `latest`, with the GitHub Release marked pre-release. ## Decision This PR moves the publish pipeline onto the v8 release-candidate line: 1. **Releases version as `8.0.0-rc.N`** — the counter advances on every release publish. "The v8 RC" is the product name; the number iterates freely underneath, so respins are cheap and there is no promise the final RC is literally `rc.1`. 2. **`latest` keeps tracking the newest release, RC included.** The package names this repo publishes (`@prisma/orm-*`, the platform packages, the `prisma-next` shim) have no pre-v8 stable audience to protect — a bare install is an early-access install. The frozen-`latest` concern belongs to the bare `prisma` package, which this repo does not publish — its v8 bin shim lives in [prisma/prisma-cli](https://github.com/prisma/prisma-cli), which publishes under `next` while v7 keeps `latest`. 3. **`pnpm bump-minor` becomes `pnpm bump-version`**, encoding the release policy: RC base → next RC; pre-8 stable base → `8.0.0-rc.1` (the one-time line transition); stable ≥ 8 → next minor. 4. **RC releases keep the full release ceremony**: committed release notes are required, and the GitHub Release is created with `--prerelease` whenever the version is on the RC line. The root version deliberately stays `0.17.0` in this PR. Until the transition release PR lands, the pipeline behaves exactly as before (verified by running the publish paths locally — see Testing performed). ## Notes for the reviewer - **No existing user is affected.** `latest` semantics are unchanged (newest release); lockfiles pin resolved versions, and existing `^0.x` ranges can never resolve to `8.0.0-rc.N` (pre-releases don't satisfy stable ranges), so `npm update` never moves anyone onto the RC line — only fresh installs get RCs once the transition PR lands. - **The canonical root-version shape widens** from "clean `X.Y.Z` only" to "clean `X.Y.Z` or `X.Y.Z-rc.N`" (`assertCanonicalBase`); anything else is still refused on `main`. - **`check-upgrade-coverage`'s publish baseline changed from "last stable tag" to "last release tag"** (`v*-rc.N` now counts; only `-dev.*`/`-beta.*` are excluded). Without this, every RC publish would diff against `v0.17.0` forever and the coverage diff would grow without bound. The `parseVersion`/`transitionLabel` machinery needed no changes — it already discards pre-release suffixes, so RC respins land in PR-mode steady-state semantics (in-flight directory `8.0-to-8.1`). - **`composeDevVersion` moved out of `determine-version.ts` into the pure utils module** so the dev-counter logic (including the new RC-base handling and counter reset across base changes) is unit-tested rather than only exercised in CI. Dev builds on the RC line are `8.0.0-rc.X-dev.N`. - The skill/docs sweep (`publish-npm-version`, `draft-release-notes`, `record-upgrade-instructions`, extension-upgrade skill, `docs/oss/versioning.md`) renames `bump-minor` → `bump-version` and updates the release procedures for the RC line. `draft-release-notes`' range lower bound now explicitly treats `-rc.N` tags as releases, so an RC respin's notes cover exactly what changed since the previous RC. ## How it fits together 1. **The version-shape vocabulary** ([scripts/determine-version-utils.ts](scripts/determine-version-utils.ts)): `assertCanonicalBase` admits the two release shapes; `computeNextReleaseVersion` and `composeDevVersion` are pure helpers over them. 2. **The publish decision** ([scripts/determine-version.ts](scripts/determine-version.ts)): unchanged trigger model — a release bump publishes `<base>` under `latest`, routine pushes compose `<base>-dev.N` via the shared helper. 3. **The maintainer entry point** ([scripts/bump-version.ts](scripts/bump-version.ts), `pnpm bump-version`): same idempotent read-from-HEAD design as before, now advancing to the next release version rather than the next minor. 4. **The workflow ceremony** ([.github/workflows/publish.yml](.github/workflows/publish.yml)): the GitHub Release step adds `--prerelease` when the published version matches `*-rc.*`; everything else (notes check, lightweight dev tags) keeps its `latest`-scoped conditions. 5. **The release-cycle baselines** ([scripts/check-upgrade-coverage.mjs](scripts/check-upgrade-coverage.mjs), [skills-contrib/draft-release-notes/SKILL.md](skills-contrib/draft-release-notes/SKILL.md)): "previous release" means the previous release tag — stable or RC — so upgrade-coverage diffs and release notes span exactly one release cycle on the RC line too. 6. **The policy documentation** ([docs/oss/versioning.md](docs/oss/versioning.md)): a new "The v8 RC line" section states the scheme, why `latest` tracks RCs for these packages, where the bare-`prisma` `next`-channel policy lives, and the one-time transition; the procedures are updated to match. ## Behavior changes & evidence - A release bump whose root version is `8.0.0-rc.N` publishes under `latest` with a pre-release GitHub Release; a stable bump behaves exactly as today. Implementation: [scripts/determine-version.ts](scripts/determine-version.ts), [.github/workflows/publish.yml](.github/workflows/publish.yml). Evidence: `computeNextReleaseVersion` and `assertCanonicalBase` suites in [scripts/determine-version-utils.test.ts](scripts/determine-version-utils.test.ts). - Dev builds on the RC line version as `8.0.0-rc.X-dev.N`, with the counter resetting whenever the base moves (new RC counter, stable→RC transition). Implementation: `composeDevVersion` in [scripts/determine-version-utils.ts](scripts/determine-version-utils.ts). Evidence: the `composeDevVersion` suite in [scripts/determine-version-utils.test.ts](scripts/determine-version-utils.test.ts). - `pnpm bump-version` from `0.17.0` produces `8.0.0-rc.1`; from `8.0.0-rc.1` produces `8.0.0-rc.2`; from a stable ≥ 8 produces the next minor. Implementation: [scripts/bump-version.ts](scripts/bump-version.ts). Evidence: the `computeNextReleaseVersion` suite in [scripts/determine-version-utils.test.ts](scripts/determine-version-utils.test.ts). - The publish-mode upgrade-coverage baseline resolves to the most recent release tag, including `v*-rc.N`. Implementation: [scripts/check-upgrade-coverage.mjs](scripts/check-upgrade-coverage.mjs). ## Summary Enables shipping the v8 RC early and iterating on it with frequent releases, using the exact publish flow that exists today — only the version shape and the pre-release marking change. ## Testing performed - `pnpm test:scripts` — 365 tests, 0 failures (includes the suites covering the new/changed version helpers). - `pnpm lint` (full repo), `pnpm lint:workflows`, `node scripts/validate-skills.mjs` — all green. - Local smoke-test of `determine-version.ts` against the real registry: dispatch resolves `0.17.0` → `latest`; push with unchanged version resolves `0.17.0-dev.N` → `dev` (continuing from the registry's actual counter). - `.github/workflows/publish.yml` parsed with the workspace `yaml` package to confirm validity after the edits. ## Skill update Updated in this PR: `skills-contrib/publish-npm-version` (RC-aware bump flow), `skills-contrib/draft-release-notes` (release-tag range bounds), `skills-contrib/record-upgrade-instructions` and `skills/extension-author/prisma-8-extension-upgrade` (`bump-version` rename). No end-user-facing CLI/API surface changes — the pipeline changes are maintainer-facing. ## Alternatives considered - **Publishing RCs under a `next` dist-tag and freezing `latest` at `0.17.0`** — rejected for these packages. Freezing `latest` protects a stable audience these package names don't have, npm publishes exactly one tag per publish so a second tag would need `npm dist-tag add` (which can't authenticate under OIDC trusted publishing — no long-lived token exists in this repo by design), and semver ranges already prevent any existing install from being moved onto an RC. The `next` channel remains the right design for the bare `prisma` package, whose `latest` genuinely must stay on v7; its v8 shim ships from [prisma/prisma-cli](https://github.com/prisma/prisma-cli). - **Nested pre-release identifiers (`8.0.0-rc.1.1`) for respins of a named RC** — rejected. Semver orders them correctly but no major ecosystem package does this (Drizzle, React, and TypeScript all use a flat counter), and the flat `rc.N` counter makes ordering and automation trivial. - **Keeping `bump-minor` and adding a separate RC bump script** — rejected; there is exactly one "advance to the next release version" operation and its meaning depends only on the current base's shape, so one script encoding the policy beats two scripts and a decision the maintainer must make each time. - **Bumping to `8.0.0-rc.1` in this same PR** — rejected to preserve the one-PR-per-release convention: merging a release bump is the publish trigger, and that merge should be its own reviewable event with its own release notes. ## Checklist - [x] All commits are signed off (`git commit -s`) per the [DCO](CONTRIBUTING.md#developer-certificate-of-origin-dco). The DCO status check will block merge if any commit is missing a `Signed-off-by:` trailer. - [x] I read [CONTRIBUTING.md](CONTRIBUTING.md) and the change is scoped to one logical concern. - [x] Tests are updated (or `n/a` if the change is doc-only / refactor with no behavioural delta). - [x] The PR title is in `TML-NNNN: <sentence-case title>` form (Linear ticket prefix + concise title naming the concrete deliverable). See `.claude/skills/create-pr/SKILL.md` for the full convention. - [x] The **Skill update** section above is filled in (or stated `n/a — internal only`). <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Added support for incremental release-candidate versions, including progression to the 8.0.0 release line. - Added development-version handling with consistent counter continuation and resets. - RC releases now use the `latest` channel and generate pre-release GitHub Releases. - **Documentation** - Updated versioning, publishing, release-note, and upgrade guidance for RC and stable releases. - Replaced the minor-bump workflow with the `pnpm bump-version` command. - Clarified tag, channel, and release-note requirements across publishing workflows. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> | 1 个月前 | |
TML-2533: enforce committed release-notes file as the GitHub Release body (#660) ## Linked issue Refs [TML-2533](https://linear.app/prisma-company/issue/TML-2533/set-up-automatic-release-notes). First of two slices. The follow-up [TML-2758](https://linear.app/prisma-company/issue/TML-2758) (blocked by this PR) adds the `draft-release-notes` skill that authors these files automatically; this PR establishes the committed-file contract it fills. ## At a glance The stable GitHub Release body now comes from a committed file instead of auto-generated PR titles. Each `latest` release ships a `docs/releases/v<version>.md`, and the publish workflow publishes it verbatim: ```yaml gh release create "v$VERSION" \ --target "$GITHUB_SHA" \ --title "v$VERSION" \ --notes-file "docs/releases/v$VERSION.md" ``` Before this PR, the same step used `--generate-notes`, which produced an uncurated list of merged-PR titles (internal `TML-NNNN:` prefixes and all), only viewable *after* the release was cut — the maintainer never got to review the notes before they shipped. ## Notes for the reviewer - **The change is an enforced invariant, not a default with an escape hatch.** A `latest` publish with no `docs/releases/v<version>.md` now *fails* rather than falling back to generated notes. That's deliberate — a missing file means someone skipped authoring, which is exactly what we want to catch. There is no `--generate-notes` fallback anywhere in `.github/workflows/` after this PR. - **The publish-mode gate is scoped to `latest` only**, unlike its siblings (`check:publish-deps`, `check:upgrade-coverage`) which run on every publish. Dev/beta builds create no GitHub Release, so there is nothing to gate — the gate step's `if:` is byte-identical to the Release step's own condition (`tag == 'latest'` ∧ non-dry-run). - **The `gh release edit` rerun branch is intentionally left untouched** (sets title/target only, preserves the already-published body). Re-pushing `--notes-file` on a rerun could clobber a hand-edited Release body. - **Largest diff is `publish.yml`** (+22/−6, mostly the gate step + a comment-block rewrite). Spot-check that no `--generate-notes` survives and that the rerun branch is unchanged. - **PR-mode shares a known, dispositioned edge case with `check-upgrade-coverage`**: its two-point `version` comparison would read a spurious "bump" on a branch sitting behind a `main` that has since bumped the version (self-corrects on rebase). Near-impossible in practice — release PRs are cut fresh off `origin/main`, and feature PRs don't change the root `version`. Any remedy should apply to *both* gates, so it's left as-is here. ## Decision This PR makes a **committed, gate-enforced file the authoritative source of every stable GitHub Release body.** Three pieces: 1. **`check:release-notes`** ([`scripts/check-release-notes.mjs`](scripts/check-release-notes.mjs)) — a two-mode presence gate mirroring the shape of the existing [`check-upgrade-coverage.mjs`](scripts/check-upgrade-coverage.mjs). 2. **Workflow wiring** — `publish.yml` publishes the committed file via `--notes-file` (no fallback) behind the gate; `ci.yml` runs the PR-mode gate so a release PR that bumps the version without its notes file fails *in review*. 3. **Convention + docs** — `docs/releases/README.md` (the convention + authoring template), a freshly-seeded `CHANGELOG.md`, and a `docs/oss/versioning.md` update threading the notes-file requirement through both the minor and patch procedures. ## How it works **Authoring + enforcement flow:** 1. A release PR commits `docs/releases/v<version>.md` alongside the version bump. [`docs/releases/README.md`](docs/releases/README.md) carries the template — section order **Breaking changes → Features → Fixes → New contributors**, written for users (no internal issue prefixes), with PR links and contributor attribution. 2. **PR mode** (`pnpm check:release-notes --mode pr`, wired into [`ci.yml`](.github/workflows/ci.yml)) fires only when a PR changes the root `package.json` `version`. It fails the release PR if the matching notes file is absent — so the omission is caught in review, not after merge. It no-ops on ordinary PRs. 3. **Publish mode** (`pnpm check:release-notes --mode publish`, wired into [`publish.yml`](.github/workflows/publish.yml)) runs immediately before the Release is created, for `latest` builds only, and fails the publish if the file is missing. 4. The Release is created with `--notes-file docs/releases/v<version>.md`; what's committed is exactly what readers see on the Releases page. ## What lands in this PR | Commit | What it adds | |---|---| | `0db8d4e3a` | `check:release-notes` presence gate (two modes) + unit tests + `package.json` wiring | | `7480b43a9` | `publish.yml` → `--notes-file` behind the publish-mode gate; `ci.yml` → PR-mode gate | | `6dce97ed2` | `docs/releases/README.md` convention + template, seeded `CHANGELOG.md`, `versioning.md` flow + patch-procedure update | No release-notes content (`docs/releases/v<version>.md`) is authored here — those are written when the next release is cut. Backfilling notes for `v0.11.0` and earlier is a non-goal; `CHANGELOG.md` points readers to GitHub Releases for that history and tracks forward from v0.12.0. ## Testing performed - `node --test scripts/check-release-notes.test.mjs` — 12/12 pass (both modes: missing/present file, version-bump/no-bump, `--version` override, `--json` envelope). - `node scripts/lint-workflow-triggers.mjs` — exit 0 (no new `on:` triggers). - `pnpm check:release-notes --mode pr` on this branch — exit 0 (this PR doesn't bump the root `version`, so PR mode no-ops as designed). - Both workflow YAMLs parse cleanly; relative doc links spot-checked against real targets. - The end-to-end check that a `latest` publish renders the committed file as the Release body is exercised on the next real release or a `workflow_dispatch` dry-run (can't run GitHub Actions from here). ## Skill update n/a — no user-facing API/CLI/config surface changes. The maintainer-facing release procedure is documented in `docs/oss/versioning.md` and `docs/releases/README.md`. The agent-authoring skill (`draft-release-notes`) is the separate follow-up TML-2758. ## Follow-ups - [TML-2758](https://linear.app/prisma-company/issue/TML-2758) — the `draft-release-notes` skill that authors `docs/releases/v<version>.md` automatically and wires into `publish-npm-version`. Blocked by this PR. ## Alternatives considered - **`.github/release.yml` + PR labels** to categorize GitHub's auto-generated notes. Rejected: it still generates from PR titles post-merge (no pre-merge review), and it imposes label discipline on every PR. - **A third-party release-notes tool** (release-drafter, changesets, etc.). Rejected: a new dependency and config surface for what a small committed-file convention + one gate script achieves, and it still wouldn't give the maintainer a reviewable file in the release PR. - **Keep `--generate-notes` as a fallback** when the file is absent. Rejected: a silent fallback is exactly the flat-notes outcome this project exists to remove. Failing loudly makes curated notes an invariant. ## Checklist - [x] All commits are signed off (`git commit -s`) per the DCO. - [x] I read CONTRIBUTING.md and the change is scoped to one logical concern. - [x] Tests are updated (`scripts/check-release-notes.test.mjs`). - [x] The PR title is in `TML-NNNN: <sentence-case title>` form. - [x] The **Skill update** section above is filled in. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Documentation** * Established standardized release notes format and authoring conventions for stable releases * Added comprehensive documentation for the release and versioning process * **Chores** * Introduced automated validation gates in CI to enforce committed release notes for all stable releases <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: Will Madden <madden@prisma.io> Co-authored-by: Will Madden <madden@prisma.io> | 4 个月前 | |
feat(cli): `prisma contract print` writes the configured contract as Prisma 8 PSL that reads back as the same contract (#30315) ## At a glance `prisma contract print` takes the contract your config already loads and writes it as a Prisma 8 PSL contract. Given this `prisma.config.ts`: ```ts contract: prisma7Schema('./prisma/schema.prisma'), ``` and this Prisma 7 schema: ```prisma enum Priority { LOW @map("low") HIGH @map("high") } model Post { id Int @id @default(autoincrement()) tags String[] meta Json @default("{\"draft\":true}") priority Priority @default(LOW) author User @relation(fields: [authorId], references: [id]) authorId Int } ``` running `prisma contract print --output prisma/contract.prisma` writes (the `User` model is left out here): ```prisma // use prisma-8 // Printed from prisma/schema.prisma by `prisma contract print`. namespace public { model Post { id Int @id @default(autoincrement()) tags String[]? @noCheck(elementNotNull) meta Jsonb @default(json`{"draft":true}`) priority pg.enum(Priority) @default("low") authorId Int author User @relation(fields: [authorId], references: [id], onDelete: Restrict, onUpdate: Cascade, index: false) } native_enum Priority { low = "low" high = "high" } } ``` Point `contract` at the written file, run `contract emit`, and the emitted contract is the one the Prisma 7 schema produced: the same serialized contract, including its hashes. Without `--output`, the command prints the PSL and writes no file: on screen in a terminal, to standard output with `--format human` (`prisma contract print --format human > printed.prisma`), or as `psl.text` in the JSON result. ## The decision The printer follows one rule: **the file it writes must read back as the same contract.** It writes each part of the contract as the PSL that reads back the same. A part with no such PSL is refused by name, and nothing is written. It never drops or changes part of the contract without saying so. This applies to any contract source, not only Prisma 7. The command loads whatever `contract` names in the config (a Prisma 7 schema, a TypeScript contract, or a PSL contract) and prints the loaded contract. The Prisma 7 cutover is the first use, and the reason the command exists, but nothing in the command is specific to Prisma 7. The rule is proven before release, not checked by the command at run time. An integration test prints every emitted Postgres contract tracked in the repo and requires each one to read back as the same contract, or to be refused with the reason the test expects. A new fixture is covered as soon as it is committed. ## How it works The command loads the contract the same way `contract emit` does, validates it the way `contract emit` does, hands it to the family's `buildPslContract`, and prints the document it returns. With `--output`, it writes the text with the same staged publish `contract infer` uses. One control stack serves the whole run: the source is loaded against it, the family instance is created from it, and its block descriptors and codecs render the text. The hook is named for what it returns, a PSL document; printing is one consumer of it. The SQL family passes the target's `buildPslContract` hook a `SqlPslBuildContext`: the stack's authoring types, codec lookup and data type lookup. The printer asks the stack which PSL type reads back as a column's codec, native type and parameters, so a column carried by an extension codec prints as that extension's type, such as `pgvector.Vector(3)`. Value-object field types and literal defaults are resolved the same way. The Postgres hook lives in `packages/3-targets/3-targets/postgres/src/core/psl-print/`. It shares its literal, index, enum-block and default-mapping builders with `contract infer` through `psl-build/`. It writes: - **Models and fields**, with `@@map` and `@map` only where the name the PSL reader would derive differs from the name in the contract. - **Types**: value objects as `type` blocks (lists of them as lists), named types as a `types` block, domain enums as `enum` blocks, native enums as `native_enum` blocks. - **Keys and indexes**: primary keys with their names, `@@unique`, and `@@index` with every argument the language has. - **Checks**, by their `name:` prefix when the wire name derives from it and by `map:` otherwise, minus the checks the reader derives for list and enum columns. - **Relations**, with the referential actions and constraint name their foreign key carries, and explicit junction models for many-to-many. - **Polymorphism**: `@@discriminator` on the base and `@@base` on each variant, for single-table and multi-table variants. - **Control policies** as `@@control`, and **row-level security** as `@@rls`, `policy_<operation>` blocks and `role` blocks in `namespace unbound`. - **Defaults**: literals through the same `mapDefault` as `contract infer`, keyed by the data type of the column's codec, so a Json object prints as a `json` tagged literal; `now()` and `autoincrement()` by name; id generators as `uuid()` and the rest; `@updatedAt` pairs as the `temporal.*` presets; every other database expression as a `sql` tagged literal. Every refusal lives in one module, `refusals.ts`, and covers a valid contract that PSL cannot express; the printer takes a validated contract, so it does not re-check structure. The module matches the `CONTRACT.PRINT_UNSUPPORTED` list in `docs/reference/error-reference.md` one to one; a test fails if the two lists differ in length. Each refusal names the model, field, column or entity. A PSL file cannot carry the contract's default control policy; the config sets it on the PSL source. When the contract has one, the command warns, names it in the next step and in the JSON result (`sourceSettings.defaultControlPolicy`), and the CLI and Postgres READMEs show a config that sets it. With `--output`, the command refuses to write over a file the project needs: any file the contract source reads (compared as real files, through symbolic links, case-insensitively on a volume that ignores case, and including every file a glob input matches or would match once written), `prisma.config.ts`, or the emitted `contract.json` and `contract.d.ts`. ## What is proven The rule is an equality. Every round-trip test prints, reads the text back through the PSL source with the same stack, and compares the serialized contracts, which carry the hashes. The comparison leaves out `capabilities` and `extensions`, which the composed stack reports rather than the source. - **Every Postgres contract in the repo** (`test/integration/test/psl-print/every-postgres-contract-roundtrip.integration.test.ts`): 272 emitted contracts. 249 print and read back as the same contract. The other 23 are refused, and the test lists each one with the reason its refusal must give. - **Every Prisma 7 fixture** (`test/integration/test/psl-print/prisma7-fixture-roundtrip.integration.test.ts`): 33 of the 35 fixtures round-trip. The other 2 declare one model name in two namespaces, and the test asserts their refusal. - **TypeScript-authored contracts** (`typescript-contract-roundtrip.integration.test.ts`): five contracts built with the TypeScript builder round-trip. - **PSL-authored cases and extension types** (`authored-contract-roundtrip.integration.test.ts`, `extension-types-roundtrip.integration.test.ts`): control policies, every index argument, domain enums, primary key names, non-default codecs, checks named by prefix, lists of value objects, row-level security with roles and policies, a model in the unbound namespace, and a `pgvector.Vector(3)` column. - **Every refusal** has a unit test that asserts its code and meta. Journeys run the command end to end. The `relations` and `supported-verify` Prisma 7 fixtures print, emit with the same storage hash the Prisma 7 source emitted, sign, and `db verify` with zero findings against the database built from the SQL Prisma 7.10.0 generated. A PSL source prints. A TypeScript contract with a default control policy prints with a warning, and the config the README shows emits the printed file with that policy. A schema with a `view`, and an `--output` path that is the schema being read, exit 2 and write nothing. ## Changes outside the printer - **Contract source format.** Every contract source states its `format`, `'psl'` or `'typescript'`, and the `orm` config schema rejects any other value or a missing one. A Prisma 7 schema is PSL text, so the Prisma 7 source declares `'psl'`, and `contract format` formats it. Upgrade instructions are in `upgrade-instructions/pending/contract-print/`. - **One PSL grammar.** The parser has no grammar option. A `view` body parses as fields in every document, and an `enum` member may carry `@` attributes in every document; each reader decides what it accepts. The SQL and Mongo readers report `PSL_UNSUPPORTED_ENUM_MEMBER_ATTRIBUTE`. That check and the unknown top-level block check live once in `@internal/psl-parser`, and both readers call them. - **Formatter.** It keeps a `//` comment written between a block's name and its `{`, moving it after the `{`. It writes a space before a list value after `:`, `,` or `=` (`fields: [authorId]`). Bare entries that shared a line are now written one per line. - **PSL printer.** It prints value-object `type` blocks, which it used to drop. It always writes the `// use prisma-8` marker, and each caller passes one description line. - **PSL reader.** - A scalar list field keeps its type parameters, so `Decimal @db.Numeric(65,30)[]` reads back with its type. One emitted fixture changes: an enum list field gains `typeParams.typeName`. Storage is unchanged. - A unique index over plain columns with no `where` makes a back-relation singular, as a unique constraint does. - It exports its naming rules (`pslModelMapName`, `pslFieldMapName`), so the printer writes `@@map` and `@map` by the same rules. - A policy expression decodes every JSON string escape. A PSL contract that wrote `\t` in a policy expression now reads a tab there; an upgrade note says so. - **CLI.** `contract emit`, `contract print`, `ControlClient.emit` and `orm init` load a contract source through one loader, which expands glob inputs. `contract print` validates the loaded contract the way `contract emit` does. - **`contract format`** formats every `.prisma` file under a source input that is a directory, as a Prisma 7 or Prisma 6 source may name one. - **Default emitted path.** The rule for where `contract emit` writes when the config sets no `output` lives once in `@internal/config`; the three facades and the CLI call it. - **Published surface.** `@prisma/orm-family-sql` gains `./contract-psl/map-names` (the two naming rules the printer shares with the reader) and `./family/psl-build`. - **`check-upgrade-coverage`** lists only the directories it reads. It listed the whole repository tree, which passed the 1 MiB child-process output limit. - **Framework vocabulary lint.** It flags the name of any Prisma version before 8 in `packages/1-framework`, except in the CLI, which names Prisma 7 when `orm init` sets Prisma 8 up beside a Prisma 7 project. ## What it cannot write yet The full list is under `CONTRACT.PRINT_UNSUPPORTED` in the error reference, and `projects/prisma7-contract-source/spec.md` records what would lift each. The ones a user is most likely to meet: - A relation into another contract space, such as a Supabase app's relation to `supabase:auth.AuthUser`. PSL can write it; the printer would need the composed extension contracts. - One model name in two namespaces. The PSL reader groups relations by bare model name. - A domain enum or value object outside the default namespace, and a value-object field with type parameters or a value set. The PSL reader does not carry them back. - A foreign key no relation travels, a to-one relation with no foreign key, a back-relation with no owning relation on the other model, and a relation with no `on` part. - A namespace whose name is not a PSL identifier or is `unbound`, and any name `__proto__`, which the PSL reader loses. - A union or dictionary field, a column with its own control policy, a model with an owner, and an entity kind a pack contributes. None has PSL syntax the printer can write. ## Alternatives considered - **Write a file by default, as `contract infer` does.** Rejected: the name `print` would be wrong about what the command does, and in a PSL project the default path would be the contract source itself, so the command would refuse whenever it ran without flags. Printing by default can never overwrite a file. - **Restrict the command to a Prisma 7 source.** That would hide the parts a Prisma 7 schema never produces (value objects, polymorphism, named types, control policies) instead of printing them. Rejected: a printer that drops what it does not understand is not safe behind any check, and the restriction would make the command's name wrong about what it does. - **Read the written file back inside the command and refuse on a mismatch.** Rejected: a published command that refuses its own output is not useful to users. Gaps must be found before release, which is what the test over every contract in the repo does. - **Name the Prisma 7 source's format `'prisma7'` in the framework, or give the parser a `prisma7` grammar.** Rejected: the framework supports PSL and TypeScript, and a Prisma 7 schema is PSL. The grammar is general; only the readers differ. - **Pick a column's PSL type from a table the target keeps.** Rejected: such a table cannot see extension types, and the stack already knows every type it can read back. 🤖 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 <noreply@anthropic.com> | 7 天前 | |
feat(cli): `prisma contract print` writes the configured contract as Prisma 8 PSL that reads back as the same contract (#30315) ## At a glance `prisma contract print` takes the contract your config already loads and writes it as a Prisma 8 PSL contract. Given this `prisma.config.ts`: ```ts contract: prisma7Schema('./prisma/schema.prisma'), ``` and this Prisma 7 schema: ```prisma enum Priority { LOW @map("low") HIGH @map("high") } model Post { id Int @id @default(autoincrement()) tags String[] meta Json @default("{\"draft\":true}") priority Priority @default(LOW) author User @relation(fields: [authorId], references: [id]) authorId Int } ``` running `prisma contract print --output prisma/contract.prisma` writes (the `User` model is left out here): ```prisma // use prisma-8 // Printed from prisma/schema.prisma by `prisma contract print`. namespace public { model Post { id Int @id @default(autoincrement()) tags String[]? @noCheck(elementNotNull) meta Jsonb @default(json`{"draft":true}`) priority pg.enum(Priority) @default("low") authorId Int author User @relation(fields: [authorId], references: [id], onDelete: Restrict, onUpdate: Cascade, index: false) } native_enum Priority { low = "low" high = "high" } } ``` Point `contract` at the written file, run `contract emit`, and the emitted contract is the one the Prisma 7 schema produced: the same serialized contract, including its hashes. Without `--output`, the command prints the PSL and writes no file: on screen in a terminal, to standard output with `--format human` (`prisma contract print --format human > printed.prisma`), or as `psl.text` in the JSON result. ## The decision The printer follows one rule: **the file it writes must read back as the same contract.** It writes each part of the contract as the PSL that reads back the same. A part with no such PSL is refused by name, and nothing is written. It never drops or changes part of the contract without saying so. This applies to any contract source, not only Prisma 7. The command loads whatever `contract` names in the config (a Prisma 7 schema, a TypeScript contract, or a PSL contract) and prints the loaded contract. The Prisma 7 cutover is the first use, and the reason the command exists, but nothing in the command is specific to Prisma 7. The rule is proven before release, not checked by the command at run time. An integration test prints every emitted Postgres contract tracked in the repo and requires each one to read back as the same contract, or to be refused with the reason the test expects. A new fixture is covered as soon as it is committed. ## How it works The command loads the contract the same way `contract emit` does, validates it the way `contract emit` does, hands it to the family's `buildPslContract`, and prints the document it returns. With `--output`, it writes the text with the same staged publish `contract infer` uses. One control stack serves the whole run: the source is loaded against it, the family instance is created from it, and its block descriptors and codecs render the text. The hook is named for what it returns, a PSL document; printing is one consumer of it. The SQL family passes the target's `buildPslContract` hook a `SqlPslBuildContext`: the stack's authoring types, codec lookup and data type lookup. The printer asks the stack which PSL type reads back as a column's codec, native type and parameters, so a column carried by an extension codec prints as that extension's type, such as `pgvector.Vector(3)`. Value-object field types and literal defaults are resolved the same way. The Postgres hook lives in `packages/3-targets/3-targets/postgres/src/core/psl-print/`. It shares its literal, index, enum-block and default-mapping builders with `contract infer` through `psl-build/`. It writes: - **Models and fields**, with `@@map` and `@map` only where the name the PSL reader would derive differs from the name in the contract. - **Types**: value objects as `type` blocks (lists of them as lists), named types as a `types` block, domain enums as `enum` blocks, native enums as `native_enum` blocks. - **Keys and indexes**: primary keys with their names, `@@unique`, and `@@index` with every argument the language has. - **Checks**, by their `name:` prefix when the wire name derives from it and by `map:` otherwise, minus the checks the reader derives for list and enum columns. - **Relations**, with the referential actions and constraint name their foreign key carries, and explicit junction models for many-to-many. - **Polymorphism**: `@@discriminator` on the base and `@@base` on each variant, for single-table and multi-table variants. - **Control policies** as `@@control`, and **row-level security** as `@@rls`, `policy_<operation>` blocks and `role` blocks in `namespace unbound`. - **Defaults**: literals through the same `mapDefault` as `contract infer`, keyed by the data type of the column's codec, so a Json object prints as a `json` tagged literal; `now()` and `autoincrement()` by name; id generators as `uuid()` and the rest; `@updatedAt` pairs as the `temporal.*` presets; every other database expression as a `sql` tagged literal. Every refusal lives in one module, `refusals.ts`, and covers a valid contract that PSL cannot express; the printer takes a validated contract, so it does not re-check structure. The module matches the `CONTRACT.PRINT_UNSUPPORTED` list in `docs/reference/error-reference.md` one to one; a test fails if the two lists differ in length. Each refusal names the model, field, column or entity. A PSL file cannot carry the contract's default control policy; the config sets it on the PSL source. When the contract has one, the command warns, names it in the next step and in the JSON result (`sourceSettings.defaultControlPolicy`), and the CLI and Postgres READMEs show a config that sets it. With `--output`, the command refuses to write over a file the project needs: any file the contract source reads (compared as real files, through symbolic links, case-insensitively on a volume that ignores case, and including every file a glob input matches or would match once written), `prisma.config.ts`, or the emitted `contract.json` and `contract.d.ts`. ## What is proven The rule is an equality. Every round-trip test prints, reads the text back through the PSL source with the same stack, and compares the serialized contracts, which carry the hashes. The comparison leaves out `capabilities` and `extensions`, which the composed stack reports rather than the source. - **Every Postgres contract in the repo** (`test/integration/test/psl-print/every-postgres-contract-roundtrip.integration.test.ts`): 272 emitted contracts. 249 print and read back as the same contract. The other 23 are refused, and the test lists each one with the reason its refusal must give. - **Every Prisma 7 fixture** (`test/integration/test/psl-print/prisma7-fixture-roundtrip.integration.test.ts`): 33 of the 35 fixtures round-trip. The other 2 declare one model name in two namespaces, and the test asserts their refusal. - **TypeScript-authored contracts** (`typescript-contract-roundtrip.integration.test.ts`): five contracts built with the TypeScript builder round-trip. - **PSL-authored cases and extension types** (`authored-contract-roundtrip.integration.test.ts`, `extension-types-roundtrip.integration.test.ts`): control policies, every index argument, domain enums, primary key names, non-default codecs, checks named by prefix, lists of value objects, row-level security with roles and policies, a model in the unbound namespace, and a `pgvector.Vector(3)` column. - **Every refusal** has a unit test that asserts its code and meta. Journeys run the command end to end. The `relations` and `supported-verify` Prisma 7 fixtures print, emit with the same storage hash the Prisma 7 source emitted, sign, and `db verify` with zero findings against the database built from the SQL Prisma 7.10.0 generated. A PSL source prints. A TypeScript contract with a default control policy prints with a warning, and the config the README shows emits the printed file with that policy. A schema with a `view`, and an `--output` path that is the schema being read, exit 2 and write nothing. ## Changes outside the printer - **Contract source format.** Every contract source states its `format`, `'psl'` or `'typescript'`, and the `orm` config schema rejects any other value or a missing one. A Prisma 7 schema is PSL text, so the Prisma 7 source declares `'psl'`, and `contract format` formats it. Upgrade instructions are in `upgrade-instructions/pending/contract-print/`. - **One PSL grammar.** The parser has no grammar option. A `view` body parses as fields in every document, and an `enum` member may carry `@` attributes in every document; each reader decides what it accepts. The SQL and Mongo readers report `PSL_UNSUPPORTED_ENUM_MEMBER_ATTRIBUTE`. That check and the unknown top-level block check live once in `@internal/psl-parser`, and both readers call them. - **Formatter.** It keeps a `//` comment written between a block's name and its `{`, moving it after the `{`. It writes a space before a list value after `:`, `,` or `=` (`fields: [authorId]`). Bare entries that shared a line are now written one per line. - **PSL printer.** It prints value-object `type` blocks, which it used to drop. It always writes the `// use prisma-8` marker, and each caller passes one description line. - **PSL reader.** - A scalar list field keeps its type parameters, so `Decimal @db.Numeric(65,30)[]` reads back with its type. One emitted fixture changes: an enum list field gains `typeParams.typeName`. Storage is unchanged. - A unique index over plain columns with no `where` makes a back-relation singular, as a unique constraint does. - It exports its naming rules (`pslModelMapName`, `pslFieldMapName`), so the printer writes `@@map` and `@map` by the same rules. - A policy expression decodes every JSON string escape. A PSL contract that wrote `\t` in a policy expression now reads a tab there; an upgrade note says so. - **CLI.** `contract emit`, `contract print`, `ControlClient.emit` and `orm init` load a contract source through one loader, which expands glob inputs. `contract print` validates the loaded contract the way `contract emit` does. - **`contract format`** formats every `.prisma` file under a source input that is a directory, as a Prisma 7 or Prisma 6 source may name one. - **Default emitted path.** The rule for where `contract emit` writes when the config sets no `output` lives once in `@internal/config`; the three facades and the CLI call it. - **Published surface.** `@prisma/orm-family-sql` gains `./contract-psl/map-names` (the two naming rules the printer shares with the reader) and `./family/psl-build`. - **`check-upgrade-coverage`** lists only the directories it reads. It listed the whole repository tree, which passed the 1 MiB child-process output limit. - **Framework vocabulary lint.** It flags the name of any Prisma version before 8 in `packages/1-framework`, except in the CLI, which names Prisma 7 when `orm init` sets Prisma 8 up beside a Prisma 7 project. ## What it cannot write yet The full list is under `CONTRACT.PRINT_UNSUPPORTED` in the error reference, and `projects/prisma7-contract-source/spec.md` records what would lift each. The ones a user is most likely to meet: - A relation into another contract space, such as a Supabase app's relation to `supabase:auth.AuthUser`. PSL can write it; the printer would need the composed extension contracts. - One model name in two namespaces. The PSL reader groups relations by bare model name. - A domain enum or value object outside the default namespace, and a value-object field with type parameters or a value set. The PSL reader does not carry them back. - A foreign key no relation travels, a to-one relation with no foreign key, a back-relation with no owning relation on the other model, and a relation with no `on` part. - A namespace whose name is not a PSL identifier or is `unbound`, and any name `__proto__`, which the PSL reader loses. - A union or dictionary field, a column with its own control policy, a model with an owner, and an entity kind a pack contributes. None has PSL syntax the printer can write. ## Alternatives considered - **Write a file by default, as `contract infer` does.** Rejected: the name `print` would be wrong about what the command does, and in a PSL project the default path would be the contract source itself, so the command would refuse whenever it ran without flags. Printing by default can never overwrite a file. - **Restrict the command to a Prisma 7 source.** That would hide the parts a Prisma 7 schema never produces (value objects, polymorphism, named types, control policies) instead of printing them. Rejected: a printer that drops what it does not understand is not safe behind any check, and the restriction would make the command's name wrong about what it does. - **Read the written file back inside the command and refuse on a mismatch.** Rejected: a published command that refuses its own output is not useful to users. Gaps must be found before release, which is what the test over every contract in the repo does. - **Name the Prisma 7 source's format `'prisma7'` in the framework, or give the parser a `prisma7` grammar.** Rejected: the framework supports PSL and TypeScript, and a Prisma 7 schema is PSL. The grammar is general; only the readers differ. - **Pick a column's PSL type from a table the target keeps.** Rejected: such a table cannot see extension types, and the stack already knows every type it can read back. 🤖 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 <noreply@anthropic.com> | 7 天前 | |
ci: combine package tests and coverage (#30082) ## Linked issue n/a — this infrastructure migration has no Linear ticket. ## At a glance ```json "coverage:packages": "turbo run build --filter='!./examples/**' --filter='!./test/**' && vitest run --coverage", "coverage:report": "node scripts/coverage-report.mjs" ``` One root Vitest invocation now runs package tests and collects coverage, replacing the duplicated package-test and coverage CI jobs. ## Decision This PR ships three related changes: 1. Package tests and package coverage run together in one root Vitest multi-project invocation on Vitest `5.0.0-rc.2`. 2. Each package owns its complete coverage policy in an adjacent `coverage.config.json`, while root composition and post-processing preserve package thresholds and time-limited warning-only exceptions. 3. The obsolete, type-test-only SQL lane query-builder package and its public facade export are removed instead of retaining a permanently unmeasurable 95% runtime-coverage policy. ## Reviewer notes - The broad config diff is mostly moving existing coverage include/exclude/threshold blocks from `vitest.config.ts` into adjacent JSON policies and removing now-redundant package coverage scripts. - Vitest 5 removes `describe.sequential`; affected suites now use `{ concurrent: false }`. Compile-only `.test-d.ts` suites also declare compile-time test cases so Vitest 5 recognizes them. - `examples/prisma-8-cloudflare-worker` intentionally remains on Vitest 4 because `@cloudflare/vitest-pool-workers@0.20.3` requires Vitest 4 peers. - Eight existing package coverage deficits remain visible as active, non-blocking warning-only entries. Expired warnings and ordinary threshold failures still block CI. ## How it fits together 1. `scripts/coverage-config.js` discovers and validates package policies deterministically, rebases package globs to the repository root, and composes process-wide V8 collection settings. 2. The root `vitest.config.ts` references every package project and applies the composed coverage settings to a single test process. 3. `scripts/coverage-report.mjs` reads the root `coverage/coverage-final.json`, attributes files to their owning package, calculates all four metrics, and enforces each package's policy and warning expiry. 4. `.github/workflows/ci.yml` runs `pnpm coverage:packages` in the test job, reports package coverage even when collection finds a test failure, and removes the standalone coverage job. Test failures remain blocking. 5. Vitest 5 compatibility updates keep type tests, sequential suites, and CLI module mocks deterministic under the new runner behavior. ## Behavior changes & evidence - **Package tests execute once in CI while still producing coverage.** The combined command and workflow live in [`package.json`](package.json) and [`.github/workflows/ci.yml`](.github/workflows/ci.yml); [`scripts/coverage-config.test.mjs`](scripts/coverage-config.test.mjs) guards the single-run workflow shape. - **Coverage ownership remains package-local and threshold enforcement remains package-aware.** Composition is implemented in [`scripts/coverage-config.js`](scripts/coverage-config.js), reporting in [`scripts/coverage-report.mjs`](scripts/coverage-report.mjs), and exercised by [`scripts/coverage-report.test.mjs`](scripts/coverage-report.test.mjs). - **Vitest 5 runs the workspace without the previous V8 merge bottleneck.** The workspace pins are in [`package.json`](package.json) and [`pnpm-lock.yaml`](pnpm-lock.yaml); representative compatibility fixes are covered by [`packages/1-framework/3-tooling/cli/test/migration-cli.test.ts`](packages/1-framework/3-tooling/cli/test/migration-cli.test.ts) and the migrated type-test suites. - **The obsolete SQL lane query-builder is no longer published.** Its package is removed, along with the facade dependency/export in [`packages/9-public/@prisma/orm-family-sql/package.json`](packages/9-public/@prisma/orm-family-sql/package.json) and publish-surface mapping in [`packages/0-shared/publish-surface/src/shells.ts`](packages/0-shared/publish-surface/src/shells.ts). ## Compatibility / migration / risk This is a pre-1.0 breaking cleanup: `@internal/sql-lane-query-builder` and `@prisma/orm-family-sql/lane-query-builder` are removed. Repository references and generated facade wiring were removed together, and the public SQL family shell rebuilds without them. Coverage semantics remain package-specific; only orchestration and report aggregation change. ## Testing performed - `CI=true TEST_TIMEOUT_MULTIPLIER=2 pnpm coverage:packages` — 1,155 files passed; 15,311 tests passed, 3 expected failures, no type errors - `pnpm coverage:report` — 69 package policies, 0 blocking failures, 8 active warnings, 0 expired warnings - `pnpm test:scripts` — 476 tests passed - `pnpm lint:deps` - `pnpm lint:manifests` - `pnpm build --filter=@prisma/orm-family-sql...` - Publish-surface tests and typecheck — 56 tests passed - Focused package tests/typechecks for CLI, Mongo runtime, SQL ORM client, SQLite codec testkit, integration tests, examples, and shell tarballs - `pnpm install --frozen-lockfile --ignore-scripts` - Targeted Biome checks and `git diff --check` ## Skill update n/a — the removed prototype query-builder export was not referenced by any user-facing skill; its package, public README, architecture docs, and publish surface were updated directly. ## Alternatives considered - **Keep Vitest 4 and optimize around it:** the single V8 run remained CPU-bound for more than 37 minutes because the relevant V8 merge optimization is only available in Vitest 5; the Vitest 4 backport was not merged. - **Switch to Istanbul coverage:** benchmarking was slower and introduced CLI language-server instrumentation timeouts, so V8 remains the provider. - **Run packages sequentially:** this preserves policy isolation but repeats runner startup and cannot eliminate duplicate test execution in CI; root collection plus package-aware post-processing keeps policy ownership without that cost. ## Checklist - [x] All commits are signed off (`git commit -s`) per the DCO. - [x] I read `CONTRIBUTING.md` and the change is scoped to one logical concern. - [x] Tests are updated. - [ ] The PR title is in `TML-NNNN: <sentence-case title>` form — no Linear ticket exists, so this uses the conventional commit title required by `CONTRIBUTING.md`. - [x] The **Skill update** section is filled in. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **Breaking Changes** - Removed the SQL lane query-builder package and its public package export. - Updated SQL documentation and package entrypoint references. - **Testing & Quality** - Centralized package coverage reporting with package-specific thresholds, exclusions, and warning policies. - Improved coverage validation, threshold reporting, and CI integration. - Updated serialized integration-test execution for compatibility with the current test runner. - **Documentation** - Expanded testing guidance for package coverage workflows and CI behavior. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: Steven McClankerton <tatarintsev@prisma.io> Co-authored-by: Steven McClankerton <tatarintsev@prisma.io> | 1 个月前 | |
ci: load Supabase stack images from the Actions cache (#30526) The Supabase Acceptance job now loads the local Supabase stack's Docker images from the Actions cache instead of pulling them from `public.ecr.aws` on every run. On 2026-09-29, three of four runs failed like this: ``` postgres Error toomanyrequests: Data limit exceeded failed to pull docker image: Error response from daemon: toomanyrequests: Data limit exceeded Retrying after 4s: public.ecr.aws/supabase/postgres:17.6.1.106 ``` `supabase start` pulls 13 images, about 2 GB compressed, anonymously. The registry limits how much data anonymous clients can pull, and our CI hits that limit. ## Why a cache works here The images never change unless we change something. Supabase CLI 2.95.4 pins every image tag, and `examples/supabase/supabase/config.toml` decides which services run. The CLI also skips pulling any image that is already present locally. So if the images are loaded with `docker load` before `supabase start`, the job makes no registry requests. ## Where the cache entry is written A run can restore cache entries written on its own ref or on the default branch. `ci.yml` runs only on `pull_request` and `merge_group`, so an entry it wrote would be visible to that one PR or queue entry, and every PR would store its own 1.6 GB copy. The repo cache is already near its 10 GB limit. So a new workflow, `supabase-images.yml`, writes the entry on `main`, where every PR and merge-queue run can read it. It runs: - on pushes to `main` that change the CLI version or the Supabase config, so a CLI bump is cached as soon as it lands; - daily, to re-create the entry if GitHub evicted it (it exits in seconds when the entry exists); - on manual dispatch. When the entry is missing, it runs `supabase start` (which pulls exactly the images CI needs), saves every `public.ecr.aws/supabase/*` image with `docker save`, and stores the archive. `ci.yml` only restores. ## Changes - `.github/actions/supabase-cli/` (new): `install.sh` installs the pinned CLI binary (the logic that was inline in `ci.yml`) and writes the cache key and archive path as the action's outputs. The key is `supabase-images-<os>-<cli version>-<hash of config.toml>`, so bumping the CLI or changing the config picks a new entry. - `ci.yml` `supabase-acceptance`: uses the action, restores the archive, and runs `docker load` on a hit. On a miss, `supabase start` pulls from the registry as before. - `.github/workflows/supabase-images.yml` (new): writes the entry on `main`. `scripts/save-supabase-images.sh` saves the images `supabase start` pulled. - `scripts/coverage-config.test.mjs`: the check that coverage shards pass results through artifacts, not the cache, now looks at the coverage jobs only. It checked the whole of `ci.yml`, so it rejected the Supabase job's cache restore. ## Verification - A temporary commit ran the cache-writing workflow on this PR ([run](https://github.com/prisma/orm/actions/runs/36672462597)). It stored a 1.6 GB entry. The Supabase Acceptance job in the same push ([run](https://github.com/prisma/orm/actions/runs/36672462668)) restored it, loaded all 13 images, and `supabase start` pulled nothing. The acceptance tests passed. The temporary commit has been dropped. - `actionlint`, `pnpm lint:workflows` and `pnpm test:scripts` pass. ## Cost With the cache, the job takes about 2 minutes longer: restoring takes 41s and `docker load` takes 2m14s, where pulling took about a minute. The job still finishes long before Integration Tests, so total CI time does not change. This PR's own merge-queue run cannot read the cache yet, because the entry on `main` is written after merge. That one run still pulls from the registry. ## Alternatives considered - **Mirror the images to GHCR** and point the CLI at it with `SUPABASE_INTERNAL_IMAGE_REGISTRY`. This avoids using cache space, but it needs a package-publishing workflow with `packages: write`, and the packages would have to be public so fork PRs can pull them. Every run would still download 2 GB. - **Pull from `ghcr.io/supabase` or Docker Hub** instead. Every run still pulls, and Docker Hub's anonymous limits are stricter. - **Exclude services the test does not use** (`supabase start -x studio,logflare,…`). That would shrink the cache entry to about 0.8 GB, but it changes the stack the job tests against, which the job exists to keep stock. - **Save the cache from the CI job itself.** Each PR would write its own 1.6 GB entry, which the cache limit cannot absorb. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Chores** * Updated the Supabase acceptance-test setup to reuse cached container images, reducing repeated image downloads when a cache is available. * Added a scheduled and manually triggerable workflow to refresh the cached images, with refreshes also triggered by relevant changes on the main branch. * Standardized CI installation of the Supabase CLI and added integrity verification for the downloaded release. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- 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> | 4 天前 | |
ci: combine package tests and coverage (#30082) ## Linked issue n/a — this infrastructure migration has no Linear ticket. ## At a glance ```json "coverage:packages": "turbo run build --filter='!./examples/**' --filter='!./test/**' && vitest run --coverage", "coverage:report": "node scripts/coverage-report.mjs" ``` One root Vitest invocation now runs package tests and collects coverage, replacing the duplicated package-test and coverage CI jobs. ## Decision This PR ships three related changes: 1. Package tests and package coverage run together in one root Vitest multi-project invocation on Vitest `5.0.0-rc.2`. 2. Each package owns its complete coverage policy in an adjacent `coverage.config.json`, while root composition and post-processing preserve package thresholds and time-limited warning-only exceptions. 3. The obsolete, type-test-only SQL lane query-builder package and its public facade export are removed instead of retaining a permanently unmeasurable 95% runtime-coverage policy. ## Reviewer notes - The broad config diff is mostly moving existing coverage include/exclude/threshold blocks from `vitest.config.ts` into adjacent JSON policies and removing now-redundant package coverage scripts. - Vitest 5 removes `describe.sequential`; affected suites now use `{ concurrent: false }`. Compile-only `.test-d.ts` suites also declare compile-time test cases so Vitest 5 recognizes them. - `examples/prisma-8-cloudflare-worker` intentionally remains on Vitest 4 because `@cloudflare/vitest-pool-workers@0.20.3` requires Vitest 4 peers. - Eight existing package coverage deficits remain visible as active, non-blocking warning-only entries. Expired warnings and ordinary threshold failures still block CI. ## How it fits together 1. `scripts/coverage-config.js` discovers and validates package policies deterministically, rebases package globs to the repository root, and composes process-wide V8 collection settings. 2. The root `vitest.config.ts` references every package project and applies the composed coverage settings to a single test process. 3. `scripts/coverage-report.mjs` reads the root `coverage/coverage-final.json`, attributes files to their owning package, calculates all four metrics, and enforces each package's policy and warning expiry. 4. `.github/workflows/ci.yml` runs `pnpm coverage:packages` in the test job, reports package coverage even when collection finds a test failure, and removes the standalone coverage job. Test failures remain blocking. 5. Vitest 5 compatibility updates keep type tests, sequential suites, and CLI module mocks deterministic under the new runner behavior. ## Behavior changes & evidence - **Package tests execute once in CI while still producing coverage.** The combined command and workflow live in [`package.json`](package.json) and [`.github/workflows/ci.yml`](.github/workflows/ci.yml); [`scripts/coverage-config.test.mjs`](scripts/coverage-config.test.mjs) guards the single-run workflow shape. - **Coverage ownership remains package-local and threshold enforcement remains package-aware.** Composition is implemented in [`scripts/coverage-config.js`](scripts/coverage-config.js), reporting in [`scripts/coverage-report.mjs`](scripts/coverage-report.mjs), and exercised by [`scripts/coverage-report.test.mjs`](scripts/coverage-report.test.mjs). - **Vitest 5 runs the workspace without the previous V8 merge bottleneck.** The workspace pins are in [`package.json`](package.json) and [`pnpm-lock.yaml`](pnpm-lock.yaml); representative compatibility fixes are covered by [`packages/1-framework/3-tooling/cli/test/migration-cli.test.ts`](packages/1-framework/3-tooling/cli/test/migration-cli.test.ts) and the migrated type-test suites. - **The obsolete SQL lane query-builder is no longer published.** Its package is removed, along with the facade dependency/export in [`packages/9-public/@prisma/orm-family-sql/package.json`](packages/9-public/@prisma/orm-family-sql/package.json) and publish-surface mapping in [`packages/0-shared/publish-surface/src/shells.ts`](packages/0-shared/publish-surface/src/shells.ts). ## Compatibility / migration / risk This is a pre-1.0 breaking cleanup: `@internal/sql-lane-query-builder` and `@prisma/orm-family-sql/lane-query-builder` are removed. Repository references and generated facade wiring were removed together, and the public SQL family shell rebuilds without them. Coverage semantics remain package-specific; only orchestration and report aggregation change. ## Testing performed - `CI=true TEST_TIMEOUT_MULTIPLIER=2 pnpm coverage:packages` — 1,155 files passed; 15,311 tests passed, 3 expected failures, no type errors - `pnpm coverage:report` — 69 package policies, 0 blocking failures, 8 active warnings, 0 expired warnings - `pnpm test:scripts` — 476 tests passed - `pnpm lint:deps` - `pnpm lint:manifests` - `pnpm build --filter=@prisma/orm-family-sql...` - Publish-surface tests and typecheck — 56 tests passed - Focused package tests/typechecks for CLI, Mongo runtime, SQL ORM client, SQLite codec testkit, integration tests, examples, and shell tarballs - `pnpm install --frozen-lockfile --ignore-scripts` - Targeted Biome checks and `git diff --check` ## Skill update n/a — the removed prototype query-builder export was not referenced by any user-facing skill; its package, public README, architecture docs, and publish surface were updated directly. ## Alternatives considered - **Keep Vitest 4 and optimize around it:** the single V8 run remained CPU-bound for more than 37 minutes because the relevant V8 merge optimization is only available in Vitest 5; the Vitest 4 backport was not merged. - **Switch to Istanbul coverage:** benchmarking was slower and introduced CLI language-server instrumentation timeouts, so V8 remains the provider. - **Run packages sequentially:** this preserves policy isolation but repeats runner startup and cannot eliminate duplicate test execution in CI; root collection plus package-aware post-processing keeps policy ownership without that cost. ## Checklist - [x] All commits are signed off (`git commit -s`) per the DCO. - [x] I read `CONTRIBUTING.md` and the change is scoped to one logical concern. - [x] Tests are updated. - [ ] The PR title is in `TML-NNNN: <sentence-case title>` form — no Linear ticket exists, so this uses the conventional commit title required by `CONTRIBUTING.md`. - [x] The **Skill update** section is filled in. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **Breaking Changes** - Removed the SQL lane query-builder package and its public package export. - Updated SQL documentation and package entrypoint references. - **Testing & Quality** - Centralized package coverage reporting with package-specific thresholds, exclusions, and warning policies. - Improved coverage validation, threshold reporting, and CI integration. - Updated serialized integration-test execution for compatibility with the current test runner. - **Documentation** - Expanded testing guidance for package coverage workflows and CI behavior. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: Steven McClankerton <tatarintsev@prisma.io> Co-authored-by: Steven McClankerton <tatarintsev@prisma.io> | 1 个月前 | |
ci: combine package tests and coverage (#30082) ## Linked issue n/a — this infrastructure migration has no Linear ticket. ## At a glance ```json "coverage:packages": "turbo run build --filter='!./examples/**' --filter='!./test/**' && vitest run --coverage", "coverage:report": "node scripts/coverage-report.mjs" ``` One root Vitest invocation now runs package tests and collects coverage, replacing the duplicated package-test and coverage CI jobs. ## Decision This PR ships three related changes: 1. Package tests and package coverage run together in one root Vitest multi-project invocation on Vitest `5.0.0-rc.2`. 2. Each package owns its complete coverage policy in an adjacent `coverage.config.json`, while root composition and post-processing preserve package thresholds and time-limited warning-only exceptions. 3. The obsolete, type-test-only SQL lane query-builder package and its public facade export are removed instead of retaining a permanently unmeasurable 95% runtime-coverage policy. ## Reviewer notes - The broad config diff is mostly moving existing coverage include/exclude/threshold blocks from `vitest.config.ts` into adjacent JSON policies and removing now-redundant package coverage scripts. - Vitest 5 removes `describe.sequential`; affected suites now use `{ concurrent: false }`. Compile-only `.test-d.ts` suites also declare compile-time test cases so Vitest 5 recognizes them. - `examples/prisma-8-cloudflare-worker` intentionally remains on Vitest 4 because `@cloudflare/vitest-pool-workers@0.20.3` requires Vitest 4 peers. - Eight existing package coverage deficits remain visible as active, non-blocking warning-only entries. Expired warnings and ordinary threshold failures still block CI. ## How it fits together 1. `scripts/coverage-config.js` discovers and validates package policies deterministically, rebases package globs to the repository root, and composes process-wide V8 collection settings. 2. The root `vitest.config.ts` references every package project and applies the composed coverage settings to a single test process. 3. `scripts/coverage-report.mjs` reads the root `coverage/coverage-final.json`, attributes files to their owning package, calculates all four metrics, and enforces each package's policy and warning expiry. 4. `.github/workflows/ci.yml` runs `pnpm coverage:packages` in the test job, reports package coverage even when collection finds a test failure, and removes the standalone coverage job. Test failures remain blocking. 5. Vitest 5 compatibility updates keep type tests, sequential suites, and CLI module mocks deterministic under the new runner behavior. ## Behavior changes & evidence - **Package tests execute once in CI while still producing coverage.** The combined command and workflow live in [`package.json`](package.json) and [`.github/workflows/ci.yml`](.github/workflows/ci.yml); [`scripts/coverage-config.test.mjs`](scripts/coverage-config.test.mjs) guards the single-run workflow shape. - **Coverage ownership remains package-local and threshold enforcement remains package-aware.** Composition is implemented in [`scripts/coverage-config.js`](scripts/coverage-config.js), reporting in [`scripts/coverage-report.mjs`](scripts/coverage-report.mjs), and exercised by [`scripts/coverage-report.test.mjs`](scripts/coverage-report.test.mjs). - **Vitest 5 runs the workspace without the previous V8 merge bottleneck.** The workspace pins are in [`package.json`](package.json) and [`pnpm-lock.yaml`](pnpm-lock.yaml); representative compatibility fixes are covered by [`packages/1-framework/3-tooling/cli/test/migration-cli.test.ts`](packages/1-framework/3-tooling/cli/test/migration-cli.test.ts) and the migrated type-test suites. - **The obsolete SQL lane query-builder is no longer published.** Its package is removed, along with the facade dependency/export in [`packages/9-public/@prisma/orm-family-sql/package.json`](packages/9-public/@prisma/orm-family-sql/package.json) and publish-surface mapping in [`packages/0-shared/publish-surface/src/shells.ts`](packages/0-shared/publish-surface/src/shells.ts). ## Compatibility / migration / risk This is a pre-1.0 breaking cleanup: `@internal/sql-lane-query-builder` and `@prisma/orm-family-sql/lane-query-builder` are removed. Repository references and generated facade wiring were removed together, and the public SQL family shell rebuilds without them. Coverage semantics remain package-specific; only orchestration and report aggregation change. ## Testing performed - `CI=true TEST_TIMEOUT_MULTIPLIER=2 pnpm coverage:packages` — 1,155 files passed; 15,311 tests passed, 3 expected failures, no type errors - `pnpm coverage:report` — 69 package policies, 0 blocking failures, 8 active warnings, 0 expired warnings - `pnpm test:scripts` — 476 tests passed - `pnpm lint:deps` - `pnpm lint:manifests` - `pnpm build --filter=@prisma/orm-family-sql...` - Publish-surface tests and typecheck — 56 tests passed - Focused package tests/typechecks for CLI, Mongo runtime, SQL ORM client, SQLite codec testkit, integration tests, examples, and shell tarballs - `pnpm install --frozen-lockfile --ignore-scripts` - Targeted Biome checks and `git diff --check` ## Skill update n/a — the removed prototype query-builder export was not referenced by any user-facing skill; its package, public README, architecture docs, and publish surface were updated directly. ## Alternatives considered - **Keep Vitest 4 and optimize around it:** the single V8 run remained CPU-bound for more than 37 minutes because the relevant V8 merge optimization is only available in Vitest 5; the Vitest 4 backport was not merged. - **Switch to Istanbul coverage:** benchmarking was slower and introduced CLI language-server instrumentation timeouts, so V8 remains the provider. - **Run packages sequentially:** this preserves policy isolation but repeats runner startup and cannot eliminate duplicate test execution in CI; root collection plus package-aware post-processing keeps policy ownership without that cost. ## Checklist - [x] All commits are signed off (`git commit -s`) per the DCO. - [x] I read `CONTRIBUTING.md` and the change is scoped to one logical concern. - [x] Tests are updated. - [ ] The PR title is in `TML-NNNN: <sentence-case title>` form — no Linear ticket exists, so this uses the conventional commit title required by `CONTRIBUTING.md`. - [x] The **Skill update** section is filled in. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **Breaking Changes** - Removed the SQL lane query-builder package and its public package export. - Updated SQL documentation and package entrypoint references. - **Testing & Quality** - Centralized package coverage reporting with package-specific thresholds, exclusions, and warning policies. - Improved coverage validation, threshold reporting, and CI integration. - Updated serialized integration-test execution for compatibility with the current test runner. - **Documentation** - Expanded testing guidance for package coverage workflows and CI behavior. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: Steven McClankerton <tatarintsev@prisma.io> Co-authored-by: Steven McClankerton <tatarintsev@prisma.io> | 1 个月前 | |
A release push also publishes a dev build (#30125) At a glance: after this change, a merged release PR leaves both dist-tags on the released base — ``` push to main bumping 8.0.0-rc.6 → 8.0.0-rc.7 publishes 8.0.0-rc.7 under latest (as before) then publishes 8.0.0-rc.7-dev.1 under dev (new) ``` **Decision:** the dev and release publishes are no longer alternatives. Every push to `main` publishes a `<base>-dev.N` build under `dev`; a version-changing push additionally publishes the release under `latest` first. Adopted from composer's model in prisma/composer#241 (prisma-cli made the identical change on 2026-08-18). Previously a release push skipped the dev leg, so `dev` sat on a dev build of the previous base until the next routine push. That staleness caused a real outage on 2026-08-25: after 8.0.0-rc.7 released, `dev` still pointed at `8.0.0-rc.6-dev.1`, whose `@prisma/orm-toolchain` peers `@prisma/cli-engine@0.2.2` while the CLI ships 0.2.3. prisma-cli's dev channel builds against this repo's `dev` tag with no fallback, so its conformance check failed with an engine-pin mismatch and blocked its release until the tag was moved by hand. How it works: `determine-version.ts` now emits a third output, `devVersion`, which is empty except on release pushes. The workflow gains a "dev follow-up" leg conditioned on it: re-stamp versions to `<base>-dev.N`, rebuild, re-run the version-sensitive dependency-specifier check, publish under `dev`, and create the lightweight `v<version>-dev.N` tag. The release steps already ran the full check battery against the same commit, so the follow-up repeats only the check the version stamp affects. Dev suffixes remain ephemeral CI stamps and are never committed. One deliberate deviation from composer's step ordering: the follow-up runs **before** the "Notify prisma-cli" step. That notification triggers prisma-cli's auto-repin, which builds its dev channel against this repo's `dev` tag — notifying before the follow-up would re-create the exact outage at notify time. The decision logic moved into a pure `planPushPublish` helper in `determine-version-utils.ts` (mirroring composer's), with unit tests covering the dev-only path, the release-plus-follow-up path, counter continuation, and the unreadable-previous-version fallback. `docs/oss/versioning.md`'s trigger-model and dist-tag sections are updated to match. Alternative considered: running the dev leg unconditionally *before* the release leg. Rejected to match composer's ordering — publishing the release first means `latest` never waits on the dev leg, and a failure in the follow-up leaves the release itself intact. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Development builds are now published on every push to `main` under the `dev` tag. * Release pushes publish both the stable package and a corresponding development build. * Development version numbering continues correctly across releases. * Re-running a release publish also restores the associated development build. * **Documentation** * Updated versioning guidance to describe the new publishing behavior and release process. * **Tests** * Added coverage for development-only publishes, releases, version lookup failures, dry runs, and continued development numbering. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io> | 1 个月前 | |
A release push also publishes a dev build (#30125) At a glance: after this change, a merged release PR leaves both dist-tags on the released base — ``` push to main bumping 8.0.0-rc.6 → 8.0.0-rc.7 publishes 8.0.0-rc.7 under latest (as before) then publishes 8.0.0-rc.7-dev.1 under dev (new) ``` **Decision:** the dev and release publishes are no longer alternatives. Every push to `main` publishes a `<base>-dev.N` build under `dev`; a version-changing push additionally publishes the release under `latest` first. Adopted from composer's model in prisma/composer#241 (prisma-cli made the identical change on 2026-08-18). Previously a release push skipped the dev leg, so `dev` sat on a dev build of the previous base until the next routine push. That staleness caused a real outage on 2026-08-25: after 8.0.0-rc.7 released, `dev` still pointed at `8.0.0-rc.6-dev.1`, whose `@prisma/orm-toolchain` peers `@prisma/cli-engine@0.2.2` while the CLI ships 0.2.3. prisma-cli's dev channel builds against this repo's `dev` tag with no fallback, so its conformance check failed with an engine-pin mismatch and blocked its release until the tag was moved by hand. How it works: `determine-version.ts` now emits a third output, `devVersion`, which is empty except on release pushes. The workflow gains a "dev follow-up" leg conditioned on it: re-stamp versions to `<base>-dev.N`, rebuild, re-run the version-sensitive dependency-specifier check, publish under `dev`, and create the lightweight `v<version>-dev.N` tag. The release steps already ran the full check battery against the same commit, so the follow-up repeats only the check the version stamp affects. Dev suffixes remain ephemeral CI stamps and are never committed. One deliberate deviation from composer's step ordering: the follow-up runs **before** the "Notify prisma-cli" step. That notification triggers prisma-cli's auto-repin, which builds its dev channel against this repo's `dev` tag — notifying before the follow-up would re-create the exact outage at notify time. The decision logic moved into a pure `planPushPublish` helper in `determine-version-utils.ts` (mirroring composer's), with unit tests covering the dev-only path, the release-plus-follow-up path, counter continuation, and the unreadable-previous-version fallback. `docs/oss/versioning.md`'s trigger-model and dist-tag sections are updated to match. Alternative considered: running the dev leg unconditionally *before* the release leg. Rejected to match composer's ordering — publishing the release first means `latest` never waits on the dev leg, and a failure in the follow-up leaves the release itself intact. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Development builds are now published on every push to `main` under the `dev` tag. * Release pushes publish both the stable package and a corresponding development build. * Development version numbering continues correctly across releases. * Re-running a release publish also restores the associated development build. * **Documentation** * Updated versioning guidance to describe the new publishing behavior and release process. * **Tests** * Added coverage for development-only publishes, releases, version lookup failures, dry runs, and continued development numbering. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io> | 1 个月前 | |
refactor: rename every user-facing prisma-next identifier to Prisma 8 (#30262) ## Linked issue n/a — no Linear ticket. Completes the rename that #30248 started for prose; builds on #30261. ## At a glance Every `prisma-next` identifier a user can see is renamed. Before and after, for a scaffolded project: ```text // use prisma-next → // use prisma-8 (schema header) prisma-next.md → prisma-8.md (primer at the project root) PRISMA_NEXT_DISABLE_TELEMETRY → PRISMA_DISABLE_TELEMETRY (and every other PRISMA_NEXT_* variable) ~/.config/prisma-next/ → ~/.config/prisma-8/ (per-user telemetry config) prisma-next contract emit → prisma contract emit (CLI invocations in docs, fixtures, recordings) ``` ## Summary After #30248 the product was called Prisma 8 in prose, but the working name was still written into user projects and printed by the CLI: the schema header, the primer file, the environment variables, the per-user config directory, the language-server diagnostic source, the Standard Schema vendor string, the contract brand symbol, the advisory-lock domain, and about 650 fixture and doc files that spelled out `prisma-next …` commands. This PR renames all of it in one pass and tightens the legacy-name lint so the only occurrences left are the ones with a reason. ## Decision One commit. The mapping: | Surface | Before | After | |---|---|---| | Schema header | `// use prisma-next` | `// use prisma-8` | | Primer file `init` writes | `prisma-next.md` | `prisma-8.md` | | CLI environment variables | `PRISMA_NEXT_*` | `PRISMA_*` | | Per-user config directory | `prisma-next/` | `prisma-8/` | | Language-server diagnostic source | `prisma-next` | `prisma` | | Standard Schema vendor, VS Code publisher | `prisma-next` | `prisma` | | Contract brand symbol | `__prisma_next_brand__` | `__prisma_8_brand__` | | Postgres advisory-lock domain | `prisma_next.contract.marker` | `prisma_8.contract.marker` | | Example database names | `prisma_next_*` | `prisma_8_*` | | README banner image | `images/prisma-next.png` | `images/prisma-8.png` | | Telemetry docs URL | `prisma-next.dev/docs/…` | `www.prisma.io/docs/…` | | New-issue links | `github.com/prisma/prisma-next/issues/new` | `github.com/prisma/orm/issues/new` | | CLI invocations in prose, fixtures, and recordings | `prisma-next db verify` | `prisma db verify` | `prisma-8` is the slug the repo already uses for the skill, the examples, and the upgrade directories, so it is the slug for everything that needs one. Environment variables drop the infix entirely because `PRISMA_*` is what users expect and nothing else in the repo claims those names. What keeps the old name, each with a lint allowance that says why: - **Dated records**: changelog, release notes, ADRs, shipped upgrade instructions, gotcha logs, the framework-gaps review, and the `projects/` and `drive/` write-ups. - **Pinned links** into the old repository by number, Linear slugs, and links to ADRs whose filenames carry the name. - **`@cipherstash/prisma-next`**, a third party's published package name. - **Retirement proofs**: the list of old skill directories `init` deletes, and the tests asserting that no `prisma-next` bin or skill directory is installed any more. ## Behavior changes & evidence - **Schema header.** The inferred-schema printer and the `init` templates write `// use prisma-8`. The language server accepts both headers, so existing schemas keep their diagnostics and completion, and its Format action rewrites the old header to the new one. [packages/1-framework/3-tooling/language-server/src/schema-directive.ts](packages/1-framework/3-tooling/language-server/src/schema-directive.ts), [packages/1-framework/2-authoring/psl-printer/src/ast-to-print-document.ts](packages/1-framework/2-authoring/psl-printer/src/ast-to-print-document.ts). Evidence: the `renameLegacyDirective` tests, the server test that formats a legacy-headed schema, and the psl-printer tests. - **Environment variables.** Telemetry gating, the endpoint override, and the debug switch read the new names. `PRISMA_NEXT_DISABLE_TELEMETRY` is still honoured as an opt-out so nobody is silently opted back in; the endpoint and debug spellings are not. [packages/1-framework/3-tooling/cli-telemetry/src/gating.ts](packages/1-framework/3-tooling/cli-telemetry/src/gating.ts). Evidence: cli-telemetry gating tests. - **Per-user config directory.** [packages/1-framework/3-tooling/cli-telemetry/src/user-config.ts](packages/1-framework/3-tooling/cli-telemetry/src/user-config.ts). Existing users see the telemetry consent prompt once more; nothing else is lost. - **Primer file.** [packages/1-framework/3-tooling/cli/src/orm/init-scaffold.ts](packages/1-framework/3-tooling/cli/src/orm/init-scaffold.ts). Evidence: init-scaffold tests and template snapshots. - **Advisory-lock domain.** A CLI on this version and one on the previous version take different locks for the same marker. Both versions running migrations against one database at the same moment is already unsupported. - **Upgrade instructions.** Entries for the header, the environment variables, and the primer file are recorded in the rc.9 → rc.10 app and extension instructions with detection patterns, so the published upgrade skill applies the rename. ## Testing performed - `pnpm test` in cli (1437), cli-telemetry (112), language-server (312), psl-printer (63), framework-components (672), target-postgres (1607), vite-plugin-contract-emit (31), emitter (231), and `pnpm test:scripts` (507): all pass after `pnpm build`. The language-server tests hard-coded the old header's length in semantic-token arrays and span offsets; those expectations are updated. - Committed migration steps and their content-addressed contract snapshots are left untouched, since rewriting them would break their hashes; the lint treats them as dated records. - `pnpm lint:legacy-name` passes with the tightened allowances; `node --test scripts/lint-legacy-name.test.mjs` passes (14 tests, including new negative cases for the header, primer, and skill names). - `pnpm check:upgrade-coverage --mode pr --prev origin/main` passes. ## Skill update `skills/prisma-8` references and the two rc.9 → rc.10 upgrade instruction files are updated in this PR. ## Checklist - [x] All commits are signed off (`git commit -s`) per the [DCO](../CONTRIBUTING.md#developer-certificate-of-origin-dco). - [x] I read [CONTRIBUTING.md](../CONTRIBUTING.md) and the change is scoped to one logical concern. - [x] Tests are updated. - [ ] The PR title is in `TML-NNNN: <sentence-case title>` form. No Linear ticket exists for this change. - [x] The **Skill update** section above is filled in. 🤖 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 Fable 5.1 <noreply@anthropic.com> | 23 天前 | |
feat: an application depends on one Prisma package (ADR 242) (#29864) ## What changes for someone using Prisma Today, an application that talks to Postgres installs a long list of our packages: ```jsonc { "dependencies": { "@prisma-next/postgres": "...", "@prisma-next/sql-runtime": "...", "@prisma-next/sql-orm-client": "...", "@prisma-next/target-postgres": "...", "@prisma-next/adapter-postgres": "...", "@prisma-next/sql-contract": "..." // ...a dozen more } } ``` After this PR, it installs one: ```jsonc { "dependencies": { "@prisma/orm-postgres": "0.16.0" } } ``` Everything else arrives as that package's own dependencies. Three of our example apps are converted in this PR to prove it — one per database — and each has exactly one Prisma package in its `dependencies`. This implements [ADR 242](https://github.com/prisma/prisma/pull/29852), which is already merged. ## What gets published 17 packages, all under the `@prisma` scope: - **3 database packages** — `@prisma/orm-postgres`, `orm-sqlite`, `orm-mongo`. An application installs exactly one. We call these *facades*: each is a small package that wires its database together and re-exports everything an application needs. - **6 extension packs** — PostGIS, pgvector, ParadeDB, Supabase, arktype-json, middleware-cache. Optional, installed alongside a database package. - **7 platform packages** — the framework, the toolchain, one per database family, one per database target. Applications never install these directly; they arrive as dependencies. Extension authors do install them. - **the `prisma` command**, as a bin-only package. Every other workspace package — around 50 of them — stops being published. They still exist in the repo as the unit we organise code in; they just stop having a life on the registry. **This PR does not make that switch yet.** It builds and proves the new surface while leaving today's publish list exactly as it is. Flipping it is a separate change. ## The problem this design has to avoid A published package can't depend on packages that won't exist on the registry. So each published package *contains a compiled copy* of the internal packages it covers. That creates a trap. If one application ends up with the same code twice — once inside a published package, once as its own package — then classes, registries, and anything compared by reference exist twice too. An `instanceof` check quietly returns false. Nothing crashes, nothing fails to compile, and both copies behave identically in isolation. You find out much later, somewhere unrelated. So the rule the whole design follows is: **every piece of internal code is published from exactly one package.** Concretely, that means: - Each published package is built in one pass, so code shared between its own entry points exists once. Verified from the build's source maps: no module appears in more than one chunk, in any published package. - When one published package needs code from another, it imports it as a real dependency rather than compiling in a second copy. - A facade re-exports from the platform packages; it never carries its own copy. `@prisma/orm-postgres/orm-client` and `@prisma/orm-family-sql/orm-client` are two names for the same object, and there's a test that asserts exactly that from installed tarballs. - One table in `packages/0-shared/publish-surface` maps every internal package to where it's published. The build, the code generator, and the lint checks all read it, so there's one answer to "where does this live" rather than three that can drift. ## Generated code follows the application Prisma writes imports into your project — contract types and migration files. Those imports have to name packages your project actually depends on, or they won't resolve. So the generator now reads the `package.json` next to the config it's generating for. A project that depends on `@prisma/orm-postgres` gets imports from that package. A project on today's names keeps today's names. Nothing to configure, because the manifest already says which it is. Contract hashes are unaffected, and that isn't an assumption — hashes are computed from a structure that import text never enters, and there's a test asserting the hash is identical across naming schemes *while* the emitted imports demonstrably differ. ## What stops the trap coming back Two checks, because the failure is silent and won't show up in a test suite: - Every example app and test project must use one naming scheme, not a mix. `lint-single-import-root` scans them and fails the build if any project imports from both, since that's the situation that loads code twice. - `lint-consumer-internal-imports` counts how many internal-package imports remain in those projects and compares against a committed number. It fails if the number goes up (someone added one) and also if it goes down without the number being updated (so improvements get locked in). Target is zero. The build itself also refuses to proceed if the published-package map would put one module in two places, or if a published package's `package.json` no longer matches what its code actually needs. ## Reading this PR It's large — 257 files — because it's a migration. The commits are grouped and meant to be read in order: 1. **Platform packages** — the build mechanism, and the seven platform packages it produces. 2. **Database packages, extension packs, the `prisma` command** — completes the set of 17. 3. **Generated imports become configurable** — one place decides which names get written, with today's names still the default. 4. **Database-family symmetry, publishing the map, the identity checks.** 5. **One package per application** — the three converted examples, the re-exports they proved necessary, and the counting check. 6. **Migration files follow the project too.** One thing worth knowing while reading: re-exporting a package republishes all of its sub-paths, not just the one that was needed. This PR adds 115 published sub-paths across the three database packages. Two candidates were dropped for exactly that reason — see below. ## Alternatives considered **Let an application install platform packages alongside its facade.** Nothing would need re-exporting and the facades would stay thinner. Rejected: an application would again juggle several Prisma dependencies whose correct combination it maintains by hand, and getting it wrong — upgrading one and not the other — produces the silent two-copies failure above. Re-exporting costs a generated line and nothing at runtime. **Re-export everything an application might plausibly want.** Rejected in review: because re-exporting brings a package's entire sub-path surface, generosity is expensive and hard to undo. Migration tooling (54 sub-paths) was dropped because its only users are extension packs, which install platform packages anyway; the SQL driver re-export was dropped because nothing imported it at all. What remains is what a converted example actually needed. **Flip the publish list in this same PR.** Rejected: it would mix "does the new surface work" with "is it safe to stop publishing 50 packages" in one review. The switch is mechanical once this lands, and gets its own change. ## Verification `build`, `typecheck` (156 tasks), `test:packages` (1077 files / 14087 tests), `test:e2e`, `lint`, `lint:deps`, `lint:docs`, `lint:manifests`, `check:publish-deps`, `check:clean-tree`, `lint:casts` and `lint:throws` (no new instances), `test:scripts`, coverage, the tarball-install suites, and regenerating every committed artifact leaves the tree unchanged. Known-unstable and unrelated to this change: the `relation-mode-gh-*` port suites (TML-3140), and several test timeouts that are too tight under load. ## Follow-ups TML-3124 switch the publish list · TML-3127 build cache can validate a stale published package on CI · TML-3140 unstable port suites · TML-3141 a test-helper sub-path reaches a package that is never published. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Added consolidated public ORM packages for PostgreSQL, MongoDB, SQLite, framework tooling, database targets, and extensions. - Generated contracts, migrations, and scaffolds now adapt imports to the consuming project’s package surface. - Added facade-provided `prisma-next` CLI access and consolidated migration entrypoints. - **Documentation** - Updated installation, package naming, public entrypoint, and migration scaffolding guidance. - **Tests** - Added coverage for package installation, exports, CLI behavior, module identity, and import compatibility. - **Chores** - Added checks preventing incompatible internal and public package imports. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> | 2 个月前 | |
TML-2685: forbid bare as-casts via cast-utils + Biome plugin + CI ratchet (#598) | 4 个月前 | |
TML-2685: forbid bare as-casts via cast-utils + Biome plugin + CI ratchet (#598) | 4 个月前 | |
feat: an application depends on one Prisma package (ADR 242) (#29864) ## What changes for someone using Prisma Today, an application that talks to Postgres installs a long list of our packages: ```jsonc { "dependencies": { "@prisma-next/postgres": "...", "@prisma-next/sql-runtime": "...", "@prisma-next/sql-orm-client": "...", "@prisma-next/target-postgres": "...", "@prisma-next/adapter-postgres": "...", "@prisma-next/sql-contract": "..." // ...a dozen more } } ``` After this PR, it installs one: ```jsonc { "dependencies": { "@prisma/orm-postgres": "0.16.0" } } ``` Everything else arrives as that package's own dependencies. Three of our example apps are converted in this PR to prove it — one per database — and each has exactly one Prisma package in its `dependencies`. This implements [ADR 242](https://github.com/prisma/prisma/pull/29852), which is already merged. ## What gets published 17 packages, all under the `@prisma` scope: - **3 database packages** — `@prisma/orm-postgres`, `orm-sqlite`, `orm-mongo`. An application installs exactly one. We call these *facades*: each is a small package that wires its database together and re-exports everything an application needs. - **6 extension packs** — PostGIS, pgvector, ParadeDB, Supabase, arktype-json, middleware-cache. Optional, installed alongside a database package. - **7 platform packages** — the framework, the toolchain, one per database family, one per database target. Applications never install these directly; they arrive as dependencies. Extension authors do install them. - **the `prisma` command**, as a bin-only package. Every other workspace package — around 50 of them — stops being published. They still exist in the repo as the unit we organise code in; they just stop having a life on the registry. **This PR does not make that switch yet.** It builds and proves the new surface while leaving today's publish list exactly as it is. Flipping it is a separate change. ## The problem this design has to avoid A published package can't depend on packages that won't exist on the registry. So each published package *contains a compiled copy* of the internal packages it covers. That creates a trap. If one application ends up with the same code twice — once inside a published package, once as its own package — then classes, registries, and anything compared by reference exist twice too. An `instanceof` check quietly returns false. Nothing crashes, nothing fails to compile, and both copies behave identically in isolation. You find out much later, somewhere unrelated. So the rule the whole design follows is: **every piece of internal code is published from exactly one package.** Concretely, that means: - Each published package is built in one pass, so code shared between its own entry points exists once. Verified from the build's source maps: no module appears in more than one chunk, in any published package. - When one published package needs code from another, it imports it as a real dependency rather than compiling in a second copy. - A facade re-exports from the platform packages; it never carries its own copy. `@prisma/orm-postgres/orm-client` and `@prisma/orm-family-sql/orm-client` are two names for the same object, and there's a test that asserts exactly that from installed tarballs. - One table in `packages/0-shared/publish-surface` maps every internal package to where it's published. The build, the code generator, and the lint checks all read it, so there's one answer to "where does this live" rather than three that can drift. ## Generated code follows the application Prisma writes imports into your project — contract types and migration files. Those imports have to name packages your project actually depends on, or they won't resolve. So the generator now reads the `package.json` next to the config it's generating for. A project that depends on `@prisma/orm-postgres` gets imports from that package. A project on today's names keeps today's names. Nothing to configure, because the manifest already says which it is. Contract hashes are unaffected, and that isn't an assumption — hashes are computed from a structure that import text never enters, and there's a test asserting the hash is identical across naming schemes *while* the emitted imports demonstrably differ. ## What stops the trap coming back Two checks, because the failure is silent and won't show up in a test suite: - Every example app and test project must use one naming scheme, not a mix. `lint-single-import-root` scans them and fails the build if any project imports from both, since that's the situation that loads code twice. - `lint-consumer-internal-imports` counts how many internal-package imports remain in those projects and compares against a committed number. It fails if the number goes up (someone added one) and also if it goes down without the number being updated (so improvements get locked in). Target is zero. The build itself also refuses to proceed if the published-package map would put one module in two places, or if a published package's `package.json` no longer matches what its code actually needs. ## Reading this PR It's large — 257 files — because it's a migration. The commits are grouped and meant to be read in order: 1. **Platform packages** — the build mechanism, and the seven platform packages it produces. 2. **Database packages, extension packs, the `prisma` command** — completes the set of 17. 3. **Generated imports become configurable** — one place decides which names get written, with today's names still the default. 4. **Database-family symmetry, publishing the map, the identity checks.** 5. **One package per application** — the three converted examples, the re-exports they proved necessary, and the counting check. 6. **Migration files follow the project too.** One thing worth knowing while reading: re-exporting a package republishes all of its sub-paths, not just the one that was needed. This PR adds 115 published sub-paths across the three database packages. Two candidates were dropped for exactly that reason — see below. ## Alternatives considered **Let an application install platform packages alongside its facade.** Nothing would need re-exporting and the facades would stay thinner. Rejected: an application would again juggle several Prisma dependencies whose correct combination it maintains by hand, and getting it wrong — upgrading one and not the other — produces the silent two-copies failure above. Re-exporting costs a generated line and nothing at runtime. **Re-export everything an application might plausibly want.** Rejected in review: because re-exporting brings a package's entire sub-path surface, generosity is expensive and hard to undo. Migration tooling (54 sub-paths) was dropped because its only users are extension packs, which install platform packages anyway; the SQL driver re-export was dropped because nothing imported it at all. What remains is what a converted example actually needed. **Flip the publish list in this same PR.** Rejected: it would mix "does the new surface work" with "is it safe to stop publishing 50 packages" in one review. The switch is mechanical once this lands, and gets its own change. ## Verification `build`, `typecheck` (156 tasks), `test:packages` (1077 files / 14087 tests), `test:e2e`, `lint`, `lint:deps`, `lint:docs`, `lint:manifests`, `check:publish-deps`, `check:clean-tree`, `lint:casts` and `lint:throws` (no new instances), `test:scripts`, coverage, the tarball-install suites, and regenerating every committed artifact leaves the tree unchanged. Known-unstable and unrelated to this change: the `relation-mode-gh-*` port suites (TML-3140), and several test timeouts that are too tight under load. ## Follow-ups TML-3124 switch the publish list · TML-3127 build cache can validate a stale published package on CI · TML-3140 unstable port suites · TML-3141 a test-helper sub-path reaches a package that is never published. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Added consolidated public ORM packages for PostgreSQL, MongoDB, SQLite, framework tooling, database targets, and extensions. - Generated contracts, migrations, and scaffolds now adapt imports to the consuming project’s package surface. - Added facade-provided `prisma-next` CLI access and consolidated migration entrypoints. - **Documentation** - Updated installation, package naming, public entrypoint, and migration scaffolding guidance. - **Tests** - Added coverage for package installation, exports, CLI behavior, module identity, and import compatibility. - **Chores** - Added checks preventing incompatible internal and public package imports. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> | 2 个月前 | |
feat: an application depends on one Prisma package (ADR 242) (#29864) ## What changes for someone using Prisma Today, an application that talks to Postgres installs a long list of our packages: ```jsonc { "dependencies": { "@prisma-next/postgres": "...", "@prisma-next/sql-runtime": "...", "@prisma-next/sql-orm-client": "...", "@prisma-next/target-postgres": "...", "@prisma-next/adapter-postgres": "...", "@prisma-next/sql-contract": "..." // ...a dozen more } } ``` After this PR, it installs one: ```jsonc { "dependencies": { "@prisma/orm-postgres": "0.16.0" } } ``` Everything else arrives as that package's own dependencies. Three of our example apps are converted in this PR to prove it — one per database — and each has exactly one Prisma package in its `dependencies`. This implements [ADR 242](https://github.com/prisma/prisma/pull/29852), which is already merged. ## What gets published 17 packages, all under the `@prisma` scope: - **3 database packages** — `@prisma/orm-postgres`, `orm-sqlite`, `orm-mongo`. An application installs exactly one. We call these *facades*: each is a small package that wires its database together and re-exports everything an application needs. - **6 extension packs** — PostGIS, pgvector, ParadeDB, Supabase, arktype-json, middleware-cache. Optional, installed alongside a database package. - **7 platform packages** — the framework, the toolchain, one per database family, one per database target. Applications never install these directly; they arrive as dependencies. Extension authors do install them. - **the `prisma` command**, as a bin-only package. Every other workspace package — around 50 of them — stops being published. They still exist in the repo as the unit we organise code in; they just stop having a life on the registry. **This PR does not make that switch yet.** It builds and proves the new surface while leaving today's publish list exactly as it is. Flipping it is a separate change. ## The problem this design has to avoid A published package can't depend on packages that won't exist on the registry. So each published package *contains a compiled copy* of the internal packages it covers. That creates a trap. If one application ends up with the same code twice — once inside a published package, once as its own package — then classes, registries, and anything compared by reference exist twice too. An `instanceof` check quietly returns false. Nothing crashes, nothing fails to compile, and both copies behave identically in isolation. You find out much later, somewhere unrelated. So the rule the whole design follows is: **every piece of internal code is published from exactly one package.** Concretely, that means: - Each published package is built in one pass, so code shared between its own entry points exists once. Verified from the build's source maps: no module appears in more than one chunk, in any published package. - When one published package needs code from another, it imports it as a real dependency rather than compiling in a second copy. - A facade re-exports from the platform packages; it never carries its own copy. `@prisma/orm-postgres/orm-client` and `@prisma/orm-family-sql/orm-client` are two names for the same object, and there's a test that asserts exactly that from installed tarballs. - One table in `packages/0-shared/publish-surface` maps every internal package to where it's published. The build, the code generator, and the lint checks all read it, so there's one answer to "where does this live" rather than three that can drift. ## Generated code follows the application Prisma writes imports into your project — contract types and migration files. Those imports have to name packages your project actually depends on, or they won't resolve. So the generator now reads the `package.json` next to the config it's generating for. A project that depends on `@prisma/orm-postgres` gets imports from that package. A project on today's names keeps today's names. Nothing to configure, because the manifest already says which it is. Contract hashes are unaffected, and that isn't an assumption — hashes are computed from a structure that import text never enters, and there's a test asserting the hash is identical across naming schemes *while* the emitted imports demonstrably differ. ## What stops the trap coming back Two checks, because the failure is silent and won't show up in a test suite: - Every example app and test project must use one naming scheme, not a mix. `lint-single-import-root` scans them and fails the build if any project imports from both, since that's the situation that loads code twice. - `lint-consumer-internal-imports` counts how many internal-package imports remain in those projects and compares against a committed number. It fails if the number goes up (someone added one) and also if it goes down without the number being updated (so improvements get locked in). Target is zero. The build itself also refuses to proceed if the published-package map would put one module in two places, or if a published package's `package.json` no longer matches what its code actually needs. ## Reading this PR It's large — 257 files — because it's a migration. The commits are grouped and meant to be read in order: 1. **Platform packages** — the build mechanism, and the seven platform packages it produces. 2. **Database packages, extension packs, the `prisma` command** — completes the set of 17. 3. **Generated imports become configurable** — one place decides which names get written, with today's names still the default. 4. **Database-family symmetry, publishing the map, the identity checks.** 5. **One package per application** — the three converted examples, the re-exports they proved necessary, and the counting check. 6. **Migration files follow the project too.** One thing worth knowing while reading: re-exporting a package republishes all of its sub-paths, not just the one that was needed. This PR adds 115 published sub-paths across the three database packages. Two candidates were dropped for exactly that reason — see below. ## Alternatives considered **Let an application install platform packages alongside its facade.** Nothing would need re-exporting and the facades would stay thinner. Rejected: an application would again juggle several Prisma dependencies whose correct combination it maintains by hand, and getting it wrong — upgrading one and not the other — produces the silent two-copies failure above. Re-exporting costs a generated line and nothing at runtime. **Re-export everything an application might plausibly want.** Rejected in review: because re-exporting brings a package's entire sub-path surface, generosity is expensive and hard to undo. Migration tooling (54 sub-paths) was dropped because its only users are extension packs, which install platform packages anyway; the SQL driver re-export was dropped because nothing imported it at all. What remains is what a converted example actually needed. **Flip the publish list in this same PR.** Rejected: it would mix "does the new surface work" with "is it safe to stop publishing 50 packages" in one review. The switch is mechanical once this lands, and gets its own change. ## Verification `build`, `typecheck` (156 tasks), `test:packages` (1077 files / 14087 tests), `test:e2e`, `lint`, `lint:deps`, `lint:docs`, `lint:manifests`, `check:publish-deps`, `check:clean-tree`, `lint:casts` and `lint:throws` (no new instances), `test:scripts`, coverage, the tarball-install suites, and regenerating every committed artifact leaves the tree unchanged. Known-unstable and unrelated to this change: the `relation-mode-gh-*` port suites (TML-3140), and several test timeouts that are too tight under load. ## Follow-ups TML-3124 switch the publish list · TML-3127 build cache can validate a stale published package on CI · TML-3140 unstable port suites · TML-3141 a test-helper sub-path reaches a package that is never published. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Added consolidated public ORM packages for PostgreSQL, MongoDB, SQLite, framework tooling, database targets, and extensions. - Generated contracts, migrations, and scaffolds now adapt imports to the consuming project’s package surface. - Added facade-provided `prisma-next` CLI access and consolidated migration entrypoints. - **Documentation** - Updated installation, package naming, public entrypoint, and migration scaffolding guidance. - **Tests** - Added coverage for package installation, exports, CLI behavior, module identity, and import compatibility. - **Chores** - Added checks preventing incompatible internal and public package imports. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> | 2 个月前 | |
Flatten framework domain directory structure Remove redundant intermediate directories (shared/, migration/, runtime/) from 0-foundation and 1-core, and wrap the 4-runtime-executor package in a proper 4-runtime/ layer directory. Foundation: 0-foundation/shared/{contract,utils} -> 0-foundation/{contract,utils} Core: 1-core/{shared,migration,runtime}/* -> 1-core/* (5 packages) Runtime: 4-runtime-executor -> 4-runtime/runtime-executor Per-package globs in architecture.config.json replace the old plane-discriminating directory globs for the core layer. All documentation, READMEs, tsconfig references, and project docs updated. | 5 个月前 | |
feat: an application depends on one Prisma package (ADR 242) (#29864) ## What changes for someone using Prisma Today, an application that talks to Postgres installs a long list of our packages: ```jsonc { "dependencies": { "@prisma-next/postgres": "...", "@prisma-next/sql-runtime": "...", "@prisma-next/sql-orm-client": "...", "@prisma-next/target-postgres": "...", "@prisma-next/adapter-postgres": "...", "@prisma-next/sql-contract": "..." // ...a dozen more } } ``` After this PR, it installs one: ```jsonc { "dependencies": { "@prisma/orm-postgres": "0.16.0" } } ``` Everything else arrives as that package's own dependencies. Three of our example apps are converted in this PR to prove it — one per database — and each has exactly one Prisma package in its `dependencies`. This implements [ADR 242](https://github.com/prisma/prisma/pull/29852), which is already merged. ## What gets published 17 packages, all under the `@prisma` scope: - **3 database packages** — `@prisma/orm-postgres`, `orm-sqlite`, `orm-mongo`. An application installs exactly one. We call these *facades*: each is a small package that wires its database together and re-exports everything an application needs. - **6 extension packs** — PostGIS, pgvector, ParadeDB, Supabase, arktype-json, middleware-cache. Optional, installed alongside a database package. - **7 platform packages** — the framework, the toolchain, one per database family, one per database target. Applications never install these directly; they arrive as dependencies. Extension authors do install them. - **the `prisma` command**, as a bin-only package. Every other workspace package — around 50 of them — stops being published. They still exist in the repo as the unit we organise code in; they just stop having a life on the registry. **This PR does not make that switch yet.** It builds and proves the new surface while leaving today's publish list exactly as it is. Flipping it is a separate change. ## The problem this design has to avoid A published package can't depend on packages that won't exist on the registry. So each published package *contains a compiled copy* of the internal packages it covers. That creates a trap. If one application ends up with the same code twice — once inside a published package, once as its own package — then classes, registries, and anything compared by reference exist twice too. An `instanceof` check quietly returns false. Nothing crashes, nothing fails to compile, and both copies behave identically in isolation. You find out much later, somewhere unrelated. So the rule the whole design follows is: **every piece of internal code is published from exactly one package.** Concretely, that means: - Each published package is built in one pass, so code shared between its own entry points exists once. Verified from the build's source maps: no module appears in more than one chunk, in any published package. - When one published package needs code from another, it imports it as a real dependency rather than compiling in a second copy. - A facade re-exports from the platform packages; it never carries its own copy. `@prisma/orm-postgres/orm-client` and `@prisma/orm-family-sql/orm-client` are two names for the same object, and there's a test that asserts exactly that from installed tarballs. - One table in `packages/0-shared/publish-surface` maps every internal package to where it's published. The build, the code generator, and the lint checks all read it, so there's one answer to "where does this live" rather than three that can drift. ## Generated code follows the application Prisma writes imports into your project — contract types and migration files. Those imports have to name packages your project actually depends on, or they won't resolve. So the generator now reads the `package.json` next to the config it's generating for. A project that depends on `@prisma/orm-postgres` gets imports from that package. A project on today's names keeps today's names. Nothing to configure, because the manifest already says which it is. Contract hashes are unaffected, and that isn't an assumption — hashes are computed from a structure that import text never enters, and there's a test asserting the hash is identical across naming schemes *while* the emitted imports demonstrably differ. ## What stops the trap coming back Two checks, because the failure is silent and won't show up in a test suite: - Every example app and test project must use one naming scheme, not a mix. `lint-single-import-root` scans them and fails the build if any project imports from both, since that's the situation that loads code twice. - `lint-consumer-internal-imports` counts how many internal-package imports remain in those projects and compares against a committed number. It fails if the number goes up (someone added one) and also if it goes down without the number being updated (so improvements get locked in). Target is zero. The build itself also refuses to proceed if the published-package map would put one module in two places, or if a published package's `package.json` no longer matches what its code actually needs. ## Reading this PR It's large — 257 files — because it's a migration. The commits are grouped and meant to be read in order: 1. **Platform packages** — the build mechanism, and the seven platform packages it produces. 2. **Database packages, extension packs, the `prisma` command** — completes the set of 17. 3. **Generated imports become configurable** — one place decides which names get written, with today's names still the default. 4. **Database-family symmetry, publishing the map, the identity checks.** 5. **One package per application** — the three converted examples, the re-exports they proved necessary, and the counting check. 6. **Migration files follow the project too.** One thing worth knowing while reading: re-exporting a package republishes all of its sub-paths, not just the one that was needed. This PR adds 115 published sub-paths across the three database packages. Two candidates were dropped for exactly that reason — see below. ## Alternatives considered **Let an application install platform packages alongside its facade.** Nothing would need re-exporting and the facades would stay thinner. Rejected: an application would again juggle several Prisma dependencies whose correct combination it maintains by hand, and getting it wrong — upgrading one and not the other — produces the silent two-copies failure above. Re-exporting costs a generated line and nothing at runtime. **Re-export everything an application might plausibly want.** Rejected in review: because re-exporting brings a package's entire sub-path surface, generosity is expensive and hard to undo. Migration tooling (54 sub-paths) was dropped because its only users are extension packs, which install platform packages anyway; the SQL driver re-export was dropped because nothing imported it at all. What remains is what a converted example actually needed. **Flip the publish list in this same PR.** Rejected: it would mix "does the new surface work" with "is it safe to stop publishing 50 packages" in one review. The switch is mechanical once this lands, and gets its own change. ## Verification `build`, `typecheck` (156 tasks), `test:packages` (1077 files / 14087 tests), `test:e2e`, `lint`, `lint:deps`, `lint:docs`, `lint:manifests`, `check:publish-deps`, `check:clean-tree`, `lint:casts` and `lint:throws` (no new instances), `test:scripts`, coverage, the tarball-install suites, and regenerating every committed artifact leaves the tree unchanged. Known-unstable and unrelated to this change: the `relation-mode-gh-*` port suites (TML-3140), and several test timeouts that are too tight under load. ## Follow-ups TML-3124 switch the publish list · TML-3127 build cache can validate a stale published package on CI · TML-3140 unstable port suites · TML-3141 a test-helper sub-path reaches a package that is never published. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Added consolidated public ORM packages for PostgreSQL, MongoDB, SQLite, framework tooling, database targets, and extensions. - Generated contracts, migrations, and scaffolds now adapt imports to the consuming project’s package surface. - Added facade-provided `prisma-next` CLI access and consolidated migration entrypoints. - **Documentation** - Updated installation, package naming, public entrypoint, and migration scaffolding guidance. - **Tests** - Added coverage for package installation, exports, CLI behavior, module identity, and import compatibility. - **Chores** - Added checks preventing incompatible internal and public package imports. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> | 2 个月前 | |
Generic block values bind the shared typed expression grammar (#30381) ## What this PR does Generic-block values now bind the same typed expression grammar attributes use, and the legacy `PslBlockParam` DSL is retired in the same change that migrates every consumer. Block authors declare values with `fixedBlock({ parameters })` or `entriesBlock({ value, allowBare })`, get inferred outputs through `InferBlock`, and lowering receives a typed envelope (`ParsedPslExtensionBlock<Values>`) — never raw parameter text. ## The design in five decisions - **Source and semantics are separate products.** The print block keeps ordered source entries `{ expression?, span }` strictly for rendering; validated values live only in the typed envelope. The printer renders provenance verbatim and never executes spec or reference factories; lowering never reads print text. - **Collect everything, then interpret.** `buildSymbolTable` collects all declarations first, then binds each registered block's spec factory against the complete table and publishes only successful envelopes in a symbol-keyed `parsedBlocks` map. Forward references work, invalid blocks keep their syntax for recovery/tooling but never lower, and every value/attribute failure is reported exactly once at its original span. - **Core stays parser-blind.** `AuthoringPslBlockDescriptor` carries one erased callable `spec` field; a single parser-owned boundary (`blockSpecFactoryOf`) restores the type, mirroring the existing attribute-factory erasure. - **Policies file at their selected target.** SQL core gains an optional `pslPlacement` hook (`SqlPslEntityPlacementOutput`); PostgreSQL registers it on policies so a policy whose target resolves through top-level fallback lands in the target's physical namespace, with destination-keyed collision checks. Other entities keep lexical placement. - **Family and native enums keep their distinct contracts.** Family enums accept bare members and native JSON through the new shared `jsonValue()` rule with codecs deciding meaning (decode, post-decode uniqueness, empty/unknown-codec checks unchanged); native enums remain explicit strings. Prisma 7 constructs trusted envelopes directly and owns duplicate-key reporting for its dialect. Supported policy predicates are `optional(str())` — previously accepted omissions and inferred documents stay valid; unsupported predicate keys are rejected as unknown fixed keys at parse time. ## What was removed `PslBlockParam*`, the five-way `PslExtensionBlockParam*` union, `variadicParameters`, both legacy validators (`psl-extension-block-validator.ts`, `validateExtensionBlockFromSymbol`), the printer's descriptor-kind rendering and codec reparse, and Postgres's raw-reading helpers (`readValueParam`, `readListRefParams`, `unwrapQuotedString`, native-enum `JSON.parse`). A tests-inclusive inventory (grep evidence in the branch history) shows zero survivals and no compatibility exports. ## Editor behavior Block-key completion and attribute signatures read the same bound specs validation uses, via metadata only (spies pin that inspection never parses or resolves). Registered block value/reference errors now surface in the parse-plus-symbol pipeline; family semantics (codecs, `@@rls`, placement, collection uniqueness) keep their owners. One deliberate regression: declaration snippets no longer pre-fill key lines (no symbol exists to bind a spec before the block is authored); keys complete inside the block immediately after insertion. ## Docs ADR 255 records the shipped design; ADR 126's block-parameter sections carry supersession notes; ADRs 231/249/246 got minimal factual corrections; extension upgrade instructions live under `upgrade-instructions/pending/typed-block-value-specs/`. ## Verification Full workspace build, root typecheck 169/169, `lint:deps`, `test:packages` (1334 files / 18001 tests), `test:integration` (4254 tests), `test:e2e` (123 tests), and `pnpm fixtures:check` with zero churn — emitted contracts and generated types are byte-equivalent. Inference round-trips parse their own output through the real pipeline to equal IR. Manual QA covered extension-author ergonomics, schema-author diagnostics, and the new-block editor flow. Two pre-existing issues surfaced during the aggregate gates and are reported separately, not addressed here: a missing turbo edge between `integration-tests#typecheck` and `@prisma/orm-postgres:build` (nested-manifest dependency invisible to the graph), and a parallel-run flake in the CLI migration-snapshot suite. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Added typed specifications for top-level PSL blocks, supporting fixed keys and arbitrary entries. - Added support for native JSON literals, including nested arrays and objects. - Added namespace-aware placement for generated extension entities. - Improved block-key completion based on each block’s specification; arbitrary-entry blocks do not offer fixed-key suggestions. - Enum members can now use bare names or JSON values. - **Bug Fixes** - Improved diagnostics for invalid, duplicate, missing, and unknown block entries and attributes. - Preserved authored entry order and expressions when printing PSL blocks. - **Documentation** - Added architecture and upgrade guidance for typed block specifications. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: Steven McClankerton <tatarintsev@prisma.io> Co-authored-by: Steven McClankerton <tatarintsev@prisma.io> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> | 6 天前 | |
refactor(lint): find framework vocabulary with a Biome plugin, not a text scan (#29988) The framework-vocabulary check was a bespoke line scanner. It is now a Biome GritQL plugin plus a counting ratchet, matching the pattern `no-bare-cast` and `no-bare-throw` already use. ## Why **Over half of what it counted was documentation.** 433 of the 801 lines it flagged were comments and JSDoc, and sampling them shows what they are: `* family-parameterized (SQL, Mongo, etc. specialize via TStorage)`, `* Family storage types (SqlStorage, MongoStorage, etc.) extend this`. That is the right way to document a generic mechanism, and the checker taxed it while burying its own signal. Matching syntax nodes ignores comment trivia, so the count now reflects real surface: types, fields, identifiers, string literals, import paths. **Suppression is now aimed and self-policing.** `// biome-ignore lint/plugin/no-family-vocabulary: <reason>` silences one line, requires a reason, and a suppression naming the wrong plugin is reported as `suppressions/unused` rather than rotting silently. The previous escape hatches were renaming to a synonym or raising the committed threshold — the first changes what users read to satisfy a linter, the second makes the number drift for reasons that are not leakage. ## Count 364, down from 801. The drop is the comments, by design. ## Scoping Biome 2.5.6 does support `plugins` inside `overrides`, and they add to the top-level list rather than replacing it — but scoping that way silently under-covers. An override's `includes` glob resolves against the config in effect for the file, and most framework packages ship their own `biome.jsonc` extending the root, so a root-level `packages/1-framework/**` glob reached only 63 of 473 sites. The plugin is registered top-level and scoped by a `$filename` guard instead, which sees the whole path. Verified: across all of `packages/`, every diagnostic lands under `packages/1-framework`. ## Known difference Five sites inside multi-line template literals now report at the chunk's first line rather than once per line, because a template chunk is one syntax node. Every affected chunk is still flagged — the resolution inside a chunk is coarser, nothing escapes. Documented in the plugin header. ## Verified - Flags `readonly table`, `parentColumns`, `nativeType`, `'@prisma/orm-postgres/config'`, `SQLite`, `PostgreSQL` - Ignores `abortable`, `urls`, `tableau`, `SQLQueryPlan`, and every comment and JSDoc line - Silent on an identical file outside `packages/1-framework` - `lint:casts` 841/841 and `lint:throws` 47/47 unchanged — the third plugin disturbs neither <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added automated detection of family- or target-specific vocabulary in framework code. * Added guidance for suppressing approved exceptions and managing vocabulary thresholds. * **Bug Fixes** * Improved detection across identifiers, strings, module paths, regular expressions, and templates. * Excluded comments and test files from findings while honoring approved framework-neutral terms. * **Tests** * Expanded integration coverage for diagnostics, duplicate findings, thresholds, suppressions, exclusions, and listing output. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io> | 1 个月前 | |
feat(cli): `prisma contract print` writes the configured contract as Prisma 8 PSL that reads back as the same contract (#30315) ## At a glance `prisma contract print` takes the contract your config already loads and writes it as a Prisma 8 PSL contract. Given this `prisma.config.ts`: ```ts contract: prisma7Schema('./prisma/schema.prisma'), ``` and this Prisma 7 schema: ```prisma enum Priority { LOW @map("low") HIGH @map("high") } model Post { id Int @id @default(autoincrement()) tags String[] meta Json @default("{\"draft\":true}") priority Priority @default(LOW) author User @relation(fields: [authorId], references: [id]) authorId Int } ``` running `prisma contract print --output prisma/contract.prisma` writes (the `User` model is left out here): ```prisma // use prisma-8 // Printed from prisma/schema.prisma by `prisma contract print`. namespace public { model Post { id Int @id @default(autoincrement()) tags String[]? @noCheck(elementNotNull) meta Jsonb @default(json`{"draft":true}`) priority pg.enum(Priority) @default("low") authorId Int author User @relation(fields: [authorId], references: [id], onDelete: Restrict, onUpdate: Cascade, index: false) } native_enum Priority { low = "low" high = "high" } } ``` Point `contract` at the written file, run `contract emit`, and the emitted contract is the one the Prisma 7 schema produced: the same serialized contract, including its hashes. Without `--output`, the command prints the PSL and writes no file: on screen in a terminal, to standard output with `--format human` (`prisma contract print --format human > printed.prisma`), or as `psl.text` in the JSON result. ## The decision The printer follows one rule: **the file it writes must read back as the same contract.** It writes each part of the contract as the PSL that reads back the same. A part with no such PSL is refused by name, and nothing is written. It never drops or changes part of the contract without saying so. This applies to any contract source, not only Prisma 7. The command loads whatever `contract` names in the config (a Prisma 7 schema, a TypeScript contract, or a PSL contract) and prints the loaded contract. The Prisma 7 cutover is the first use, and the reason the command exists, but nothing in the command is specific to Prisma 7. The rule is proven before release, not checked by the command at run time. An integration test prints every emitted Postgres contract tracked in the repo and requires each one to read back as the same contract, or to be refused with the reason the test expects. A new fixture is covered as soon as it is committed. ## How it works The command loads the contract the same way `contract emit` does, validates it the way `contract emit` does, hands it to the family's `buildPslContract`, and prints the document it returns. With `--output`, it writes the text with the same staged publish `contract infer` uses. One control stack serves the whole run: the source is loaded against it, the family instance is created from it, and its block descriptors and codecs render the text. The hook is named for what it returns, a PSL document; printing is one consumer of it. The SQL family passes the target's `buildPslContract` hook a `SqlPslBuildContext`: the stack's authoring types, codec lookup and data type lookup. The printer asks the stack which PSL type reads back as a column's codec, native type and parameters, so a column carried by an extension codec prints as that extension's type, such as `pgvector.Vector(3)`. Value-object field types and literal defaults are resolved the same way. The Postgres hook lives in `packages/3-targets/3-targets/postgres/src/core/psl-print/`. It shares its literal, index, enum-block and default-mapping builders with `contract infer` through `psl-build/`. It writes: - **Models and fields**, with `@@map` and `@map` only where the name the PSL reader would derive differs from the name in the contract. - **Types**: value objects as `type` blocks (lists of them as lists), named types as a `types` block, domain enums as `enum` blocks, native enums as `native_enum` blocks. - **Keys and indexes**: primary keys with their names, `@@unique`, and `@@index` with every argument the language has. - **Checks**, by their `name:` prefix when the wire name derives from it and by `map:` otherwise, minus the checks the reader derives for list and enum columns. - **Relations**, with the referential actions and constraint name their foreign key carries, and explicit junction models for many-to-many. - **Polymorphism**: `@@discriminator` on the base and `@@base` on each variant, for single-table and multi-table variants. - **Control policies** as `@@control`, and **row-level security** as `@@rls`, `policy_<operation>` blocks and `role` blocks in `namespace unbound`. - **Defaults**: literals through the same `mapDefault` as `contract infer`, keyed by the data type of the column's codec, so a Json object prints as a `json` tagged literal; `now()` and `autoincrement()` by name; id generators as `uuid()` and the rest; `@updatedAt` pairs as the `temporal.*` presets; every other database expression as a `sql` tagged literal. Every refusal lives in one module, `refusals.ts`, and covers a valid contract that PSL cannot express; the printer takes a validated contract, so it does not re-check structure. The module matches the `CONTRACT.PRINT_UNSUPPORTED` list in `docs/reference/error-reference.md` one to one; a test fails if the two lists differ in length. Each refusal names the model, field, column or entity. A PSL file cannot carry the contract's default control policy; the config sets it on the PSL source. When the contract has one, the command warns, names it in the next step and in the JSON result (`sourceSettings.defaultControlPolicy`), and the CLI and Postgres READMEs show a config that sets it. With `--output`, the command refuses to write over a file the project needs: any file the contract source reads (compared as real files, through symbolic links, case-insensitively on a volume that ignores case, and including every file a glob input matches or would match once written), `prisma.config.ts`, or the emitted `contract.json` and `contract.d.ts`. ## What is proven The rule is an equality. Every round-trip test prints, reads the text back through the PSL source with the same stack, and compares the serialized contracts, which carry the hashes. The comparison leaves out `capabilities` and `extensions`, which the composed stack reports rather than the source. - **Every Postgres contract in the repo** (`test/integration/test/psl-print/every-postgres-contract-roundtrip.integration.test.ts`): 272 emitted contracts. 249 print and read back as the same contract. The other 23 are refused, and the test lists each one with the reason its refusal must give. - **Every Prisma 7 fixture** (`test/integration/test/psl-print/prisma7-fixture-roundtrip.integration.test.ts`): 33 of the 35 fixtures round-trip. The other 2 declare one model name in two namespaces, and the test asserts their refusal. - **TypeScript-authored contracts** (`typescript-contract-roundtrip.integration.test.ts`): five contracts built with the TypeScript builder round-trip. - **PSL-authored cases and extension types** (`authored-contract-roundtrip.integration.test.ts`, `extension-types-roundtrip.integration.test.ts`): control policies, every index argument, domain enums, primary key names, non-default codecs, checks named by prefix, lists of value objects, row-level security with roles and policies, a model in the unbound namespace, and a `pgvector.Vector(3)` column. - **Every refusal** has a unit test that asserts its code and meta. Journeys run the command end to end. The `relations` and `supported-verify` Prisma 7 fixtures print, emit with the same storage hash the Prisma 7 source emitted, sign, and `db verify` with zero findings against the database built from the SQL Prisma 7.10.0 generated. A PSL source prints. A TypeScript contract with a default control policy prints with a warning, and the config the README shows emits the printed file with that policy. A schema with a `view`, and an `--output` path that is the schema being read, exit 2 and write nothing. ## Changes outside the printer - **Contract source format.** Every contract source states its `format`, `'psl'` or `'typescript'`, and the `orm` config schema rejects any other value or a missing one. A Prisma 7 schema is PSL text, so the Prisma 7 source declares `'psl'`, and `contract format` formats it. Upgrade instructions are in `upgrade-instructions/pending/contract-print/`. - **One PSL grammar.** The parser has no grammar option. A `view` body parses as fields in every document, and an `enum` member may carry `@` attributes in every document; each reader decides what it accepts. The SQL and Mongo readers report `PSL_UNSUPPORTED_ENUM_MEMBER_ATTRIBUTE`. That check and the unknown top-level block check live once in `@internal/psl-parser`, and both readers call them. - **Formatter.** It keeps a `//` comment written between a block's name and its `{`, moving it after the `{`. It writes a space before a list value after `:`, `,` or `=` (`fields: [authorId]`). Bare entries that shared a line are now written one per line. - **PSL printer.** It prints value-object `type` blocks, which it used to drop. It always writes the `// use prisma-8` marker, and each caller passes one description line. - **PSL reader.** - A scalar list field keeps its type parameters, so `Decimal @db.Numeric(65,30)[]` reads back with its type. One emitted fixture changes: an enum list field gains `typeParams.typeName`. Storage is unchanged. - A unique index over plain columns with no `where` makes a back-relation singular, as a unique constraint does. - It exports its naming rules (`pslModelMapName`, `pslFieldMapName`), so the printer writes `@@map` and `@map` by the same rules. - A policy expression decodes every JSON string escape. A PSL contract that wrote `\t` in a policy expression now reads a tab there; an upgrade note says so. - **CLI.** `contract emit`, `contract print`, `ControlClient.emit` and `orm init` load a contract source through one loader, which expands glob inputs. `contract print` validates the loaded contract the way `contract emit` does. - **`contract format`** formats every `.prisma` file under a source input that is a directory, as a Prisma 7 or Prisma 6 source may name one. - **Default emitted path.** The rule for where `contract emit` writes when the config sets no `output` lives once in `@internal/config`; the three facades and the CLI call it. - **Published surface.** `@prisma/orm-family-sql` gains `./contract-psl/map-names` (the two naming rules the printer shares with the reader) and `./family/psl-build`. - **`check-upgrade-coverage`** lists only the directories it reads. It listed the whole repository tree, which passed the 1 MiB child-process output limit. - **Framework vocabulary lint.** It flags the name of any Prisma version before 8 in `packages/1-framework`, except in the CLI, which names Prisma 7 when `orm init` sets Prisma 8 up beside a Prisma 7 project. ## What it cannot write yet The full list is under `CONTRACT.PRINT_UNSUPPORTED` in the error reference, and `projects/prisma7-contract-source/spec.md` records what would lift each. The ones a user is most likely to meet: - A relation into another contract space, such as a Supabase app's relation to `supabase:auth.AuthUser`. PSL can write it; the printer would need the composed extension contracts. - One model name in two namespaces. The PSL reader groups relations by bare model name. - A domain enum or value object outside the default namespace, and a value-object field with type parameters or a value set. The PSL reader does not carry them back. - A foreign key no relation travels, a to-one relation with no foreign key, a back-relation with no owning relation on the other model, and a relation with no `on` part. - A namespace whose name is not a PSL identifier or is `unbound`, and any name `__proto__`, which the PSL reader loses. - A union or dictionary field, a column with its own control policy, a model with an owner, and an entity kind a pack contributes. None has PSL syntax the printer can write. ## Alternatives considered - **Write a file by default, as `contract infer` does.** Rejected: the name `print` would be wrong about what the command does, and in a PSL project the default path would be the contract source itself, so the command would refuse whenever it ran without flags. Printing by default can never overwrite a file. - **Restrict the command to a Prisma 7 source.** That would hide the parts a Prisma 7 schema never produces (value objects, polymorphism, named types, control policies) instead of printing them. Rejected: a printer that drops what it does not understand is not safe behind any check, and the restriction would make the command's name wrong about what it does. - **Read the written file back inside the command and refuse on a mismatch.** Rejected: a published command that refuses its own output is not useful to users. Gaps must be found before release, which is what the test over every contract in the repo does. - **Name the Prisma 7 source's format `'prisma7'` in the framework, or give the parser a `prisma7` grammar.** Rejected: the framework supports PSL and TypeScript, and a Prisma 7 schema is PSL. The grammar is general; only the readers differ. - **Pick a column's PSL type from a table the target keeps.** Rejected: such a table cannot see extension types, and the stack already knows every type it can read back. 🤖 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 <noreply@anthropic.com> | 7 天前 | |
chore(release): bump to 8.0.0-rc.12 (#30395) ## Release: 8.0.0-rc.11 → 8.0.0-rc.12 This is the release PR described in [docs/oss/versioning.md](https://github.com/prisma/orm/blob/main/docs/oss/versioning.md). It bumps every workspace package to 8.0.0-rc.12 and moves the Prisma dependencies to their latest versions. **Merging this PR ships the release.** The push to `main` carries the new root `version`. The `Publish to npm` workflow then publishes 8.0.0-rc.12 under `latest` and creates a pre-release GitHub Release from the notes file. ## Review these first - [docs/releases/v8.0.0-rc.12.md](https://github.com/prisma/orm/blob/release/8.0.0-rc.12/docs/releases/v8.0.0-rc.12.md): the release notes, which become the GitHub Release body. The same entry is at the top of `CHANGELOG.md`. - The upgrade guides for [apps](https://github.com/prisma/orm/blob/release/8.0.0-rc.12/skills/prisma-8/upgrading/app/upgrades/8.0.0-rc.11-to-8.0.0-rc.12/instructions.md) and [extensions](https://github.com/prisma/orm/blob/release/8.0.0-rc.12/skills/prisma-8/upgrading/extension/upgrades/8.0.0-rc.11-to-8.0.0-rc.12/instructions.md). They merge the 24 pending fragments. The original fragments are moved unchanged to `upgrade-instructions/releases/8.0.0-rc.11-to-8.0.0-rc.12/sources/`. - Four guide entries have no fragment behind them. The `migration new` default and its removed error codes (#30389) had no guide entry. Neither did the PSL parser API changes (#30312, #30344, #30335, #30379). I wrote those entries while preparing the release. - Where fragments contradicted later code, the guide follows the code. Examples: the Supabase storage hash, `voidParamsSchema`, and quoted defaults printed by `infer`. ## Dependency updates | Package | From | To | Where | | --- | --- | --- | --- | | `@prisma/cli-engine` | 0.4.0 | 0.6.1 | examples, test fixtures, apps (the toolchain packages were already on 0.6.1 from #30372) | | `@prisma/dev` | 0.25.1 | 0.25.2 | the workspace catalog | | `@prisma/compute-sdk` | ^0.39.0 | ^0.43.0 | `apps/telemetry-backend` | | `@prisma/management-api-sdk` | ^1.56.0 | ^1.76.0 | `apps/telemetry-backend` | compute-sdk 0.43 renames "service" to "app" and "version" to "deployment". The telemetry deploy script now uses the new names. Both SDK versions call `/v1/apps/{appId}`, so the ID stored in the existing `TELEMETRY_DEPLOY_SERVICE_ID` secret is still correct. The app's typecheck now includes `scripts/`, so it catches the next SDK rename. The repo does not depend on `@prisma/composer`. ## Fixes needed to publish - **The publish workflow has failed on `main` since #30372.** `check:conformance` called the `orm` config validator as `validate(value)`. Engine 0.6 always calls `validate(value, provenance)`, and the validator reads `provenance.files`, so it threw on every input. The check now passes the same provenance the engine would. The prisma-cli copy of this check already does this. - `set-version` rewrote `workspace:@internal/cli@<version>` to `workspace:<version>`, dropping the alias. The prisma7-adoption example uses that alias. This is the first bump since the alias was added. - `lint:legacy-name` and the `add-model-map` test pointed at the pending fragment paths. They now point at the archived sources. ## Verification Passed locally: - `pnpm build` - `pnpm typecheck` - `pnpm lint` - `pnpm test:scripts` (563 tests) - `pnpm check:conformance` - `pnpm check:publish-deps` - `pnpm check:upgrade-coverage`, in both publish and PR mode - `pnpm check:release-notes`, in both publish and PR mode - `pnpm lint:legacy-name` - `pnpm lint:skills` - `pnpm test:packages`: all 18,196 tests passed Not covered locally, left to CI: - Three `test:packages` suites install packed tarballs from the registry. This machine's pnpm refuses `@vercel/detect-agent@1.2.5` because it has no provenance. CI passed the same suites on #30390. - `prisma-8-cloudflare-worker` needs a local Hyperdrive database. - The telemetry backend tests need Node 24.16 with `Temporal`. This machine has 24.13. - `fixtures:check` needs Postgres. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added PostgreSQL full-text search, multi-file schemas, prepared ORM reads and aggregates, and conflict-skipping options for bulk creation. * Added support for using a Prisma 7 schema as the contract source, JavaScript `Date` timestamps on PostgreSQL, editor support for attribute arguments, and per-finding diagnostics. * **Breaking Changes** * Prisma 8 schema files now require `// use prisma-8` on the first line; unmapped models use their names verbatim for table names. * Replace `dbgenerated(...)` with SQL tagged literals. Defaults must be valid for their column types, creation timestamps use the application clock, and native PostgreSQL enums no longer support text operations. * Config naming and path resolution, migration starting points, and extension contracts have changed. * **Bug Fixes** * Improved migration checks and branching warnings, contract generation and inference, default verification, and type checking. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- 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> | 10 天前 | |
chore(release): bump to 8.0.0-rc.12 (#30395) ## Release: 8.0.0-rc.11 → 8.0.0-rc.12 This is the release PR described in [docs/oss/versioning.md](https://github.com/prisma/orm/blob/main/docs/oss/versioning.md). It bumps every workspace package to 8.0.0-rc.12 and moves the Prisma dependencies to their latest versions. **Merging this PR ships the release.** The push to `main` carries the new root `version`. The `Publish to npm` workflow then publishes 8.0.0-rc.12 under `latest` and creates a pre-release GitHub Release from the notes file. ## Review these first - [docs/releases/v8.0.0-rc.12.md](https://github.com/prisma/orm/blob/release/8.0.0-rc.12/docs/releases/v8.0.0-rc.12.md): the release notes, which become the GitHub Release body. The same entry is at the top of `CHANGELOG.md`. - The upgrade guides for [apps](https://github.com/prisma/orm/blob/release/8.0.0-rc.12/skills/prisma-8/upgrading/app/upgrades/8.0.0-rc.11-to-8.0.0-rc.12/instructions.md) and [extensions](https://github.com/prisma/orm/blob/release/8.0.0-rc.12/skills/prisma-8/upgrading/extension/upgrades/8.0.0-rc.11-to-8.0.0-rc.12/instructions.md). They merge the 24 pending fragments. The original fragments are moved unchanged to `upgrade-instructions/releases/8.0.0-rc.11-to-8.0.0-rc.12/sources/`. - Four guide entries have no fragment behind them. The `migration new` default and its removed error codes (#30389) had no guide entry. Neither did the PSL parser API changes (#30312, #30344, #30335, #30379). I wrote those entries while preparing the release. - Where fragments contradicted later code, the guide follows the code. Examples: the Supabase storage hash, `voidParamsSchema`, and quoted defaults printed by `infer`. ## Dependency updates | Package | From | To | Where | | --- | --- | --- | --- | | `@prisma/cli-engine` | 0.4.0 | 0.6.1 | examples, test fixtures, apps (the toolchain packages were already on 0.6.1 from #30372) | | `@prisma/dev` | 0.25.1 | 0.25.2 | the workspace catalog | | `@prisma/compute-sdk` | ^0.39.0 | ^0.43.0 | `apps/telemetry-backend` | | `@prisma/management-api-sdk` | ^1.56.0 | ^1.76.0 | `apps/telemetry-backend` | compute-sdk 0.43 renames "service" to "app" and "version" to "deployment". The telemetry deploy script now uses the new names. Both SDK versions call `/v1/apps/{appId}`, so the ID stored in the existing `TELEMETRY_DEPLOY_SERVICE_ID` secret is still correct. The app's typecheck now includes `scripts/`, so it catches the next SDK rename. The repo does not depend on `@prisma/composer`. ## Fixes needed to publish - **The publish workflow has failed on `main` since #30372.** `check:conformance` called the `orm` config validator as `validate(value)`. Engine 0.6 always calls `validate(value, provenance)`, and the validator reads `provenance.files`, so it threw on every input. The check now passes the same provenance the engine would. The prisma-cli copy of this check already does this. - `set-version` rewrote `workspace:@internal/cli@<version>` to `workspace:<version>`, dropping the alias. The prisma7-adoption example uses that alias. This is the first bump since the alias was added. - `lint:legacy-name` and the `add-model-map` test pointed at the pending fragment paths. They now point at the archived sources. ## Verification Passed locally: - `pnpm build` - `pnpm typecheck` - `pnpm lint` - `pnpm test:scripts` (563 tests) - `pnpm check:conformance` - `pnpm check:publish-deps` - `pnpm check:upgrade-coverage`, in both publish and PR mode - `pnpm check:release-notes`, in both publish and PR mode - `pnpm lint:legacy-name` - `pnpm lint:skills` - `pnpm test:packages`: all 18,196 tests passed Not covered locally, left to CI: - Three `test:packages` suites install packed tarballs from the registry. This machine's pnpm refuses `@vercel/detect-agent@1.2.5` because it has no provenance. CI passed the same suites on #30390. - `prisma-8-cloudflare-worker` needs a local Hyperdrive database. - The telemetry backend tests need Node 24.16 with `Temporal`. This machine has 24.13. - `fixtures:check` needs Postgres. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added PostgreSQL full-text search, multi-file schemas, prepared ORM reads and aggregates, and conflict-skipping options for bulk creation. * Added support for using a Prisma 7 schema as the contract source, JavaScript `Date` timestamps on PostgreSQL, editor support for attribute arguments, and per-finding diagnostics. * **Breaking Changes** * Prisma 8 schema files now require `// use prisma-8` on the first line; unmapped models use their names verbatim for table names. * Replace `dbgenerated(...)` with SQL tagged literals. Defaults must be valid for their column types, creation timestamps use the application clock, and native PostgreSQL enums no longer support text operations. * Config naming and path resolution, migration starting points, and extension contracts have changed. * **Bug Fixes** * Improved migration checks and branching warnings, contract generation and inference, default verification, and type checking. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- 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> | 10 天前 | |
review #533: note 2: rename family validateContract → deserializeContract Sweep finishes the partial rename in the working tree by: - Fixing two malformed JSDoc blocks (control-instances.ts, sql + mongo family control-instance.ts) whose embedded `/**` glob broke typecheck. - Updating the remaining `validateContract` references (comments, READMEs, lint-script error string, drive/code README) to point at the family-instance method `deserializeContract` and dropping the now- redundant cursor rule `contract-normalization-responsibilities.mdc`. - Realigning `.cursor/rules/as-contract-cast-smell.mdc` on the `familyInstance.deserializeContract(...)` idiom. - Renaming the stale call site `validateContract(JSON.parse(...))` in `migration-plan.ts`'s `readPredecessorEndContract` to the local `deserializeContract` parameter the function already declares. The freestanding framework function `validateContract<T>` keeps its name in this commit; Note 1 follow-up collapses it into the family serializer. Signed-off-by: Will Madden <madden@prisma.io> | 4 个月前 | |
fix: only packages/9-public is publishable, enforced (#29880) ## What this is The privatization that should have shipped inside #29864. `main` merged with **64 internal packages still publishable** — the flip was scoped to the switchover slice, whose dispatch was cancelled and never revived, and I consolidated the project PR without folding it in or surfacing the omission as a decision. The only thing keeping those packages off the registry was the publish workflow failing at an unrelated gate. ## Changes - Every package outside `packages/9-public/` is `"private": true` — all 64. `scripts/list-publishable-packages.mjs` now returns exactly the 17 ADR 242 packages. - **`pnpm lint:publishability`** — fails in both directions: a publishable package outside `9-public`, or a private one inside it. Wired into CI beside `lint:legacy-name` and `lint:consumer-internal-imports`. Verified against a planted violation in each direction. ## Verification `lint:publishability` green (and red on planted violations both ways) · `lint:manifests`: all 17 publishable packages OK · `check:publish-deps`: 17 packages, declaration deps clean · `lint:legacy-name` · `lint:consumer-internal-imports` · `pnpm build` · `test:scripts` 298/298. ## What this deliberately does not do The remaining switchover items — the phantom `@internal/*` devDependencies strip at pack time, the shim's mirrored dependencies, and the publish pipeline's missing-stable-tag assumption — are still open and tracked on TML-3124. This PR is the safety flip only, kept minimal so it can merge immediately. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Chores** * Clarified package visibility so internal components are not published accidentally. * Added automated validation to ensure only designated public packages are publishable. * Integrated publishability checks into the continuous integration lint workflow. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> | 2 个月前 | |
Serialize tarball packaging suites in the integration test package (#30345) ## Linked issue n/a — standalone packaging-test race fix; Linear link omitted as requested. Kept separate from the generic-block-value-specs feature. ## Summary Two Vitest suites — the Postgres facade tarball suite and the pgvector extension tarball suite — `pnpm pack` the same real `@prisma/orm-postgres` directory concurrently. Its `prepack` rewrites the `skills/` tree in place, so concurrent packs corrupt each other (confirmed `ENOENT` race in `sync-package-skills.ts`). This PR fixes the race by scheduling instead of locking: both suites move into the integration test package under a dedicated Vitest project that runs its files sequentially. The first commit on this branch implemented a cross-process lock (Lamport bakery around `pnpm pack`); review judged it too complex for the problem, and the second commit reverts it. The third commit is the replacement: - `test/integration/test/packaging/` now holds `facade-tarball.test.ts` and `extension-tarball.test.ts` (moved as-is; only the `repoRoot` depth changed). - `test/integration/vitest.config.ts` splits into two projects: `integration` (everything else, parallelism unchanged) and `packaging` (`test/packaging/**`, `fileParallelism: false`). Vitest runs every `fileParallelism: false` project in one shared sequential execution group, so only the packing suites serialize. - `@prisma/orm-postgres` and `@prisma/orm-extension-pgvector` lose their now-empty test rigs (`test` script, `vitest.config.ts`, test-only devDependencies) and their dead `turbo.json` task overrides. - `scripts/lint-single-import-root.mjs` gets a narrow exemption for `test/integration/test/packaging/`: the moved suites' `@prisma/orm-*` specifiers are strings executed inside isolated scratch installs in child processes, never imports in the integration-tests module graph, so the dual-copy hazard the lint guards against cannot occur. Covered by new cases in its test. ## Verification - Both moved suites pass under the `packaging` project: 17/17. JSON-reporter timestamps prove sequential scheduling: `facade-tarball` ran 15:23:32.647–15:23:39.247, `extension-tarball` started 15:23:45.227 — no overlap. - `scripts/lint-single-import-root.test.mjs`: 8/8, including the new exemption cases (one proves the package is still reported when the exemption list is emptied). - `pnpm lint:deps`, `pnpm lint:manifests`, `pnpm lint:vitest-timeouts`, `pnpm lint:legacy-name`, integration-tests `typecheck` + `lint`, and both donor packages' `lint` all pass. ## Known limitations - The protection is scheduler-scoped: two independently launched Vitest processes could still pack concurrently. Accepted as the simpler trade-off over cross-process locking. - `orm-framework`'s tarball suites and `orm-target-postgres`'s cross-shell suite stay in `test:packages` and pack overlapping platform-shell directories in separate Vitest projects; those directories have no in-place-rewriting `prepack`, and that pre-existing exposure is unchanged by this PR. - Validation surfaced an orthogonal breakage: fresh scratch installs currently fail with `ERR_PNPM_TRUST_DOWNGRADE` for `@vercel/detect-agent@1.2.5` (resolved via `^1.2.4` from `@prisma/orm-toolchain`; 1.2.5 carries no provenance where earlier versions did). This breaks the tarball suites on `main` in their old location too. The green runs above used a temporary local trust exclusion that is deliberately **not** committed — whether to pin `1.2.4` or vouch for `1.2.5` in `trustPolicyExclude` is a separate supply-chain decision. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Tests** * Separated integration and packaging tests into distinct projects. * Packaging tests now run sequentially with extended timeout settings. * Updated packaging test paths and repository layout references. * Updated coverage validation for the revised package structure. * **Chores** * Removed standalone test scripts, coverage settings, and test configurations from the PostgreSQL ORM packages. * Updated import validation and legacy-name checks for relocated packaging tests. * Added tooling required by integration tests. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: Steven McClankerton <tatarintsev@prisma.io> Co-authored-by: Steven McClankerton <tatarintsev@prisma.io> | 16 天前 | |
Serialize tarball packaging suites in the integration test package (#30345) ## Linked issue n/a — standalone packaging-test race fix; Linear link omitted as requested. Kept separate from the generic-block-value-specs feature. ## Summary Two Vitest suites — the Postgres facade tarball suite and the pgvector extension tarball suite — `pnpm pack` the same real `@prisma/orm-postgres` directory concurrently. Its `prepack` rewrites the `skills/` tree in place, so concurrent packs corrupt each other (confirmed `ENOENT` race in `sync-package-skills.ts`). This PR fixes the race by scheduling instead of locking: both suites move into the integration test package under a dedicated Vitest project that runs its files sequentially. The first commit on this branch implemented a cross-process lock (Lamport bakery around `pnpm pack`); review judged it too complex for the problem, and the second commit reverts it. The third commit is the replacement: - `test/integration/test/packaging/` now holds `facade-tarball.test.ts` and `extension-tarball.test.ts` (moved as-is; only the `repoRoot` depth changed). - `test/integration/vitest.config.ts` splits into two projects: `integration` (everything else, parallelism unchanged) and `packaging` (`test/packaging/**`, `fileParallelism: false`). Vitest runs every `fileParallelism: false` project in one shared sequential execution group, so only the packing suites serialize. - `@prisma/orm-postgres` and `@prisma/orm-extension-pgvector` lose their now-empty test rigs (`test` script, `vitest.config.ts`, test-only devDependencies) and their dead `turbo.json` task overrides. - `scripts/lint-single-import-root.mjs` gets a narrow exemption for `test/integration/test/packaging/`: the moved suites' `@prisma/orm-*` specifiers are strings executed inside isolated scratch installs in child processes, never imports in the integration-tests module graph, so the dual-copy hazard the lint guards against cannot occur. Covered by new cases in its test. ## Verification - Both moved suites pass under the `packaging` project: 17/17. JSON-reporter timestamps prove sequential scheduling: `facade-tarball` ran 15:23:32.647–15:23:39.247, `extension-tarball` started 15:23:45.227 — no overlap. - `scripts/lint-single-import-root.test.mjs`: 8/8, including the new exemption cases (one proves the package is still reported when the exemption list is emptied). - `pnpm lint:deps`, `pnpm lint:manifests`, `pnpm lint:vitest-timeouts`, `pnpm lint:legacy-name`, integration-tests `typecheck` + `lint`, and both donor packages' `lint` all pass. ## Known limitations - The protection is scheduler-scoped: two independently launched Vitest processes could still pack concurrently. Accepted as the simpler trade-off over cross-process locking. - `orm-framework`'s tarball suites and `orm-target-postgres`'s cross-shell suite stay in `test:packages` and pack overlapping platform-shell directories in separate Vitest projects; those directories have no in-place-rewriting `prepack`, and that pre-existing exposure is unchanged by this PR. - Validation surfaced an orthogonal breakage: fresh scratch installs currently fail with `ERR_PNPM_TRUST_DOWNGRADE` for `@vercel/detect-agent@1.2.5` (resolved via `^1.2.4` from `@prisma/orm-toolchain`; 1.2.5 carries no provenance where earlier versions did). This breaks the tarball suites on `main` in their old location too. The green runs above used a temporary local trust exclusion that is deliberately **not** committed — whether to pin `1.2.4` or vouch for `1.2.5` in `trustPolicyExclude` is a separate supply-chain decision. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Tests** * Separated integration and packaging tests into distinct projects. * Packaging tests now run sequentially with extended timeout settings. * Updated packaging test paths and repository layout references. * Updated coverage validation for the revised package structure. * **Chores** * Removed standalone test scripts, coverage settings, and test configurations from the PostgreSQL ORM packages. * Updated import validation and legacy-name checks for relocated packaging tests. * Added tooling required by integration tests. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: Steven McClankerton <tatarintsev@prisma.io> Co-authored-by: Steven McClankerton <tatarintsev@prisma.io> | 16 天前 | |
Ship the prisma-8 skill inside the three ORM target tarballs (#30096) The prisma-8 skill now ships inside the npm tarballs users actually install, instead of being fetched from GitHub by `npx skills add` at init time. ## What changed - **The two upgrade skills fold into the `prisma-8` router.** `prisma-next-upgrade` and `prisma-8-extension-upgrade` become an "upgrading" branch of `skills/prisma-8/` (per-transition `upgrades/<from>-to-<to>/` layout kept), their trigger phrases move into the router's `description`, and the router opens with a preamble telling the agent the installed version's skill is the source of truth. Three registered skills become one. - **Version stamp under `metadata`.** The skill frontmatter carries `metadata.library` (the npm package name) and `metadata.library_version`, stamped by the version pipeline (`scripts/set-version.ts`) so the stamp and the package version cannot diverge. The keys live under the Agent Skills spec's `metadata` map — a string→string extension point — rather than as undefined top-level keys. - **The skill travels in three tarballs.** `skills/prisma-8/` is staged into `@prisma/orm-postgres`, `@prisma/orm-sqlite`, and `@prisma/orm-mongo` at `prepack` time, with `"skills"` in each package's `files`. Each copy's `metadata.library` names the package it ships in. - **The packaging is proved from the artifact, not the working tree.** The publish-surface test deletes the staged tree, runs `pnpm pack` the way the publish workflow does, reads the stamped `SKILL.md` back out of the tarball, and byte-compares every file against the tracked source. It fails if the `files` entry or the `prepack` script is removed. - **The upgrade-coverage check follows the fold.** `USER_SKILL_PKG` / `EXT_SKILL_PKG` point at the folded directories and the path regex is derived from them. - Docs updated: `skills/README.md` (authoring rules for the stamp, the GitHub route demoted to a manual fallback), `docs/oss/versioning.md`, `docs/reference/error-reference.md`. ## What consumes this `prisma skills sync` in prisma/prisma-cli ([prisma/prisma-cli#219](https://github.com/prisma/prisma-cli/pull/219)) copies these skills from the installed packages into the agent harness directories and reads the `metadata` stamp to detect staleness. The init wiring that runs sync lands separately, stacked on this branch. Merge order: prisma/prisma-cli#219 ships first (it owns the `prisma skills` command), then this, then the init wiring. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Prisma ORM packages now bundle a version-matched `prisma-8` skill for application and extension upgrades. * Added upgrade guidance and migration tools covering historical Prisma version transitions. * Skills now synchronize automatically during package initialization and packaging. * **Documentation** * Updated installation, synchronization, versioning, error-handling, and authoring guidance. * **Bug Fixes** * Improved skill metadata validation and version stamping. * Retired legacy standalone upgrade skill references and installation paths. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io> | 1 个月前 | |
Ship the prisma-8 skill inside the three ORM target tarballs (#30096) The prisma-8 skill now ships inside the npm tarballs users actually install, instead of being fetched from GitHub by `npx skills add` at init time. ## What changed - **The two upgrade skills fold into the `prisma-8` router.** `prisma-next-upgrade` and `prisma-8-extension-upgrade` become an "upgrading" branch of `skills/prisma-8/` (per-transition `upgrades/<from>-to-<to>/` layout kept), their trigger phrases move into the router's `description`, and the router opens with a preamble telling the agent the installed version's skill is the source of truth. Three registered skills become one. - **Version stamp under `metadata`.** The skill frontmatter carries `metadata.library` (the npm package name) and `metadata.library_version`, stamped by the version pipeline (`scripts/set-version.ts`) so the stamp and the package version cannot diverge. The keys live under the Agent Skills spec's `metadata` map — a string→string extension point — rather than as undefined top-level keys. - **The skill travels in three tarballs.** `skills/prisma-8/` is staged into `@prisma/orm-postgres`, `@prisma/orm-sqlite`, and `@prisma/orm-mongo` at `prepack` time, with `"skills"` in each package's `files`. Each copy's `metadata.library` names the package it ships in. - **The packaging is proved from the artifact, not the working tree.** The publish-surface test deletes the staged tree, runs `pnpm pack` the way the publish workflow does, reads the stamped `SKILL.md` back out of the tarball, and byte-compares every file against the tracked source. It fails if the `files` entry or the `prepack` script is removed. - **The upgrade-coverage check follows the fold.** `USER_SKILL_PKG` / `EXT_SKILL_PKG` point at the folded directories and the path regex is derived from them. - Docs updated: `skills/README.md` (authoring rules for the stamp, the GitHub route demoted to a manual fallback), `docs/oss/versioning.md`, `docs/reference/error-reference.md`. ## What consumes this `prisma skills sync` in prisma/prisma-cli ([prisma/prisma-cli#219](https://github.com/prisma/prisma-cli/pull/219)) copies these skills from the installed packages into the agent harness directories and reads the `metadata` stamp to detect staleness. The init wiring that runs sync lands separately, stacked on this branch. Merge order: prisma/prisma-cli#219 ships first (it owns the `prisma skills` command), then this, then the init wiring. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Prisma ORM packages now bundle a version-matched `prisma-8` skill for application and extension upgrades. * Added upgrade guidance and migration tools covering historical Prisma version transitions. * Skills now synchronize automatically during package initialization and packaging. * **Documentation** * Updated installation, synchronization, versioning, error-handling, and authoring guidance. * **Bug Fixes** * Improved skill metadata validation and version stamping. * Retired legacy standalone upgrade skill references and installation paths. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io> | 1 个月前 | |
migration plan and migration new no longer need contract.d.ts on disk; vitest configs cannot budget with timeouts.default (#30298) ## Linked issue n/a — the two follow-ups deferred from #30293 (db init, db update and migrate no longer strand the database when contract.d.ts is missing). ## Skill update n/a — no user-facing surface changes; `migration plan` and `migration new` stop needing a `contract.d.ts` next to `contract.json`, which no skill ever told users to keep. ## At a glance Before, `migration plan` copied whatever `contract.d.ts` sat next to `contract.json` into the snapshot store, after the package was already written: ```ts const [contractJsonRaw, contractDts] = await Promise.all([ readFile(destinationArtifacts.jsonPath, 'utf-8'), readFile(destinationArtifacts.dtsPath, 'utf-8'), ]); await writeContractSnapshot(migrationsDir, destHash, { contractJson: JSON.parse(contractJsonRaw), contractDts }); ``` Now it renders the declarations from the emitted JSON it already parsed, before anything is written, through the same client method the db commands use: ```ts // packages/1-framework/3-tooling/cli/src/control-api/operations/migration-plan.ts const rendered = await renderSnapshotDeclarations({ client: options.client, contractJson: emittedContractJson, contractJsonPath: contractPathAbsolute, resolveImportSpecifier, }); ``` And a vitest config can no longer do this: ```text $ pnpm lint:vitest-timeouts lint-vitest-timeouts: timeouts.default is 100ms (200ms on CI) and fails healthy tests; use timeouts.vitestPackageDefault, or timeouts.databaseOperation for a package that talks to a database: packages/a/vitest.config.ts:6 testTimeout: timeouts.default ``` ## Decision This PR ships three things: 1. **Every snapshot writer renders its declarations from the JSON it stores.** `migration plan` and `migration new` were the last two writers that copied the sibling `contract.d.ts` from disk. They now render through `renderContractDts` on the control client, before any package or store entry is written. With no reader left, `readContractSnapshotDts` and the `contractDts` field on `contractAt` are gone from `@internal/migration-tools`. 2. **`contract.d.ts` is generated from the canonical contract.** `emit()` canonicalised the contract for `contract.json` but generated the declarations from the contract as authored, so a snapshot rendered from the JSON could differ from the emitted file in the order of models, fields, relations, and the keys of literal types. The mongo e2e journey compares the two byte for byte and caught it. `emit()` now generates from the canonical JSON round-trip and the literal-type serializer sorts keys, so a render from `contract.json` reproduces the emitted file exactly. Every committed `contract.d.ts` fixture is re-emitted in that order; the diff is reordering only. 3. **A lint keeps `timeouts.default` out of vitest test and hook budgets.** #30293 swept 31 configs off the 100ms value that CI doubles into a false failure. `timeouts.default` stays, because tests use it for short waits, so the guard is a lint over tracked `vitest.config.ts` files, wired into `pnpm lint:vitest-timeouts`, `test:scripts`, and CI. ## Reviewer notes - **The fixture re-emit is large but mechanical.** 227 `contract.d.ts` files change, all of them collections reordered into canonical order. Nothing in `contract.json` changes. The in-flight upgrade entry for rc.11 to rc.12 declares `changes: []`, since a consumer who re-emits sees the same reordering and has nothing to do. - **The from-side snapshot writes are gone, not moved.** `migration plan` used to write the from contract's store entry when the origin was a ref or an auto-baseline. In both cases that contract came out of the store through `contractAt`, so the entry already existed and the write-if-absent store made the write a no-op. The same holds for a `--to` destination. Only the emitted contract can be new, so only it is rendered and written. - **The plan and new commands now take the client factory** like the db commands, with `migrationPlanCommand` and `migrationNewCommand` kept as the constants the family tree mounts. The command tests mount a fake client from [offline-project.ts](packages/1-framework/3-tooling/cli/test/orm/fixtures/offline-project.ts), whose fake family cannot drive the real emitter. - **`timeouts.default` is not retired.** Ten tests use it as a short wait: a connection timeout, a polling budget. The lint rejects it only as `testTimeout` or `hookTimeout` in a vitest config, and its doc comment now says so. ## How it fits together 1. **One helper renders for every writer.** [snapshot-declarations.ts](packages/1-framework/3-tooling/cli/src/control-api/operations/snapshot-declarations.ts) takes a client, the JSON, its path and the project's import resolver, and returns the declarations or the same structured errors the ref preflight already produced. `preflightRefAdvancement` in [ref-advancement.ts](packages/1-framework/3-tooling/cli/src/control-api/operations/ref-advancement.ts) now calls it. 2. **The scaffolding operations render before they write.** [migration-plan.ts](packages/1-framework/3-tooling/cli/src/control-api/operations/migration-plan.ts) renders right after the import resolver is built, ahead of the seed phase, and writes the destination entry with the planned package. [migration-new.ts](packages/1-framework/3-tooling/cli/src/control-api/operations/migration-new.ts) renders before `writeMigrationPackage`. 3. **The store stops serving declarations.** [aggregate.ts](packages/1-framework/3-tooling/migration/src/aggregate/aggregate.ts) reads only `contract.json` for `contractAt`; [plan-resolution.ts](packages/1-framework/3-tooling/cli/src/control-api/operations/plan-resolution.ts) drops the fields nobody consumes. 4. **The lint.** [lint-vitest-timeouts.mjs](scripts/lint-vitest-timeouts.mjs) walks `git ls-files` for vitest configs and names each `testTimeout` or `hookTimeout` set to `timeouts.default`. ## Behavior changes & evidence - **`migration plan` and `migration new` refuse before writing anything when the destination's declarations cannot be rendered**, and write a snapshot whose `contract.d.ts` is rendered from the emitted JSON. Implementation: [migration-plan.ts](packages/1-framework/3-tooling/cli/src/control-api/operations/migration-plan.ts), [migration-new.ts](packages/1-framework/3-tooling/cli/src/control-api/operations/migration-new.ts). Evidence: [migration-plan.test.ts](packages/1-framework/3-tooling/cli/test/orm/migration-plan.test.ts), [migration-new.test.ts](packages/1-framework/3-tooling/cli/test/orm/migration-new.test.ts). - **Neither command needs `contract.d.ts` on disk any more.** The offline fixture stopped writing one, and every existing plan, new, and tamper test still passes. Evidence: [offline-project.ts](packages/1-framework/3-tooling/cli/test/orm/fixtures/offline-project.ts). - **`contractAt` no longer carries `contractDts`, and `readContractSnapshotDts` is gone.** Implementation: [contract-snapshot-store.ts](packages/1-framework/3-tooling/migration/src/contract-snapshot-store.ts), [types.ts](packages/1-framework/3-tooling/migration/src/aggregate/types.ts). Evidence: [contract-at.test.ts](packages/1-framework/3-tooling/migration/test/aggregate/contract-at.test.ts). - **A vitest config budgeting with `timeouts.default` fails CI.** Implementation: [lint-vitest-timeouts.mjs](scripts/lint-vitest-timeouts.mjs), [ci.yml](.github/workflows/ci.yml). Evidence: [lint-vitest-timeouts.test.mjs](scripts/lint-vitest-timeouts.test.mjs). - **Docs.** The migration-system subsystem doc describes the scaffolding snapshot write and states that nothing reads a `.d.ts` out of the store. ## Testing performed - `pnpm typecheck` and `pnpm test` in `@internal/migration-tools` (43 files, 601 tests), then `pnpm build` - `pnpm typecheck` and `pnpm test` in `@internal/cli` (116 files, 1476 tests) against the rebuilt migration-tools, then `pnpm build` - `pnpm typecheck` in `@internal/extension-sqlite` against the rebuilt CLI - `node --test scripts/lint-vitest-timeouts.test.mjs` (9 tests) and `pnpm lint:vitest-timeouts` on the repo - `pnpm test` in `@internal/emitter` (234 tests) and in the sql and mongo family emitters, then a full `pnpm build` - `pnpm fixtures:check` against the full build, and `pnpm check:upgrade-coverage --mode pr --prev origin/main` - `test/cli-journeys/mongo-migration.e2e.test.ts` in the integration suite, which compares a snapshot's `contract.d.ts` with the emitted one byte for byte ## Alternatives considered - **Render the from-side snapshots too.** Rendering an old snapshot's JSON under the current install could refuse a plan for a contract that already has a valid entry, for no gain: the write was a no-op. Dropping the write is both simpler and safer. - **Retire `timeouts.default`.** Its ten remaining uses are real short waits inside tests. A lint on the two config settings catches the class without taking a value away. - **Make the fake families in the offline fixtures render for real.** That would mean shipping a real target's serializer and emission hook into structural stand-ins. The client is the seam the db command tests already use, so the plan and new commands take the same factory. ## Checklist - [x] All commits are signed off (`git commit -s`) per the [DCO](../CONTRIBUTING.md#developer-certificate-of-origin-dco). - [x] I read [CONTRIBUTING.md](../CONTRIBUTING.md) and the change is scoped to one logical concern. - [x] Tests are updated. - [ ] The PR title is in `TML-NNNN: <sentence-case title>` form — no Linear ticket exists for this change. - [x] The **Skill update** section above is filled in. ## Notes for the reviewer See the reviewer notes above. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Migration creation and planning now generate contract declarations for destination snapshots. - Contract rendering errors prevent incomplete migration outputs from being written. - **Bug Fixes** - Improved migration snapshot consistency by deriving declarations from contract definitions. - Standardized generated contract declaration ordering for consistent output. - **Chores** - Added automated checks for inappropriate default timeout budgets in Vitest configurations. - **Documentation** - Clarified recommended timeout settings and migration contract snapshot behavior. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io> Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com> | 19 天前 | |
migration plan and migration new no longer need contract.d.ts on disk; vitest configs cannot budget with timeouts.default (#30298) ## Linked issue n/a — the two follow-ups deferred from #30293 (db init, db update and migrate no longer strand the database when contract.d.ts is missing). ## Skill update n/a — no user-facing surface changes; `migration plan` and `migration new` stop needing a `contract.d.ts` next to `contract.json`, which no skill ever told users to keep. ## At a glance Before, `migration plan` copied whatever `contract.d.ts` sat next to `contract.json` into the snapshot store, after the package was already written: ```ts const [contractJsonRaw, contractDts] = await Promise.all([ readFile(destinationArtifacts.jsonPath, 'utf-8'), readFile(destinationArtifacts.dtsPath, 'utf-8'), ]); await writeContractSnapshot(migrationsDir, destHash, { contractJson: JSON.parse(contractJsonRaw), contractDts }); ``` Now it renders the declarations from the emitted JSON it already parsed, before anything is written, through the same client method the db commands use: ```ts // packages/1-framework/3-tooling/cli/src/control-api/operations/migration-plan.ts const rendered = await renderSnapshotDeclarations({ client: options.client, contractJson: emittedContractJson, contractJsonPath: contractPathAbsolute, resolveImportSpecifier, }); ``` And a vitest config can no longer do this: ```text $ pnpm lint:vitest-timeouts lint-vitest-timeouts: timeouts.default is 100ms (200ms on CI) and fails healthy tests; use timeouts.vitestPackageDefault, or timeouts.databaseOperation for a package that talks to a database: packages/a/vitest.config.ts:6 testTimeout: timeouts.default ``` ## Decision This PR ships three things: 1. **Every snapshot writer renders its declarations from the JSON it stores.** `migration plan` and `migration new` were the last two writers that copied the sibling `contract.d.ts` from disk. They now render through `renderContractDts` on the control client, before any package or store entry is written. With no reader left, `readContractSnapshotDts` and the `contractDts` field on `contractAt` are gone from `@internal/migration-tools`. 2. **`contract.d.ts` is generated from the canonical contract.** `emit()` canonicalised the contract for `contract.json` but generated the declarations from the contract as authored, so a snapshot rendered from the JSON could differ from the emitted file in the order of models, fields, relations, and the keys of literal types. The mongo e2e journey compares the two byte for byte and caught it. `emit()` now generates from the canonical JSON round-trip and the literal-type serializer sorts keys, so a render from `contract.json` reproduces the emitted file exactly. Every committed `contract.d.ts` fixture is re-emitted in that order; the diff is reordering only. 3. **A lint keeps `timeouts.default` out of vitest test and hook budgets.** #30293 swept 31 configs off the 100ms value that CI doubles into a false failure. `timeouts.default` stays, because tests use it for short waits, so the guard is a lint over tracked `vitest.config.ts` files, wired into `pnpm lint:vitest-timeouts`, `test:scripts`, and CI. ## Reviewer notes - **The fixture re-emit is large but mechanical.** 227 `contract.d.ts` files change, all of them collections reordered into canonical order. Nothing in `contract.json` changes. The in-flight upgrade entry for rc.11 to rc.12 declares `changes: []`, since a consumer who re-emits sees the same reordering and has nothing to do. - **The from-side snapshot writes are gone, not moved.** `migration plan` used to write the from contract's store entry when the origin was a ref or an auto-baseline. In both cases that contract came out of the store through `contractAt`, so the entry already existed and the write-if-absent store made the write a no-op. The same holds for a `--to` destination. Only the emitted contract can be new, so only it is rendered and written. - **The plan and new commands now take the client factory** like the db commands, with `migrationPlanCommand` and `migrationNewCommand` kept as the constants the family tree mounts. The command tests mount a fake client from [offline-project.ts](packages/1-framework/3-tooling/cli/test/orm/fixtures/offline-project.ts), whose fake family cannot drive the real emitter. - **`timeouts.default` is not retired.** Ten tests use it as a short wait: a connection timeout, a polling budget. The lint rejects it only as `testTimeout` or `hookTimeout` in a vitest config, and its doc comment now says so. ## How it fits together 1. **One helper renders for every writer.** [snapshot-declarations.ts](packages/1-framework/3-tooling/cli/src/control-api/operations/snapshot-declarations.ts) takes a client, the JSON, its path and the project's import resolver, and returns the declarations or the same structured errors the ref preflight already produced. `preflightRefAdvancement` in [ref-advancement.ts](packages/1-framework/3-tooling/cli/src/control-api/operations/ref-advancement.ts) now calls it. 2. **The scaffolding operations render before they write.** [migration-plan.ts](packages/1-framework/3-tooling/cli/src/control-api/operations/migration-plan.ts) renders right after the import resolver is built, ahead of the seed phase, and writes the destination entry with the planned package. [migration-new.ts](packages/1-framework/3-tooling/cli/src/control-api/operations/migration-new.ts) renders before `writeMigrationPackage`. 3. **The store stops serving declarations.** [aggregate.ts](packages/1-framework/3-tooling/migration/src/aggregate/aggregate.ts) reads only `contract.json` for `contractAt`; [plan-resolution.ts](packages/1-framework/3-tooling/cli/src/control-api/operations/plan-resolution.ts) drops the fields nobody consumes. 4. **The lint.** [lint-vitest-timeouts.mjs](scripts/lint-vitest-timeouts.mjs) walks `git ls-files` for vitest configs and names each `testTimeout` or `hookTimeout` set to `timeouts.default`. ## Behavior changes & evidence - **`migration plan` and `migration new` refuse before writing anything when the destination's declarations cannot be rendered**, and write a snapshot whose `contract.d.ts` is rendered from the emitted JSON. Implementation: [migration-plan.ts](packages/1-framework/3-tooling/cli/src/control-api/operations/migration-plan.ts), [migration-new.ts](packages/1-framework/3-tooling/cli/src/control-api/operations/migration-new.ts). Evidence: [migration-plan.test.ts](packages/1-framework/3-tooling/cli/test/orm/migration-plan.test.ts), [migration-new.test.ts](packages/1-framework/3-tooling/cli/test/orm/migration-new.test.ts). - **Neither command needs `contract.d.ts` on disk any more.** The offline fixture stopped writing one, and every existing plan, new, and tamper test still passes. Evidence: [offline-project.ts](packages/1-framework/3-tooling/cli/test/orm/fixtures/offline-project.ts). - **`contractAt` no longer carries `contractDts`, and `readContractSnapshotDts` is gone.** Implementation: [contract-snapshot-store.ts](packages/1-framework/3-tooling/migration/src/contract-snapshot-store.ts), [types.ts](packages/1-framework/3-tooling/migration/src/aggregate/types.ts). Evidence: [contract-at.test.ts](packages/1-framework/3-tooling/migration/test/aggregate/contract-at.test.ts). - **A vitest config budgeting with `timeouts.default` fails CI.** Implementation: [lint-vitest-timeouts.mjs](scripts/lint-vitest-timeouts.mjs), [ci.yml](.github/workflows/ci.yml). Evidence: [lint-vitest-timeouts.test.mjs](scripts/lint-vitest-timeouts.test.mjs). - **Docs.** The migration-system subsystem doc describes the scaffolding snapshot write and states that nothing reads a `.d.ts` out of the store. ## Testing performed - `pnpm typecheck` and `pnpm test` in `@internal/migration-tools` (43 files, 601 tests), then `pnpm build` - `pnpm typecheck` and `pnpm test` in `@internal/cli` (116 files, 1476 tests) against the rebuilt migration-tools, then `pnpm build` - `pnpm typecheck` in `@internal/extension-sqlite` against the rebuilt CLI - `node --test scripts/lint-vitest-timeouts.test.mjs` (9 tests) and `pnpm lint:vitest-timeouts` on the repo - `pnpm test` in `@internal/emitter` (234 tests) and in the sql and mongo family emitters, then a full `pnpm build` - `pnpm fixtures:check` against the full build, and `pnpm check:upgrade-coverage --mode pr --prev origin/main` - `test/cli-journeys/mongo-migration.e2e.test.ts` in the integration suite, which compares a snapshot's `contract.d.ts` with the emitted one byte for byte ## Alternatives considered - **Render the from-side snapshots too.** Rendering an old snapshot's JSON under the current install could refuse a plan for a contract that already has a valid entry, for no gain: the write was a no-op. Dropping the write is both simpler and safer. - **Retire `timeouts.default`.** Its ten remaining uses are real short waits inside tests. A lint on the two config settings catches the class without taking a value away. - **Make the fake families in the offline fixtures render for real.** That would mean shipping a real target's serializer and emission hook into structural stand-ins. The client is the seam the db command tests already use, so the plan and new commands take the same factory. ## Checklist - [x] All commits are signed off (`git commit -s`) per the [DCO](../CONTRIBUTING.md#developer-certificate-of-origin-dco). - [x] I read [CONTRIBUTING.md](../CONTRIBUTING.md) and the change is scoped to one logical concern. - [x] Tests are updated. - [ ] The PR title is in `TML-NNNN: <sentence-case title>` form — no Linear ticket exists for this change. - [x] The **Skill update** section above is filled in. ## Notes for the reviewer See the reviewer notes above. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Migration creation and planning now generate contract declarations for destination snapshots. - Contract rendering errors prevent incomplete migration outputs from being written. - **Bug Fixes** - Improved migration snapshot consistency by deriving declarations from contract definitions. - Standardized generated contract declaration ordering for consistent output. - **Chores** - Added automated checks for inappropriate default timeout budgets in Vitest configurations. - **Documentation** - Clarified recommended timeout settings and migration contract snapshot behavior. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io> Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com> | 19 天前 | |
fix(lint): handle escaped backslashes in YAML quote tracking Signed-off-by: Will Madden <willsmadden@gmail.com> stripYamlComment toggled inDouble on every double-quote, missing the fact that YAML treats \" as an escaped quote inside a double-quoted scalar. With a contrived line like: title: "\" # pull_request_target inside string" the buggy code closed the string at the second " and treated the # as a comment marker, dropping the pull_request_target match — a possible false negative. Fix by counting consecutive backslashes before each ": odd means the quote is escaped (data, not terminator); even (incl. zero) means it opens or closes the scalar. Add three tests covering \", \\\" and the \\" boundary case where the string genuinely closes after a literal backslash. Refs: PR #488 review (CodeRabbit) Signed-off-by: Will Madden <madden@prisma.io> | 4 个月前 | |
fix(lint): handle escaped backslashes in YAML quote tracking Signed-off-by: Will Madden <willsmadden@gmail.com> stripYamlComment toggled inDouble on every double-quote, missing the fact that YAML treats \" as an escaped quote inside a double-quoted scalar. With a contrived line like: title: "\" # pull_request_target inside string" the buggy code closed the string at the second " and treated the # as a comment marker, dropping the pull_request_target match — a possible false negative. Fix by counting consecutive backslashes before each ": odd means the quote is escaped (data, not terminator); even (incl. zero) means it opens or closes the scalar. Add three tests covering \", \\\" and the \\" boundary case where the string genuinely closes after a literal backslash. Refs: PR #488 review (CodeRabbit) Signed-off-by: Will Madden <madden@prisma.io> | 4 个月前 | |
refactor: rename every user-facing prisma-next identifier to Prisma 8 (#30262) ## Linked issue n/a — no Linear ticket. Completes the rename that #30248 started for prose; builds on #30261. ## At a glance Every `prisma-next` identifier a user can see is renamed. Before and after, for a scaffolded project: ```text // use prisma-next → // use prisma-8 (schema header) prisma-next.md → prisma-8.md (primer at the project root) PRISMA_NEXT_DISABLE_TELEMETRY → PRISMA_DISABLE_TELEMETRY (and every other PRISMA_NEXT_* variable) ~/.config/prisma-next/ → ~/.config/prisma-8/ (per-user telemetry config) prisma-next contract emit → prisma contract emit (CLI invocations in docs, fixtures, recordings) ``` ## Summary After #30248 the product was called Prisma 8 in prose, but the working name was still written into user projects and printed by the CLI: the schema header, the primer file, the environment variables, the per-user config directory, the language-server diagnostic source, the Standard Schema vendor string, the contract brand symbol, the advisory-lock domain, and about 650 fixture and doc files that spelled out `prisma-next …` commands. This PR renames all of it in one pass and tightens the legacy-name lint so the only occurrences left are the ones with a reason. ## Decision One commit. The mapping: | Surface | Before | After | |---|---|---| | Schema header | `// use prisma-next` | `// use prisma-8` | | Primer file `init` writes | `prisma-next.md` | `prisma-8.md` | | CLI environment variables | `PRISMA_NEXT_*` | `PRISMA_*` | | Per-user config directory | `prisma-next/` | `prisma-8/` | | Language-server diagnostic source | `prisma-next` | `prisma` | | Standard Schema vendor, VS Code publisher | `prisma-next` | `prisma` | | Contract brand symbol | `__prisma_next_brand__` | `__prisma_8_brand__` | | Postgres advisory-lock domain | `prisma_next.contract.marker` | `prisma_8.contract.marker` | | Example database names | `prisma_next_*` | `prisma_8_*` | | README banner image | `images/prisma-next.png` | `images/prisma-8.png` | | Telemetry docs URL | `prisma-next.dev/docs/…` | `www.prisma.io/docs/…` | | New-issue links | `github.com/prisma/prisma-next/issues/new` | `github.com/prisma/orm/issues/new` | | CLI invocations in prose, fixtures, and recordings | `prisma-next db verify` | `prisma db verify` | `prisma-8` is the slug the repo already uses for the skill, the examples, and the upgrade directories, so it is the slug for everything that needs one. Environment variables drop the infix entirely because `PRISMA_*` is what users expect and nothing else in the repo claims those names. What keeps the old name, each with a lint allowance that says why: - **Dated records**: changelog, release notes, ADRs, shipped upgrade instructions, gotcha logs, the framework-gaps review, and the `projects/` and `drive/` write-ups. - **Pinned links** into the old repository by number, Linear slugs, and links to ADRs whose filenames carry the name. - **`@cipherstash/prisma-next`**, a third party's published package name. - **Retirement proofs**: the list of old skill directories `init` deletes, and the tests asserting that no `prisma-next` bin or skill directory is installed any more. ## Behavior changes & evidence - **Schema header.** The inferred-schema printer and the `init` templates write `// use prisma-8`. The language server accepts both headers, so existing schemas keep their diagnostics and completion, and its Format action rewrites the old header to the new one. [packages/1-framework/3-tooling/language-server/src/schema-directive.ts](packages/1-framework/3-tooling/language-server/src/schema-directive.ts), [packages/1-framework/2-authoring/psl-printer/src/ast-to-print-document.ts](packages/1-framework/2-authoring/psl-printer/src/ast-to-print-document.ts). Evidence: the `renameLegacyDirective` tests, the server test that formats a legacy-headed schema, and the psl-printer tests. - **Environment variables.** Telemetry gating, the endpoint override, and the debug switch read the new names. `PRISMA_NEXT_DISABLE_TELEMETRY` is still honoured as an opt-out so nobody is silently opted back in; the endpoint and debug spellings are not. [packages/1-framework/3-tooling/cli-telemetry/src/gating.ts](packages/1-framework/3-tooling/cli-telemetry/src/gating.ts). Evidence: cli-telemetry gating tests. - **Per-user config directory.** [packages/1-framework/3-tooling/cli-telemetry/src/user-config.ts](packages/1-framework/3-tooling/cli-telemetry/src/user-config.ts). Existing users see the telemetry consent prompt once more; nothing else is lost. - **Primer file.** [packages/1-framework/3-tooling/cli/src/orm/init-scaffold.ts](packages/1-framework/3-tooling/cli/src/orm/init-scaffold.ts). Evidence: init-scaffold tests and template snapshots. - **Advisory-lock domain.** A CLI on this version and one on the previous version take different locks for the same marker. Both versions running migrations against one database at the same moment is already unsupported. - **Upgrade instructions.** Entries for the header, the environment variables, and the primer file are recorded in the rc.9 → rc.10 app and extension instructions with detection patterns, so the published upgrade skill applies the rename. ## Testing performed - `pnpm test` in cli (1437), cli-telemetry (112), language-server (312), psl-printer (63), framework-components (672), target-postgres (1607), vite-plugin-contract-emit (31), emitter (231), and `pnpm test:scripts` (507): all pass after `pnpm build`. The language-server tests hard-coded the old header's length in semantic-token arrays and span offsets; those expectations are updated. - Committed migration steps and their content-addressed contract snapshots are left untouched, since rewriting them would break their hashes; the lint treats them as dated records. - `pnpm lint:legacy-name` passes with the tightened allowances; `node --test scripts/lint-legacy-name.test.mjs` passes (14 tests, including new negative cases for the header, primer, and skill names). - `pnpm check:upgrade-coverage --mode pr --prev origin/main` passes. ## Skill update `skills/prisma-8` references and the two rc.9 → rc.10 upgrade instruction files are updated in this PR. ## Checklist - [x] All commits are signed off (`git commit -s`) per the [DCO](../CONTRIBUTING.md#developer-certificate-of-origin-dco). - [x] I read [CONTRIBUTING.md](../CONTRIBUTING.md) and the change is scoped to one logical concern. - [x] Tests are updated. - [ ] The PR title is in `TML-NNNN: <sentence-case title>` form. No Linear ticket exists for this change. - [x] The **Skill update** section above is filled in. 🤖 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 Fable 5.1 <noreply@anthropic.com> | 23 天前 | |
TML-3067: all error codes become dotted NAMESPACE.SUBCODE (ADR 239) (#1016) ## Linked issue Refs [TML-3067](https://linear.app/prisma-company/issue/TML-3067/error-consolidation-one-structural-error-scheme-with-dotted-namespace) — the error-consolidation work item of Prisma 8 RC1 ("Error-code scheme ratified" milestone). Codes freeze at RC. ## At a glance ```ts import { isStructuredError, structuredError } from '@prisma-next/utils/structured-error'; const err = structuredError('MIGRATION.FILE_MISSING', 'Migration file not found', { why: 'No migration.ts under "migrations/app/20260721".', fix: 'Run `prisma-next migration new` or check the path.', }); throw err; // throwable… return notOk(err); // …or a Result failure — same value, no conversion isStructuredError(err); // true — a structural field check, never instanceof ``` Before this PR the same failure appeared under five different schemes: numeric `PN-MIG-2002` from one class, dotted `MIGRATION.FILE_MISSING` from another, and a bare `'EXECUTION_FAILED'` enum on runner `Result`s — with no shared recognition mechanism. ## Decision This PR ships the error-consolidation slice ratified in [ADR 239](docs/architecture%20docs/adrs/ADR%20239%20-%20Errors%20are%20structural%20envelopes%20with%20dotted%20namespace%20codes.md) (supersedes ADR 027 + ADR 068): 1. **One code scheme.** Every published error code is now a dotted `NAMESPACE.SUBCODE` name from a closed, ADR-governed namespace list. All numeric `PN-DOMAIN-NNNN` codes are renamed — including two catalogues the original census missed (`PN-MIG-CHECK-*`, `PN-SCHEMA-0001`) — with a complete old→new crosswalk in the ADR. 2. **Structural recognition.** A `StructuredError` interface + `isStructuredError` predicate (field-shape check, never `instanceof`) in `@prisma-next/utils`, so errors survive the control/execution split, JSON round-trips, and duplicate library copies. `CliStructuredError` remains as a convenience class that implements the interface; the `domain` concept is deleted. 3. **User-facing vs internal.** New `InternalError` + `assertNever` for bugs; `invariant`/`assertDefined` now throw it. Never caught except at the outermost boundary. 4. **A ban with a ratchet.** A `no-bare-throw` Biome plugin flags `throw new Error(` at severity `info`; a CI ratchet (mirroring `no-bare-cast`) fails any PR that raises the count. ~950 pre-existing sites burn down in later per-plane sweeps. 5. **Exit codes aligned to the reserved table.** Expected structured failures exit `2` (user-abort `3`); `1` is reserved for internal errors. Previously every non-CLI structured error exited `1`, colliding with its documented "internal error" meaning. ## Reviewer notes - **The crosswalk is the review.** The freeze-critical artifact is the old→new table in ADR 239 — every rename in the diff must match it. The judgment-call mappings: marker/verify codes went to `CONTRACT` (not `CLI`/`RUN`) because they're about the contract↔DB relationship; planning/runner failures went to `MIGRATION`; config-shaped `PN-CLI-4xxx` codes went to `CONFIG`. - **Largest commit** is the rename (91 files) but it's almost entirely mechanical string + assertion updates; the one structural change is in `packages/1-framework/1-core/errors/src/control.ts` (domain removal, `implements StructuredError`). - **Three latent bugs fixed in passing**, visible as behavior changes: `PN-CLI-4012` was assigned to two unrelated errors (now split as `CLI.CONFIG_ARG_MISSING_PATH` / `CLI.INVALID_VERIFY_MODE`); mongo schema-verify reported the marker-required code for schema failures (now `CONTRACT.SCHEMA_VERIFICATION_FAILED`); driver envelopes hardcoded `category: 'RUNTIME'` for `DRIVER.*` codes (now `DRIVER`). - **Deleted surface:** relational-core's `planInvalid`/`planUnsupported` + its duplicate `RuntimeError` interface had zero production callers — deleted, not migrated. - **Local flakes ruled pre-existing:** `removed-verb-redirects`/`version` CLI tests (500 ms spawn timeout vs ~0.9 s local CLI startup) fail identically on a merge-base build; untouched by this branch. ## How it fits together 1. **Foundation** — `@prisma-next/utils` gains `structured-error` (interface, predicate, factory, `docsUrlFor` with a single `DOCS_BASE`) and `internal-error` (`InternalError`, `isInternalError`, `assertNever`). No code enumeration here: each namespace's codes live as a typed union in the module that owns the namespace. 2. **Rename** — the `@prisma-next/errors` factories, the init-command factories, the `migration check` catalogue, and every direct construction emit dotted codes; ~140 test assertions updated across the repo. 3. **Reconciliation** — the runtime envelope's category union gains `DRIVER`/`MIGRATION`/`ORM` (no more silent fold to `RUNTIME`); SQL and mongo runner `Result` codes become `MIGRATION.*`; dead PLAN surface deleted. 4. **Enforcement** — `biome-plugins/no-bare-throw.grit` + `scripts/lint-throws.mjs` wired into `biome.jsonc`, CI, and `test:scripts`, with fixtures proving fire/no-fire (TypeError/RangeError/InternalError and test files are exempt). 5. **Docs** — ADR 239 with the full crosswalk; ADR 027/068 marked superseded; `docs/Error Handling.md` and `docs/CLI Style Guide.md` updated; the 0.15-to-0.16 upgrade instructions record the rename with a detection glob for old code strings. ## Behavior changes & evidence - **All published codes are dotted.** Implementation: [packages/1-framework/1-core/errors/src/control.ts](packages/1-framework/1-core/errors/src/control.ts), [execution.ts](packages/1-framework/1-core/errors/src/execution.ts), [migration.ts](packages/1-framework/1-core/errors/src/migration.ts). Evidence: [packages/1-framework/1-core/errors/test](packages/1-framework/1-core/errors/test) and the CLI golden tests; repo-wide grep for `PN-DOMAIN-NNNN` outside `docs/` returns zero. - **Structural recognition ships.** Implementation: [packages/1-framework/0-foundation/utils/src/structured-error.ts](packages/1-framework/0-foundation/utils/src/structured-error.ts). Evidence: [test/structured-error.test.ts](packages/1-framework/0-foundation/utils/test/structured-error.test.ts) asserts a bare `{ code, message }` object (no prototype) is recognized. - **Bugs throw `InternalError`.** Implementation: [internal-error.ts](packages/1-framework/0-foundation/utils/src/internal-error.ts), [assertions.ts](packages/1-framework/0-foundation/utils/src/assertions.ts). Evidence: [test/internal-error.test.ts](packages/1-framework/0-foundation/utils/test/internal-error.test.ts). - **Structured failures exit 2, user-abort 3.** Implementation: [packages/1-framework/3-tooling/cli/src/utils/result-handler.ts](packages/1-framework/3-tooling/cli/src/utils/result-handler.ts), [commands/init/init.ts](packages/1-framework/3-tooling/cli/src/commands/init/init.ts). Evidence: CLI command tests updated alongside. - **Runner failures carry `MIGRATION.*` codes on `Result`.** Implementation: [packages/2-sql/9-family/src/core/migrations/types.ts](packages/2-sql/9-family/src/core/migrations/types.ts), postgres/sqlite/mongo runners. Evidence: runner unit + integration tests in all three target packages. - **Bare `throw new Error` is ratcheted.** Implementation: [biome-plugins/no-bare-throw.grit](biome-plugins/no-bare-throw.grit), [scripts/lint-throws.mjs](scripts/lint-throws.mjs). Evidence: [biome-plugins/fixtures](biome-plugins/fixtures) fire/no-fire files, [scripts/lint-throws.test.mjs](scripts/lint-throws.test.mjs). ## Testing performed - `pnpm build` — 68/68 tasks. - `pnpm test:packages` — 13,133+ passed; the only reds are the two pre-existing local-machine timeout suites noted above (untouched by this branch; reproduce at merge-base). - `pnpm lint:deps`, `pnpm test:scripts` (200/200, includes the new ratchet tests), `pnpm check:upgrade-coverage --mode pr` — clean. - Per-package suites for every touched package (errors, cli 1347/1347, framework-components, relational-core, sql/mongo families, targets, drivers, adapters). ## Skill update The 0.15-to-0.16 upgrade-skill instructions ([skills/upgrade/prisma-next-upgrade/upgrades/0.15-to-0.16/instructions.md](skills/upgrade/prisma-next-upgrade/upgrades/0.15-to-0.16/instructions.md)) gain an entry for the code rename with a detection glob over old `PN-*` strings. No shipped skill documents specific error codes (verified by grep). ## Follow-ups - Per-plane sweeps of the ~250 codeless user-facing throws (ORM, authoring, adapters) onto `structuredError`, ratcheting `lint:throws` down — trails RC per the ADR's freeze-scope; codes added later are non-breaking. - Centralize the hardcoded `prisma-next.dev` docs URLs in factories onto `docsUrlFor` (one-line domain flip at RC). ## Alternatives considered - **A single base class recognized by `instanceof`** — rejected; a shared prototype doesn't survive the control/execution split, JSON rehydration, or duplicate library copies. The old code already duck-typed around this; the ADR makes the workaround the mechanism. - **One physical union module listing every code** — rejected; it would invert `lint:deps` layering (foundation naming codes owned by targets/extensions). Per-namespace unions keep each code with its owner; the ADR crosswalk is the registry. - **Keeping numeric `PN-DOMAIN-NNNN`** — rejected in the scheme decision; dotted names are self-describing and already had 2:1 adoption. - **Converting all ~750 bare throws before RC** — rejected; only renames of *published* codes are breaking and must freeze. The ratchet lets the tail burn down safely after RC. ## Checklist - [x] All commits are signed off (`git commit -s`) per the [DCO](../CONTRIBUTING.md#developer-certificate-of-origin-dco). - [x] I read [CONTRIBUTING.md](../CONTRIBUTING.md) and the change is scoped to one logical concern. - [x] Tests are updated. - [x] The PR title is in `TML-NNNN: <sentence-case title>` form. - [x] The **Skill update** section above is filled in. --------- Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io> | 2 个月前 | |
feat(ci): explicitly retrieve list of publishable packages for pkg.pr.new | 8 个月前 | |
feat: an application depends on one Prisma package (ADR 242) (#29864) ## What changes for someone using Prisma Today, an application that talks to Postgres installs a long list of our packages: ```jsonc { "dependencies": { "@prisma-next/postgres": "...", "@prisma-next/sql-runtime": "...", "@prisma-next/sql-orm-client": "...", "@prisma-next/target-postgres": "...", "@prisma-next/adapter-postgres": "...", "@prisma-next/sql-contract": "..." // ...a dozen more } } ``` After this PR, it installs one: ```jsonc { "dependencies": { "@prisma/orm-postgres": "0.16.0" } } ``` Everything else arrives as that package's own dependencies. Three of our example apps are converted in this PR to prove it — one per database — and each has exactly one Prisma package in its `dependencies`. This implements [ADR 242](https://github.com/prisma/prisma/pull/29852), which is already merged. ## What gets published 17 packages, all under the `@prisma` scope: - **3 database packages** — `@prisma/orm-postgres`, `orm-sqlite`, `orm-mongo`. An application installs exactly one. We call these *facades*: each is a small package that wires its database together and re-exports everything an application needs. - **6 extension packs** — PostGIS, pgvector, ParadeDB, Supabase, arktype-json, middleware-cache. Optional, installed alongside a database package. - **7 platform packages** — the framework, the toolchain, one per database family, one per database target. Applications never install these directly; they arrive as dependencies. Extension authors do install them. - **the `prisma` command**, as a bin-only package. Every other workspace package — around 50 of them — stops being published. They still exist in the repo as the unit we organise code in; they just stop having a life on the registry. **This PR does not make that switch yet.** It builds and proves the new surface while leaving today's publish list exactly as it is. Flipping it is a separate change. ## The problem this design has to avoid A published package can't depend on packages that won't exist on the registry. So each published package *contains a compiled copy* of the internal packages it covers. That creates a trap. If one application ends up with the same code twice — once inside a published package, once as its own package — then classes, registries, and anything compared by reference exist twice too. An `instanceof` check quietly returns false. Nothing crashes, nothing fails to compile, and both copies behave identically in isolation. You find out much later, somewhere unrelated. So the rule the whole design follows is: **every piece of internal code is published from exactly one package.** Concretely, that means: - Each published package is built in one pass, so code shared between its own entry points exists once. Verified from the build's source maps: no module appears in more than one chunk, in any published package. - When one published package needs code from another, it imports it as a real dependency rather than compiling in a second copy. - A facade re-exports from the platform packages; it never carries its own copy. `@prisma/orm-postgres/orm-client` and `@prisma/orm-family-sql/orm-client` are two names for the same object, and there's a test that asserts exactly that from installed tarballs. - One table in `packages/0-shared/publish-surface` maps every internal package to where it's published. The build, the code generator, and the lint checks all read it, so there's one answer to "where does this live" rather than three that can drift. ## Generated code follows the application Prisma writes imports into your project — contract types and migration files. Those imports have to name packages your project actually depends on, or they won't resolve. So the generator now reads the `package.json` next to the config it's generating for. A project that depends on `@prisma/orm-postgres` gets imports from that package. A project on today's names keeps today's names. Nothing to configure, because the manifest already says which it is. Contract hashes are unaffected, and that isn't an assumption — hashes are computed from a structure that import text never enters, and there's a test asserting the hash is identical across naming schemes *while* the emitted imports demonstrably differ. ## What stops the trap coming back Two checks, because the failure is silent and won't show up in a test suite: - Every example app and test project must use one naming scheme, not a mix. `lint-single-import-root` scans them and fails the build if any project imports from both, since that's the situation that loads code twice. - `lint-consumer-internal-imports` counts how many internal-package imports remain in those projects and compares against a committed number. It fails if the number goes up (someone added one) and also if it goes down without the number being updated (so improvements get locked in). Target is zero. The build itself also refuses to proceed if the published-package map would put one module in two places, or if a published package's `package.json` no longer matches what its code actually needs. ## Reading this PR It's large — 257 files — because it's a migration. The commits are grouped and meant to be read in order: 1. **Platform packages** — the build mechanism, and the seven platform packages it produces. 2. **Database packages, extension packs, the `prisma` command** — completes the set of 17. 3. **Generated imports become configurable** — one place decides which names get written, with today's names still the default. 4. **Database-family symmetry, publishing the map, the identity checks.** 5. **One package per application** — the three converted examples, the re-exports they proved necessary, and the counting check. 6. **Migration files follow the project too.** One thing worth knowing while reading: re-exporting a package republishes all of its sub-paths, not just the one that was needed. This PR adds 115 published sub-paths across the three database packages. Two candidates were dropped for exactly that reason — see below. ## Alternatives considered **Let an application install platform packages alongside its facade.** Nothing would need re-exporting and the facades would stay thinner. Rejected: an application would again juggle several Prisma dependencies whose correct combination it maintains by hand, and getting it wrong — upgrading one and not the other — produces the silent two-copies failure above. Re-exporting costs a generated line and nothing at runtime. **Re-export everything an application might plausibly want.** Rejected in review: because re-exporting brings a package's entire sub-path surface, generosity is expensive and hard to undo. Migration tooling (54 sub-paths) was dropped because its only users are extension packs, which install platform packages anyway; the SQL driver re-export was dropped because nothing imported it at all. What remains is what a converted example actually needed. **Flip the publish list in this same PR.** Rejected: it would mix "does the new surface work" with "is it safe to stop publishing 50 packages" in one review. The switch is mechanical once this lands, and gets its own change. ## Verification `build`, `typecheck` (156 tasks), `test:packages` (1077 files / 14087 tests), `test:e2e`, `lint`, `lint:deps`, `lint:docs`, `lint:manifests`, `check:publish-deps`, `check:clean-tree`, `lint:casts` and `lint:throws` (no new instances), `test:scripts`, coverage, the tarball-install suites, and regenerating every committed artifact leaves the tree unchanged. Known-unstable and unrelated to this change: the `relation-mode-gh-*` port suites (TML-3140), and several test timeouts that are too tight under load. ## Follow-ups TML-3124 switch the publish list · TML-3127 build cache can validate a stale published package on CI · TML-3140 unstable port suites · TML-3141 a test-helper sub-path reaches a package that is never published. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Added consolidated public ORM packages for PostgreSQL, MongoDB, SQLite, framework tooling, database targets, and extensions. - Generated contracts, migrations, and scaffolds now adapt imports to the consuming project’s package surface. - Added facade-provided `prisma-next` CLI access and consolidated migration entrypoints. - **Documentation** - Updated installation, package naming, public entrypoint, and migration scaffolding guidance. - **Tests** - Added coverage for package installation, exports, CLI behavior, module identity, and import compatibility. - **Chores** - Added checks preventing incompatible internal and public package imports. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> | 2 个月前 | |
feat: an application depends on one Prisma package (ADR 242) (#29864) ## What changes for someone using Prisma Today, an application that talks to Postgres installs a long list of our packages: ```jsonc { "dependencies": { "@prisma-next/postgres": "...", "@prisma-next/sql-runtime": "...", "@prisma-next/sql-orm-client": "...", "@prisma-next/target-postgres": "...", "@prisma-next/adapter-postgres": "...", "@prisma-next/sql-contract": "..." // ...a dozen more } } ``` After this PR, it installs one: ```jsonc { "dependencies": { "@prisma/orm-postgres": "0.16.0" } } ``` Everything else arrives as that package's own dependencies. Three of our example apps are converted in this PR to prove it — one per database — and each has exactly one Prisma package in its `dependencies`. This implements [ADR 242](https://github.com/prisma/prisma/pull/29852), which is already merged. ## What gets published 17 packages, all under the `@prisma` scope: - **3 database packages** — `@prisma/orm-postgres`, `orm-sqlite`, `orm-mongo`. An application installs exactly one. We call these *facades*: each is a small package that wires its database together and re-exports everything an application needs. - **6 extension packs** — PostGIS, pgvector, ParadeDB, Supabase, arktype-json, middleware-cache. Optional, installed alongside a database package. - **7 platform packages** — the framework, the toolchain, one per database family, one per database target. Applications never install these directly; they arrive as dependencies. Extension authors do install them. - **the `prisma` command**, as a bin-only package. Every other workspace package — around 50 of them — stops being published. They still exist in the repo as the unit we organise code in; they just stop having a life on the registry. **This PR does not make that switch yet.** It builds and proves the new surface while leaving today's publish list exactly as it is. Flipping it is a separate change. ## The problem this design has to avoid A published package can't depend on packages that won't exist on the registry. So each published package *contains a compiled copy* of the internal packages it covers. That creates a trap. If one application ends up with the same code twice — once inside a published package, once as its own package — then classes, registries, and anything compared by reference exist twice too. An `instanceof` check quietly returns false. Nothing crashes, nothing fails to compile, and both copies behave identically in isolation. You find out much later, somewhere unrelated. So the rule the whole design follows is: **every piece of internal code is published from exactly one package.** Concretely, that means: - Each published package is built in one pass, so code shared between its own entry points exists once. Verified from the build's source maps: no module appears in more than one chunk, in any published package. - When one published package needs code from another, it imports it as a real dependency rather than compiling in a second copy. - A facade re-exports from the platform packages; it never carries its own copy. `@prisma/orm-postgres/orm-client` and `@prisma/orm-family-sql/orm-client` are two names for the same object, and there's a test that asserts exactly that from installed tarballs. - One table in `packages/0-shared/publish-surface` maps every internal package to where it's published. The build, the code generator, and the lint checks all read it, so there's one answer to "where does this live" rather than three that can drift. ## Generated code follows the application Prisma writes imports into your project — contract types and migration files. Those imports have to name packages your project actually depends on, or they won't resolve. So the generator now reads the `package.json` next to the config it's generating for. A project that depends on `@prisma/orm-postgres` gets imports from that package. A project on today's names keeps today's names. Nothing to configure, because the manifest already says which it is. Contract hashes are unaffected, and that isn't an assumption — hashes are computed from a structure that import text never enters, and there's a test asserting the hash is identical across naming schemes *while* the emitted imports demonstrably differ. ## What stops the trap coming back Two checks, because the failure is silent and won't show up in a test suite: - Every example app and test project must use one naming scheme, not a mix. `lint-single-import-root` scans them and fails the build if any project imports from both, since that's the situation that loads code twice. - `lint-consumer-internal-imports` counts how many internal-package imports remain in those projects and compares against a committed number. It fails if the number goes up (someone added one) and also if it goes down without the number being updated (so improvements get locked in). Target is zero. The build itself also refuses to proceed if the published-package map would put one module in two places, or if a published package's `package.json` no longer matches what its code actually needs. ## Reading this PR It's large — 257 files — because it's a migration. The commits are grouped and meant to be read in order: 1. **Platform packages** — the build mechanism, and the seven platform packages it produces. 2. **Database packages, extension packs, the `prisma` command** — completes the set of 17. 3. **Generated imports become configurable** — one place decides which names get written, with today's names still the default. 4. **Database-family symmetry, publishing the map, the identity checks.** 5. **One package per application** — the three converted examples, the re-exports they proved necessary, and the counting check. 6. **Migration files follow the project too.** One thing worth knowing while reading: re-exporting a package republishes all of its sub-paths, not just the one that was needed. This PR adds 115 published sub-paths across the three database packages. Two candidates were dropped for exactly that reason — see below. ## Alternatives considered **Let an application install platform packages alongside its facade.** Nothing would need re-exporting and the facades would stay thinner. Rejected: an application would again juggle several Prisma dependencies whose correct combination it maintains by hand, and getting it wrong — upgrading one and not the other — produces the silent two-copies failure above. Re-exporting costs a generated line and nothing at runtime. **Re-export everything an application might plausibly want.** Rejected in review: because re-exporting brings a package's entire sub-path surface, generosity is expensive and hard to undo. Migration tooling (54 sub-paths) was dropped because its only users are extension packs, which install platform packages anyway; the SQL driver re-export was dropped because nothing imported it at all. What remains is what a converted example actually needed. **Flip the publish list in this same PR.** Rejected: it would mix "does the new surface work" with "is it safe to stop publishing 50 packages" in one review. The switch is mechanical once this lands, and gets its own change. ## Verification `build`, `typecheck` (156 tasks), `test:packages` (1077 files / 14087 tests), `test:e2e`, `lint`, `lint:deps`, `lint:docs`, `lint:manifests`, `check:publish-deps`, `check:clean-tree`, `lint:casts` and `lint:throws` (no new instances), `test:scripts`, coverage, the tarball-install suites, and regenerating every committed artifact leaves the tree unchanged. Known-unstable and unrelated to this change: the `relation-mode-gh-*` port suites (TML-3140), and several test timeouts that are too tight under load. ## Follow-ups TML-3124 switch the publish list · TML-3127 build cache can validate a stale published package on CI · TML-3140 unstable port suites · TML-3141 a test-helper sub-path reaches a package that is never published. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Added consolidated public ORM packages for PostgreSQL, MongoDB, SQLite, framework tooling, database targets, and extensions. - Generated contracts, migrations, and scaffolds now adapt imports to the consuming project’s package surface. - Added facade-provided `prisma-next` CLI access and consolidated migration entrypoints. - **Documentation** - Updated installation, package naming, public entrypoint, and migration scaffolding guidance. - **Tests** - Added coverage for package installation, exports, CLI behavior, module identity, and import compatibility. - **Chores** - Added checks preventing incompatible internal and public package imports. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> | 2 个月前 | |
chore: public-package-surface project close-out, publish retry hardening (#29886) Closes out the ADR 242 project (single @prisma scope, 17 published packages) per its close-out checklist, plus one hardening item from the first trusted-publishing run — as one PR. ## Close-out - **[Package Naming Conventions](https://github.com/prisma/prisma/blob/main/docs/reference/Package%20Naming%20Conventions.md) rewritten** around the delivered reality: three scopes (@prisma published; @internal and @repo private), the 17-package surface, publishability as a directory property, and a pointer to `packages/0-shared/publish-surface/src/shells.ts` as the canonical directory→entrypoint mapping — no hand-maintained copy. The enforcement section now lists the real commands (`lint:deps`, `lint:publishability`, `lint:manifests`, `lint:legacy-name`, `check:publish-deps`). - **Final retro, landed as calibration** (`drive/calibration/failure-modes.md`): F27 — mid-merge `git checkout` discards MERGE_HEAD and produces a single-parent fake merge (acceptance for any merge = printed `--is-ancestor` output); F28 — test files no configured suite runs, i.e. coverage that never executes (hit twice this project; the conversion that fixed it immediately exposed drifted stubs); F29 — a cancelled dispatch treated as cancelled scope (the privatization incident; scope must be re-homed on every cancellation). - **User upgrade instructions** (`skills/upgrade/prisma-next-upgrade/upgrades/0.16-to-0.17`) gain a first entry covering the package move end to end: one facade dependency per application, extension-pack renames, reinstall, contract regeneration (contractHash unchanged), and the rewrite map for hand-written imports — facade subpaths for same-package entrypoints, `@prisma/orm-toolchain/*` for programmatic tooling. Detection: any old-scope specifier in manifests or source. The extension-author skill already had its counterpart entry. - **`projects/public-npm-surface/` deleted.** DoD verified against the spec before deletion: publish list = exactly the 17 packages (proven live on the registry), two-direction publishability lint + planted-violation proofs, per-family tarball proofs, decomposed-install proof, emitted-import audits in both emitter modes, docs rewritten (this PR), ADR 211 amended (#29883), upgrade instructions recorded (this PR). No repo references to the project dir remain outside the historical failure-mode entries, which are allowlisted by design. ## Publish retry hardening The first trusted-publishing run failed one package (`@prisma/orm-extension-supabase`) with a sigstore transparency-log conflict (`TLOG_CREATE_ENTRY_ERROR`, 409 "equivalent entry already exists") — and the version was **absent** from the registry afterwards, so this must not be classified as already-published success. `classifyPublishResult` now returns a third state, `retryable`; `publish-packages.mjs` retries such a failure once and the retry's outcome stands (a genuine duplicate then classifies as the normal already-published no-op). Classifier covered by tests (5/5), wired into `test:scripts`. ## Verification `test:scripts` (347 tests, 0 failures), `lint:legacy-name`, `lint:docs`, `lint:skills`, `check-upgrade-coverage` — all green with visible exit codes. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Package publishing now automatically retries transient transparency-log conflicts, improving resilience during releases. * **Documentation** * Updated package naming and publishing guidance, including scopes, package visibility, workspace conventions, and validation checks. * Added guidance for handling merge, test execution, and cancelled-dispatch failure scenarios. * Added upgrade instructions for consolidating Prisma packages during the 0.16-to-0.17 migration. * **Bug Fixes** * Improved publishing outcome reporting so retryable and non-retryable failures are distinguished accurately. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io> | 1 个月前 | |
chore: public-package-surface project close-out, publish retry hardening (#29886) Closes out the ADR 242 project (single @prisma scope, 17 published packages) per its close-out checklist, plus one hardening item from the first trusted-publishing run — as one PR. ## Close-out - **[Package Naming Conventions](https://github.com/prisma/prisma/blob/main/docs/reference/Package%20Naming%20Conventions.md) rewritten** around the delivered reality: three scopes (@prisma published; @internal and @repo private), the 17-package surface, publishability as a directory property, and a pointer to `packages/0-shared/publish-surface/src/shells.ts` as the canonical directory→entrypoint mapping — no hand-maintained copy. The enforcement section now lists the real commands (`lint:deps`, `lint:publishability`, `lint:manifests`, `lint:legacy-name`, `check:publish-deps`). - **Final retro, landed as calibration** (`drive/calibration/failure-modes.md`): F27 — mid-merge `git checkout` discards MERGE_HEAD and produces a single-parent fake merge (acceptance for any merge = printed `--is-ancestor` output); F28 — test files no configured suite runs, i.e. coverage that never executes (hit twice this project; the conversion that fixed it immediately exposed drifted stubs); F29 — a cancelled dispatch treated as cancelled scope (the privatization incident; scope must be re-homed on every cancellation). - **User upgrade instructions** (`skills/upgrade/prisma-next-upgrade/upgrades/0.16-to-0.17`) gain a first entry covering the package move end to end: one facade dependency per application, extension-pack renames, reinstall, contract regeneration (contractHash unchanged), and the rewrite map for hand-written imports — facade subpaths for same-package entrypoints, `@prisma/orm-toolchain/*` for programmatic tooling. Detection: any old-scope specifier in manifests or source. The extension-author skill already had its counterpart entry. - **`projects/public-npm-surface/` deleted.** DoD verified against the spec before deletion: publish list = exactly the 17 packages (proven live on the registry), two-direction publishability lint + planted-violation proofs, per-family tarball proofs, decomposed-install proof, emitted-import audits in both emitter modes, docs rewritten (this PR), ADR 211 amended (#29883), upgrade instructions recorded (this PR). No repo references to the project dir remain outside the historical failure-mode entries, which are allowlisted by design. ## Publish retry hardening The first trusted-publishing run failed one package (`@prisma/orm-extension-supabase`) with a sigstore transparency-log conflict (`TLOG_CREATE_ENTRY_ERROR`, 409 "equivalent entry already exists") — and the version was **absent** from the registry afterwards, so this must not be classified as already-published success. `classifyPublishResult` now returns a third state, `retryable`; `publish-packages.mjs` retries such a failure once and the retry's outcome stands (a genuine duplicate then classifies as the normal already-published no-op). Classifier covered by tests (5/5), wired into `test:scripts`. ## Verification `test:scripts` (347 tests, 0 failures), `lint:legacy-name`, `lint:docs`, `lint:skills`, `check-upgrade-coverage` — all green with visible exit codes. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Package publishing now automatically retries transient transparency-log conflicts, improving resilience during releases. * **Documentation** * Updated package naming and publishing guidance, including scopes, package visibility, workspace conventions, and validation checks. * Added guidance for handling merge, test execution, and cancelled-dispatch failure scenarios. * Added upgrade instructions for consolidating Prisma packages during the 0.16-to-0.17 migration. * **Bug Fixes** * Improved publishing outcome reporting so retryable and non-retryable failures are distinguished accurately. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io> | 1 个月前 | |
chore: public-package-surface project close-out, publish retry hardening (#29886) Closes out the ADR 242 project (single @prisma scope, 17 published packages) per its close-out checklist, plus one hardening item from the first trusted-publishing run — as one PR. ## Close-out - **[Package Naming Conventions](https://github.com/prisma/prisma/blob/main/docs/reference/Package%20Naming%20Conventions.md) rewritten** around the delivered reality: three scopes (@prisma published; @internal and @repo private), the 17-package surface, publishability as a directory property, and a pointer to `packages/0-shared/publish-surface/src/shells.ts` as the canonical directory→entrypoint mapping — no hand-maintained copy. The enforcement section now lists the real commands (`lint:deps`, `lint:publishability`, `lint:manifests`, `lint:legacy-name`, `check:publish-deps`). - **Final retro, landed as calibration** (`drive/calibration/failure-modes.md`): F27 — mid-merge `git checkout` discards MERGE_HEAD and produces a single-parent fake merge (acceptance for any merge = printed `--is-ancestor` output); F28 — test files no configured suite runs, i.e. coverage that never executes (hit twice this project; the conversion that fixed it immediately exposed drifted stubs); F29 — a cancelled dispatch treated as cancelled scope (the privatization incident; scope must be re-homed on every cancellation). - **User upgrade instructions** (`skills/upgrade/prisma-next-upgrade/upgrades/0.16-to-0.17`) gain a first entry covering the package move end to end: one facade dependency per application, extension-pack renames, reinstall, contract regeneration (contractHash unchanged), and the rewrite map for hand-written imports — facade subpaths for same-package entrypoints, `@prisma/orm-toolchain/*` for programmatic tooling. Detection: any old-scope specifier in manifests or source. The extension-author skill already had its counterpart entry. - **`projects/public-npm-surface/` deleted.** DoD verified against the spec before deletion: publish list = exactly the 17 packages (proven live on the registry), two-direction publishability lint + planted-violation proofs, per-family tarball proofs, decomposed-install proof, emitted-import audits in both emitter modes, docs rewritten (this PR), ADR 211 amended (#29883), upgrade instructions recorded (this PR). No repo references to the project dir remain outside the historical failure-mode entries, which are allowlisted by design. ## Publish retry hardening The first trusted-publishing run failed one package (`@prisma/orm-extension-supabase`) with a sigstore transparency-log conflict (`TLOG_CREATE_ENTRY_ERROR`, 409 "equivalent entry already exists") — and the version was **absent** from the registry afterwards, so this must not be classified as already-published success. `classifyPublishResult` now returns a third state, `retryable`; `publish-packages.mjs` retries such a failure once and the retry's outcome stands (a genuine duplicate then classifies as the normal already-published no-op). Classifier covered by tests (5/5), wired into `test:scripts`. ## Verification `test:scripts` (347 tests, 0 failures), `lint:legacy-name`, `lint:docs`, `lint:skills`, `check-upgrade-coverage` — all green with visible exit codes. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Package publishing now automatically retries transient transparency-log conflicts, improving resilience during releases. * **Documentation** * Updated package naming and publishing guidance, including scopes, package visibility, workspace conventions, and validation checks. * Added guidance for handling merge, test execution, and cancelled-dispatch failure scenarios. * Added upgrade instructions for consolidating Prisma packages during the 0.16-to-0.17 migration. * **Bug Fixes** * Improved publishing outcome reporting so retryable and non-retryable failures are distinguished accurately. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io> | 1 个月前 | |
TML-3233: emit Models namespace + models const; Scalars, Shape, and ResultType on ORM queries (#30231) ## Linked issue Refs [TML-3233](https://linear.app/prisma-company/issue/TML-3233) · Linear project [Model and result types](https://linear.app/prisma-company/project/model-and-result-types-080d7caa544f). Design: [ADR 250](docs/architecture%20docs/adrs/ADR%20250%20-%20Models%20and%20views%20are%20emitted%20from%20the%20contract.md). ## At a glance ```ts import type { models, Models } from '../prisma/contract'; import type { Scalars, Shape } from '@prisma/orm-postgres/family-contract/types'; import type { ResultType } from '@prisma/orm-postgres/components/runtime'; // Models types are directly available in two forms. This one is a bit nicer to type: type User = typeof models.public.User; // And the raw model type which doesn't require `typeof`: type User2 = Models.public_User; // A plain query returns the scalar form of the model, ie. all its properties without relations type UserRow = Scalars<User>; // But you often need a selection of your model properties and its relations. In Prisma 7 and below // you'd use GetPayload<> for this. In Prisma 8, it looks like the following. Mark fields or // relations for inclusion with `'+'`, or exclusion with `'-'`. Naming only relations in `'+'` keeps every scalar. type UserResponse = Shape<User, { '-': 'passwordHash'; posts: { '+': 'id' | 'title' | 'comments' }; }>; // { id; name; email; ...; posts: { id; title; comments: Scalars<Comment>[] }[] } // This is useful in situations where you need to declare a type derived from your models, eg an // API response type (real code: examples/prisma-8-demo/src/orm-client/get-user-profile.ts). TypeScript will then enforce that the result of your function, which will // involve Prisma queries, matches the expected output shape. If you change the contract, the // resulting shape will update. export async function getUserWithPosts(id: string): Promise<UserResponse | null> { const user = await db.orm.public.User.where({ id }).include('posts', (p) => p.include('comments')).first(); if (user === null) return null; const { passwordHash, ...rest } = user; return { ...rest, posts: user.posts.map(({ id, title, comments }) => ({ id, title, comments })) }; } // And if you just want the exact type of a query you already wrote: const usersWithPosts = db.orm.public.User.include('posts'); type UserWithPosts = ResultType<typeof usersWithPosts>; ``` Before this PR, `contract.d.ts` exported no model type, and `ResultType` returned `never` on ORM queries. ## Summary Two Prisma 7 users asked where `Prisma.User` and `Prisma.BookGetPayload<{ include: { author: true } }>` went. Prisma 8 had no answer: models were reachable only as `FieldOutputTypes['public']['User']`, and naming a query result meant `NonNullable<Awaited<ReturnType<typeof q.first>>>`. This PR gives users the model type, the scalar row a default fetch returns, a data structure derived from the model with picked or dropped scalars and nested relations, and the result type of any ORM query. ## Skill update `skills/prisma-8/references/queries.md` gains a "Naming model and result types" section and the routing table in `skills/prisma-8/SKILL.md` routes "type of my model", `GetPayload`, `ResultType`, `Scalars`, `Shape`, and `Models` prompts to it. ## Decision The model is the whole row plus its relations, as written in PSL. A query result is a view on it. Selection belongs to the query, not the model. [ADR 250](docs/architecture%20docs/adrs/ADR%20250%20-%20Models%20and%20views%20are%20emitted%20from%20the%20contract.md) records this. Concretely, this PR ships: 1. `contract.d.ts` emits `export namespace Models` with one member per model, the namespace folded into the member name (`public_User`, `unbound_Audit`; bare names on targets without namespaces), an `Any<Base>` union per polymorphic base, and `export declare const models` for dotted type-only access. 2. `RelationKeys`, `Scalars<M>`, and `Shape<M, Spec>` in `framework-components`, re-exported by both family contract packages. `Shape` takes an object spec: `'+'` keeps named scalars and relations, `'-'` drops scalars, any other key is a relation with a nested spec, and wrong names or `'+'` beside `'-'` are compile errors on the offending key (`projects/model-and-result-types/shape-design-brief.md`). 3. A `_row` phantom on both ORM collections so the existing `ResultType` works on ORM queries. 4. Type tests proving the ORM's rows equal `Scalars` and `Shape` of the emitted models (plain includes, projections, select plus include, nested includes, polymorphism) for SQL and for Mongo, and a demo test that declares an endpoint response with `Shape` and returns a transformed query result from a function with that return type. 5. Relation nullability is recorded on the contract. Every one-to-one and many-to-one relation carries `nullable`, set from the `?` on the relation field in PSL or from the TypeScript builder, and read by the emitter and both ORMs. A contract written before this change loads unchanged: a missing flag is derived at hydration from the foreign-key columns' storage nullability, and a present flag is checked against storage on load. The emitter requires the flag on the contracts it consumes. The storage-plane reconstruction in the ORM types and the emitter hook that mirrored it are gone. 6. Every emitted fixture regenerated, a reference page, and the user skill update. ## Reviewer notes - The largest diff is fixture regeneration: every emitted `contract.d.ts` in the repo gains the `Models` block, including the 204 port fixtures and the migration snapshot stores that `pnpm fixtures:check` did not cover before. The check now covers them through `test/integration/scripts/emit-fixture-configs.mjs` and `scripts/refresh-contract-snapshot.mjs`. 143 `contract.json` files change, and every changed line is the `_generated` banner, which those stale fixtures had never picked up. Spot-check `packages/3-extensions/sql-orm-client/test/fixtures/generated/contract.d.ts` and the polymorphism fixture under `test/integration/test/sql-orm-client/fixtures/polymorphism/`. - A default fetch returns `Scalars<Model>`, not the model. This is the opposite of Prisma 7, where the generated `User` is scalars-only, and the reference page says so in its first paragraph. - The namespace is in the member name wherever a collision is possible: on Postgres a default-schema model is `Models.unbound_User`, because `public.User` can sit beside it, matching `db.enums` and the contract views. A target whose descriptor declares `namespaceSupport: 'none'` (SQLite) emits bare names, `Models.User` and `models.User`. The choice is a declared target capability, never a count of the namespaces in a particular contract. - A relation whose target is a polymorphic base is emitted as the `Any<Base>` union, because that is what the ORM returns for such includes. - Relation nullability comes from the schema, not storage. `author User?` gives `nullable: true`, `author User` gives `false`; authoring rejects a required relation field over a nullable foreign key and the reverse. The side of a one-to-one that does not own the foreign key is always nullable, in PSL and in the builder, because nothing guarantees the related row exists. A refined to-one include is `| null` regardless, because the refinement can exclude the row. - Mongo follows the schema too. Its ORM used to type every to-one include as `| null`; now it reads the flag. A required reference whose document is missing comes back with the key absent at runtime, which the type no longer admits. That is a general read-time concern on Mongo, recorded in `projects/model-and-result-types/deferred.md`, not changed here. - `contract.json` gains one boolean per to-one relation. The domain section is canonicalized as empty for the storage hash, so no contract hash changes, and migration snapshots written before this change still load because a missing flag is derived at hydration. Seven ported `.prisma` schemas had an optional relation field over a required foreign key, which authoring now rejects; the `?` was removed and no emitted type changed. - The to-one nullability rule lives once, in `@internal/contract-authoring`, and the two PSL interpreters and two TypeScript builders call it. `Scalars` and `RelationNamesOf` infer the relation keys without a constraint fallback so they work in projects without `exactOptionalPropertyTypes`; a test compiles the fixture with that flag off. - Mongo embedded models carry no `RelationKeys` phantom, so `Scalars` of an owner's embed field equals the ORM's embed row. - `Shape` flattens each level so `toEqualTypeOf` can compare it with ORM rows and hover text shows one object. - Adjacent fixes in the second commit: `examples/prisma-8-demo` now passes `pnpm lint` (every bare throw uses a named `Error` subclass from `src/errors.ts` or `TypeError`), and the `no-bare-cast`, `no-bare-throw`, and `no-family-vocabulary` plugins exclude `.test.tsx`, `.test.mts`, and `.test.cts` the same way they exclude `.test.ts`. Forty orphan `contract.*` copies under `relation-mode-gh-*` fixtures, produced by an older emit and referenced by nothing, are deleted. - Left as they are, with reasons in the commit: two hand-authored minimal `contract.d.ts` test inputs, two telemetry-backend snapshots whose PSL no longer exists, 44 snapshots under `examples/prisma-8-demo/fixtures/*/migrations` that no script produces, and two vendored extension snapshots whose refresh belongs to the extension install flow. - Pre-existing flakes, not caused here: `@prisma/orm-framework test/module-identity.test.ts` races on `pnpm pack`'s skill sync and passes on rerun; `driver-adapters-error-forwarding › correctly forwards error for queryRaw` is a `test.fails` port that now passes, identically with the old fixture restored. - This PR is also the project's close-out: the transient artifacts under `projects/model-and-result-types/` are deleted in the last commit, ADR 250 and the reference page are the durable record, the final retro is in `drive/retro/findings.md`, and every deferred item has a Linear issue (TML-3235, 3236, 3237, 3242 to 3246). ## How it fits together 1. **Framework types.** `packages/1-framework/1-core/framework-components/src/execution/model-types.ts` declares the `RelationKeys` unique symbol and the two utilities. Both are distributive, so they work on `Any<Base>` unions. `Shape` validates the spec through a mapped constraint over the spec's own keys and reads each relation's wrapper from the model's own field type. 2. **Emission.** `packages/1-framework/3-tooling/emitter/src/model-types-emission.ts` renders the block from `contract.domain`, sharing the field-type resolver with `FieldOutputTypes` via `resolveModelFieldType`, and reads each to-one relation's `nullable` for the `| null` wrapper. Name collisions, non-identifier names, and unresolvable same-space relation targets throw structured errors before anything is written. 3. **ORM phantoms.** One `declare readonly _row?: Row` line on `CollectionImpl` and one `readonly _row?: SimplifyDeep<IncludedRow<...>>` member on `MongoCollection`. 4. **Proof.** Type tests in both ORM packages and the demo assert equality between the ORM's rows and the emitted types for every fixture model, every cardinality, polymorphic roots and variants, projections, nested and refined includes, and every `Shape` refusal as a `@ts-expect-error`. 5. **Docs.** `docs/reference/model-and-result-types.md`, with every snippet copied from a passing type test; ADR 250; the subsystem doc paragraph; README links; the user skill. ## Behavior changes & evidence - **`contract.d.ts` exports `Models` and `models`.** `packages/1-framework/3-tooling/emitter/src/model-types-emission.ts`, `packages/1-framework/3-tooling/emitter/src/generate-contract-dts.ts`. Evidence: `packages/1-framework/3-tooling/emitter/test/model-types-emission.test.ts`, `packages/3-extensions/sql-orm-client/test/fixtures/generated/contract.d.ts`. - **`Scalars` and `Shape` are exported from both families' contract types.** `packages/1-framework/1-core/framework-components/src/execution/model-types.ts`, `packages/2-sql/1-core/contract/src/exports/types.ts`, `packages/2-mongo-family/1-foundation/mongo-contract/src/exports/index.ts`. Evidence: `packages/1-framework/1-core/framework-components/test/model-types.test-d.ts`. - **`ResultType` works on ORM collections.** `packages/3-extensions/sql-orm-client/src/collection.ts`, `packages/2-mongo-family/5-query-builders/orm/src/collection.ts`. Evidence: `packages/3-extensions/sql-orm-client/test/model-types.test-d.ts`, `packages/2-mongo-family/5-query-builders/orm/test/model-types.test-d.ts`. - **Relation nullability is a contract fact.** `packages/1-framework/0-foundation/contract/src/domain-types.ts`, `packages/1-framework/0-foundation/contract/src/validate-domain.ts`, `packages/2-sql/2-authoring/contract-psl/src/psl-relation-resolution.ts`. Evidence: the validator tests beside `validate-domain.ts`, the PSL interpreter tests in both families, and `packages/3-extensions/sql-orm-client/test/include-cardinality.test-d.ts`. ## Project close-out Project definition of done, from the spec: | Item | Evidence | | --- | --- | | Repo checks, `pnpm fixtures:check`, Linear close-out | CI green on the previous head; `fixtures:check` stable across two runs; TML-3233 In Review, TML-3234 Done | | Every test in the spec's test list exists and passes | `packages/1-framework/1-core/framework-components/test/shape.test-d.ts`, `packages/3-extensions/sql-orm-client/test/model-types.test-d.ts`, `packages/2-mongo-family/5-query-builders/orm/test/model-types.test-d.ts`, `packages/1-framework/3-tooling/emitter/test/model-types-emission.test.ts`, `examples/prisma-8-demo/test/demo-dx.types.test.ts` | | All fixtures regenerated; `contract.json` changes limited to the new field | `pnpm fixtures:check` covers the port fixtures and snapshot stores; JSON diffs are `nullable` flags and the `_generated` banner only | | Docs page, index and README links, subsystem paragraph, ADR | `docs/reference/model-and-result-types.md`, `docs/README.md`, both ORM READMEs, subsystem doc 2, ADR 250 | | One PR over 1,000 lines on this branch | This PR | Migration: nothing to migrate; ADR 250 and the reference page were written in place during the project. Reference strip: no file outside the project folder referenced it. Deleted: `projects/model-and-result-types/` (spec, plan, design notes, design brief, Shape brief, slice 2 brief, deferred). Retro: `drive/retro/findings.md` 2026-09-10, with F20 in `drive/calibration/failure-modes.md` and a new project-DoD item in `drive/calibration/dod.md`. ## Testing performed - `pnpm build` (root) - `pnpm test:packages` (1186 files; one pre-existing race in `module-identity.test.ts`, passes on rerun) - `pnpm test` in framework-components (635), emitter (222), SQL emitter (184), Mongo emitter (64), sql-orm-client (787), Mongo ORM (235), prisma-8-demo (74) - `pnpm typecheck` and `pnpm lint` in every touched package (demo lint failure pre-existing, see reviewer notes) - `pnpm lint:deps`, `pnpm lint:skills` - `pnpm fixtures:check` (stable across two runs; `contract.json` diffs are the `_generated` banner only) - `pnpm test:integration` (372/373 files; the one failure is the pre-existing `test.fails` port noted above) - `pnpm lint:casts`, `pnpm lint:throws`, `pnpm lint:framework-vocabulary` ## Follow-ups - Rename `ResultType` to `Result` (open question in the brief; not done here). - `Scalars` versus `Row` naming, and the `_` separator, are open in the brief and can be changed before release. ## Alternatives considered - **Emit `GetPayload`-style types per query.** Ties the contract to one lane's vocabulary and grows `contract.d.ts` without bound. - **`With<M, 'rel'>`, a model plus a union of relation names.** The first draft. Replaced by `Shape` before merge: it could not drop or narrow scalars or nest, so an endpoint response still needed hand-written types. `With<User, 'posts'>` is `Shape<User, { posts: {} }>`. - **Prisma 7's boolean form, `{ id: true; posts: { title: true } }`.** Verbose: every scalar must be listed for the wide case. `'+'`/`'-'` sigils were chosen over words because words collide with field names. - **A relation-selection parameter on the contract, `Model<Contract, 'User', { posts: { comments: true } }>`.** Takes the contract rather than the model and reads as a query. `Shape` is a pure utility over the model type and describes end states, not queries. - **A parameter mapping relation name to the model type that sits there.** Makes the user import and restate what the contract already knows. - **`Models.public.User` as nested TypeScript namespaces.** `namespace public` does not compile; `public` is reserved in strict mode and is the default Postgres schema. Folding the schema into the member name gives the importable form; the declared constant gives the dotted form. - **Flat `export type User` at the top level.** Adding a second `User` in another schema would silently remove the alias and break every import of it. - **A runtime `db.models` accessor.** Either an object pretending to be a row or a definition object whose `typeof` is not the model. Dotted access already exists with no runtime. - **A separate `RowOf` helper for ORM queries.** `ResultType` exists and is documented; the collections now carry the marker it reads. ## Checklist - [x] All commits are signed off (`git commit -s`) per the [DCO](../CONTRIBUTING.md#developer-certificate-of-origin-dco). - [x] I read [CONTRIBUTING.md](../CONTRIBUTING.md) and the change is scoped to one logical concern. - [x] Tests are updated. - [x] The PR title is in `TML-NNNN: <sentence-case title>` form. - [x] The **Skill update** section above is filled in. ## Notes for the reviewer See Reviewer notes above. 🤖 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 Fable 5.1 <noreply@anthropic.com> | 24 天前 | |
Config files import definePrismaConfig, the engine's current name for the marker (#30129) ## Why now `@prisma/cli-engine` has exported the config marker as `definePrismaConfig` since 0.2.0, keeping `defineConfig` as a deprecated alias. Engine 0.3.0 drops the alias before launch. Every `prisma.config.ts` in this repo still imports the old name, so on 0.3.0 they all fail at evaluation with `defineConfig is not a function`. Doing the rename now, on the current 0.2.3 pin, costs nothing. The two names are literally the same function object there: ```js import('@prisma/cli-engine').then((m) => m.definePrismaConfig === m.defineConfig); // true ``` So this PR is mergeable today and changes no behaviour. It clears the last blocker on moving the engine pin to 0.3.0, which #30128 is waiting for. ## What changed **306 TypeScript files** import the marker from `@prisma/cli-engine`. All move to `definePrismaConfig`, at the import and at the call. Almost all are `prisma.config.ts` fixtures under `test/`, plus the example and app configs. Two things that share the name are deliberately untouched: - The **framework's own `defineConfig`**, exported by the target facades and `@internal/cli/config-types`. It is always imported aliased (`defineConfig as ormConfig`), so it never collided with the sweep. - **`defineConfigSection`**, a separate and current engine export. Replacements were anchored on the exact import line and on `defineConfig(`, never on a bare word match. After the sweep: - no `defineConfig` reference to `@prisma/cli-engine` remains in any `.ts`, `.mts`, `.mjs` or `.js` file - both `defineConfigSection` occurrences are intact, and no `definePrismaConfigSection` exists anywhere - the aliased framework imports are unchanged - the diff is 643 insertions against 643 deletions — line for line, nothing added or dropped **21 more files** spell the old name out rather than importing it: - `scripts/regen-example-migrations.mjs` generates a temporary config file. Its `engineDefineConfig` alias existed only because the engine marker and the framework builder shared a name, so the alias goes with the rename. - The `CONFIG.VERSION_MARKER_MISSING` diagnostic told the reader to create the config with `defineConfig`. Its summary, explanation and fix now name `definePrismaConfig`. So does its entry in `docs/reference/error-reference.md`, which additionally pointed at the target package's `/config` entrypoint — the wrong import for the marker. - Doc comments in the config loader, the ORM loader and its config types, `init` and its package resolution, and the publish-surface import roots. - Four test names, and an `init` assertion that only checked for the substring `defineConfig` and so passed by accident against the scaffold's `definePrismaConfig`. It now asserts the real name. The CHANGELOG, the rc.2 release notes and the rc.1-to-rc.2 upgrade recipes still say `defineConfig`, deliberately: they record releases where that was the name. ## Verification All on the current 0.2.3 pin — no override, no tarball. | Check | Result | | --- | --- | | `pnpm build` | 85/85 tasks pass | | `turbo run typecheck --force` | 166/166 tasks pass, nothing cached. Covers `integration-tests`, whose tsconfig includes `test/**/*`, so all 262 fixture configs are typechecked. | | `pnpm test:packages` | 15630 tests — the same count as `main` before the change, so nothing was lost or silently skipped | | `pnpm test:integration` | 2061 pass across 370 of 371 files. These evaluate the renamed config files for real. | | `@internal/cli` | 1436 tests pass | | `@internal/config-loader` | 46 tests pass | | `@internal/errors` | 124 tests pass | | `@internal/config` | 23 tests pass | | `@internal/language-server` | 273 tests pass | | `pnpm lint` | 99/99 tasks pass | | `pnpm lint:deps`, `lint:docs`, `lint:manifests` | pass | | `pnpm check:conformance` | pass | | `pnpm check:error-reference` | pass, all 288 codes listed | Three test files fail somewhere in those runs. Each was checked and none is caused by this change: - `test/ports/.../driver-adapters-error-forwarding` fails identically on unmodified `origin/main` — I checked out `main` in this working copy and reproduced it. - `@prisma/orm-framework`'s `module-identity` also fails on unmodified `main` here: `pnpm pack` trips over a leftover gitignored `skills/prisma-8` directory. - `@internal/adapter-postgres`'s `order-by-enum` PGlite test fails only under parallel load; it passes on its own (7/7). ## Related - prisma/prisma-cli#233 — the engine change that removes the alias - #30128 — the ORM config-path change that needs engine 0.3.0; this PR is its prerequisite 🤖 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 Fable 5 <noreply@anthropic.com> | 12 天前 | |
TML-3233: emit Models namespace + models const; Scalars, Shape, and ResultType on ORM queries (#30231) ## Linked issue Refs [TML-3233](https://linear.app/prisma-company/issue/TML-3233) · Linear project [Model and result types](https://linear.app/prisma-company/project/model-and-result-types-080d7caa544f). Design: [ADR 250](docs/architecture%20docs/adrs/ADR%20250%20-%20Models%20and%20views%20are%20emitted%20from%20the%20contract.md). ## At a glance ```ts import type { models, Models } from '../prisma/contract'; import type { Scalars, Shape } from '@prisma/orm-postgres/family-contract/types'; import type { ResultType } from '@prisma/orm-postgres/components/runtime'; // Models types are directly available in two forms. This one is a bit nicer to type: type User = typeof models.public.User; // And the raw model type which doesn't require `typeof`: type User2 = Models.public_User; // A plain query returns the scalar form of the model, ie. all its properties without relations type UserRow = Scalars<User>; // But you often need a selection of your model properties and its relations. In Prisma 7 and below // you'd use GetPayload<> for this. In Prisma 8, it looks like the following. Mark fields or // relations for inclusion with `'+'`, or exclusion with `'-'`. Naming only relations in `'+'` keeps every scalar. type UserResponse = Shape<User, { '-': 'passwordHash'; posts: { '+': 'id' | 'title' | 'comments' }; }>; // { id; name; email; ...; posts: { id; title; comments: Scalars<Comment>[] }[] } // This is useful in situations where you need to declare a type derived from your models, eg an // API response type (real code: examples/prisma-8-demo/src/orm-client/get-user-profile.ts). TypeScript will then enforce that the result of your function, which will // involve Prisma queries, matches the expected output shape. If you change the contract, the // resulting shape will update. export async function getUserWithPosts(id: string): Promise<UserResponse | null> { const user = await db.orm.public.User.where({ id }).include('posts', (p) => p.include('comments')).first(); if (user === null) return null; const { passwordHash, ...rest } = user; return { ...rest, posts: user.posts.map(({ id, title, comments }) => ({ id, title, comments })) }; } // And if you just want the exact type of a query you already wrote: const usersWithPosts = db.orm.public.User.include('posts'); type UserWithPosts = ResultType<typeof usersWithPosts>; ``` Before this PR, `contract.d.ts` exported no model type, and `ResultType` returned `never` on ORM queries. ## Summary Two Prisma 7 users asked where `Prisma.User` and `Prisma.BookGetPayload<{ include: { author: true } }>` went. Prisma 8 had no answer: models were reachable only as `FieldOutputTypes['public']['User']`, and naming a query result meant `NonNullable<Awaited<ReturnType<typeof q.first>>>`. This PR gives users the model type, the scalar row a default fetch returns, a data structure derived from the model with picked or dropped scalars and nested relations, and the result type of any ORM query. ## Skill update `skills/prisma-8/references/queries.md` gains a "Naming model and result types" section and the routing table in `skills/prisma-8/SKILL.md` routes "type of my model", `GetPayload`, `ResultType`, `Scalars`, `Shape`, and `Models` prompts to it. ## Decision The model is the whole row plus its relations, as written in PSL. A query result is a view on it. Selection belongs to the query, not the model. [ADR 250](docs/architecture%20docs/adrs/ADR%20250%20-%20Models%20and%20views%20are%20emitted%20from%20the%20contract.md) records this. Concretely, this PR ships: 1. `contract.d.ts` emits `export namespace Models` with one member per model, the namespace folded into the member name (`public_User`, `unbound_Audit`; bare names on targets without namespaces), an `Any<Base>` union per polymorphic base, and `export declare const models` for dotted type-only access. 2. `RelationKeys`, `Scalars<M>`, and `Shape<M, Spec>` in `framework-components`, re-exported by both family contract packages. `Shape` takes an object spec: `'+'` keeps named scalars and relations, `'-'` drops scalars, any other key is a relation with a nested spec, and wrong names or `'+'` beside `'-'` are compile errors on the offending key (`projects/model-and-result-types/shape-design-brief.md`). 3. A `_row` phantom on both ORM collections so the existing `ResultType` works on ORM queries. 4. Type tests proving the ORM's rows equal `Scalars` and `Shape` of the emitted models (plain includes, projections, select plus include, nested includes, polymorphism) for SQL and for Mongo, and a demo test that declares an endpoint response with `Shape` and returns a transformed query result from a function with that return type. 5. Relation nullability is recorded on the contract. Every one-to-one and many-to-one relation carries `nullable`, set from the `?` on the relation field in PSL or from the TypeScript builder, and read by the emitter and both ORMs. A contract written before this change loads unchanged: a missing flag is derived at hydration from the foreign-key columns' storage nullability, and a present flag is checked against storage on load. The emitter requires the flag on the contracts it consumes. The storage-plane reconstruction in the ORM types and the emitter hook that mirrored it are gone. 6. Every emitted fixture regenerated, a reference page, and the user skill update. ## Reviewer notes - The largest diff is fixture regeneration: every emitted `contract.d.ts` in the repo gains the `Models` block, including the 204 port fixtures and the migration snapshot stores that `pnpm fixtures:check` did not cover before. The check now covers them through `test/integration/scripts/emit-fixture-configs.mjs` and `scripts/refresh-contract-snapshot.mjs`. 143 `contract.json` files change, and every changed line is the `_generated` banner, which those stale fixtures had never picked up. Spot-check `packages/3-extensions/sql-orm-client/test/fixtures/generated/contract.d.ts` and the polymorphism fixture under `test/integration/test/sql-orm-client/fixtures/polymorphism/`. - A default fetch returns `Scalars<Model>`, not the model. This is the opposite of Prisma 7, where the generated `User` is scalars-only, and the reference page says so in its first paragraph. - The namespace is in the member name wherever a collision is possible: on Postgres a default-schema model is `Models.unbound_User`, because `public.User` can sit beside it, matching `db.enums` and the contract views. A target whose descriptor declares `namespaceSupport: 'none'` (SQLite) emits bare names, `Models.User` and `models.User`. The choice is a declared target capability, never a count of the namespaces in a particular contract. - A relation whose target is a polymorphic base is emitted as the `Any<Base>` union, because that is what the ORM returns for such includes. - Relation nullability comes from the schema, not storage. `author User?` gives `nullable: true`, `author User` gives `false`; authoring rejects a required relation field over a nullable foreign key and the reverse. The side of a one-to-one that does not own the foreign key is always nullable, in PSL and in the builder, because nothing guarantees the related row exists. A refined to-one include is `| null` regardless, because the refinement can exclude the row. - Mongo follows the schema too. Its ORM used to type every to-one include as `| null`; now it reads the flag. A required reference whose document is missing comes back with the key absent at runtime, which the type no longer admits. That is a general read-time concern on Mongo, recorded in `projects/model-and-result-types/deferred.md`, not changed here. - `contract.json` gains one boolean per to-one relation. The domain section is canonicalized as empty for the storage hash, so no contract hash changes, and migration snapshots written before this change still load because a missing flag is derived at hydration. Seven ported `.prisma` schemas had an optional relation field over a required foreign key, which authoring now rejects; the `?` was removed and no emitted type changed. - The to-one nullability rule lives once, in `@internal/contract-authoring`, and the two PSL interpreters and two TypeScript builders call it. `Scalars` and `RelationNamesOf` infer the relation keys without a constraint fallback so they work in projects without `exactOptionalPropertyTypes`; a test compiles the fixture with that flag off. - Mongo embedded models carry no `RelationKeys` phantom, so `Scalars` of an owner's embed field equals the ORM's embed row. - `Shape` flattens each level so `toEqualTypeOf` can compare it with ORM rows and hover text shows one object. - Adjacent fixes in the second commit: `examples/prisma-8-demo` now passes `pnpm lint` (every bare throw uses a named `Error` subclass from `src/errors.ts` or `TypeError`), and the `no-bare-cast`, `no-bare-throw`, and `no-family-vocabulary` plugins exclude `.test.tsx`, `.test.mts`, and `.test.cts` the same way they exclude `.test.ts`. Forty orphan `contract.*` copies under `relation-mode-gh-*` fixtures, produced by an older emit and referenced by nothing, are deleted. - Left as they are, with reasons in the commit: two hand-authored minimal `contract.d.ts` test inputs, two telemetry-backend snapshots whose PSL no longer exists, 44 snapshots under `examples/prisma-8-demo/fixtures/*/migrations` that no script produces, and two vendored extension snapshots whose refresh belongs to the extension install flow. - Pre-existing flakes, not caused here: `@prisma/orm-framework test/module-identity.test.ts` races on `pnpm pack`'s skill sync and passes on rerun; `driver-adapters-error-forwarding › correctly forwards error for queryRaw` is a `test.fails` port that now passes, identically with the old fixture restored. - This PR is also the project's close-out: the transient artifacts under `projects/model-and-result-types/` are deleted in the last commit, ADR 250 and the reference page are the durable record, the final retro is in `drive/retro/findings.md`, and every deferred item has a Linear issue (TML-3235, 3236, 3237, 3242 to 3246). ## How it fits together 1. **Framework types.** `packages/1-framework/1-core/framework-components/src/execution/model-types.ts` declares the `RelationKeys` unique symbol and the two utilities. Both are distributive, so they work on `Any<Base>` unions. `Shape` validates the spec through a mapped constraint over the spec's own keys and reads each relation's wrapper from the model's own field type. 2. **Emission.** `packages/1-framework/3-tooling/emitter/src/model-types-emission.ts` renders the block from `contract.domain`, sharing the field-type resolver with `FieldOutputTypes` via `resolveModelFieldType`, and reads each to-one relation's `nullable` for the `| null` wrapper. Name collisions, non-identifier names, and unresolvable same-space relation targets throw structured errors before anything is written. 3. **ORM phantoms.** One `declare readonly _row?: Row` line on `CollectionImpl` and one `readonly _row?: SimplifyDeep<IncludedRow<...>>` member on `MongoCollection`. 4. **Proof.** Type tests in both ORM packages and the demo assert equality between the ORM's rows and the emitted types for every fixture model, every cardinality, polymorphic roots and variants, projections, nested and refined includes, and every `Shape` refusal as a `@ts-expect-error`. 5. **Docs.** `docs/reference/model-and-result-types.md`, with every snippet copied from a passing type test; ADR 250; the subsystem doc paragraph; README links; the user skill. ## Behavior changes & evidence - **`contract.d.ts` exports `Models` and `models`.** `packages/1-framework/3-tooling/emitter/src/model-types-emission.ts`, `packages/1-framework/3-tooling/emitter/src/generate-contract-dts.ts`. Evidence: `packages/1-framework/3-tooling/emitter/test/model-types-emission.test.ts`, `packages/3-extensions/sql-orm-client/test/fixtures/generated/contract.d.ts`. - **`Scalars` and `Shape` are exported from both families' contract types.** `packages/1-framework/1-core/framework-components/src/execution/model-types.ts`, `packages/2-sql/1-core/contract/src/exports/types.ts`, `packages/2-mongo-family/1-foundation/mongo-contract/src/exports/index.ts`. Evidence: `packages/1-framework/1-core/framework-components/test/model-types.test-d.ts`. - **`ResultType` works on ORM collections.** `packages/3-extensions/sql-orm-client/src/collection.ts`, `packages/2-mongo-family/5-query-builders/orm/src/collection.ts`. Evidence: `packages/3-extensions/sql-orm-client/test/model-types.test-d.ts`, `packages/2-mongo-family/5-query-builders/orm/test/model-types.test-d.ts`. - **Relation nullability is a contract fact.** `packages/1-framework/0-foundation/contract/src/domain-types.ts`, `packages/1-framework/0-foundation/contract/src/validate-domain.ts`, `packages/2-sql/2-authoring/contract-psl/src/psl-relation-resolution.ts`. Evidence: the validator tests beside `validate-domain.ts`, the PSL interpreter tests in both families, and `packages/3-extensions/sql-orm-client/test/include-cardinality.test-d.ts`. ## Project close-out Project definition of done, from the spec: | Item | Evidence | | --- | --- | | Repo checks, `pnpm fixtures:check`, Linear close-out | CI green on the previous head; `fixtures:check` stable across two runs; TML-3233 In Review, TML-3234 Done | | Every test in the spec's test list exists and passes | `packages/1-framework/1-core/framework-components/test/shape.test-d.ts`, `packages/3-extensions/sql-orm-client/test/model-types.test-d.ts`, `packages/2-mongo-family/5-query-builders/orm/test/model-types.test-d.ts`, `packages/1-framework/3-tooling/emitter/test/model-types-emission.test.ts`, `examples/prisma-8-demo/test/demo-dx.types.test.ts` | | All fixtures regenerated; `contract.json` changes limited to the new field | `pnpm fixtures:check` covers the port fixtures and snapshot stores; JSON diffs are `nullable` flags and the `_generated` banner only | | Docs page, index and README links, subsystem paragraph, ADR | `docs/reference/model-and-result-types.md`, `docs/README.md`, both ORM READMEs, subsystem doc 2, ADR 250 | | One PR over 1,000 lines on this branch | This PR | Migration: nothing to migrate; ADR 250 and the reference page were written in place during the project. Reference strip: no file outside the project folder referenced it. Deleted: `projects/model-and-result-types/` (spec, plan, design notes, design brief, Shape brief, slice 2 brief, deferred). Retro: `drive/retro/findings.md` 2026-09-10, with F20 in `drive/calibration/failure-modes.md` and a new project-DoD item in `drive/calibration/dod.md`. ## Testing performed - `pnpm build` (root) - `pnpm test:packages` (1186 files; one pre-existing race in `module-identity.test.ts`, passes on rerun) - `pnpm test` in framework-components (635), emitter (222), SQL emitter (184), Mongo emitter (64), sql-orm-client (787), Mongo ORM (235), prisma-8-demo (74) - `pnpm typecheck` and `pnpm lint` in every touched package (demo lint failure pre-existing, see reviewer notes) - `pnpm lint:deps`, `pnpm lint:skills` - `pnpm fixtures:check` (stable across two runs; `contract.json` diffs are the `_generated` banner only) - `pnpm test:integration` (372/373 files; the one failure is the pre-existing `test.fails` port noted above) - `pnpm lint:casts`, `pnpm lint:throws`, `pnpm lint:framework-vocabulary` ## Follow-ups - Rename `ResultType` to `Result` (open question in the brief; not done here). - `Scalars` versus `Row` naming, and the `_` separator, are open in the brief and can be changed before release. ## Alternatives considered - **Emit `GetPayload`-style types per query.** Ties the contract to one lane's vocabulary and grows `contract.d.ts` without bound. - **`With<M, 'rel'>`, a model plus a union of relation names.** The first draft. Replaced by `Shape` before merge: it could not drop or narrow scalars or nest, so an endpoint response still needed hand-written types. `With<User, 'posts'>` is `Shape<User, { posts: {} }>`. - **Prisma 7's boolean form, `{ id: true; posts: { title: true } }`.** Verbose: every scalar must be listed for the wide case. `'+'`/`'-'` sigils were chosen over words because words collide with field names. - **A relation-selection parameter on the contract, `Model<Contract, 'User', { posts: { comments: true } }>`.** Takes the contract rather than the model and reads as a query. `Shape` is a pure utility over the model type and describes end states, not queries. - **A parameter mapping relation name to the model type that sits there.** Makes the user import and restate what the contract already knows. - **`Models.public.User` as nested TypeScript namespaces.** `namespace public` does not compile; `public` is reserved in strict mode and is the default Postgres schema. Folding the schema into the member name gives the importable form; the declared constant gives the dotted form. - **Flat `export type User` at the top level.** Adding a second `User` in another schema would silently remove the alias and break every import of it. - **A runtime `db.models` accessor.** Either an object pretending to be a row or a definition object whose `typeof` is not the model. Dotted access already exists with no runtime. - **A separate `RowOf` helper for ORM queries.** `ResultType` exists and is documented; the collections now carry the marker it reads. ## Checklist - [x] All commits are signed off (`git commit -s`) per the [DCO](../CONTRIBUTING.md#developer-certificate-of-origin-dco). - [x] I read [CONTRIBUTING.md](../CONTRIBUTING.md) and the change is scoped to one logical concern. - [x] Tests are updated. - [x] The PR title is in `TML-NNNN: <sentence-case title>` form. - [x] The **Skill update** section above is filled in. ## Notes for the reviewer See Reviewer notes above. 🤖 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 Fable 5.1 <noreply@anthropic.com> | 24 天前 | |
feat(mongo): Int64, Decimal128, Binary and Json, with codecs owned by the target package (#30396) A Mongo schema in Prisma 8 can now declare 64-bit integers, decimals, binary data, and free-form JSON: ```prisma model Product { id ObjectId @id name String stock Int32 unitsSold Int64 rating Double price Decimal128 thumbnail Binary active Bool listedAt Date meta Json } ``` ```ts const Product = model('Product', { fields: { name: field.string(), unitsSold: field.int64(), price: field.decimal128(), thumbnail: field.binary(), meta: field.json(), }, }); ``` The ORM reads them back as `bigint`, decimal text, `Uint8Array`, and a JSON value. In the database they are stored as BSON `Long`, `Decimal128`, `Binary`, and whatever BSON value was given. Until this PR, none of these types existed for Mongo, so most real-world Mongo schemas could not be expressed. ## The decision Two decisions, both about ownership. **Scalar types are named after the target.** A Mongo PSL scalar says what MongoDB stores, in the BSON specification's own vocabulary, and the same token appears on all three surfaces: the PSL name is the token in PascalCase, the TypeScript helper is `field.<token>()`, and the codec id is `mongo/<token>@1`. So `Int` becomes `Int32`, `Float` becomes `Double`, `Boolean` becomes `Bool`, `DateTime` becomes `Date`, and the four new types are `Int64`, `Decimal128`, `Binary`, `Json`. Codec ids do not change, so no `contract.json`, hash, or signed database moves; only `.prisma` files change, and the old names report a diagnostic that states the new name. The reasoning, the Postgres and SQLite follow-up, and the `Json`/`Bson` split that comes next are written up in the project's design notes; the short version is that the Prisma 6/7 names were symmetric in spelling and wrong about storage (`Int` is 32-bit here, int4 on Postgres, and a 64-bit integer on SQLite). **Codecs live in the target package.** Codecs for the Mongo target live in the target package (`@internal/target-mongo`), and the adapter package consumes them. This is how Postgres already works: the target package describes the database's value space, the adapter implements the lowering and wire concerns against that description. Mongo had it the other way round, with the adapter owning the codecs and the target reaching into the adapter to get them. The four new codecs are added in the target on top of that layout, so every Mongo codec has one home. ## How the pieces fit **A target must not import its adapter.** Targets, adapters, and drivers are meant to be interchangeable behind interfaces, and ADR 198 (migration runner decoupled from the driver) says so explicitly for the target package. Yet `target-mongo` imported `adapter-mongo` in three files and `driver-mongo` in one, mostly to build the dependencies its migration runner needs. That wiring now goes through the Mongo family's control-adapter interface: the interface gains `createRunnerDependencies(driver)`, the adapter implements it, the family instance forwards it, and the target's `createRunner` calls `family.createRunnerDependencies({ driver })`, the same way the Postgres runner reaches everything through `this.family`. The old free function `createMongoRunnerDeps` is deleted. A test in the target package asserts it has no adapter or driver imports. **With that dependency gone, the codecs can move.** Codec ids, codec implementations, descriptors, data types, and the `CodecTypes` map move from the adapter to the target and are exported as `target/codecs`, `target/codec-ids`, `target/data-types`, and `target/codec-types` on the public packages. The adapter keeps only the PSL scalar-name map and registers the target's codec registry on its runtime descriptor, exactly as the Postgres adapter does. **The four codecs mirror the Postgres ones for the equivalent SQL types**, so contract defaults and future schema converters print the same literal forms across families: | Codec | BSON | Application type | JSON form | Mirrors | |---|---|---|---|---| | `mongo/int64@1` | `Long` | `bigint` | decimal text | `pg/int8@1` | | `mongo/decimal128@1` | `Decimal128` | decimal text, no exponent | same | `pg/numeric@1` | | `mongo/binary@1` | `Binary` | `Uint8Array` | unwrapped base64 | `pg/bytea@1` | | `mongo/json@1` | any value | `JsonValue` | identity | `pg/json@1` | Two details are specific to Mongo. `Long.fromBigInt` silently keeps only the low 64 bits, and unlike Postgres the database cannot reject the truncated value, so the int64 codec refuses anything outside the signed 64-bit range on the client. And decoders check the BSON type tag (`_bsontype`) rather than `instanceof`, because the `mongodb` driver loads its own copy of `bson`, so values read from the database are never instances of the classes the target imports. **A `Json` field needs an explicit place in the collection validator.** Prisma 8 generates a closed `$jsonSchema` validator per collection (`additionalProperties: false`). A field with no BSON type used to be left out of `properties`, which meant every document carrying a `Json` field was rejected on write. Such a field now gets the empty schema `{}`, which admits any value, and contract canonicalisation keeps that empty object instead of dropping it. No existing contract had such a field, so no hash moves. ## What you will see in the diff - **Regenerated fixtures.** 43 emitted `contract.d.ts` files (integration fixtures, examples, migration snapshots) change one import line from `adapter/codec-types` to `target/codec-types`. The sprawl under `test/integration` and `examples` is that regeneration, not code change. - **Moved tests.** Five migration-runner tests that need a real adapter and a live database moved from the target package to `test/integration/test/mongo/target-runner/`, where the Postgres runner tests live; the target can no longer devDepend on its adapter without a package cycle. - **Layering registration.** `architecture.config.json` registers the target's `src/core/**` and `exports/control.ts` planes per the Repo Map. Note that dependency-cruiser still cannot forbid target-to-adapter imports, because all target packages sit in the `extensions` domain, which may import `targets`. The layering test in the target package is what enforces the rule; fixing the domain mapping is a separate change. - **Upgrade instructions.** `upgrade-instructions/pending/mongo-target-owns-codecs/app/instructions.md` and `.../extension/instructions.md` cover the moved subpaths, the deleted `createMongoRunnerDeps`, the moved runner-dependency types, and the new requirement that a Mongo control stack includes the adapter before the runner executes. - **Renamed scalars.** 33 `.prisma` files (examples, migration copies, integration fixtures) and 8 test files use the new names; every regenerated `contract.json` is byte-identical, which is the proof that only names moved. The app upgrade fragment carries the rewrite table with a detection pattern. - **Plane registration.** The Mongo target's migration-plane files (runner, planner, control descriptor, serializers, renderers) moved into `src/core/migrations/`, and the shared glob narrowed to `src/core/*.ts`, because dependency-cruiser does not resolve overlapping globs; the Repo Map doc is corrected accordingly. - **Error context.** A codec's own `RUNTIME.DECODE_FAILED` or `RUNTIME.ENCODE_FAILED` is now wrapped with the collection, field path, codec, and a wire preview, keeping the original as `cause`, so a Decimal128 field holding a double names the field, as SQL errors name the column. - **Docs.** ADR 198 is made consistent with the shipped code (its examples show the runner dependencies as they exist, the DDL-visitor design is recorded as the superseded alternative, and the layering bullet names the test that enforces the boundary); the Adapters and Targets glossary states that the target owns its codecs; the MongoDB Family subsystem doc, the codec authoring guide, and the package READMEs describe the new layout and codecs. ## Alternatives considered - **Add the four codecs in the adapter, where the others were.** Rejected: it entrenches the layout mistake, and the codec types are shared by the control and runtime sides, which is exactly why Postgres keeps them in the target. - **Move the codecs without fixing the runner wiring.** Rejected: the adapter would import the target while the target imported the adapter, a package cycle. - **Leave `Json` fields out of the validator, as the original design said.** Rejected once the end-to-end test showed every write was refused. - **Keep `createMongoRunnerDeps` as a compatibility export.** Rejected: the repo does not keep backward-compatibility shims, and its only callers were tests. Follow-up work, recorded in the project plan: the dependency-cruiser domain mapping for target packages (so `lint:deps` rather than a test enforces the boundary), retiring the runner-dependencies object in favour of direct family operations, deriving `CodecTypes` from the codecs, and the `Json`/`Bson` split in the next slice. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added MongoDB support for `Int64`, `Decimal128`, `Binary`, and `Json`, including BSON conversion and contract-builder helpers. * MongoDB JSON fields now appear in collection validators without imposing a value-type restriction. * Deprecated scalar names remain accepted, with replacement guidance available during contract emission and in editor diagnostics. * **Bug Fixes** * Improved MongoDB scalar encoding and decoding errors with more useful field and value details. * **Documentation** * Expanded MongoDB scalar and codec guidance, including BSON storage behavior and migration notes. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io> Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com> | 9 天前 | |
Refactor rules-footprint script for clarity and maintainability Address code quality feedback by restructuring the monolithic main(): - Use gray-matter instead of fragile custom frontmatter parser - Load thresholds from .cursor/rules-footprint.config.json with sensible defaults and clear error handling if missing/malformed - Extract scanRulecardFiles(dir) to gather rulecard statistics - Extract readAgentsStats(file) to read AGENTS.md metrics - Extract printReport(stats) for the report output - Extract checkThresholds(stats, thresholds, checkMode) for validation - Slim main() to pure orchestration (~10 lines) Each helper has clear inputs/outputs, improving testability. | 8 个月前 | |
fix(scripts): bound :agent runs with a portable timeout so a hung suite fails loudly (#853) ## Problem The root `:agent` script wrappers (`test:integration:agent`, `test:packages:agent`, …) redirect a slow command to a timestamped log under `wip/` and write its exit code to a sibling `.exit` marker, so an agent can fire-and-check rather than stream output. The inline shell was: ``` ts=…; log=wip/<name>.$ts.log; echo "log: $log"; pnpm <cmd> > "$log" 2>&1; status=$?; echo $status > wip/<name>.$ts.exit; exit $status ``` On an intermittent vitest hang **after** it prints its full passing summary (`Test Files … passed`, `Duration …s`), the inner `pnpm <cmd>` never returns. The `status=$?; echo … > …exit; exit` tail never runs, the `.exit` marker is never written, and the wrapper **blocks forever** until something external kills it. ## Diagnosis I could not reproduce the post-summary hang on macOS in 10 full integration runs — the process always exits after the summary (cleanly, or non-zero on a test failure, in which case the `.exit` is written correctly). The hang is intermittent and matches the documented **Linux-only PGlite (WASM) teardown abort** (`jit_page_->allocations_.erase`) that `test/integration/vitest.config.ts` already tries to mitigate with `--no-memory-protection-keys` and that still "intermittently aborts on Linux" per that config's own comment. It lives in the `@prisma/dev` dependency's WASM teardown, not in this repo's test teardown: I traced every suspected leak (the `pg.Pool`/`pg.Client` in the postgres driver tests and integration runtime helper, every `MongoMemoryReplSet`) and each is closed — `driver.close()` ends the pool/client, and every replset has a matching `stop()`. So there is no missing `afterAll` to fix here. ## Fix Replace the eight inline-shell `:agent` wrappers with one portable Node wrapper, `scripts/run-logged.mjs`, that runs the command under a **hard timeout** (default 60 min, override with `AGENT_CMD_TIMEOUT_SECONDS`). On timeout it kills the child **process group** (so turbo/vitest grandchildren die too), records `124` in the `.exit` file, and returns. A hang now fails loudly with a non-zero `.exit` instead of blocking forever — the reliable backstop the root-cause (a dependency's intermittent WASM teardown) doesn't give us. `timeout`/`gtimeout` are not portable (absent on macOS, where agents run), so the bound is enforced in Node rather than via the `timeout` binary. Bonus: this drops the `status=$?` idiom, which is a **read-only variable in zsh** and broke the wrappers there. That makes it a superset of #842 (the standalone `status`→`rc` rename) — **#842 can be closed in favour of this.** ## Verification - `scripts/run-logged.test.mjs` (wired into `test:scripts`) covers all four exit paths: success → `.exit` 0; failure → `.exit` is the child code; timeout → `.exit` 124 + `TIMEOUT` marker; and idempotency (the SIGKILL-triggered `close` can't overwrite `124`). - End-to-end on the real wrappers: `build:agent` returns rc 0 and writes `.exit` 0; `test:integration:agent` returns rc 1 on a failing run and writes `.exit` 1 (faithful pass-through). Both print the `log:` line and capture full output. ## Out of scope / follow-up The package-level vitest configs (run by `test:packages`) lack the `--no-memory-protection-keys` flag that integration/e2e set, which is the most likely contributor to the Linux `test:packages` hang. Propagating it is a separate, low-risk change tracked as a follow-up; it only *reduces* the abort, so the timeout wrapper here is the actual backstop. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Updated agent command execution to run through a logging wrapper that records stdout/stderr to timestamped logs and prints the log path. * Added hard timeouts for `:agent` runs (default **5 minutes**; configurable); on timeout the process is terminated and the run exits with **124**. * **Documentation** * Refreshed running-tests guidance to clarify timeout and timeout-termination behavior. * **Tests** * Added tests covering successful runs, exit-code propagation, and timeout behavior (including ensuring the `.exit` file isn’t overwritten). <!-- end of auto-generated comment: release notes by coderabbit.ai --> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> | 3 个月前 | |
fix(scripts): bound :agent runs with a portable timeout so a hung suite fails loudly (#853) ## Problem The root `:agent` script wrappers (`test:integration:agent`, `test:packages:agent`, …) redirect a slow command to a timestamped log under `wip/` and write its exit code to a sibling `.exit` marker, so an agent can fire-and-check rather than stream output. The inline shell was: ``` ts=…; log=wip/<name>.$ts.log; echo "log: $log"; pnpm <cmd> > "$log" 2>&1; status=$?; echo $status > wip/<name>.$ts.exit; exit $status ``` On an intermittent vitest hang **after** it prints its full passing summary (`Test Files … passed`, `Duration …s`), the inner `pnpm <cmd>` never returns. The `status=$?; echo … > …exit; exit` tail never runs, the `.exit` marker is never written, and the wrapper **blocks forever** until something external kills it. ## Diagnosis I could not reproduce the post-summary hang on macOS in 10 full integration runs — the process always exits after the summary (cleanly, or non-zero on a test failure, in which case the `.exit` is written correctly). The hang is intermittent and matches the documented **Linux-only PGlite (WASM) teardown abort** (`jit_page_->allocations_.erase`) that `test/integration/vitest.config.ts` already tries to mitigate with `--no-memory-protection-keys` and that still "intermittently aborts on Linux" per that config's own comment. It lives in the `@prisma/dev` dependency's WASM teardown, not in this repo's test teardown: I traced every suspected leak (the `pg.Pool`/`pg.Client` in the postgres driver tests and integration runtime helper, every `MongoMemoryReplSet`) and each is closed — `driver.close()` ends the pool/client, and every replset has a matching `stop()`. So there is no missing `afterAll` to fix here. ## Fix Replace the eight inline-shell `:agent` wrappers with one portable Node wrapper, `scripts/run-logged.mjs`, that runs the command under a **hard timeout** (default 60 min, override with `AGENT_CMD_TIMEOUT_SECONDS`). On timeout it kills the child **process group** (so turbo/vitest grandchildren die too), records `124` in the `.exit` file, and returns. A hang now fails loudly with a non-zero `.exit` instead of blocking forever — the reliable backstop the root-cause (a dependency's intermittent WASM teardown) doesn't give us. `timeout`/`gtimeout` are not portable (absent on macOS, where agents run), so the bound is enforced in Node rather than via the `timeout` binary. Bonus: this drops the `status=$?` idiom, which is a **read-only variable in zsh** and broke the wrappers there. That makes it a superset of #842 (the standalone `status`→`rc` rename) — **#842 can be closed in favour of this.** ## Verification - `scripts/run-logged.test.mjs` (wired into `test:scripts`) covers all four exit paths: success → `.exit` 0; failure → `.exit` is the child code; timeout → `.exit` 124 + `TIMEOUT` marker; and idempotency (the SIGKILL-triggered `close` can't overwrite `124`). - End-to-end on the real wrappers: `build:agent` returns rc 0 and writes `.exit` 0; `test:integration:agent` returns rc 1 on a failing run and writes `.exit` 1 (faithful pass-through). Both print the `log:` line and capture full output. ## Out of scope / follow-up The package-level vitest configs (run by `test:packages`) lack the `--no-memory-protection-keys` flag that integration/e2e set, which is the most likely contributor to the Linux `test:packages` hang. Propagating it is a separate, low-risk change tracked as a follow-up; it only *reduces* the abort, so the timeout wrapper here is the actual backstop. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Updated agent command execution to run through a logging wrapper that records stdout/stderr to timestamped logs and prints the log path. * Added hard timeouts for `:agent` runs (default **5 minutes**; configurable); on timeout the process is terminated and the run exits with **124**. * **Documentation** * Refreshed running-tests guidance to clarify timeout and timeout-termination behavior. * **Tests** * Added tests covering successful runs, exit-code propagation, and timeout behavior (including ensuring the `.exit` file isn’t overwritten). <!-- end of auto-generated comment: release notes by coderabbit.ai --> Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> | 3 个月前 | |
chore(release): bump to 8.0.0-rc.12 (#30395) ## Release: 8.0.0-rc.11 → 8.0.0-rc.12 This is the release PR described in [docs/oss/versioning.md](https://github.com/prisma/orm/blob/main/docs/oss/versioning.md). It bumps every workspace package to 8.0.0-rc.12 and moves the Prisma dependencies to their latest versions. **Merging this PR ships the release.** The push to `main` carries the new root `version`. The `Publish to npm` workflow then publishes 8.0.0-rc.12 under `latest` and creates a pre-release GitHub Release from the notes file. ## Review these first - [docs/releases/v8.0.0-rc.12.md](https://github.com/prisma/orm/blob/release/8.0.0-rc.12/docs/releases/v8.0.0-rc.12.md): the release notes, which become the GitHub Release body. The same entry is at the top of `CHANGELOG.md`. - The upgrade guides for [apps](https://github.com/prisma/orm/blob/release/8.0.0-rc.12/skills/prisma-8/upgrading/app/upgrades/8.0.0-rc.11-to-8.0.0-rc.12/instructions.md) and [extensions](https://github.com/prisma/orm/blob/release/8.0.0-rc.12/skills/prisma-8/upgrading/extension/upgrades/8.0.0-rc.11-to-8.0.0-rc.12/instructions.md). They merge the 24 pending fragments. The original fragments are moved unchanged to `upgrade-instructions/releases/8.0.0-rc.11-to-8.0.0-rc.12/sources/`. - Four guide entries have no fragment behind them. The `migration new` default and its removed error codes (#30389) had no guide entry. Neither did the PSL parser API changes (#30312, #30344, #30335, #30379). I wrote those entries while preparing the release. - Where fragments contradicted later code, the guide follows the code. Examples: the Supabase storage hash, `voidParamsSchema`, and quoted defaults printed by `infer`. ## Dependency updates | Package | From | To | Where | | --- | --- | --- | --- | | `@prisma/cli-engine` | 0.4.0 | 0.6.1 | examples, test fixtures, apps (the toolchain packages were already on 0.6.1 from #30372) | | `@prisma/dev` | 0.25.1 | 0.25.2 | the workspace catalog | | `@prisma/compute-sdk` | ^0.39.0 | ^0.43.0 | `apps/telemetry-backend` | | `@prisma/management-api-sdk` | ^1.56.0 | ^1.76.0 | `apps/telemetry-backend` | compute-sdk 0.43 renames "service" to "app" and "version" to "deployment". The telemetry deploy script now uses the new names. Both SDK versions call `/v1/apps/{appId}`, so the ID stored in the existing `TELEMETRY_DEPLOY_SERVICE_ID` secret is still correct. The app's typecheck now includes `scripts/`, so it catches the next SDK rename. The repo does not depend on `@prisma/composer`. ## Fixes needed to publish - **The publish workflow has failed on `main` since #30372.** `check:conformance` called the `orm` config validator as `validate(value)`. Engine 0.6 always calls `validate(value, provenance)`, and the validator reads `provenance.files`, so it threw on every input. The check now passes the same provenance the engine would. The prisma-cli copy of this check already does this. - `set-version` rewrote `workspace:@internal/cli@<version>` to `workspace:<version>`, dropping the alias. The prisma7-adoption example uses that alias. This is the first bump since the alias was added. - `lint:legacy-name` and the `add-model-map` test pointed at the pending fragment paths. They now point at the archived sources. ## Verification Passed locally: - `pnpm build` - `pnpm typecheck` - `pnpm lint` - `pnpm test:scripts` (563 tests) - `pnpm check:conformance` - `pnpm check:publish-deps` - `pnpm check:upgrade-coverage`, in both publish and PR mode - `pnpm check:release-notes`, in both publish and PR mode - `pnpm lint:legacy-name` - `pnpm lint:skills` - `pnpm test:packages`: all 18,196 tests passed Not covered locally, left to CI: - Three `test:packages` suites install packed tarballs from the registry. This machine's pnpm refuses `@vercel/detect-agent@1.2.5` because it has no provenance. CI passed the same suites on #30390. - `prisma-8-cloudflare-worker` needs a local Hyperdrive database. - The telemetry backend tests need Node 24.16 with `Temporal`. This machine has 24.13. - `fixtures:check` needs Postgres. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added PostgreSQL full-text search, multi-file schemas, prepared ORM reads and aggregates, and conflict-skipping options for bulk creation. * Added support for using a Prisma 7 schema as the contract source, JavaScript `Date` timestamps on PostgreSQL, editor support for attribute arguments, and per-finding diagnostics. * **Breaking Changes** * Prisma 8 schema files now require `// use prisma-8` on the first line; unmapped models use their names verbatim for table names. * Replace `dbgenerated(...)` with SQL tagged literals. Defaults must be valid for their column types, creation timestamps use the application clock, and native PostgreSQL enums no longer support text operations. * Config naming and path resolution, migration starting points, and extension contracts have changed. * **Bug Fixes** * Improved migration checks and branching warnings, contract generation and inference, default verification, and type checking. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- 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> | 10 天前 | |
chore(release): bump to 8.0.0-rc.12 (#30395) ## Release: 8.0.0-rc.11 → 8.0.0-rc.12 This is the release PR described in [docs/oss/versioning.md](https://github.com/prisma/orm/blob/main/docs/oss/versioning.md). It bumps every workspace package to 8.0.0-rc.12 and moves the Prisma dependencies to their latest versions. **Merging this PR ships the release.** The push to `main` carries the new root `version`. The `Publish to npm` workflow then publishes 8.0.0-rc.12 under `latest` and creates a pre-release GitHub Release from the notes file. ## Review these first - [docs/releases/v8.0.0-rc.12.md](https://github.com/prisma/orm/blob/release/8.0.0-rc.12/docs/releases/v8.0.0-rc.12.md): the release notes, which become the GitHub Release body. The same entry is at the top of `CHANGELOG.md`. - The upgrade guides for [apps](https://github.com/prisma/orm/blob/release/8.0.0-rc.12/skills/prisma-8/upgrading/app/upgrades/8.0.0-rc.11-to-8.0.0-rc.12/instructions.md) and [extensions](https://github.com/prisma/orm/blob/release/8.0.0-rc.12/skills/prisma-8/upgrading/extension/upgrades/8.0.0-rc.11-to-8.0.0-rc.12/instructions.md). They merge the 24 pending fragments. The original fragments are moved unchanged to `upgrade-instructions/releases/8.0.0-rc.11-to-8.0.0-rc.12/sources/`. - Four guide entries have no fragment behind them. The `migration new` default and its removed error codes (#30389) had no guide entry. Neither did the PSL parser API changes (#30312, #30344, #30335, #30379). I wrote those entries while preparing the release. - Where fragments contradicted later code, the guide follows the code. Examples: the Supabase storage hash, `voidParamsSchema`, and quoted defaults printed by `infer`. ## Dependency updates | Package | From | To | Where | | --- | --- | --- | --- | | `@prisma/cli-engine` | 0.4.0 | 0.6.1 | examples, test fixtures, apps (the toolchain packages were already on 0.6.1 from #30372) | | `@prisma/dev` | 0.25.1 | 0.25.2 | the workspace catalog | | `@prisma/compute-sdk` | ^0.39.0 | ^0.43.0 | `apps/telemetry-backend` | | `@prisma/management-api-sdk` | ^1.56.0 | ^1.76.0 | `apps/telemetry-backend` | compute-sdk 0.43 renames "service" to "app" and "version" to "deployment". The telemetry deploy script now uses the new names. Both SDK versions call `/v1/apps/{appId}`, so the ID stored in the existing `TELEMETRY_DEPLOY_SERVICE_ID` secret is still correct. The app's typecheck now includes `scripts/`, so it catches the next SDK rename. The repo does not depend on `@prisma/composer`. ## Fixes needed to publish - **The publish workflow has failed on `main` since #30372.** `check:conformance` called the `orm` config validator as `validate(value)`. Engine 0.6 always calls `validate(value, provenance)`, and the validator reads `provenance.files`, so it threw on every input. The check now passes the same provenance the engine would. The prisma-cli copy of this check already does this. - `set-version` rewrote `workspace:@internal/cli@<version>` to `workspace:<version>`, dropping the alias. The prisma7-adoption example uses that alias. This is the first bump since the alias was added. - `lint:legacy-name` and the `add-model-map` test pointed at the pending fragment paths. They now point at the archived sources. ## Verification Passed locally: - `pnpm build` - `pnpm typecheck` - `pnpm lint` - `pnpm test:scripts` (563 tests) - `pnpm check:conformance` - `pnpm check:publish-deps` - `pnpm check:upgrade-coverage`, in both publish and PR mode - `pnpm check:release-notes`, in both publish and PR mode - `pnpm lint:legacy-name` - `pnpm lint:skills` - `pnpm test:packages`: all 18,196 tests passed Not covered locally, left to CI: - Three `test:packages` suites install packed tarballs from the registry. This machine's pnpm refuses `@vercel/detect-agent@1.2.5` because it has no provenance. CI passed the same suites on #30390. - `prisma-8-cloudflare-worker` needs a local Hyperdrive database. - The telemetry backend tests need Node 24.16 with `Temporal`. This machine has 24.13. - `fixtures:check` needs Postgres. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added PostgreSQL full-text search, multi-file schemas, prepared ORM reads and aggregates, and conflict-skipping options for bulk creation. * Added support for using a Prisma 7 schema as the contract source, JavaScript `Date` timestamps on PostgreSQL, editor support for attribute arguments, and per-finding diagnostics. * **Breaking Changes** * Prisma 8 schema files now require `// use prisma-8` on the first line; unmapped models use their names verbatim for table names. * Replace `dbgenerated(...)` with SQL tagged literals. Defaults must be valid for their column types, creation timestamps use the application clock, and native PostgreSQL enums no longer support text operations. * Config naming and path resolution, migration starting points, and extension contracts have changed. * **Bug Fixes** * Improved migration checks and branching warnings, contract generation and inference, default verification, and type checking. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- 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> | 10 天前 | |
chore(release): the version bump restamps extension versions in emitted contract artefacts (#30282) ## At a glance What a release cut looks like after this PR, compared with rc.9 and rc.10: ```text before: pnpm bump-version → Fixtures job fails on 8 Supabase artefacts hand-edit 8 version stamps → check:upgrade-coverage fails: no recipe entry hand-write a recipe entry → green after: pnpm bump-version → green (the 8 stamps move with the bump) ``` ## Why The Supabase extension writes its own package version into every contract it emits. The tracked example and fixture artefacts therefore go stale on every bump, `fixtures:check` diffs them, and the coverage check then insists the bump PR declare the "re-emit" in the upgrade recipe. It is a version move, not a consumer-facing change, and both rc.9 (#30239) and rc.10 (#30268) paid for it with an extra commit and a CI round trip. ## What changes - `scripts/set-version-utils.ts` gains `restampExtensionVersion`, which moves only the `version` that directly follows an extension entry's `targetId`, in both the JSON and the `.d.ts` shape, and leaves every other `version` field alone. Tests cover both shapes, the no-op case, and idempotence. - `scripts/set-version.ts` reads the previous root version before rewriting and applies the helper to every tracked `contract.json` / `contract.d.ts` outside `migrations/snapshots/` (those are content-addressed and must never change). A dry run against `main` touches exactly the eight Supabase files, one line each, and nothing else among the 531 tracked artefacts. - `scripts/check-upgrade-coverage.mjs` treats an artefact whose only difference is that stamp as translation-irrelevant, alongside the existing version-only `package.json` and `$schema` cases. Two new tests: a stamp-only sweep passes without a fresh declaration; an artefact whose shape also changed still fails. - The `publish-npm-version` skill's sanity-check step now expects the restamp from the bump instead of describing the manual follow-up added in #30271. ## Validation - `node --test scripts/set-version-utils.test.ts`: 22 pass. - `node --test scripts/check-upgrade-coverage.test.mjs`: 84 pass. - `pnpm lint` and `pnpm lint:skills` pass. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **Improvements** - Contract artefacts now automatically receive updated extension version stamps during release versioning. - Unrelated version fields remain unchanged, and already-updated artefacts are skipped. - Release checks now distinguish version-only contract updates from substantive contract changes. - **Bug Fixes** - Prevented version-only changes in generated contract files from incorrectly triggering upgrade-coverage requirements. - **Documentation** - Updated release guidance to reflect automatic contract version updates and streamlined validation steps. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io> Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com> | 20 天前 | |
TML-2705: reject dead .md rules; only .mdc is ever loaded (#612) ## Why Follow-up fix to the merged TML-2705 work (#609, commit `74cb2c1`). That change consolidated agent rules into `.agents/rules/` and treated **both** `.mdc` and `.md` as valid rule extensions. But the agent harnesses (Cursor, Claude Code) only load `.mdc` files — a rule with a `.md` extension is silently never picked up. So: - `no-backward-compatibility.md` was a **dead rule**: tracked, symlinked into both presentation trees, and never loaded by anything. - The sync script and docs actively *blessed* the `.md` extension, so the same mistake could recur on the next rule someone authored. ## What changed - **Renamed** `.agents/rules/no-backward-compatibility.md` → `.mdc` so the rule actually loads (its frontmatter was already valid). - **`scripts/sync-agent-rules.mjs`**: rules are now `.mdc`-only. `README.md` remains the single mirrored `.md` (it's the index, not a rule). Any *other* `.md` in the canonical dir is rejected — a hard error in sync mode (`pnpm rules:sync` / the `prepare` hook), and reported as drift in `--check` mode (`pnpm lint:rules:symlinks`). The mistake now fails CI instead of shipping a dead rule. - **Docs**: `AGENTS.md` and the rules `README.md` no longer document `.{md,mdc}`; they state plainly that rule files must be `.mdc`. - **Tests**: updated/added cases covering the README-only `.md` exemption and the dead-`.md`-rule rejection in both modes. ## Scope Rules + the sync tool + its docs only. No product code touched. `pnpm lint:rules`, `pnpm lint:rules:symlinks`, `pnpm lint:rules:footprint`, and `pnpm test:scripts` (118 tests) all pass locally. Signed-off-by: Will Madden <madden@prisma.io> | 4 个月前 | |
TML-2705: reject dead .md rules; only .mdc is ever loaded (#612) ## Why Follow-up fix to the merged TML-2705 work (#609, commit `74cb2c1`). That change consolidated agent rules into `.agents/rules/` and treated **both** `.mdc` and `.md` as valid rule extensions. But the agent harnesses (Cursor, Claude Code) only load `.mdc` files — a rule with a `.md` extension is silently never picked up. So: - `no-backward-compatibility.md` was a **dead rule**: tracked, symlinked into both presentation trees, and never loaded by anything. - The sync script and docs actively *blessed* the `.md` extension, so the same mistake could recur on the next rule someone authored. ## What changed - **Renamed** `.agents/rules/no-backward-compatibility.md` → `.mdc` so the rule actually loads (its frontmatter was already valid). - **`scripts/sync-agent-rules.mjs`**: rules are now `.mdc`-only. `README.md` remains the single mirrored `.md` (it's the index, not a rule). Any *other* `.md` in the canonical dir is rejected — a hard error in sync mode (`pnpm rules:sync` / the `prepare` hook), and reported as drift in `--check` mode (`pnpm lint:rules:symlinks`). The mistake now fails CI instead of shipping a dead rule. - **Docs**: `AGENTS.md` and the rules `README.md` no longer document `.{md,mdc}`; they state plainly that rule files must be `.mdc`. - **Tests**: updated/added cases covering the README-only `.md` exemption and the dead-`.md`-rule rejection in both modes. ## Scope Rules + the sync tool + its docs only. No product code touched. `pnpm lint:rules`, `pnpm lint:rules:symlinks`, `pnpm lint:rules:footprint`, and `pnpm test:scripts` (118 tests) all pass locally. Signed-off-by: Will Madden <madden@prisma.io> | 4 个月前 | |
Ship the prisma-8 skill inside the three ORM target tarballs (#30096) The prisma-8 skill now ships inside the npm tarballs users actually install, instead of being fetched from GitHub by `npx skills add` at init time. ## What changed - **The two upgrade skills fold into the `prisma-8` router.** `prisma-next-upgrade` and `prisma-8-extension-upgrade` become an "upgrading" branch of `skills/prisma-8/` (per-transition `upgrades/<from>-to-<to>/` layout kept), their trigger phrases move into the router's `description`, and the router opens with a preamble telling the agent the installed version's skill is the source of truth. Three registered skills become one. - **Version stamp under `metadata`.** The skill frontmatter carries `metadata.library` (the npm package name) and `metadata.library_version`, stamped by the version pipeline (`scripts/set-version.ts`) so the stamp and the package version cannot diverge. The keys live under the Agent Skills spec's `metadata` map — a string→string extension point — rather than as undefined top-level keys. - **The skill travels in three tarballs.** `skills/prisma-8/` is staged into `@prisma/orm-postgres`, `@prisma/orm-sqlite`, and `@prisma/orm-mongo` at `prepack` time, with `"skills"` in each package's `files`. Each copy's `metadata.library` names the package it ships in. - **The packaging is proved from the artifact, not the working tree.** The publish-surface test deletes the staged tree, runs `pnpm pack` the way the publish workflow does, reads the stamped `SKILL.md` back out of the tarball, and byte-compares every file against the tracked source. It fails if the `files` entry or the `prepack` script is removed. - **The upgrade-coverage check follows the fold.** `USER_SKILL_PKG` / `EXT_SKILL_PKG` point at the folded directories and the path regex is derived from them. - Docs updated: `skills/README.md` (authoring rules for the stamp, the GitHub route demoted to a manual fallback), `docs/oss/versioning.md`, `docs/reference/error-reference.md`. ## What consumes this `prisma skills sync` in prisma/prisma-cli ([prisma/prisma-cli#219](https://github.com/prisma/prisma-cli/pull/219)) copies these skills from the installed packages into the agent harness directories and reads the `metadata` stamp to detect staleness. The init wiring that runs sync lands separately, stacked on this branch. Merge order: prisma/prisma-cli#219 ships first (it owns the `prisma skills` command), then this, then the init wiring. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Prisma ORM packages now bundle a version-matched `prisma-8` skill for application and extension upgrades. * Added upgrade guidance and migration tools covering historical Prisma version transitions. * Skills now synchronize automatically during package initialization and packaging. * **Documentation** * Updated installation, synchronization, versioning, error-handling, and authoring guidance. * **Bug Fixes** * Improved skill metadata validation and version stamping. * Retired legacy standalone upgrade skill references and installation paths. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io> | 1 个月前 | |
fix: avoid upgrade instruction conflicts with independent fragments (#30311) Unrelated PRs currently have to edit the same upgrade guide for the next release. This causes merge conflicts even when their code changes do not overlap. This PR lets each change add its upgrade instructions in a separate folder instead. Contributors no longer need to know which release will include their change. ## How it works - Each PR adds its own instructions for app users, extension authors, or both. It can explicitly say that no upgrade action is needed. - When preparing a release, the release agent combines those instructions into one guide for each audience. The original files are kept in the repository but are not included in published packages. - A release cannot proceed while any instructions are still waiting to be included. This is checked before merge and again before publishing, so instructions added during release preparation cannot be silently left behind. - Development builds can still include unfinished release work. Existing instructions are preserved. Users receive the same guide format as before, and the process for testing each PR’s upgrade instructions stays the same. This does not add a new release-testing system or new commands for assembling guides. ## Checks run - Full build passed. - All 504 script tests and 10 package-content tests passed. - Type checks and code-style checks passed for the affected package. - Contributor-skill, CI-workflow, and upgrade-instruction checks passed. - Confirmed that moving the existing instructions preserved their contents, removing only the release-number fields. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Added a documented lifecycle for creating, reviewing, assembling, and archiving audience-specific upgrade guides. - Upgrade guides now support larger version jumps when intermediate releases were not published. - **Bug Fixes** - Improved release checks to detect missing or incomplete upgrade instructions before publication. - Development and prerelease builds now validate pending upgrade guidance without blocking releases. - CI checks now use event-specific commit references for more reliable validation. - **Documentation** - Updated release procedures and authoring guidance for upgrade instructions, release notes, and late changes. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: Steven McClankerton <tatarintsev@prisma.io> Co-authored-by: Steven McClankerton <tatarintsev@prisma.io> | 18 天前 | |
chore: update npm provenance repository (#30145) ## Summary - update package repository metadata from `prisma/prisma` to `prisma/orm` - update the manifest provenance guard and its tests ## Validation - `node --test scripts/validate-package-manifests.test.mjs` - `node scripts/validate-package-manifests.mjs` - parsed all package manifests as JSON - `git diff --check` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Chores** * Updated package repository metadata to reference the Prisma ORM repository. * Updated package manifest validation and its tests to recognize the new canonical repository URL. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: Steven McClankerton <tatarintsev@prisma.io> | 1 个月前 | |
chore: update npm provenance repository (#30145) ## Summary - update package repository metadata from `prisma/prisma` to `prisma/orm` - update the manifest provenance guard and its tests ## Validation - `node --test scripts/validate-package-manifests.test.mjs` - `node scripts/validate-package-manifests.mjs` - parsed all package manifests as JSON - `git diff --check` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Chores** * Updated package repository metadata to reference the Prisma ORM repository. * Updated package manifest validation and its tests to recognize the new canonical repository URL. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: Steven McClankerton <tatarintsev@prisma.io> | 1 个月前 | |
Remove dependency sections from READMEs (#30141) Removes manually maintained dependency inventories from repository READMEs and stops treating them as part of the package README contract. ## Changes - **READMEs**: Remove the exact `Dependencies` sections from 69 package and test READMEs while preserving all surrounding documentation. - **Documentation validation**: Stop warning when package READMEs omit `Dependencies`; title, presence, and `Responsibilities` checks remain unchanged. Package discovery now propagates `find` failures instead of silently validating zero packages. - **Regression coverage**: Add focused validator tests proving a README with a title and responsibilities passes without a dependency section and discovery failures remain fatal. - **Upgrade declaration**: Add a no-op declaration at `skills/prisma-8/upgrading/extension/upgrades/8.0.0-rc.8-to-8.0.0-rc.9/instructions.md` because the extension README edits require no consumer migration. ## Why Dependency inventories duplicate package manifests and can drift from the actual dependency graph. Removing the sections and their validator rule keeps READMEs focused on package purpose and responsibilities while leaving dependency declarations to their source of truth. ## Verification - `node --test scripts/validate-package-readmes.test.mjs` - `pnpm lint:docs` - `pnpm check:upgrade-coverage --mode pr --prev origin/main` - Confirmed no tracked README retains an exact `Dependencies` heading - `git diff --check` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Documentation** * Updated package READMEs to remove outdated dependency and dependent-package sections. * Added related package and subsystem references where appropriate. * Added upgrade guidance for Prisma 8.0.0-rc.8 to 8.0.0-rc.9. * **Tests** * Added validation coverage for READMEs without dependency sections and package-discovery failures. * Included the README validation test in the test scripts. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Steven McClankerton <tatarintsev@prisma.io> | 1 个月前 | |
Remove dependency sections from READMEs (#30141) Removes manually maintained dependency inventories from repository READMEs and stops treating them as part of the package README contract. ## Changes - **READMEs**: Remove the exact `Dependencies` sections from 69 package and test READMEs while preserving all surrounding documentation. - **Documentation validation**: Stop warning when package READMEs omit `Dependencies`; title, presence, and `Responsibilities` checks remain unchanged. Package discovery now propagates `find` failures instead of silently validating zero packages. - **Regression coverage**: Add focused validator tests proving a README with a title and responsibilities passes without a dependency section and discovery failures remain fatal. - **Upgrade declaration**: Add a no-op declaration at `skills/prisma-8/upgrading/extension/upgrades/8.0.0-rc.8-to-8.0.0-rc.9/instructions.md` because the extension README edits require no consumer migration. ## Why Dependency inventories duplicate package manifests and can drift from the actual dependency graph. Removing the sections and their validator rule keeps READMEs focused on package purpose and responsibilities while leaving dependency declarations to their source of truth. ## Verification - `node --test scripts/validate-package-readmes.test.mjs` - `pnpm lint:docs` - `pnpm check:upgrade-coverage --mode pr --prev origin/main` - Confirmed no tracked README retains an exact `Dependencies` heading - `git diff --check` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Documentation** * Updated package READMEs to remove outdated dependency and dependent-package sections. * Added related package and subsystem references where appropriate. * Added upgrade guidance for Prisma 8.0.0-rc.8 to 8.0.0-rc.9. * **Tests** * Added validation coverage for READMEs without dependency sections and package-discovery failures. * Included the README validation test in the test scripts. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Steven McClankerton <tatarintsev@prisma.io> | 1 个月前 | |
feat(rules): consistently symlink agent rules from a canonical home Agent rules now mirror the skills-contrib model: `.agents/rules/` is the only git-tracked home, and `.cursor/rules` + `.claude/rules` are git-ignored presentation trees containing nothing but relative symlinks back into it. - Add scripts/sync-agent-rules.mjs (+ tests): consolidates stray real-file rules into the canonical dir, regenerates the symlink trees, and prunes dangling/orphan symlinks. `--check` is the lint gate. - Wire `rules:sync` into `prepare` and `lint:rules:symlinks` into CI. - Fix .gitignore so `.agents/rules/**` is tracked (the missing whitelist exception was the root cause: rules added only to `.cursor/rules` were silently git-ignored and lost). - Migrate 68 rules into `.agents/rules`, remove the dangling drive-project-workflow symlink, and add frontmatter to three rules that had none. - Point validate-rules at the canonical dir; bump footprint thresholds for the previously-orphaned alwaysApply rules now actually loaded. - Rewrite the rules index and the AGENTS.md/CLAUDE.md "where rules live" section to match the new model. Closes TML-2705 Signed-off-by: Will Madden <madden@prisma.io> | 4 个月前 | |
refactor: rename every user-facing prisma-next identifier to Prisma 8 (#30262) ## Linked issue n/a — no Linear ticket. Completes the rename that #30248 started for prose; builds on #30261. ## At a glance Every `prisma-next` identifier a user can see is renamed. Before and after, for a scaffolded project: ```text // use prisma-next → // use prisma-8 (schema header) prisma-next.md → prisma-8.md (primer at the project root) PRISMA_NEXT_DISABLE_TELEMETRY → PRISMA_DISABLE_TELEMETRY (and every other PRISMA_NEXT_* variable) ~/.config/prisma-next/ → ~/.config/prisma-8/ (per-user telemetry config) prisma-next contract emit → prisma contract emit (CLI invocations in docs, fixtures, recordings) ``` ## Summary After #30248 the product was called Prisma 8 in prose, but the working name was still written into user projects and printed by the CLI: the schema header, the primer file, the environment variables, the per-user config directory, the language-server diagnostic source, the Standard Schema vendor string, the contract brand symbol, the advisory-lock domain, and about 650 fixture and doc files that spelled out `prisma-next …` commands. This PR renames all of it in one pass and tightens the legacy-name lint so the only occurrences left are the ones with a reason. ## Decision One commit. The mapping: | Surface | Before | After | |---|---|---| | Schema header | `// use prisma-next` | `// use prisma-8` | | Primer file `init` writes | `prisma-next.md` | `prisma-8.md` | | CLI environment variables | `PRISMA_NEXT_*` | `PRISMA_*` | | Per-user config directory | `prisma-next/` | `prisma-8/` | | Language-server diagnostic source | `prisma-next` | `prisma` | | Standard Schema vendor, VS Code publisher | `prisma-next` | `prisma` | | Contract brand symbol | `__prisma_next_brand__` | `__prisma_8_brand__` | | Postgres advisory-lock domain | `prisma_next.contract.marker` | `prisma_8.contract.marker` | | Example database names | `prisma_next_*` | `prisma_8_*` | | README banner image | `images/prisma-next.png` | `images/prisma-8.png` | | Telemetry docs URL | `prisma-next.dev/docs/…` | `www.prisma.io/docs/…` | | New-issue links | `github.com/prisma/prisma-next/issues/new` | `github.com/prisma/orm/issues/new` | | CLI invocations in prose, fixtures, and recordings | `prisma-next db verify` | `prisma db verify` | `prisma-8` is the slug the repo already uses for the skill, the examples, and the upgrade directories, so it is the slug for everything that needs one. Environment variables drop the infix entirely because `PRISMA_*` is what users expect and nothing else in the repo claims those names. What keeps the old name, each with a lint allowance that says why: - **Dated records**: changelog, release notes, ADRs, shipped upgrade instructions, gotcha logs, the framework-gaps review, and the `projects/` and `drive/` write-ups. - **Pinned links** into the old repository by number, Linear slugs, and links to ADRs whose filenames carry the name. - **`@cipherstash/prisma-next`**, a third party's published package name. - **Retirement proofs**: the list of old skill directories `init` deletes, and the tests asserting that no `prisma-next` bin or skill directory is installed any more. ## Behavior changes & evidence - **Schema header.** The inferred-schema printer and the `init` templates write `// use prisma-8`. The language server accepts both headers, so existing schemas keep their diagnostics and completion, and its Format action rewrites the old header to the new one. [packages/1-framework/3-tooling/language-server/src/schema-directive.ts](packages/1-framework/3-tooling/language-server/src/schema-directive.ts), [packages/1-framework/2-authoring/psl-printer/src/ast-to-print-document.ts](packages/1-framework/2-authoring/psl-printer/src/ast-to-print-document.ts). Evidence: the `renameLegacyDirective` tests, the server test that formats a legacy-headed schema, and the psl-printer tests. - **Environment variables.** Telemetry gating, the endpoint override, and the debug switch read the new names. `PRISMA_NEXT_DISABLE_TELEMETRY` is still honoured as an opt-out so nobody is silently opted back in; the endpoint and debug spellings are not. [packages/1-framework/3-tooling/cli-telemetry/src/gating.ts](packages/1-framework/3-tooling/cli-telemetry/src/gating.ts). Evidence: cli-telemetry gating tests. - **Per-user config directory.** [packages/1-framework/3-tooling/cli-telemetry/src/user-config.ts](packages/1-framework/3-tooling/cli-telemetry/src/user-config.ts). Existing users see the telemetry consent prompt once more; nothing else is lost. - **Primer file.** [packages/1-framework/3-tooling/cli/src/orm/init-scaffold.ts](packages/1-framework/3-tooling/cli/src/orm/init-scaffold.ts). Evidence: init-scaffold tests and template snapshots. - **Advisory-lock domain.** A CLI on this version and one on the previous version take different locks for the same marker. Both versions running migrations against one database at the same moment is already unsupported. - **Upgrade instructions.** Entries for the header, the environment variables, and the primer file are recorded in the rc.9 → rc.10 app and extension instructions with detection patterns, so the published upgrade skill applies the rename. ## Testing performed - `pnpm test` in cli (1437), cli-telemetry (112), language-server (312), psl-printer (63), framework-components (672), target-postgres (1607), vite-plugin-contract-emit (31), emitter (231), and `pnpm test:scripts` (507): all pass after `pnpm build`. The language-server tests hard-coded the old header's length in semantic-token arrays and span offsets; those expectations are updated. - Committed migration steps and their content-addressed contract snapshots are left untouched, since rewriting them would break their hashes; the lint treats them as dated records. - `pnpm lint:legacy-name` passes with the tightened allowances; `node --test scripts/lint-legacy-name.test.mjs` passes (14 tests, including new negative cases for the header, primer, and skill names). - `pnpm check:upgrade-coverage --mode pr --prev origin/main` passes. ## Skill update `skills/prisma-8` references and the two rc.9 → rc.10 upgrade instruction files are updated in this PR. ## Checklist - [x] All commits are signed off (`git commit -s`) per the [DCO](../CONTRIBUTING.md#developer-certificate-of-origin-dco). - [x] I read [CONTRIBUTING.md](../CONTRIBUTING.md) and the change is scoped to one logical concern. - [x] Tests are updated. - [ ] The PR title is in `TML-NNNN: <sentence-case title>` form. No Linear ticket exists for this change. - [x] The **Skill update** section above is filled in. 🤖 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 Fable 5.1 <noreply@anthropic.com> | 23 天前 | |
refactor: rename every user-facing prisma-next identifier to Prisma 8 (#30262) ## Linked issue n/a — no Linear ticket. Completes the rename that #30248 started for prose; builds on #30261. ## At a glance Every `prisma-next` identifier a user can see is renamed. Before and after, for a scaffolded project: ```text // use prisma-next → // use prisma-8 (schema header) prisma-next.md → prisma-8.md (primer at the project root) PRISMA_NEXT_DISABLE_TELEMETRY → PRISMA_DISABLE_TELEMETRY (and every other PRISMA_NEXT_* variable) ~/.config/prisma-next/ → ~/.config/prisma-8/ (per-user telemetry config) prisma-next contract emit → prisma contract emit (CLI invocations in docs, fixtures, recordings) ``` ## Summary After #30248 the product was called Prisma 8 in prose, but the working name was still written into user projects and printed by the CLI: the schema header, the primer file, the environment variables, the per-user config directory, the language-server diagnostic source, the Standard Schema vendor string, the contract brand symbol, the advisory-lock domain, and about 650 fixture and doc files that spelled out `prisma-next …` commands. This PR renames all of it in one pass and tightens the legacy-name lint so the only occurrences left are the ones with a reason. ## Decision One commit. The mapping: | Surface | Before | After | |---|---|---| | Schema header | `// use prisma-next` | `// use prisma-8` | | Primer file `init` writes | `prisma-next.md` | `prisma-8.md` | | CLI environment variables | `PRISMA_NEXT_*` | `PRISMA_*` | | Per-user config directory | `prisma-next/` | `prisma-8/` | | Language-server diagnostic source | `prisma-next` | `prisma` | | Standard Schema vendor, VS Code publisher | `prisma-next` | `prisma` | | Contract brand symbol | `__prisma_next_brand__` | `__prisma_8_brand__` | | Postgres advisory-lock domain | `prisma_next.contract.marker` | `prisma_8.contract.marker` | | Example database names | `prisma_next_*` | `prisma_8_*` | | README banner image | `images/prisma-next.png` | `images/prisma-8.png` | | Telemetry docs URL | `prisma-next.dev/docs/…` | `www.prisma.io/docs/…` | | New-issue links | `github.com/prisma/prisma-next/issues/new` | `github.com/prisma/orm/issues/new` | | CLI invocations in prose, fixtures, and recordings | `prisma-next db verify` | `prisma db verify` | `prisma-8` is the slug the repo already uses for the skill, the examples, and the upgrade directories, so it is the slug for everything that needs one. Environment variables drop the infix entirely because `PRISMA_*` is what users expect and nothing else in the repo claims those names. What keeps the old name, each with a lint allowance that says why: - **Dated records**: changelog, release notes, ADRs, shipped upgrade instructions, gotcha logs, the framework-gaps review, and the `projects/` and `drive/` write-ups. - **Pinned links** into the old repository by number, Linear slugs, and links to ADRs whose filenames carry the name. - **`@cipherstash/prisma-next`**, a third party's published package name. - **Retirement proofs**: the list of old skill directories `init` deletes, and the tests asserting that no `prisma-next` bin or skill directory is installed any more. ## Behavior changes & evidence - **Schema header.** The inferred-schema printer and the `init` templates write `// use prisma-8`. The language server accepts both headers, so existing schemas keep their diagnostics and completion, and its Format action rewrites the old header to the new one. [packages/1-framework/3-tooling/language-server/src/schema-directive.ts](packages/1-framework/3-tooling/language-server/src/schema-directive.ts), [packages/1-framework/2-authoring/psl-printer/src/ast-to-print-document.ts](packages/1-framework/2-authoring/psl-printer/src/ast-to-print-document.ts). Evidence: the `renameLegacyDirective` tests, the server test that formats a legacy-headed schema, and the psl-printer tests. - **Environment variables.** Telemetry gating, the endpoint override, and the debug switch read the new names. `PRISMA_NEXT_DISABLE_TELEMETRY` is still honoured as an opt-out so nobody is silently opted back in; the endpoint and debug spellings are not. [packages/1-framework/3-tooling/cli-telemetry/src/gating.ts](packages/1-framework/3-tooling/cli-telemetry/src/gating.ts). Evidence: cli-telemetry gating tests. - **Per-user config directory.** [packages/1-framework/3-tooling/cli-telemetry/src/user-config.ts](packages/1-framework/3-tooling/cli-telemetry/src/user-config.ts). Existing users see the telemetry consent prompt once more; nothing else is lost. - **Primer file.** [packages/1-framework/3-tooling/cli/src/orm/init-scaffold.ts](packages/1-framework/3-tooling/cli/src/orm/init-scaffold.ts). Evidence: init-scaffold tests and template snapshots. - **Advisory-lock domain.** A CLI on this version and one on the previous version take different locks for the same marker. Both versions running migrations against one database at the same moment is already unsupported. - **Upgrade instructions.** Entries for the header, the environment variables, and the primer file are recorded in the rc.9 → rc.10 app and extension instructions with detection patterns, so the published upgrade skill applies the rename. ## Testing performed - `pnpm test` in cli (1437), cli-telemetry (112), language-server (312), psl-printer (63), framework-components (672), target-postgres (1607), vite-plugin-contract-emit (31), emitter (231), and `pnpm test:scripts` (507): all pass after `pnpm build`. The language-server tests hard-coded the old header's length in semantic-token arrays and span offsets; those expectations are updated. - Committed migration steps and their content-addressed contract snapshots are left untouched, since rewriting them would break their hashes; the lint treats them as dated records. - `pnpm lint:legacy-name` passes with the tightened allowances; `node --test scripts/lint-legacy-name.test.mjs` passes (14 tests, including new negative cases for the header, primer, and skill names). - `pnpm check:upgrade-coverage --mode pr --prev origin/main` passes. ## Skill update `skills/prisma-8` references and the two rc.9 → rc.10 upgrade instruction files are updated in this PR. ## Checklist - [x] All commits are signed off (`git commit -s`) per the [DCO](../CONTRIBUTING.md#developer-certificate-of-origin-dco). - [x] I read [CONTRIBUTING.md](../CONTRIBUTING.md) and the change is scoped to one logical concern. - [x] Tests are updated. - [ ] The PR title is in `TML-NNNN: <sentence-case title>` form. No Linear ticket exists for this change. - [x] The **Skill update** section above is filled in. 🤖 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 Fable 5.1 <noreply@anthropic.com> | 23 天前 | |
docs(TML-1810): fix stale lint:ts-peer reference in validator header comment Signed-off-by: Will Madden <madden@prisma.io> | 4 个月前 | |
feat: an application depends on one Prisma package (ADR 242) (#29864) ## What changes for someone using Prisma Today, an application that talks to Postgres installs a long list of our packages: ```jsonc { "dependencies": { "@prisma-next/postgres": "...", "@prisma-next/sql-runtime": "...", "@prisma-next/sql-orm-client": "...", "@prisma-next/target-postgres": "...", "@prisma-next/adapter-postgres": "...", "@prisma-next/sql-contract": "..." // ...a dozen more } } ``` After this PR, it installs one: ```jsonc { "dependencies": { "@prisma/orm-postgres": "0.16.0" } } ``` Everything else arrives as that package's own dependencies. Three of our example apps are converted in this PR to prove it — one per database — and each has exactly one Prisma package in its `dependencies`. This implements [ADR 242](https://github.com/prisma/prisma/pull/29852), which is already merged. ## What gets published 17 packages, all under the `@prisma` scope: - **3 database packages** — `@prisma/orm-postgres`, `orm-sqlite`, `orm-mongo`. An application installs exactly one. We call these *facades*: each is a small package that wires its database together and re-exports everything an application needs. - **6 extension packs** — PostGIS, pgvector, ParadeDB, Supabase, arktype-json, middleware-cache. Optional, installed alongside a database package. - **7 platform packages** — the framework, the toolchain, one per database family, one per database target. Applications never install these directly; they arrive as dependencies. Extension authors do install them. - **the `prisma` command**, as a bin-only package. Every other workspace package — around 50 of them — stops being published. They still exist in the repo as the unit we organise code in; they just stop having a life on the registry. **This PR does not make that switch yet.** It builds and proves the new surface while leaving today's publish list exactly as it is. Flipping it is a separate change. ## The problem this design has to avoid A published package can't depend on packages that won't exist on the registry. So each published package *contains a compiled copy* of the internal packages it covers. That creates a trap. If one application ends up with the same code twice — once inside a published package, once as its own package — then classes, registries, and anything compared by reference exist twice too. An `instanceof` check quietly returns false. Nothing crashes, nothing fails to compile, and both copies behave identically in isolation. You find out much later, somewhere unrelated. So the rule the whole design follows is: **every piece of internal code is published from exactly one package.** Concretely, that means: - Each published package is built in one pass, so code shared between its own entry points exists once. Verified from the build's source maps: no module appears in more than one chunk, in any published package. - When one published package needs code from another, it imports it as a real dependency rather than compiling in a second copy. - A facade re-exports from the platform packages; it never carries its own copy. `@prisma/orm-postgres/orm-client` and `@prisma/orm-family-sql/orm-client` are two names for the same object, and there's a test that asserts exactly that from installed tarballs. - One table in `packages/0-shared/publish-surface` maps every internal package to where it's published. The build, the code generator, and the lint checks all read it, so there's one answer to "where does this live" rather than three that can drift. ## Generated code follows the application Prisma writes imports into your project — contract types and migration files. Those imports have to name packages your project actually depends on, or they won't resolve. So the generator now reads the `package.json` next to the config it's generating for. A project that depends on `@prisma/orm-postgres` gets imports from that package. A project on today's names keeps today's names. Nothing to configure, because the manifest already says which it is. Contract hashes are unaffected, and that isn't an assumption — hashes are computed from a structure that import text never enters, and there's a test asserting the hash is identical across naming schemes *while* the emitted imports demonstrably differ. ## What stops the trap coming back Two checks, because the failure is silent and won't show up in a test suite: - Every example app and test project must use one naming scheme, not a mix. `lint-single-import-root` scans them and fails the build if any project imports from both, since that's the situation that loads code twice. - `lint-consumer-internal-imports` counts how many internal-package imports remain in those projects and compares against a committed number. It fails if the number goes up (someone added one) and also if it goes down without the number being updated (so improvements get locked in). Target is zero. The build itself also refuses to proceed if the published-package map would put one module in two places, or if a published package's `package.json` no longer matches what its code actually needs. ## Reading this PR It's large — 257 files — because it's a migration. The commits are grouped and meant to be read in order: 1. **Platform packages** — the build mechanism, and the seven platform packages it produces. 2. **Database packages, extension packs, the `prisma` command** — completes the set of 17. 3. **Generated imports become configurable** — one place decides which names get written, with today's names still the default. 4. **Database-family symmetry, publishing the map, the identity checks.** 5. **One package per application** — the three converted examples, the re-exports they proved necessary, and the counting check. 6. **Migration files follow the project too.** One thing worth knowing while reading: re-exporting a package republishes all of its sub-paths, not just the one that was needed. This PR adds 115 published sub-paths across the three database packages. Two candidates were dropped for exactly that reason — see below. ## Alternatives considered **Let an application install platform packages alongside its facade.** Nothing would need re-exporting and the facades would stay thinner. Rejected: an application would again juggle several Prisma dependencies whose correct combination it maintains by hand, and getting it wrong — upgrading one and not the other — produces the silent two-copies failure above. Re-exporting costs a generated line and nothing at runtime. **Re-export everything an application might plausibly want.** Rejected in review: because re-exporting brings a package's entire sub-path surface, generosity is expensive and hard to undo. Migration tooling (54 sub-paths) was dropped because its only users are extension packs, which install platform packages anyway; the SQL driver re-export was dropped because nothing imported it at all. What remains is what a converted example actually needed. **Flip the publish list in this same PR.** Rejected: it would mix "does the new surface work" with "is it safe to stop publishing 50 packages" in one review. The switch is mechanical once this lands, and gets its own change. ## Verification `build`, `typecheck` (156 tasks), `test:packages` (1077 files / 14087 tests), `test:e2e`, `lint`, `lint:deps`, `lint:docs`, `lint:manifests`, `check:publish-deps`, `check:clean-tree`, `lint:casts` and `lint:throws` (no new instances), `test:scripts`, coverage, the tarball-install suites, and regenerating every committed artifact leaves the tree unchanged. Known-unstable and unrelated to this change: the `relation-mode-gh-*` port suites (TML-3140), and several test timeouts that are too tight under load. ## Follow-ups TML-3124 switch the publish list · TML-3127 build cache can validate a stale published package on CI · TML-3140 unstable port suites · TML-3141 a test-helper sub-path reaches a package that is never published. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Added consolidated public ORM packages for PostgreSQL, MongoDB, SQLite, framework tooling, database targets, and extensions. - Generated contracts, migrations, and scaffolds now adapt imports to the consuming project’s package surface. - Added facade-provided `prisma-next` CLI access and consolidated migration entrypoints. - **Documentation** - Updated installation, package naming, public entrypoint, and migration scaffolding guidance. - **Tests** - Added coverage for package installation, exports, CLI behavior, module identity, and import compatibility. - **Chores** - Added checks preventing incompatible internal and public package imports. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> | 2 个月前 |
| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
| 10 天前 | ||
| 1 个月前 | ||
| 4 个月前 | ||
| 1 个月前 | ||
| 4 个月前 | ||
| 4 个月前 | ||
| 4 个月前 | ||
| 4 个月前 | ||
| 10 天前 | ||
| 10 天前 | ||
| 2 个月前 | ||
| 2 个月前 | ||
| 2 个月前 | ||
| 2 个月前 | ||
| 1 个月前 | ||
| 4 个月前 | ||
| 7 天前 | ||
| 7 天前 | ||
| 1 个月前 | ||
| 4 天前 | ||
| 1 个月前 | ||
| 1 个月前 | ||
| 1 个月前 | ||
| 1 个月前 | ||
| 23 天前 | ||
| 2 个月前 | ||
| 4 个月前 | ||
| 4 个月前 | ||
| 2 个月前 | ||
| 2 个月前 | ||
| 5 个月前 | ||
| 2 个月前 | ||
| 6 天前 | ||
| 1 个月前 | ||
| 7 天前 | ||
| 10 天前 | ||
| 10 天前 | ||
| 4 个月前 | ||
| 2 个月前 | ||
| 16 天前 | ||
| 16 天前 | ||
| 1 个月前 | ||
| 1 个月前 | ||
| 19 天前 | ||
| 19 天前 | ||
| 4 个月前 | ||
| 4 个月前 | ||
| 23 天前 | ||
| 2 个月前 | ||
| 8 个月前 | ||
| 2 个月前 | ||
| 2 个月前 | ||
| 1 个月前 | ||
| 1 个月前 | ||
| 1 个月前 | ||
| 24 天前 | ||
| 12 天前 | ||
| 24 天前 | ||
| 9 天前 | ||
| 8 个月前 | ||
| 3 个月前 | ||
| 3 个月前 | ||
| 10 天前 | ||
| 10 天前 | ||
| 20 天前 | ||
| 4 个月前 | ||
| 4 个月前 | ||
| 1 个月前 | ||
| 18 天前 | ||
| 1 个月前 | ||
| 1 个月前 | ||
| 1 个月前 | ||
| 1 个月前 | ||
| 4 个月前 | ||
| 23 天前 | ||
| 23 天前 | ||
| 4 个月前 | ||
| 2 个月前 |