@prismakit/cli 3.2.2 → 4.0.1

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.
@@ -11,7 +11,7 @@ description: >-
11
11
 
12
12
  Framework-agnostic Prisma repository kit. Not a Prisma fork. Only repositories talk to Prisma.
13
13
 
14
- NestJS apps: also load skill `prismakit-nestjs` after this contract.
14
+ **Line:** 4.0 (pre-stable). NestJS apps: also load skill `prismakit-nestjs` after this contract.
15
15
 
16
16
  ## Non-negotiables
17
17
 
@@ -29,10 +29,10 @@ Violations are bugs. Enforce with `@prismakit/eslint-plugin` + this skill.
29
29
 
30
30
  | Required | How |
31
31
  |----------|-----|
32
- | Reads/writes | `*Repository` from `createRepository` / Nest factories |
32
+ | Reads/writes | `*Repository` from `createRepository` / Nest `createDefineRepo` |
33
33
  | Tx writes | `invalidate: 'none'` then `invalidateCache` after commit |
34
34
  | User-facing reads | `setCache: true` when the repo has cache config |
35
- | Relations in `select` | `model` + Prisma meta loaded (or `scalarFields` when meta unavailable) |
35
+ | Relations in `select` | `model` + Prisma meta loaded (`loadPrismaMetaFromSchema` / `loadPrismaMetaFromDmmf` or Nest `schemaPath` / `dmmf`) |
36
36
  | ESLint | `prismakit.configs.recommended` |
37
37
 
38
38
  ## Layers
@@ -47,7 +47,7 @@ Helpers may inject repositories — never the Prisma client.
47
47
 
48
48
  | Task | Use |
49
49
  |------|-----|
50
- | New repository | `createRepository` (core) or Nest `defineRepo` — see `prismakit-nestjs` |
50
+ | New repository | `createRepository` (core) or Nest `defineAppRepo` — see `prismakit-nestjs` |
51
51
  | User-facing get by id | `getThrowById` / `getById` + `setCache: true` |
52
52
  | Existence / uniqueness / auth | `getFirst` — **no** `setCache` |
53
53
  | List | `getMany` + `setCache: true` + optional `cacheTags` |
@@ -65,15 +65,18 @@ Helpers may inject repositories — never the Prisma client.
65
65
 
66
66
  ```typescript
67
67
  import { PrismaClient } from '@prisma/client';
68
- import { createRepository } from '@prismakit/core';
68
+ import { createRepository, loadPrismaMetaFromSchema } from '@prismakit/core';
69
69
  import { RedisCacheAdapter } from '@prismakit/redis';
70
70
 
71
+ loadPrismaMetaFromSchema('prisma/schema.prisma');
72
+ // Prisma 5/6: loadPrismaMetaFromDmmf(Prisma.dmmf)
73
+
71
74
  const DAY = 86_400;
72
75
 
73
76
  const UserRepoClass = createRepository({
74
77
  model: 'user',
75
78
  cache: { ttl: DAY, nullTtl: 60, sensitiveFields: ['password'] },
76
- lock: true, // table + columns resolved from Prisma schema meta
79
+ lock: true, // table + columns from Prisma meta
77
80
  });
78
81
 
79
82
  const prisma = new PrismaClient();
@@ -81,19 +84,16 @@ const cache = new RedisCacheAdapter({ prefix: 'myapp' });
81
84
  export const users = new UserRepoClass({ prisma, cache });
82
85
  ```
83
86
 
84
- `defineRepository` is an alias of `createRepository` in core. Note: `createPrismaRepository` is also an alias in core, but in `@prismakit/nestjs` it aliases `createInjectableRepository` instead — avoid using it to prevent confusion.
87
+ Public factory: **`createRepository` only** (no `defineRepository` / `createPrismaRepository` aliases).
85
88
 
86
- **NestJS apps:** use `createDefineRepo` / `defineAppRepo` with app-wide cache defaults instead — see skill `prismakit-nestjs`.
89
+ **NestJS apps:** use `createDefineRepo` / `defineAppRepo` — see skill `prismakit-nestjs`.
87
90
 
88
91
  | Option | Description |
89
92
  |--------|-------------|
90
- | `model` | Prisma client key (`prisma.user` → `'user'`). Needed for cache + compose. |
91
- | `scalarFields` | Usually `Prisma.XScalarFieldEnum`. **Optional** when `schemaPath` / DMMF meta is loaded (default since 3.1). |
93
+ | `model` | Prisma client key (`prisma.user` → `'user'`). **Required.** |
92
94
  | `cache` | `CacheOptions` or `true` (uses app defaults when bound via `createDefineRepo`). |
93
- | `lock` | `true` / client key / `@@map` table / `{ tableName, columns }`. |
94
- | `primaryKey` | Override only. Defaults to schema `@id` / `@@id` (composite `string[]`) or `id`. |
95
- | `schemaPath` | Path to `schema.prisma` when meta is not loaded globally. Default: `prisma/schema.prisma`. |
96
- | `getDelegate` | Optional. Defaults to `(c) => c[model]`. |
95
+ | `lock` | `true` (from meta) or `{ tableName, columns? }`. |
96
+ | `toPayload` | Optional payload mapper (default identity). |
97
97
 
