@prisma/orm-sqlite 8.0.0-rc.13-dev.5 → 8.0.0-rc.13-dev.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prisma/orm-sqlite",
3
- "version": "8.0.0-rc.13-dev.5",
3
+ "version": "8.0.0-rc.13-dev.6",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -10,17 +10,17 @@
10
10
  "skills"
11
11
  ],
12
12
  "dependencies": {
13
- "@prisma/orm-family-sql": "8.0.0-rc.13-dev.5",
14
- "@prisma/orm-framework": "8.0.0-rc.13-dev.5",
15
- "@prisma/orm-target-sqlite": "8.0.0-rc.13-dev.5",
16
- "@prisma/orm-toolchain": "8.0.0-rc.13-dev.5",
13
+ "@prisma/orm-family-sql": "8.0.0-rc.13-dev.6",
14
+ "@prisma/orm-framework": "8.0.0-rc.13-dev.6",
15
+ "@prisma/orm-target-sqlite": "8.0.0-rc.13-dev.6",
16
+ "@prisma/orm-toolchain": "8.0.0-rc.13-dev.6",
17
17
  "pathe": "^2.0.3",
18
18
  "tinyglobby": "^0.2.17"
19
19
  },
20
20
  "devDependencies": {
21
- "@internal/sqlite": "8.0.0-rc.13-dev.5",
22
- "@repo/tsconfig": "8.0.0-rc.13-dev.5",
23
- "@repo/tsdown": "8.0.0-rc.13-dev.5",
21
+ "@internal/sqlite": "8.0.0-rc.13-dev.6",
22
+ "@repo/tsconfig": "8.0.0-rc.13-dev.6",
23
+ "@repo/tsdown": "8.0.0-rc.13-dev.6",
24
24
  "tsdown": "0.22.14",
25
25
  "typescript": "5.9.3"
26
26
  },
@@ -15,7 +15,7 @@ description: >-
15
15
  ORM 7 or earlier (schema.prisma + @prisma/client).
16
16
  metadata:
17
17
  library: '@prisma/orm-sqlite'
18
- library_version: '8.0.0-rc.13-dev.5'
18
+ library_version: '8.0.0-rc.13-dev.6'
19
19
  version: '2026-09-12'
20
20
  ---
21
21
 
@@ -70,7 +70,7 @@ You do **not** need a `collect()` / `toArray()` helper — `await` is enough. In
70
70
  const rows: Promise<User[]> = db.orm.public.User.select('id', 'email').all().toArray();
71
71
 
72
72
  // Iterate — decode and handle rows one at a time. Whether the raw rows are
73
- // also fetched incrementally depends on the façade; see *Streaming* below.
73
+ // also fetched incrementally depends on the `cursor` option; see *Streaming* below.
74
74
  for await (const user of db.orm.public.User.select('id', 'email').all()) {
75
75
  process(user);
76
76
  }
@@ -127,12 +127,12 @@ await db.close();
127
127
 
128
128
  ## Streaming
129
129
 
130
- Every read terminal (`.all()`, and `runtime.query(plan)` for a SQL-builder plan) returns an `AsyncIterableResult`, so `for await` is always available. What it buys you depends on the façade:
130
+ Every read terminal (`.all()`, and `runtime.query(plan)` for a SQL-builder plan) returns an `AsyncIterableResult`, so `for await` is always available. What it buys you depends on the `cursor` option, which `postgres()` and `postgresServerless()` (`@prisma/orm-postgres/serverless`) both accept:
131
131
 
