@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 +1 -1
- package/skills/prismakit/SKILL.md +11 -6
- package/skills/prismakit/examples.md +3 -3
- package/skills/prismakit/reference.md +15 -7
- package/skills/prismakit-nestjs/SKILL.md +1 -2
- package/skills/prismakit-nestjs/examples.md +27 -15
- package/skills/prismakit-nestjs/reference.md +4 -4
package/package.json
CHANGED
|
@@ -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`
|
|
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`
|
|
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
|
-
| `
|
|
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` / `
|
|
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:
|
|
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
|
-
|
|
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?`, `
|
|
66
|
-
| `exists` | `where?`, `setCache?`, `cacheTags?` | `
|
|
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 `
|
|
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 '
|
|
55
|
+
import type { Prisma } from 'src/infrastructure/prisma/prisma-client';
|
|
56
56
|
|
|
57
|
-
|
|
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 '
|
|
65
|
-
import {
|
|
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
|
|
89
|
+
export class UserRepository extends defineAppRepo({
|
|
84
90
|
model: 'user',
|
|
85
|
-
scalarFields: Prisma.UserScalarFieldEnum,
|
|
86
91
|
cache: {
|
|
87
|
-
|
|
88
|
-
nullTtl: 60,
|
|
92
|
+
defaultSetCache: false, // auth lookups pass setCache explicitly
|
|
89
93
|
sensitiveFields: ['password'],
|
|
90
94
|
methods: { getFirst: { enabled: false } },
|
|
91
95
|
},
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
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`
|
|
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
|
|
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]
|