98
98
  Put files under `**/repositories/**`. Keep Prisma client construction under `**/infrastructure/prisma/**`.
99
99
 
@@ -167,7 +167,7 @@ If Redis is down, `RedisCacheAdapter` **fails open** — queries still hit Prism
167
167
  | Auth, uniqueness, JWT lookup | omit / `false` |
168
168
  | Inside `tx` | ignored |
169
169
 
170
- Repository `cache` is the source of truth. Omit `cacheModels` (fail-open). An optional allowlist throws if a cached repo's model is missing from the list.
170
+ Repository `cache` is the source of truth. There is no Nest `cacheModels` allowlist in 4.0.
171
171
 
172
172
  `setCache` / `cacheTags` / `invalidate` / `invalidateCache` exist on the type **only** when the repo has `cache` config. Do not force them on uncached repos.
173
173
 
@@ -200,9 +200,9 @@ await posts.getThrowById({
200
200
  });
201
201
  ```
202
202
 
203
- Requirements: source repo has `model`; `scalarFields` **or** Prisma meta loaded (`loadPrismaMetaFromDmmf(Prisma.dmmf)` on Prisma 5/6, `loadPrismaMetaFromSchema('prisma/schema.prisma')` on Prisma 7); related model repos are registered.
203
+ Requirements: source repo has `model`; Prisma meta loaded; related model repos are registered.
204
204
 
205
- AutoComposer injects the target primary key into nested selects even if omitted. Relation field names resolve from schema / DMMF meta (`schemaPath` defaults to `prisma/schema.prisma`).
205
+ AutoComposer injects the target primary key into nested selects even if omitted. Relation field names resolve from schema / DMMF meta.
206
206
 
207
207
  ## Row locks
208
208
 
@@ -244,9 +244,10 @@ Allowed Prisma usage: `**/repositories/**`, `**/infrastructure/prisma/**`. Rules
244
244
 
245
245
  ## Observability
246
246
 
247
- - Core: `setTelemetry({ enabled, onEvent, slowThreshold })` or Nest `telemetry` / `queryLog.slowThreshold`.
248
- - Optional: `@prismakit/opentelemetry` → `createPrismaKitTelemetry({ slowThreshold })`.
249
- - Events: `cache.hit` / `cache.miss` / `cache.bypass` / `cache.invalidate` / `cache.error`, `compose.*`, `lock.*`, `stampede.*`, `query.complete` / `query.slow`.
247
+ - Core: `setTelemetry({ enabled, onEvent, slowThreshold })`
248
+ - Nest: `telemetry: { enabled, slowThreshold, onSlowQuery, onEvent }`
249
+ - Optional: `@prismakit/opentelemetry` `createPrismaKitTelemetry({ slowThreshold })`
250
+ - Events: `cache.hit` / `cache.miss` / `cache.bypass` / `cache.invalidate` / `cache.error`, `compose.*`, `lock.*`, `stampede.*`, `query.complete` / `query.slow`
250
251
 
251
252
  ## Clean code
252
253
 
@@ -23,7 +23,6 @@ export const userSelectPresets = {
23
23
 
24
24
  export const UserRepoClass = createRepository({
25
25
  model: 'user',
26
- // scalarFields: optional when schemaPath/DMMF meta is loaded (default since 3.1)
27
26
  cache: {
28
27
  ttl: DAY,
29
28
  nullTtl: 60,
@@ -202,7 +201,6 @@ import { MemoryCacheAdapter } from '@prismakit/memory';
202
201
 
203
202
  const UserRepo = createRepository({
204
203
  model: 'user',
205
- scalarFields: { id: 'id', email: 'email', name: 'name' },
206
204
  cache: { ttl: 60 },
207
205
  });
208
206
 
@@ -1,19 +1,19 @@
1
1
  # PrismaKit core reference
2
2
 
3
- API surface for `@prismakit/core` 3.x. Read [SKILL.md](SKILL.md) first.
3
+ API surface for `@prismakit/core` **4.0** (pre-stable). Read [SKILL.md](SKILL.md) first.
4
4
 
5
5
  ## Packages
6
6
 
7
7
  | Package | Role |
8
8
  |---------|------|
9
9
  | `@prismakit/core` | `createRepository`, AutoComposer, locks, pagination, `CacheAdapter` |
10
- | `@prismakit/redis` | `RedisCacheAdapter` |
10
+ | `@prismakit/redis` | `RedisCacheAdapter`, `createRedisJsonReviver` |
11
11
  | `@prismakit/memory` | `MemoryCacheAdapter` (tests / local) |
12
12
  | `@prismakit/opentelemetry` | Map telemetry → OTel metrics/spans |
13
13
  | `@prismakit/cli` | `prismakit generate / validate / skills` |
14
14
  | `@prismakit/eslint-plugin` | Repository-only data-access rules |
15
15
 
16
- Node ≥ 20. Line **3.2.x**. Install:
16
+ Node ≥ 20. Install:
17
17
 
18
18
  ```bash
19
19
  pnpm add @prismakit/core
@@ -29,7 +29,7 @@ pnpm add -D @prismakit/eslint-plugin @prismakit/cli
29
29
  createRepository(options) → new RepoClass(deps: RepositoryDeps)
30
30
  ```
31
31
 
32
- Alias: `defineRepository`. (`createPrismaRepository` is also an alias in core but maps to `createInjectableRepository` in `@prismakit/nestjs` — avoid it to prevent confusion.)
32
+ Public factory: **`createRepository` only**.
33
33
 
34
34
  `RepositoryDeps`: `{ prisma, cache?, registry?, autoCompose? }`.
35
35
 
@@ -37,14 +37,12 @@ Alias: `defineRepository`. (`createPrismaRepository` is also an alias in core bu
37
37
 
38
38
  | Field | Type | Notes |
39
39
  |-------|------|-------|
40
- | `model` | `string` | Client key. Required for cache + compose. |
41
- | `scalarFields` | `Record<string, string>` | `Prisma.XScalarFieldEnum`; inferred from meta if omitted. |
40
+ | `model` | `string` | Client key. **Required.** |
42
41
  | `cache` | `CacheOptions \| true` | `true` → `{ ttl: 86400, sensitiveFields: ['password'] }`. |
43
- | `lock` | `true \| string \| RepositoryLockConfig` | `true` / client key / Pascal name / `@@map` table. |
44
- | `schemaPath` | `string` | Lock/schema helpers when global meta is missing. |
45
- | `primaryKey` | `string \| string[]` | `*ById` + row locks. Default: meta PK or `id`. Composite → Prisma `a_b: { a, b }` where. |
46
- | `getDelegate` | `(client) => delegate` | Default `(c) => c[model]`. |
47
- | `toPayload` | `(data) => payload` | Default identity. Prefer typed factories over this. |
42
+ | `lock` | `true \| RepositoryLockConfig` | `true` resolves table/columns from Prisma meta. |
43
+ | `toPayload` | `(data) => payload` | Default identity. |
44
+
45
+ Scalars, primary key, and relations come from Prisma meta (`loadPrismaMetaFromSchema` / `loadPrismaMetaFromDmmf`).
48
46
 
49
47
  `RepositoryLockConfig`: `{ tableName: string; columns?: Record<string, string> }`.
50
48
 
@@ -120,10 +118,10 @@ await repo.invalidateCache({ id?: string; tags?: string[] });
120
118
  | `ttl` | `86400` | Entity TTL (seconds). |
121
119
  | `nullTtl` | — | Negative cache for null results. |
122
120
  | `sensitiveFields` | `['password']` | Selects containing these never cache. |
123
- | `methods` | — | Per-method `{ enabled?, ttl? }` for `getById`, `getThrowById`, `getFirst`, `getThrowFirst`, `getMany`, `getManyPaginate`, `getManyCursor`, `count`, `exists`, `aggregate`, `groupBy`. |
121
+ | `methods` | — | Per-method `{ enabled?, ttl? }`. |
124
122
  | `defaultSetCache` | `false` | Reads cache unless caller passes `setCache: false`. |
125
123
  | `stampede` | see below | Per-repo stampede overrides. |
126
- | `compression` | | Hint for adapters (`'none' \| 'zstd' \| 'lz4'`). Redis adapter uses `'none' \| 'gzip'`. |
124
+ | `strictInvalidation` | `false` | When true, invalidation failures rethrow. |
127
125
 
128
126
  ### `InvalidateMode`
129
127
 
@@ -133,11 +131,6 @@ await repo.invalidateCache({ id?: string; tags?: string[] });
133
131
  | `entity` | Entity keys only |
134
132
  | `queries` | Query index only |
135
133
  | `none` | Skip — **required inside transactions** |
136
- | `stale` | Core treats like entity+queries (adapter may layer SWR) |
137
-
138
- ### Allowlist
139
-
140
- `setRegisteredCacheModels(['user', 'product'])`. Empty/unset = fail-open. When set, a repo with `cache` whose `model` is missing throws at init.
141
134
 
142
135
  ### Key schema
143
136
 
@@ -150,7 +143,7 @@ await repo.invalidateCache({ id?: string; tags?: string[] });
150
143
  {prefix}:v2:repo:{model}:t:{tag}:__idx
151
144
  ```
152
145
 
153
- Redis payloads use tagged JSON for `Date`, `BigInt`, `Bytes`, and `Decimal` (`__date`, `__bigint`, ). Bump to v2 keys causes a one-time miss after upgrade.
146
+ Redis payloads use tagged JSON for `Date`, `BigInt`, `Bytes`, and `Decimal`. Pass `decimalFactory: (s) => new Prisma.Decimal(s)` on `RedisCacheAdapter` (and/or Nest `PrismaKitModule.forRoot`) so compose clone and cache hits return native Prisma scalars — `JSON.stringify` otherwise turns `Date`/`Decimal` into strings via `toJSON`.
154
147
 
155
148
  ### Debug
156
149
 
@@ -170,8 +163,6 @@ Hits/misses/bypasses via `cacheDebugStorage` from `@prismakit/core`.
170
163
  | `backoff` | `'exponential'` (`'fixed'` also valid) |
171
164
  | `totalTimeoutMs` | `3000` |
172
165
 
173
- In-process `singleflight` is also used. If Redis is down, fail open.
174
-
175
166
  ### Adapters
176
167
 
177
168
  **Redis** (`@prismakit/redis`):
@@ -184,6 +175,7 @@ new RedisCacheAdapter({
184
175
  prefix: 'myapp', // default 'prismakit'
185
176
  compression: 'gzip', // 'none' | 'gzip'
186
177
  compressionThresholdBytes: 1024,
178
+ decimalFactory: (s) => new Prisma.Decimal(s),
187
179
  });
188
180
  ```
189
181
 
@@ -193,11 +185,11 @@ new RedisCacheAdapter({
193
185
  new MemoryCacheAdapter({ prefix: 'test', maxSize: 1000, defaultTtl: 300 });
194
186
  ```
195
187
 
196
- Custom: implement `CacheAdapter` (`get`/`set`/`del`/`setNx`/`setWithIndex`/`invalidateByIndex`/`saddAndExpire`/`smembers`/`isReady`/`getPrefix` + `safe*` wrappers). `get`/`safeGet` **must** return a fresh copy (structuredClone / JSON). Prefer fail-open `safe*` semantics.
188
+ Custom: implement `CacheAdapter`. Prefer fail-open `safe*` semantics.
197
189
 
198
190
  ## Auto-compose
199
191
 
200
- `splitSelect` keeps scalars (+ FK fields from DMMF) for the Prisma query. Relation keys load via the target repository. Target PK is always injected into the nested select.
192
+ `splitSelect` keeps scalars (+ FK fields from meta) for the Prisma query. Relation keys load via the target repository. Target PK is always injected into the nested select.
201
193
 
202
194
  Load meta once at bootstrap:
203
195
 
@@ -208,8 +200,6 @@ loadPrismaMetaFromDmmf(Prisma.dmmf); // Prisma 5/6
208
200
  loadPrismaMetaFromSchema('prisma/schema.prisma'); // Prisma 7 (no Prisma.dmmf)
209
201
  ```
210
202
 
211
- Without meta, AutoComposer cannot resolve renamed relations; pass `schemaPath` or `dmmf`. Relation field → registry key is source-scoped (`Category.products` vs `Tag.products` can target different models).
212
-
213
203
  Validate: `npx prismakit validate --auto-register` or `assertSelectComposeValid`.
214
204
 
215
205
  ### `ComposeOptions` (global via `setComposeOptions`)
@@ -219,7 +209,6 @@ Validate: `npx prismakit validate --auto-register` or `assertSelectComposeValid`
219
209
  | `maxDepth` | `10` | Max relation nesting. |
220
210
  | `parallel` | `true` | Same-level relations via `Promise.all`. |
221
211
  | `setCache` | `true` | Nested fetches pass `setCache: true` unless parent has `tx` / `setCache: false`. |
222
- | `tx` | — | Per-call only; forwarded to nested `getMany`. |
223
212
 
224
213
  Related repos must be registered on `RepositoryRegistry`.
225
214
 
@@ -235,7 +224,7 @@ Related repos must be registered on `RepositoryRegistry`.
235
224
  | `nowait` | Fail immediately if locked. |
236
225
  | `skipLocked` | Skip locked rows. **Cannot** combine with `nowait`. |
237
226
 
238
- `lock: true` / `'wallet'` / `'Wallet'` / `'wallets'` resolve via Prisma meta when available, else `buildLockConfigFromSchema`.
227
+ `lock: true` resolves via Prisma meta. Explicit: `{ tableName, columns? }`.
239
228
 
240
229
  ## Telemetry
241
230
 
@@ -265,10 +254,6 @@ npx prismakit validate [--schema <path>] [--auto-register] [--no-assert]
265
254
  npx prismakit help
266
255
  ```
267
256
 
268
- Default generate output: `src/modules/<kebab>/repositories/<kebab>.repository.ts`.
269
-
270
- `--full` emits Nest module + controller + service + select/where types. Register `*Module` in `app.module.ts` afterwards.
271
-
272
257
  ## ESLint
273
258
 
274
259
  ```js
@@ -276,8 +261,6 @@ import prismakit from '@prismakit/eslint-plugin';
276
261
  export default [prismakit.configs.recommended];
277
262
  ```
278
263
 
279
- `recommended` turns all rules on at **error**.
280
-
281
264
  | Rule | Forbids |
282
265
  |------|---------|
283
266
  | `prismakit/no-prisma-service-outside-repos` | Inject/reference `PrismaService` / `PrismaClient` outside allowlist |
@@ -285,23 +268,15 @@ export default [prismakit.configs.recommended];
285
268
  | `prismakit/require-transaction-service` | `.$transaction` in feature code |
286
269
  | `prismakit/require-cached-repo-provider` | Cached repo class missing from Nest `providers` |
287
270
 
288
- Allowed path patterns (forward slashes):
289
-
290
- - `**/repositories/**`
291
- - `**/infrastructure/prisma/**`
292
- - `**/node_modules/**`
293
- - `packages/(core|nestjs|redis)/**` (PrismaKit monorepo)
294
-
295
- Mirror this layout rather than weakening the plugin.
271
+ Allowed path patterns: `**/repositories/**`, `**/infrastructure/prisma/**`.
296
272
 
297
273
  ## Bootstrap (plain Node)
298
274
 
299
275
  ```typescript
300
- import { Prisma, PrismaClient } from '@prisma/client';
276
+ import { PrismaClient } from '@prisma/client';
301
277
  import {
302
278
  createRepository,
303
279
  loadPrismaMetaFromSchema,
304
- setRegisteredCacheModels,
305
280
  setComposeOptions,
306
281
  setTelemetry,
307
282
  RepositoryRegistry,
@@ -311,7 +286,6 @@ import { RedisCacheAdapter } from '@prismakit/redis';
311
286
 
312
287
  const prisma = new PrismaClient();
313
288
  loadPrismaMetaFromSchema('prisma/schema.prisma');
314
- setRegisteredCacheModels(['user', 'post']);
315
289
  setComposeOptions({ maxDepth: 6, parallel: true, setCache: true });
316
290
  setTelemetry({ enabled: true, onEvent: (e) => console.debug('[pk]', e.type) });
317
291
 
@@ -26,8 +26,7 @@ Task Progress:
26
26
  - [ ] New model access goes through `createRepository` (core) or the app's Nest `defineRepo` binder — not a one-off Prisma call
27
27
  - [ ] File lives under `**/repositories/**` as `*.repository.ts`
28
28
  - [ ] `model` is the Prisma client key (`'user'`, not `'User'`)
29
- - [ ] `scalarFields` is set **or** Prisma meta is loaded at bootstrap (`dmmf` / `schemaPath`)
30
- - [ ] `cacheModels` is omitted unless the app wants an extra allowlist
29
+ - [ ] Prisma meta is loaded at bootstrap (`dmmf` / `schemaPath` / `loadPrismaMetaFrom*`)
31
30
 
32
31
  ## Selects
33
32
 
@@ -54,7 +53,7 @@ Task Progress:
54
53
 
55
54
  ## Locks
56
55
 
57
- - [ ] Repo declares `lock` (`true`, client key, table name, or config) before any call passes `lock`
56
+ - [ ] Repo declares `lock` (`true` or `{ tableName, columns }`) before any call passes `lock`
58
57
  - [ ] Every `lock: { mode }` call also passes `tx`
59
58
  - [ ] `skipLocked` is not combined with `nowait`
60
59
  - [ ] Locked transactions stay short
@@ -2,15 +2,17 @@
2
2
  name: prismakit-nestjs
3
3
  description: >-
4
4
  PrismaKit NestJS adapter: PrismaKitModule.forRoot/forRootAsync wiring, injectable repositories via
5
- createDefineRepo/defineInjectableRepository, TransactionService.execTx with afterCommit invalidation,
6
- and PRISMAKIT_* DI tokens. Use when working with @prismakit/nestjs, Nest feature modules that need
7
- Prisma data access, or Nest transactions and cache invalidation.
5
+ createDefineRepo (default) / createInjectableRepository (escape hatch), TransactionService.execTx
6
+ with afterCommit invalidation, and PRISMAKIT_* DI tokens. Use when working with @prismakit/nestjs,
7
+ Nest feature modules that need Prisma data access, or Nest transactions and cache invalidation.
8
8
  ---
9
9
 
10
10
  # PrismaKit NestJS
11
11
 
12
12
  Adapter for NestJS. The data-access contract (layering, cache, compose, locks, ESLint) lives in skill `prismakit` — follow it. This skill covers module wiring, injectable repos, and `TransactionService`.
13
13
 
14
+ **Line:** 4.0 (pre-stable). Default factory: **`createDefineRepo` / app `defineAppRepo`**.
15
+
14
16
  ## Bootstrap
15
17
 
16
18
  ```bash
@@ -28,10 +30,18 @@ import { RedisCacheAdapter } from '@prismakit/redis';
28
30
  imports: [
29
31
  PrismaKitModule.forRoot({
30
32
  prisma: prismaClient,
31
- cache: new RedisCacheAdapter({ prefix: 'myapp' }),
33
+ cache: new RedisCacheAdapter({
34
+ prefix: 'myapp',
35
+ decimalFactory: (s) => new Prisma.Decimal(s),
36
+ }),
32
37
  schemaPath: 'prisma/schema.prisma', // default; Prisma 5/6: dmmf: Prisma.dmmf
33
38
  validateCompose: true,
34
39
  compose: { maxDepth: 6, parallel: true, setCache: true },
40
+ telemetry: {
41
+ enabled: true,
42
+ slowThreshold: 500,
43
+ onEvent: (e) => console.debug(e.type),
44
+ },
35
45
  }),
36
46
  ],
37
47
  })
@@ -40,7 +50,7 @@ export class AppModule {}
40
50
 
41
51
  `forRootAsync` when cache/URL come from `ConfigService` — see [examples.md](examples.md).
42
52
 
43
- `schemaPath` defaults to `prisma/schema.prisma`. Always load Prisma meta (`dmmf` or `schemaPath`) so auto-compose and `lock: true` resolve FKs/`@@map` from the schema — no relation-alias map.
53
+ `schemaPath` defaults to `prisma/schema.prisma`. Always load Prisma meta (`dmmf` or `schemaPath`) so auto-compose and `lock: true` resolve FKs/`@@map` from the schema.
44
54
 
45
55
  ## Factory (one default)
46
56
 
@@ -95,14 +105,13 @@ export class AuditLogRepository extends defineAppRepo({
95
105
  }) {}
96
106
  ```
97
107
 
98
- `scalarFields` is **optional** when `schemaPath` or DMMF meta is loaded (default since 3.1). Omit it in new repos.
108
+ Per-repo options: `model` (required), `cache?`, `lock?: true | RepositoryLockConfig`, `toPayload?`.
99
109
 
100
- Escape hatches (do not use as the app default):
110
+ Escape hatch (do not use as the app default):
101
111
 
102
- - `defineInjectableRepository` from `@prismakit/nestjs` (package alias `defineRepository`) phantoms + payload HKT when TypeMap is unavailable.
103
- - `createInjectableRepository` without a types bag — thin, results are `unknown`. Alias: `createPrismaRepository`.
112
+ - `createInjectableRepository` thin Nest wrapper when TypeMap binding is unavailable (results are thinly typed).
104
113
 
105
- Do not import `defineRepo` from `@prismakit/nestjs` in apps that already bind `createDefineRepo` as `defineAppRepo`.
114
+ Removed in 4.0: `defineInjectableRepository`, `defineRepo`, `defineRepository`, `createPrismaRepository`, Nest `cacheModels`, Nest `queryLog`.
106
115
 
107
116
  ## Register and inject
108
117
 
@@ -200,7 +209,7 @@ await this.tx.execTx(
200
209
 
201
210
  When the repo options include `cache`, TypeScript exposes `setCache`, `cacheTags`, mutation `invalidate`/`tags`, and `invalidateCache`. Without `cache`, those fields are omitted — do not pass them.
202
211
 
203
- Repository `cache` is the source of truth. Omit `cacheModels` (fail-open). Pass an allowlist only if you want a second check.
212
+ Repository `cache` is the source of truth.
204
213
 
205
214
  `cache.defaultSetCache: true` makes user-facing reads cache by default; still pass `setCache: false` on auth/uniqueness.
206
215
 
@@ -219,12 +228,10 @@ Repository `cache` is the source of truth. Omit `cacheModels` (fail-open). Pass
219
228
 
220
229
  | Option | Use |
221
230
  |--------|-----|
222
- | `cacheModels` | Optional extra allowlist (omit — repo `cache` is enough) |
223
231
  | `validateCompose: true` | Assert compose-safe selects on boot |
224
232
  | `strictCachedRepos` | Fail boot if a `cache` repo class is not in Nest `providers` (default `true`) |
225
233
  | `compose` | `{ maxDepth, parallel, setCache }` |
226
- | `telemetry` | `{ enabled: true, onEvent }` or `createPrismaKitTelemetry()` from `@prismakit/opentelemetry` |
227
- | `queryLog` | `{ slowThreshold, onSlowQuery }` — enables telemetry / `query.slow` |
234
+ | `telemetry` | `{ enabled, slowThreshold, onSlowQuery, onEvent }` or `createPrismaKitTelemetry()` |
228
235
  | `autoRegisterModels` | `true` or `string[]` — stub repos for compose-only models |
229
236
 
230
237
  ## Scaffolding
@@ -2,7 +2,7 @@
2
2
 
3
3
  Production-shaped snippets. Contract: skill `prismakit` + [SKILL.md](SKILL.md).
4
4
 
5
- ## 1. App module (Redis, allowlist, slow-query log)
5
+ ## 1. App module (Redis, telemetry)
6
6
 
7
7
  ```typescript
8
8
  // src/app.module.ts
@@ -31,7 +31,8 @@ import { UserModule } from './modules/users/user.module';
31
31
  validateCompose: true,
32
32
  compose: { maxDepth: 6, parallel: true, setCache: true },
33
33
  autoRegisterModels: true,
34
- queryLog: {
34
+ telemetry: {
35
+ enabled: true,
35
36
  slowThreshold: 500,
36
37
  onSlowQuery: (e) => {
37
38
  console.warn(`Slow ${e.model}.${e.method}: ${e.durationMs}ms`);
@@ -1,6 +1,6 @@
1
1
  # PrismaKit NestJS reference
2
2
 
3
- API surface for `@prismakit/nestjs` 3.x. Repository methods, cache, compose, and locks are documented in skill `prismakit` (`reference.md` in that skill). This file covers the Nest adapter only.
3
+ API surface for `@prismakit/nestjs` **4.0** (pre-stable). Repository methods, cache, compose, and locks are documented in skill `prismakit` (`reference.md` in that skill). This file covers the Nest adapter only.
4
4
 
5
5
  ## `PrismaKitModuleOptions`
6
6
 
@@ -13,13 +13,11 @@ API surface for `@prismakit/nestjs` 3.x. Repository methods, cache, compose, and
13
13
  | `validateCompose` | no | When `true`, `assertSelectComposeValid` on module init. |
14
14
  | `strictCachedRepos` | no | Fail boot when a cached repo is missing from `providers`, or listed in two modules. Default `true`. |
15
15
  | `modulesRoot` | no | Directory scanned by `strictCachedRepos`. Default `src/modules`. |
16
- | `cacheModels` | no | Optional extra allowlist. Omit — repo `cache` is the source of truth. |
17
- | `compose` | no | `ComposeOptions`: `maxDepth` (default 10), `parallel` (default true), `setCache` (default true). `tx` is per-call only. |
18
- | `telemetry` | no | `{ enabled?: boolean; onEvent?: (event) => void }`. |
19
- | `queryLog` | no | `{ slowThreshold?: number; onSlowQuery?: (e) => void }`. Default threshold 500ms. Setting this enables telemetry. |
16
+ | `compose` | no | `ComposeOptions`: `maxDepth` (default 10), `parallel` (default true), `setCache` (default true). |
17
+ | `telemetry` | no | `{ enabled?, slowThreshold?, onSlowQuery?, onEvent? }`. |
20
18
  | `autoRegisterModels` | no | `true` = stub repos for all schema/DMMF models; `string[]` = those client keys only. |
21
19
 
22
- `queryLog.onSlowQuery` receives `{ model?, method?, durationMs, thresholdMs }` for `query.complete` events at/above the threshold.
20
+ `onSlowQuery` receives `{ model?, method?, durationMs, thresholdMs }` for slow queries. Setting `slowThreshold` / `onSlowQuery` enables telemetry unless `enabled: false`.
23
21
 
24
22
  ## Async config
25
23
 
@@ -42,6 +40,10 @@ PrismaKitModule.forRootAsync({
42
40
  prefix: config.get('CACHE_PREFIX') ?? 'myapp',
43
41
  }),
44
42
  schemaPath: 'prisma/schema.prisma',
43
+ telemetry: {
44
+ enabled: true,
45
+ slowThreshold: 500,
46
+ },
45
47
  }),
46
48
  });
47
49
  ```
@@ -68,67 +70,25 @@ execTx<T, TClient = unknown>(
68
70
 
69
71
  `TransactionOptions`: `{ maxWait?: number; timeout?: number; isolationLevel?: 'ReadUncommitted' | 'ReadCommitted' | 'RepeatableRead' | 'Serializable' | string }`.
70
72
 
71
- Type the client when useful:
72
-
73
- ```typescript
74
- await this.tx.execTx<User, Prisma.TransactionClient>(async (tx) => { /* ... */ });
75
- ```
76
-
77
73
  `afterCommit` runs only after `prisma.$transaction` resolves successfully.
78
74
 
79
75
  ## Repository factories
80
76
 
81
77
  | Factory | When |
82
78
  |---------|------|
83
- | `createDefineRepo<Prisma.TypeMap>()` then local `defineRepo({ model, ... })` | **Default** for apps. Zero phantoms; `model` + `scalarFields` + `cache` / `lock`. |
84
- | `defineInjectableRepository({ model, select, create, update, where, orderBy, payload, ... })` | TypeMap unavailable. Package aliases: `defineRepository`. |
85
- | `createInjectableRepository({ model, ... })` | Thin / untyped. Results `unknown` unless `toPayload` is supplied. Alias: `createPrismaRepository`. |
79
+ | `createDefineRepo<Prisma.TypeMap>()` then app `defineAppRepo({ model, ... })` | **Default** for apps. Zero phantoms; `model` + `cache` / `lock` / `toPayload`. |
80
+ | `createInjectableRepository({ model, ... })` | Low-level escape hatch. Results thinly typed unless `toPayload` is supplied. |
86
81
 
87
- `createDefineRepo` accepts app-wide defaults (`cache`, `schemaPath`) and per-repo options: `model`, `scalarFields?` (optional since 3.1), `primaryKey?`, `cache?` (`true` inherits app defaults), `lock?`, `schemaPath?`.
82
+ `createDefineRepo` accepts app-wide defaults (`cache`) and per-repo options: `model`, `cache?` (`true` inherits app defaults), `lock?`, `toPayload?`.
88
83
 
89
84
  When `cache` is set, the returned API includes `setCache` / `cacheTags` / `invalidate` / `tags` / `invalidateCache`. Otherwise those fields are omitted from the type (`HasCacheFromOptions`).
90
85
 
91
- `createDefineRepo` / `RepositoryApiFromTypeMap` includes the full runtime surface: `create`, `createMany`, `createManyAndReturn`, `update`, `updateById`, `updateMany`, `updateManyAndReturn`, `upsert`, `upsertMany`, `delete`, `deleteById`, `deleteMany`, `getThrowFirst`, `count`, `exists`, `aggregate`, `groupBy`, `getManyCursor`, `queryRaw`, `executeRaw`, `lock` + `orderBy` on `getFirst`, `lock` on `getMany`, and composite-PK `id` on `*ById`. `primaryKey` is optional — composite `@@id` is read from schema meta.
92
-
93
- Export the instance type with interface merging so `cache` on options gates `setCache` (a same-name `type` alias collapses to `any`):
86
+ Export the instance type with interface merging so `cache` on options gates `setCache`:
94
87
 
95
88
  ```typescript