132
- - **Long-lived `postgres()` façade** (the usual `db.ts`): the driver runs with cursors disabled. The full result set is fetched from the server before the first row is yielded; only *decoding* happens per row. `for await` therefore does not bound the memory held by the raw result. For very large sets, paginate (`.limit()` / `.offset()`, or `.orderBy(...).cursor(...)`) instead.
133
- - **Serverless façade** (`@prisma/orm-postgres/serverless`, one `connect()` per invocation): the driver reads through a server-side cursor in batches of 100 rows by default (`cursor: { batchSize }` on the façade options), so `for await` really does stream.
132
+ - **Default (cursors off), on a client and on a connection:** the full result set is fetched from the server before the first row is yielded; only *decoding* happens per row. `for await` therefore does not bound the memory held by the raw result. For very large sets, paginate (`.limit()` / `.offset()`, or `.orderBy(...).cursor(...)`) instead.
133
+ - **`cursor` set in the options of `postgres()` or `postgresServerless()` (`{ batchSize: 100 }`; `{}` or `{ batchSize: undefined }` for batches of 100; a `batchSize` that is not a positive integer fails the factory call; there is no flag that turns cursors off):** the driver reads through a server-side cursor in batches of that size, so `for await` over `db.orm...all()` or `db.runtime().query(plan)` really does stream, and an early `break` stops reading. In a Worker, put the option only on a second serverless client that the streaming paths open their connections from, and keep the serverless client every other path uses without it. On a connection with cursors on, finish or `break` the `for await` loop before sending another query through `db`: the cursor holds the connection's only database connection until the loop ends, so a query inside the loop waits forever. On a client, the same query takes another database connection from the pool. Behind Cloudflare Hyperdrive, reads with cursors on hang, so the streaming paths hang there and the other paths do not.
134
134
 
135
- There is no `.stream()` method on either façade.
135
+ There is no `.stream()` method on a client or on a connection.
136
136
 
137
137
  ## Prepared statements (Postgres, SQLite)
138
138
 
@@ -216,11 +216,10 @@ Target-specific pitfalls live in the per-target guides.
216
216
  - **A raw-SQL lane.** This one exists. Write whole-query raw SQL through the client's raw lane: ``db.raw.sql`SELECT ...`.returnsRow({ ... }).build()`` for rows, or `.affectedCount()` for a mutation's row count. Each declared column names the codec that decodes it, so the row stays typed. For an expression fragment inside a builder query, use `fns.raw` in a `.select(...)` callback instead.
217
217
  - **TypedSQL (`.sql` files compiled into typed callables).** Not implemented. For a repeated query, use `db.prepare(...)` (see *Prepared statements* above) or a function that returns the built plan and `db.runtime().query(plan)` at the call site. If you want a `.sql`-file compile path, file a feature request via `references/feedback.md`.
218
218
  - **`EXPLAIN` / query-plan inspection.** Prisma 8 does not expose an `.explain()` method. Workaround: connect a `pg.Pool` you control via the runtime's `pg:` binding (see `references/runtime.md`) and issue `EXPLAIN ANALYZE` through it. If you want a first-class plan-inspection surface, file a feature request via `references/feedback.md`.
219
- - **Cursor-backed streaming on the long-lived façade.** `for await` works everywhere, but on `postgres()` the raw result is fetched in full before iteration (see *Streaming* above); only the serverless façade reads through a cursor. Paginate for very large sets on the long-lived façade. If you want cursor streaming there, file a feature request via `references/feedback.md`.
220
219
  - **Multi-statement batching (Prisma-7-style `db.$transaction([call1, call2])`).** Prisma 8 runs each call sequentially. Workaround: wrap atomically-related work in `db.transaction(async (tx) => { ... })` on Postgres. If you want batch-as-array semantics, file a feature request via `references/feedback.md`.
221
220
  - **Mongo façade transactions.** `@internal/mongo/runtime` does not expose `db.transaction(...)`. Multi-document atomicity is not yet wrapped in the Prisma 8 Mongo façade. Workaround: use the MongoDB driver's session API directly if you control the client binding (`mongoClient:` option). File a feature request via `references/feedback.md` if you need a first-class façade surface.
