@prismakit/cli 3.2.1 → 3.2.2

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": "@prismakit/cli",
3
- "version": "3.2.1",
3
+ "version": "3.2.2",
4
4
  "description": "CLI for PrismaKit — generate modules, validate compose, install agent skills",
5
5
  "license": "Apache-2.0",
6
6
  "engines": {
@@ -32,7 +32,7 @@ Violations are bugs. Enforce with `@prismakit/eslint-plugin` + this skill.
32
32
  | Reads/writes | `*Repository` from `createRepository` / Nest factories |
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` + `scalarFields` or loaded Prisma meta |
35
+ | Relations in `select` | `model` + Prisma meta loaded (or `scalarFields` when meta unavailable) |
36
36
  | ESLint | `prismakit.configs.recommended` |
37
37
 
38
38
  ## Layers
@@ -81,7 +81,7 @@ const cache = new RedisCacheAdapter({ prefix: 'myapp' });
81
81
  export const users = new UserRepoClass({ prisma, cache });
82
82
  ```
83
83
 
84
- `defineRepository` and `createPrismaRepository` are aliases of `createRepository`.
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.
85
85
 
86
86
  **NestJS apps:** use `createDefineRepo` / `defineAppRepo` with app-wide cache defaults instead — see skill `prismakit-nestjs`.
87
87
 
@@ -123,7 +123,8 @@ await users.getFirst({
123
123
  | `getMany` | array (`take` / `skip` / `orderBy`) |
124
124
  | `getManyPaginate` | `{ data, meta: { page, pageSize, totalItems, totalPages } }` |
125
125
  | `getManyCursor` | `{ data, nextCursor, hasMore }` |
126
- | `count` / `exists` | `{ count }` / `{ exists }` |
126
+ | `getThrowFirst` | first match; throws if missing |
127
+ | `count` / `exists` | `number` / `boolean` |
127
128
  | `aggregate` / `groupBy` | Prisma delegate results |
128
129
 
129
130
  `id` is `string` or `Record<string, string>` for composite PKs (object form for `@@id([a,b])`).
@@ -134,8 +135,11 @@ await users.getFirst({
134
135
 
135
136
  | Method | Default `invalidate` |
136
137
  |--------|----------------------|
137
- | `create` / `createMany` | `queries` |
138
- | `updateById` / `updateMany` / `upsert` / `deleteById` / `deleteMany` | `all` |
138
+ | `create` / `createMany` / `createManyAndReturn` | `queries` |
139
+ | `updateById` / `update` / `updateMany` / `updateManyAndReturn` | `all` |
140
+ | `upsert` / `upsertMany` | `all` |
141
+ | `deleteById` / `delete` / `deleteMany` | `all` |
142
+ | `queryRaw` / `executeRaw` | none (raw SQL — no cache) |
139
143
 
140
144
  ```typescript
141
145
  await users.updateById({
@@ -240,8 +244,9 @@ Allowed Prisma usage: `**/repositories/**`, `**/infrastructure/prisma/**`. Rules
240
244
 
241
245
  ## Observability
242
246
 
243
- - Core: `setTelemetry({ enabled, onEvent })` or Nest `telemetry` / `queryLog.slowThreshold`.
247
+ - Core: `setTelemetry({ enabled, onEvent, slowThreshold })` or Nest `telemetry` / `queryLog.slowThreshold`.
244
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`.
245
250
 
246
251
  ## Clean code
247
252
 
@@ -6,7 +6,7 @@ Copy-paste patterns for `@prismakit/core`. Contract: [SKILL.md](SKILL.md).
6
6
 
7
7
  ```typescript
8
8
  // src/modules/users/repositories/user.repository.ts
9
- import { Prisma } from '@prisma/client';
9
+ import { Prisma } from '@prisma/client'; // or generated client path for Prisma 7
10
10
  import { createRepository } from '@prismakit/core';
11
11
 
12
12
  const DAY = 86_400;
@@ -23,7 +23,7 @@ export const userSelectPresets = {
23
23
 
24
24
  export const UserRepoClass = createRepository({
25
25
  model: 'user',
26
- scalarFields: Prisma.UserScalarFieldEnum,
26
+ // scalarFields: optional when schemaPath/DMMF meta is loaded (default since 3.1)
27
27
  cache: {
28
28
  ttl: DAY,
29
29
  nullTtl: 60,
@@ -40,7 +40,7 @@ export type UserRepository = InstanceType<typeof UserRepoClass>;
40
40
 
41
41
  ```typescript
42
42
  // src/infrastructure/prisma/repos.ts
43
- import { PrismaClient } from '@prisma/client';
43
+ import { PrismaClient } from '@prisma/client'; // or generated client for Prisma 7
44
44
  import { RedisCacheAdapter } from '@prismakit/redis';
45
45
  import { UserRepoClass } from '../../modules/users/repositories/user.repository';
46
46
 
@@ -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
- Aliases: `defineRepository`, `createPrismaRepository`.
32
+ Alias: `defineRepository`. (`createPrismaRepository` is also an alias in core but maps to `createInjectableRepository` in `@prismakit/nestjs` — avoid it to prevent confusion.)
33
33
 
34
34
  `RepositoryDeps`: `{ prisma, cache?, registry?, autoCompose? }`.
35
35
 
@@ -58,12 +58,13 @@ All methods accept optional `tx`. Cached repos also accept cache fields (see bel
58
58
  |--------|------------|---------|
59
59
  | `getById` | `id`, `select?`, `lock?`, `setCache?` | `T \| null` |
60
60
  | `getThrowById` | same | `T` (throws if missing) |
61
- | `getFirst` | `where?`, `select?`, `lock?`, `setCache?`, `cacheTags?` | `T \| null` |
61
+ | `getFirst` | `where?`, `select?`, `lock?`, `setCache?`, `cacheTags?`, `orderBy?` | `T \| null` |
62
+ | `getThrowFirst` | same as `getFirst` | `T` (throws if missing) |
62
63
  | `getMany` | `where?`, `select?`, `orderBy?`, `take?`, `skip?`, `lock?`, `setCache?`, `cacheTags?` | `T[]` |
63
64
  | `getManyPaginate` | `where?`, `select?`, `orderBy?`, `page?`, `pageSize?`, `setCache?`, `cacheTags?` | `PaginatedResult<T>` |
64
65
  | `getManyCursor` | `where?`, `select?`, `orderBy?`, `cursor?`, `take?`, `skip?`, `setCache?`, `cacheTags?` | `CursorPage<T>`; with `cursor`, default `skip: 1` |
65
- | `count` | `where?`, `select?`, `setCache?`, `cacheTags?` | `{ count: number }` |
66
- | `exists` | `where?`, `setCache?`, `cacheTags?` | `{ exists: boolean }` |
66
+ | `count` | `where?`, `setCache?`, `cacheTags?` | `number` |
67
+ | `exists` | `where?`, `setCache?`, `cacheTags?` | `boolean` |
67
68
  | `aggregate` | Prisma aggregate args + `setCache?`, `cacheTags?` | delegate result |
68
69
  | `groupBy` | Prisma groupBy args + `setCache?`, `cacheTags?` | delegate result |
69
70
 
@@ -86,6 +87,13 @@ All methods accept optional `tx`. Cached repos also accept cache fields (see bel
86
87
  | `upsert` | `where`, `create`, `update`, `select?`, `invalidate?`, `tags?` | `all` | `T` |
87
88
  | `deleteById` | `id`, `select?`, `invalidate?`, `tags?` | `all` | `T` |
88
89
  | `deleteMany` | `where`, `invalidate?`, `tags?` | `all` | `{ count }` |
90
+ | `update` | `where`, `data`, `select?`, `invalidate?`, `tags?` | `all` | `T` |
91
+ | `delete` | `where`, `select?`, `invalidate?`, `tags?` | `all` | `T` |
92
+ | `createManyAndReturn` | `data[]`, `select?`, `skipDuplicates?`, `invalidate?`, `tags?` | `queries` | `T[]` |
93
+ | `updateManyAndReturn` | `where`, `data`, `select?`, `invalidate?`, `tags?` | `all` | `T[]` |
94
+ | `upsertMany` | `data[]`, `skipDuplicates?`, `invalidate?`, `tags?` | `all` | `{ count }` |
95
+ | `queryRaw` | `sql`, `...params` | — | raw result |
96
+ | `executeRaw` | `sql`, `...params` | — | `number` (affected rows) |
89
97
 
90
98
  Mutation `tags`: `string[] | null | undefined | ((result) => string[] | null | undefined)`.
91
99
 
@@ -112,7 +120,7 @@ await repo.invalidateCache({ id?: string; tags?: string[] });
112
120
  | `ttl` | `86400` | Entity TTL (seconds). |
113
121
  | `nullTtl` | — | Negative cache for null results. |
114
122
  | `sensitiveFields` | `['password']` | Selects containing these never cache. |
115
- | `methods` | — | Per-method `{ enabled?, ttl? }` for `getById`, `getThrowById`, `getFirst`, `getMany`, `getManyPaginate`. |
123
+ | `methods` | — | Per-method `{ enabled?, ttl? }` for `getById`, `getThrowById`, `getFirst`, `getThrowFirst`, `getMany`, `getManyPaginate`, `getManyCursor`, `count`, `exists`, `aggregate`, `groupBy`. |
116
124
  | `defaultSetCache` | `false` | Reads cache unless caller passes `setCache: false`. |
117
125
  | `stampede` | see below | Per-repo stampede overrides. |
118
126
  | `compression` | — | Hint for adapters (`'none' \| 'zstd' \| 'lz4'`). Redis adapter uses `'none' \| 'gzip'`. |
@@ -237,12 +245,13 @@ import { setTelemetry } from '@prismakit/core';
237
245
  setTelemetry({
238
246
  enabled: true,
239
247
  onEvent: (event) => { /* metrics */ },
248
+ slowThreshold: 500, // emits query.slow for queries ≥ this ms
240
249
  });
241
250
  ```
242
251
 
243
252
  | Type | When |
244
253
  |------|------|
245
- | `cache.hit` / `cache.miss` / `cache.bypass` / `cache.invalidate` | Cache-aside path |
254
+ | `cache.hit` / `cache.miss` / `cache.bypass` / `cache.invalidate` / `cache.error` | Cache-aside path |
246
255
  | `compose.start` / `compose.complete` | Auto-compose (`queryCount`, `durationMs`) |
247
256
  | `lock.acquired` / `lock.waited` / `lock.timeout` | Row locks |
248
257
  | `stampede.locked` / `stampede.waited` / `stampede.fallthrough` | Stampede protection |
@@ -312,7 +321,6 @@ const autoCompose = new AutoComposer(registry);
312
321
 
313
322
  const UserRepo = createRepository({
314
323
  model: 'user',
315
- scalarFields: Prisma.UserScalarFieldEnum,
316
324
  cache: { ttl: 86_400 },
317
325
  });
318
326
  const users = new UserRepo({ prisma, cache, registry, autoCompose });
@@ -23,7 +23,6 @@ pnpm add -D @prismakit/eslint-plugin @prismakit/cli
23
23
  import { Module } from '@nestjs/common';
24
24
  import { PrismaKitModule } from '@prismakit/nestjs';
25
25
  import { RedisCacheAdapter } from '@prismakit/redis';
26
- import { Prisma } from '@prisma/client';
27
26
 
28
27
  @Module({
29
28
  imports: [
@@ -244,7 +243,7 @@ Reference app: [starter-prismakit-nestjs](https://github.com/fikiap23/starter-pr
244
243
 
245
244
  ## Clean code (Nest)
246
245
 
247
- - One `defineRepo` binder under `src/infrastructure/prisma/`. Do not call `createDefineRepo` per feature.
246
+ - One `defineAppRepo` binder under `src/infrastructure/prisma/`. Do not call `createDefineRepo` per feature.
248
247
  - Feature modules own their repository providers; do not make every repo global.
249
248
  - Controllers stay HTTP-only: map DTO → service method. No repository calls in controllers.
250
249
  - Select presets live next to the repository (`minimal` / `general` / `withPassword`).
@@ -47,24 +47,30 @@ export class AppModule {}
47
47
 
48
48
  Prisma 5/6: pass `dmmf: Prisma.dmmf` instead of (or in addition to skipping) `schemaPath`. Prisma 7: `schemaPath` only.
49
49
 
50
- ## 2. TypeMap binder
50
+ ## 2. TypeMap binder with app-wide cache defaults
51
51
 
52
52
  ```typescript
53
- // src/infrastructure/prisma/define-repo.ts
53
+ // src/infrastructure/prisma/define-app-repo.ts
54
54
  import { createDefineRepo } from '@prismakit/nestjs';
55
- import type { Prisma } from '@prisma/client';
55
+ import type { Prisma } from 'src/infrastructure/prisma/prisma-client';
56
56
 
57
- export const defineRepo = createDefineRepo<Prisma.TypeMap>();
57
+ const DAY = 86_400;
58
+
59
+ export const defineAppRepo = createDefineRepo<Prisma.TypeMap>({
60
+ cache: {
61
+ ttl: DAY,
62
+ nullTtl: 60,
63
+ defaultSetCache: true,
64
+ },
65
+ });
58
66
  ```
59
67
 
60
68
  ## 3. Feature repository + select presets
61
69
 
62
70
  ```typescript
63
71
  // src/modules/users/repositories/user.repository.ts
64
- import { Prisma } from '@prisma/client';
65
- import { defineRepo } from '../../../infrastructure/prisma/define-repo';
66
-
67
- const DAY = 86_400;
72
+ import { Prisma } from 'src/infrastructure/prisma/prisma-client';
73
+ import { defineAppRepo } from 'src/infrastructure/prisma/define-app-repo';
68
74
 
69
75
  export const userSelectPresets = {
70
76
  minimal: { id: true } satisfies Prisma.UserSelect,
@@ -80,18 +86,24 @@ export const userSelectPresets = {
80
86
  } satisfies Prisma.UserSelect,
81
87
  };
82
88
 
83
- export const UserRepository = defineRepo({
89
+ export class UserRepository extends defineAppRepo({
84
90
  model: 'user',
85
- scalarFields: Prisma.UserScalarFieldEnum,
86
91
  cache: {
87
- ttl: DAY,
88
- nullTtl: 60,
92
+ defaultSetCache: false, // auth lookups pass setCache explicitly
89
93
  sensitiveFields: ['password'],
90
94
  methods: { getFirst: { enabled: false } },
91
95
  },
92
- lock: true,
93
- });
94
- export interface UserRepository extends InstanceType<typeof UserRepository> {}
96
+ }) {}
97
+ ```
98
+
99
+ ```typescript
100
+ // Simple cached repo — inherits app-wide defaults
101
+ import { defineAppRepo } from 'src/infrastructure/prisma/define-app-repo';
102
+
103
+ export class CategoryRepository extends defineAppRepo({
104
+ model: 'category',
105
+ cache: true,
106
+ }) {}
95
107
  ```
96
108
 
97
109
  ## 4. Feature module + thin controller + service
@@ -84,11 +84,11 @@ await this.tx.execTx<User, Prisma.TransactionClient>(async (tx) => { /* ... */ }
84
84
  | `defineInjectableRepository({ model, select, create, update, where, orderBy, payload, ... })` | TypeMap unavailable. Package aliases: `defineRepository`. |
85
85
  | `createInjectableRepository({ model, ... })` | Thin / untyped. Results `unknown` unless `toPayload` is supplied. Alias: `createPrismaRepository`. |
86
86
 
87
- `createDefineRepo` runtime options: `model`, `scalarFields?`, `primaryKey?`, `cache?`, `lock?`, `schemaPath?`.
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?`.
88
88
 
89
89
  When `cache` is set, the returned API includes `setCache` / `cacheTags` / `invalidate` / `tags` / `invalidateCache`. Otherwise those fields are omitted from the type (`HasCacheFromOptions`).
90
90
 
91
- `createDefineRepo` / `RepositoryApiFromTypeMap` includes the full runtime surface: `createMany`, `updateMany`, `upsert`, `deleteMany`, `lock` + `orderBy` on `getFirst`, `lock` on `getMany`, and composite-PK `id` on `*ById`. `primaryKey` is optional — composite `@@id` is read from schema meta.
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
92
 
93
93
  Export the instance type with interface merging so `cache` on options gates `setCache` (a same-name `type` alias collapses to `any`):
94
94
 
@@ -108,7 +108,7 @@ type Of<S> = S extends Prisma.UserSelect
108
108
 
109
109
  export const UserRepository = defineInjectableRepository({
110
110
  model: 'user',
111
- scalarFields: Prisma.UserScalarFieldEnum,
111
+ scalarFields: Prisma.UserScalarFieldEnum, // required for this escape hatch (meta not typed)
112
112
  select: null! as Prisma.UserSelect,
113
113
  create: null! as Prisma.UserCreateInput,
114
114
  update: null! as Prisma.UserUpdateInput,
@@ -135,7 +135,7 @@ Prefer importing Nest-only APIs from `@prismakit/nestjs` and core-only helpers f
135
135
  src/
136
136
  app.module.ts # PrismaKitModule.forRootAsync
137
137
  infrastructure/prisma/
138
- define-repo.ts # createDefineRepo<Prisma.TypeMap>()
138
+ define-app-repo.ts # createDefineRepo<Prisma.TypeMap>({ cache defaults })
139
139
  prisma.service.ts # client construction only
140
140
  modules/<feature>/
141
141
  <feature>.module.ts # providers: [Service, XRepository]