96
89
  export interface UserRepository extends InstanceType<typeof UserRepository> {}
97
90
  ```
98
91
 
99
- ## `defineInjectableRepository` shape (escape hatch)
100
-
101
- ```typescript
102
- import { Prisma } from '@prisma/client';
103
- import { defineInjectableRepository } from '@prismakit/nestjs';
104
-
105
- type Of<S> = S extends Prisma.UserSelect
106
- ? Prisma.UserGetPayload<{ select: S }>
107
- : never;
108
-
109
- export const UserRepository = defineInjectableRepository({
110
- model: 'user',
111
- scalarFields: Prisma.UserScalarFieldEnum, // required for this escape hatch (meta not typed)
112
- select: null! as Prisma.UserSelect,
113
- create: null! as Prisma.UserCreateInput,
114
- update: null! as Prisma.UserUpdateInput,
115
- where: null! as Prisma.UserWhereInput,
116
- orderBy: null! as Prisma.UserOrderByWithRelationInput,
117
- payload: class {
118
- declare readonly _select: unknown;
119
- declare type: () => Of<this['_select']>;
120
- },
121
- cache: { ttl: 86_400, sensitiveFields: ['password'] },
122
- lock: true,
123
- });
124
- ```
125
-
126
- ## Re-exports from core
127
-
128
- `@prismakit/nestjs` re-exports: `AutoComposer`, `RepositoryRegistry`, `CacheAdapter`, repository option/instance types, `RepoPayloadHKT`, `ComposeOptions`, `TelemetryOptions`, `TelemetryEvent`, `loadPrismaMetaFromDmmf`, `loadPrismaMetaFromSchema`, `setComposeOptions`, `setTelemetry`.
129
-
130
- Prefer importing Nest-only APIs from `@prismakit/nestjs` and core-only helpers from `@prismakit/core`.
131
-
132
92
  ## Layout
133
93
 
134
94
  ```