222
221
  - **Mongo ORM aggregates.** No `.aggregate(...)` / `.groupBy(...)` on `db.orm.<root>`. Workaround: express aggregations through `db.query.from(...).group(...).build()` and `runtime.query(plan)`.
223
- - **Mongo filter helpers on the façade.** Rich filters (`.in`, ranges, boolean composition) currently import from `@prisma/orm-mongo/query-ast/execution` (`MongoFieldFilter`, etc.) — not re-exported on `@internal/mongo/runtime`. Workaround: use object equality `.where({ field: value })` where possible; import from the internal package only when necessary. Tracked alongside façade-completeness gaps in Linear `TML-2526`.
222
+ - **Mongo filter helpers on the façade.** Rich filters (`.in`, ranges, boolean composition) currently import from `@prisma/orm-mongo/query-ast/execution` (`MongoFieldFilter`, etc.) — not re-exported on `@internal/mongo/runtime`. Workaround: use object equality `.where({ field: value })` where possible; import from the internal package only when necessary. The façade does not re-export them yet.
224
223
  - **Automatic N+1 detection.** Prisma 8 does not warn when an `.include(...)` is missing. Workaround: be deliberate about `.include(...)` in code review; the `lints` middleware (see `references/runtime.md`) catches the more common authoring slips (missing `WHERE` on a `DELETE` / `UPDATE`, missing `LIMIT` on a `SELECT`).
225
224
 
226
225
  ## Reference Files
@@ -13,7 +13,8 @@ This skill covers the **runtime entry point** — `db.ts` — and how to compose
13
13
  - User wants to switch between the Postgres, SQLite, and Mongo façades.
14
14
  - User wants to wrap operations in `db.transaction(...)` (Postgres and SQLite).
15
15
  - User is running a one-off script (`tsx my-script.ts`, Node CLI, CI task) and the process won't exit after queries finish, or they need script teardown (`db.close()`, `await using`).
16
- - User mentions: *db.ts, postgres(), mongo(), middleware, lints, budgets, cache, query log, slow query, DATABASE_URL, .env, connection pool, poolOptions, dev vs prod, transactions, read replicas, multi-database, script won't exit, hangs, db.close, db.end, close connection, pool.end, await using*.
16
+ - User is deploying to a per-request runtime (Cloudflare Workers + Hyperdrive, AWS Lambda, Vercel Edge, Deno Deploy, Bun edge) and needs `postgresServerless()` with `postgres.connect({ url })` per request.
17
+ - User mentions: *db.ts, postgres(), mongo(), middleware, lints, budgets, cache, query log, slow query, DATABASE_URL, .env, connection pool, poolOptions, dev vs prod, transactions, read replicas, multi-database, script won't exit, hangs, db.close, db.end, close connection, pool.end, await using, serverless, edge, Cloudflare Workers, Hyperdrive, postgresServerless, connect per request*.
17
18
 
18
19
  ## When Not to Use
19
20
 
@@ -26,9 +27,9 @@ This skill covers the **runtime entry point** — `db.ts` — and how to compose
26
27
 
27
28
  ## Key Concepts
28
29
 
29
- - **`db.ts` is the runtime entry point.** Imports the runtime factory from the `@internal/<target>` façade (`@internal/postgres/runtime`, `@internal/sqlite/runtime`, or `@internal/mongo/runtime`), the contract artefacts (`contract.json` + the `Contract` type from `contract.d.ts`), and any middleware. Exports a `db` value the rest of your app imports.
30
+ - **`db.ts` is the runtime entry point.** Imports the runtime factory from the `@internal/<target>` façade (`@internal/postgres/runtime`, `@internal/sqlite/runtime`, or `@internal/mongo/runtime`), the contract artefacts (`contract.json` + the `Contract` type from `contract.d.ts`), and any middleware. Exports a `db` value the rest of your app imports. On a per-request runtime, `db.ts` imports `postgresServerless` from `@internal/postgres/serverless` instead and exports the serverless client `postgres`; see *Workflow — Serverless and per-request runtimes*.
30
31
  - **The façade's runtime factory is the only surface user-authored `db.ts` imports from.** Each factory is a *default* export. For Postgres: `import postgres from '@internal/postgres/runtime'`; SQLite: `import sqlite from '@internal/sqlite/runtime'`; Mongo: `import mongo from '@internal/mongo/runtime'`. The factory signature is `<Target><Contract>(options)` — a single type parameter (the `Contract` type from `contract.d.ts`), and one options object.