@@ -22,7 +22,6 @@ Task Progress:
22
22
  - [ ] `prisma` is the shared client instance
23
23
  - [ ] Prisma meta is loaded: `dmmf: Prisma.dmmf` (Prisma 5/6) or `schemaPath: 'prisma/schema.prisma'` (Prisma 7)
24
24
  - [ ] Production cache uses `RedisCacheAdapter` with a stable `prefix`
25
- - [ ] `cacheModels` is omitted (repo `cache` is source of truth), or lists every cached model if an allowlist is used
26
25
  - [ ] `validateCompose: true` is on for apps that nest relations in `select`
27
26
  - [ ] Cached repo classes are in feature `providers` (`strictCachedRepos` fails boot otherwise)
28
27
 
@@ -32,7 +31,7 @@ Task Progress:
32
31
  - [ ] Feature repos call that binder — not `createInjectableRepository` unless types are intentionally thin
33
32
  - [ ] `export interface XRepository extends InstanceType<typeof XRepository> {}` (infers cache from options; do not use a same-name `type` alias)
34
33
  - [ ] Repo class is in `providers` and `exports` of the feature module
35
- - [ ] `lock: true` (or table/client key) is set when any call uses `lock`
34
+ - [ ] `lock: true` (or `{ tableName, columns }`) is set when any call uses `lock`
36
35
 