31
- - **Lazy connect.** The factory does not connect to the database synchronously. Static query surfaces (`db.sql`, `db.orm`) are available immediately; the driver / pool is instantiated on the first call that needs a runtime (or when you explicitly call `await db.connect({ url })`). This is why `db.ts` can be imported in modules that load before the env is ready.
32
+ - **Lazy connect.** The factory does not connect to the database synchronously. `db.sql` and `db.orm` are available immediately; the driver / pool is instantiated on the first call that needs a runtime (or when you explicitly call `await db.connect({ url })`, which binds the client to that URL), and the pool opens its first database connection on the first query. This is why `db.ts` can be imported in modules that load before the env is ready.
32
33
  - **Middleware composes in order.** The first middleware in the `middleware: [...]` array runs *outermost* — its `beforeQuery` sees the operation first, and `interceptQuery` hooks are consulted in registration order with the first non-`undefined` result winning. Register a cache first so it gets first claim on a hit. Every middleware's `afterQuery` still fires on a cache hit, with `result.source: 'middleware'`.
33
34
  - **`prisma.config.ts` vs `.env`.** The config (`definePrismaConfig({ orm: ormConfig({ contract, db, extensions, migrations }) })` — see `references/contract.md`) is for static project shape: contract path, installed extensions, migrations directory, default connection string. `.env` is for per-environment values (`DATABASE_URL`, secrets). Nothing reads `.env` on its own: the scaffolded `prisma.config.ts` starts with `import 'dotenv/config'`, and that import is what loads `.env` into `process.env` before the config (and the CLI running it) reads `process.env['DATABASE_URL']`. Keep the import; a config without it sees no `.env` values. Hardcoding `DATABASE_URL` in the config file leaks credentials and bypasses per-env overrides.
34
35
  - **Build-system / dev-server integration is a separate skill.** `vite dev` auto-emit lives in `references/build.md`. The runtime side (this skill) reads `contract.json` / `contract.d.ts` regardless of how they got onto disk, so the two skills compose cleanly.
@@ -63,7 +64,7 @@ The Mongo façade has the same construction shape — `import mongo from '@inter
63
64
 
64
65
  ## Workflow — Running as a script (teardown)
65
66
 
66
- The concept: short scripts that connect, query, then expect the process to exit will **hang on Postgres** because the façade-owned `pg.Pool` keeps Node's event loop alive. The data round-trip succeeds; the script never exits. Call `await db.close()` before the script returns (or use `await using` **at the top of a script module** so teardown runs when the module exits — see the block-scope warning below for why this matters).
67
+ The concept: short scripts that connect, query, then expect the process to exit will **hang on Postgres** because the `pg.Pool` owned by `postgres()` keeps Node's event loop alive. The data round-trip succeeds; the script never exits. Call `await db.close()` before the script returns (or use `await using` **at the top of a script module** so teardown runs when the module exits — see the block-scope warning below for why this matters).
67
68
 
68
69
  **Plain shape** — export `db` from `db.ts`, import it in the script, close at the end:
69
70
 
@@ -93,7 +94,9 @@ console.log(user);
93
94
  // db.close() runs automatically when the script module exits.
94
95
  ```
95
96
 
96
- ### `await using` is **block-scoped** — do not put it inside a request handler
97
+ ### `await using` on a `postgres()` client is **block-scoped** — do not put it inside a request handler
98
+
99
+ This rule is about the long-lived `postgres()` client. On a per-request runtime, `await using db = await postgres.connect({ url })` inside the handler is the correct pattern; see *Workflow — Serverless and per-request runtimes* below.
97
100
 
98
101
  This is the most important rule in this section. `await using db = postgres(...)` disposes when the *enclosing block* exits. In a script module, that block is the module body and disposal fires at process exit — fine. In a request handler, the enclosing block is the handler function, so disposal fires **after every request** — a fresh `pg.Pool` per call, TCP-connect storm, hot loop tearing connections up and down.
99
102
 
@@ -126,12 +129,47 @@ Servers (HTTP handlers, workers in a request loop) **do not call `db.close()`**
126
129
  **Semantics:**
127
130
 
128
131
  - **`close()` is idempotent.** Calling it twice is a no-op.
129
- - **`close()` is terminal.** There is no reconnect on a closed `db` — construct a new client if you need another connection. After close, `db.runtime()`, `db.connect(...)`, `db.transaction(...)`, and `db.prepare(...)` reject with `Error('<target> client is closed')` (e.g. `'Postgres client is closed'`, `'SQLite client is closed'`, `'Mongo client is closed'`).
132
+ - **`close()` is terminal.** There is no reconnect on a closed `db` — construct a new client if you need to reach the database again. After close, `db.runtime()`, `db.connect(...)`, `db.transaction(...)`, and `db.prepare(...)` reject with `Error('<target> client is closed')` (e.g. `'Postgres client is closed'`, `'SQLite client is closed'`, `'Mongo client is closed'`).
130
133
  - **`close()` does not abort in-flight queries.** `await` outstanding work before calling `close()`. Async iterators from `db.runtime().query(plan)` and `PreparedStatement` handles held after `close()` fail on their next call.
131
134
  - **Ownership.** `close()` releases only what the façade constructed (`pg.Pool` from `{ url }`, `MongoClient` from `{ url }` / `{ uri, dbName }`, SQLite handle from `{ path }`). If you supplied your own `pg.Pool` / `pg.Client` (Postgres `pg:` option), `mongodb.MongoClient` (Mongo `mongoClient:` option), or a pre-built `binding`, `db.close()` does **not** touch those — you own their lifecycle.
132
135
 
133
136
  **`db.end()` does not exist.** The universal `node-postgres` name is `pool.end()` on a `pg.Pool`; the Prisma 8 runtime client is not a `pg.Pool`. The right call is `await db.close()`.
134
137
 