37
36
  ## DI
38
37
 
@@ -20,10 +20,13 @@ describe('cli generate templates', () => {
20
20
  expect(files).toHaveLength(1);
21
21
  expect(files[0].relativePath).toContain('repositories/product.repository.ts');
22
22
  expect(files[0].content).toContain("model: 'product'");
23
- expect(files[0].content).toContain('cache: {');
23
+ expect(files[0].content).toContain('cache: true');
24
24
  expect(files[0].content).not.toContain('getDelegate');
25
25
  expect(files[0].content).not.toContain('scalarFields');
26
- expect(files[0].content).toContain("from '@prismakit/nestjs'");
26
+ expect(files[0].content).toContain('defineAppRepo');
27
+ expect(files[0].content).toContain(
28
+ "from 'src/infrastructure/prisma/define-app-repo'",
29
+ );
27
30
  });
28
31
 
29
32
  it('emits full Nest module when full: true', () => {
@@ -44,7 +44,6 @@ export function findSkillsRoot(startDir: string): string | undefined {
44
44
  for (const candidate of [
45
45
  path.join(dir, 'skills'),
46
46
  dir,
47
- path.join(dir, 'templates', 'cursor-skills'),
48
47
  ]) {
49
48
  if (isSkillsRoot(candidate)) return candidate;
50
49
  }
package/src/templates.ts CHANGED
@@ -40,19 +40,16 @@ function renderRepository(
40
40
  prismaImport: string,
41
41
  base: string,
42
42
  ): GeneratedFile {
43
- const cacheBlock = cacheEnabled
44
- ? ` cache: {
45
- ttl: 86400,
46
- sensitiveFields: ['password'],
47
- defaultSetCache: true,
48
- },
49
- `
50
- : '';
43
+ const cacheBlock = cacheEnabled ? ` cache: true,\n` : '';
51
44
 
52
45
  const content = apply(
53
- `import { createInjectableRepository } from '@prismakit/nestjs';
46
+ `import { defineAppRepo } from 'src/infrastructure/prisma/define-app-repo';
54
47
 
55
- export class {{pascal}}Repository extends createInjectableRepository({
48
+ /**
49
+ * Bind once in infrastructure:
50
+ * export const defineAppRepo = createDefineRepo<Prisma.TypeMap>({ cache: { ... } });
51
+ */
52
+ export class {{pascal}}Repository extends defineAppRepo({
56
53
  model: '{{repoModel}}',
57
54
  {{cacheBlock}}}) {}
58
55
  `,