138
+ ## Workflow — Serverless and per-request runtimes
139
+
140
+ The concept: a per-request runtime (Cloudflare Workers + Hyperdrive, AWS Lambda, Vercel Edge, Deno Deploy, Bun edge) must not keep a database connection at module scope. Use `postgresServerless()` from `@internal/postgres/serverless`. It returns a **serverless client**; name it `postgres`. It holds no database connection. In each request, open a **connection** named `db` with `await using db = await postgres.connect({ url })`. A connection owns one database connection, a `pg.Client`, and `await using` closes it when the handler returns. `db` has the members of a `postgres()` client except `connect`.
141
+
142
+ ```typescript
143
+ // src/prisma/db.ts — the serverless client: module scope, built once per isolate, holds no database connection
144
+ import postgresServerless from '@internal/postgres/serverless';
145
+ import type { Contract } from './contract.d';
146
+ import contractJson from './contract.json' with { type: 'json' };
147
+
148
+ export const postgres = postgresServerless<Contract>({ contractJson });
149
+
150
+ // src/worker.ts
151
+ import { postgres } from './prisma/db';
152
+
153
+ export default {
154
+ async fetch(request: Request, env: Env): Promise<Response> {
155
+ await using db = await postgres.connect({ url: env.HYPERDRIVE.connectionString });
156
+ const users = await db.orm.public.User.all();
157
+ return Response.json(users);
158
+ },
159
+ };
160
+ ```
161
+
162
+ The rule to remember: inside a request, `db` does everything the `db` from `postgres()` does, so any documented `db.orm...`, `db.sql...`, `db.raw...`, `db.transaction(...)`, `db.prepare(...)` or `db.runtime().query(...)` snippet works unchanged. Await every query before the `await using` scope ends: the connection closes when the scope ends, so a query returned from the scope without `await` (`return db.orm...` instead of `return await db.orm...`) fails when its rows are read, with `CONTRACT.MARKER_READ_FAILED` (whose cause is `DRIVER.NOT_CONNECTED`) under the default options, or `DRIVER.NOT_CONNECTED` after an earlier awaited query or with `verifyMarker: false`.
163
+
164
+ - **`connect` connects before it returns.** When the database refuses the connection, rejects the credentials, or does not answer within 20 seconds, `postgres.connect({ url })` rejects with `DRIVER.CONNECTION_FAILED` and leaves nothing open; an empty URL, a string that is not a URL, or a scheme other than `postgres://` or `postgresql://` rejects with `RUNTIME.BINDING_INVALID`. Handle an unreachable database at the `connect` call, not at the first query. Answer a request that needs no query, such as an unknown route, before `connect`, because `connect` opens a database connection whether or not a query follows.
165
+ - **Never call `connect` at module scope.** A connection opened there is shared by every request in the isolate, and fails in four ways: its database connection goes stale after the isolate idles, concurrent requests queue behind each other on one `pg.Client`, nothing closes it when a request ends, and Cloudflare Workers reject a socket used across requests.
166
+ - **`db.orm` is the default way to use the ORM.** Build `orm({ runtime: db.runtime(), context: db.context, collections })` only for custom collection classes, and build it inside the request.
167
+ - **Anything that takes a runtime gets `db.runtime()`.** That includes `orm({ runtime, ... })`, `withTransaction(runtime, fn)` and a prepared statement's `query(runtime, params)`. Do not pass `db` itself to `orm()`.
168
+ - **Inside `db.transaction(async (tx) => ...)`, run every query through `tx`.** A connection has one database connection, so inside `db.transaction(async (tx) => ...)` a query through `db` is not independent of the transaction; run every query through `tx`. A query that uses the connection's database connection directly, such as a `db.orm` read, a `db.orm` create of one row, an `updateAll(...)` or `deleteAll()`, or `db.runtime().query(...)`, runs inside the open transaction without saying so. An operation that asks for a database connection of its own, such as a `db.orm` `update(...)` or `delete()` of one row, `db.runtime().connection()`, a `db.orm` create that also writes related rows, or a nested `db.transaction(...)`, waits for the database connection the transaction holds, and the request hangs.
169
+ - **After `db.close()`** (or the end of the `await using` scope), `db.runtime()`, ORM queries, `db.transaction(...)` and `db.prepare(...)` fail with `DRIVER.NOT_CONNECTED`. Call `postgres.connect({ url })` again for a new connection.
170
+
171
+ How connections read rows, and the `cursor` option on the serverless client (with the Hyperdrive caveat), is in `references/queries.md` § *Streaming*.
172
+
135
173
  ## Workflow — Custom middleware (query log, slow-query warning)
136
174
 
137
175
  The concept: a middleware is a plain object with `name`, `familyId: 'sql'`, and any of the hooks `beforeQuery`, `interceptQuery`, `afterQuery`. There is no separate telemetry package — observe queries with `afterQuery`, which fires once per execution after the rows are consumed, with `result.latencyMs`, `result.rowCount`, and `result.source` (`'driver'` or `'middleware'` for a cache hit). The `SqlMiddleware` type comes from `@prisma/orm-postgres/family-runtime`. `examples/prisma-8-demo/src/prisma/slow-query-warning.ts` is the canonical example:
@@ -231,7 +269,7 @@ Order matters: `beforeQuery` runs in registration order for every middleware bef
231
269
 
232
270
  ## Workflow — Configure the connection
233
271
 
234
- The concept: the runtime takes one of three binding shapes — `url`, `pg` (a pre-constructed `pg.Pool` or `pg.Client`), or `binding` (an explicit kind tag). They're mutually exclusive. The `pg` form is for projects that already manage their own pool (e.g. a Lambda layer); `url` is the default. Pool tuning is `poolOptions.connectionTimeoutMillis` / `poolOptions.idleTimeoutMillis` — *not* `driverOptions`.
272
+ The concept: the runtime takes one of three binding shapes — `url`, `pg` (a pre-constructed `pg.Pool` or `pg.Client`), or `binding` (an explicit kind tag). They're mutually exclusive. The `pg` form is for projects that already manage their own pool (e.g. a Lambda layer); `url` is the default. Pool tuning is `poolOptions.connectionTimeoutMillis` / `poolOptions.idleTimeoutMillis` — *not* `driverOptions`. Reads are buffered by default; setting `cursor` (`{ batchSize: 100 }`; `{}` or `{ batchSize: undefined }` for batches of 100; a `batchSize` that is not a positive integer fails the factory call) makes them stream through a server-side cursor, and there is no flag that turns cursors off (see `references/queries.md` § *Streaming*). `postgresServerless()` takes the same `cursor` option.
235
273
 
236
274
  ```typescript
237
275
  // Default — URL string, factory constructs the pool.
@@ -242,6 +280,7 @@ postgres<Contract>({
242
280
  connectionTimeoutMillis: 20_000,
243
281
  idleTimeoutMillis: 30_000,
244
282
  },
283
+ // cursor: { batchSize: 100 }, // optional — stream reads in batches instead of buffering
245
284
  });
246
285
 
247
286
  // BYO pool — pass a pg.Pool you already created.
@@ -337,7 +376,7 @@ The runtime side (this skill) is the same regardless: `db.ts` reads `contract.js
337
376
  5. **Importing middleware from a non-existent package or subpath.** There is no `@internal/postgres/middleware` subpath and no `@internal/middleware-telemetry` package. `lints` / `budgets` / `SqlMiddleware` come from `@prisma/orm-postgres/family-runtime`; the cache comes from `@prisma/orm-extension-middleware-cache`; a query log or telemetry hook is a custom `afterQuery` middleware (above).
338
377
  6. **Confabulating lint / budget option names.** Lints take `severities` (with the five keys above), not `requireWhere` / `maxRowsWithoutLimit`. Budgets use `maxLatencyMs` (not `maxDurationMs`) plus `maxRows` / `defaultTableRows` / `tableRows`. When in doubt, read the source.
339
378
  7. **Switching targets without re-emitting.** The contract artefacts are target-shaped; emit after the target change.
340
- 8. **Script hangs after queries finish on Postgres.** The `pg.Pool` keeps Node's event loop alive. Solution: `await db.close()` before the script returns, or `await using db = postgres<Contract>(...)` at the top of a script module. Do not put `await using db = postgres(...)` inside a request handler — it's block-scoped and would close the pool after every request. The right server pattern is a module-level singleton in `db.ts` that lives for the process lifetime.
379
+ 8. **Script hangs after queries finish on Postgres.** The `pg.Pool` keeps Node's event loop alive. Solution: `await db.close()` before the script returns, or `await using db = postgres<Contract>(...)` at the top of a script module. Do not put `await using db = postgres(...)` inside a request handler — it's block-scoped and would close the pool after every request. The right server pattern is a module-level singleton in `db.ts` that lives for the process lifetime. On a per-request runtime, use `postgresServerless()` and `await using db = await postgres.connect({ url })` in the handler instead (see *Workflow — Serverless and per-request runtimes*).
341
380
 
342
381
  ## What Prisma 8 doesn't do yet
343
382