prisma-generator-express 1.62.1 → 1.62.3

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.
@@ -10,37 +10,79 @@ function pathExpr(suffix: string): string {
10
10
  return `\`\${basePath}${suffix}\``
11
11
  }
12
12
 
13
- function emitReadOp(meta: (typeof OPERATION_METADATA)[number], modelName: string): string {
13
+ function opKindFor(opName: string): string {
14
+ switch (opName) {
15
+ case 'findUnique':
16
+ case 'findUniqueOrThrow':
17
+ return 'readUnique'
18
+ case 'findMany':
19
+ case 'findFirst':
20
+ case 'findFirstOrThrow':
21
+ case 'findManyPaginated':
22
+ case 'count':
23
+ case 'aggregate':
24
+ case 'groupBy':
25
+ return 'read'
26
+ case 'create':
27
+ return 'create'
28
+ case 'createMany':
29
+ case 'createManyAndReturn':
30
+ return 'createMany'
31
+ case 'update':
32
+ return 'update'
33
+ case 'updateMany':
34
+ case 'updateManyAndReturn':
35
+ return 'updateMany'
36
+ case 'upsert':
37
+ return 'upsert'
38
+ case 'delete':
39
+ return 'delete'
40
+ case 'deleteMany':
41
+ return 'deleteMany'
42
+ default:
43
+ return 'noop'
44
+ }
45
+ }
46
+
47
+ function emitReadOp(
48
+ meta: (typeof OPERATION_METADATA)[number],
49
+ modelName: string,
50
+ ): string {
14
51
  const c = meta.name.charAt(0).toUpperCase() + meta.name.slice(1)
15
52
  const handlerName = `${modelName}${c}`
16
53
  const pathValue = pathExpr(meta.pathSuffix)
54
+ const opKind = opKindFor(meta.name)
17
55
 
18
56
  const postReadLine = meta.supportsPostRead
19
57
  ? meta.name === 'findMany'
20
58
  ? ` if (postReadsEnabled) {
21
59
  const postPath = basePath ? \`\${basePath}/read\` : '/read'
22
- instance.post(postPath, handleRead(opConfig, ${handlerName}, parseBodyAsQueryHook))
60
+ instance.post(postPath, handleRead(opConfig, ${handlerName}, parseBodyAsQueryHook, '${opKind}'))
23
61
  }`
24
- : ` if (postReadsEnabled) instance.post(path, handleRead(opConfig, ${handlerName}, parseBodyAsQueryHook))`
62
+ : ` if (postReadsEnabled) instance.post(path, handleRead(opConfig, ${handlerName}, parseBodyAsQueryHook, '${opKind}'))`
25
63
  : ''
26
64
 
27
65
  return ` if (isEnabled(config.${meta.configKey})) {
28
66
  const opConfig: OperationConfigLike = (config.${meta.configKey} as OperationConfigLike | undefined) ?? defaultOpConfig
29
67
  const path = ${pathValue}
30
- instance.get(path, handleRead(opConfig, ${handlerName}, parseQueryHook))
68
+ instance.get(path, handleRead(opConfig, ${handlerName}, parseQueryHook, '${opKind}'))
31
69
  ${postReadLine}
32
70
  }`
33
71
  }
34
72
 
35
- function emitWriteOp(meta: (typeof OPERATION_METADATA)[number], modelName: string): string {
73
+ function emitWriteOp(
74
+ meta: (typeof OPERATION_METADATA)[number],
75
+ modelName: string,
76
+ ): string {
36
77
  const c = meta.name.charAt(0).toUpperCase() + meta.name.slice(1)
37
78
  const handlerName = `${modelName}${c}`
38
79
  const pathValue = pathExpr(meta.pathSuffix)
80
+ const opKind = opKindFor(meta.name)
39
81
 
40
82
  return ` if (isEnabled(config.${meta.configKey})) {
41
83
  const opConfig: OperationConfigLike = (config.${meta.configKey} as OperationConfigLike | undefined) ?? defaultOpConfig
42
84
  const path = ${pathValue}
43
- instance.${meta.method}(path, handleWrite(opConfig, ${handlerName}))
85
+ instance.${meta.method}(path, handleWrite(opConfig, ${handlerName}, '${opKind}'))
44
86
  }`
45
87
  }
46
88
 
@@ -89,13 +131,15 @@ import type {
89
131
  PaginationConfig,
90
132
  } from '../routeConfig.target${ext}'
91
133
  import { parseQueryParams } from '../parseQueryParams${ext}'
92
- import { sanitizeKeys, normalizePrefix, getEnv } from '../misc${ext}'
134
+ import { sanitizeKeys, normalizePrefix, getEnv, isPlainObject } from '../misc${ext}'
93
135
  import { buildModelOpenApi } from '../buildModelOpenApi${ext}'
94
136
  import { validateCountSourceWhere } from '../routeConfig${ext}'
95
137
  import type { OperationContext } from '../operationRuntime${ext}'
96
138
  import { transformResult } from '../operationRuntime${ext}'
97
139
  import { HttpError, mapError } from '../errorMapper${ext}'
98
140
  import { mergePaginationConfig } from '../pagination${ext}'
141
+ import { applyDroppedGuard } from '../projectionDefaults${ext}'
142
+ import type { OpKind } from '../projectionDefaults${ext}'
99
143
  import { MODEL_FIELDS, MODEL_ENUMS } from './${modelName}Metadata${ext}'
100
144
 
101
145
  ${generateRouteConfigType(modelName, 'FastifyHookHandler', guardShapesImport, importStyle, 'fastify')}
@@ -156,26 +200,58 @@ function parseBodyAsQueryHook(request: FastifyRequest): void {
156
200
  (request as FastifyExtended).parsedQuery = sanitizeKeys(body as Record<string, unknown>)
157
201
  }
158
202
 
203
+ function buildResolveContext(
204
+ config: ${modelName}RouteConfig,
205
+ request: FastifyRequest,
206
+ ): (() => unknown | Promise<unknown>) | undefined {
207
+ if (typeof config.resolveContext !== 'function') return undefined
208
+ return () => (config.resolveContext as (r: FastifyRequest) => unknown | Promise<unknown>)(request)
209
+ }
210
+
159
211
  function makeShapeHook(
160
212
  config: ${modelName}RouteConfig,
161
213
  opConfig: OperationConfigLike,
162
- ): (request: FastifyRequest) => void {
163
- return (request: FastifyRequest) => {
214
+ opKind: OpKind,
215
+ ): (request: FastifyRequest) => Promise<void> {
216
+ return async (request: FastifyRequest) => {
164
217
  const fx = request as FastifyExtended
165
218
  const merged = mergePaginationConfig(config.pagination, opConfig.pagination)
166
- if (merged) {
167
- fx.routeConfig = { pagination: merged }
168
- }
219
+ if (merged) fx.routeConfig = { pagination: merged }
220
+
169
221
  const headerName = (config.guard?.variantHeader || 'x-api-variant').toLowerCase()
170
222
  const headerValue = request.headers[headerName]
171
223
  const caller = config.guard?.resolveVariant?.(request)
172
224
  ?? (Array.isArray(headerValue) ? headerValue[0] : headerValue)
173
225
  ?? undefined
174
- if (caller) {
175
- fx.guardCaller = caller
176
- }
177
- if (opConfig.shape && !DROP_GUARD) {
178
- fx.guardShape = opConfig.shape
226
+ if (caller) fx.guardCaller = caller
227
+
228
+ if (opConfig.shape) {
229
+ if (!DROP_GUARD) {
230
+ fx.guardShape = opConfig.shape
231
+ } else {
232
+ await applyDroppedGuard(
233
+ opConfig.shape,
234
+ caller,
235
+ buildResolveContext(config, request),
236
+ opKind,
237
+ {
238
+ readQuery: fx.parsedQuery,
239
+ writeBody: isPlainObject(request.body)
240
+ ? (request.body as Record<string, unknown>)
241
+ : undefined,
242
+ },
243
+ () => {
244
+ if (!fx.parsedQuery) fx.parsedQuery = {}
245
+ return fx.parsedQuery
246
+ },
247
+ () => {
248
+ if (!isPlainObject(request.body)) {
249
+ ;(request as unknown as { body: unknown }).body = {}
250
+ }
251
+ return request.body as Record<string, unknown>
252
+ },
253
+ )
254
+ }
179
255
  }
180
256
  }
181
257
  }
@@ -302,10 +378,11 @@ export async function ${routerFunctionName}<TCtx = unknown, TPrisma = any>(
302
378
  opConfig: OperationConfigLike,
303
379
  handlerFn: (req: FastifyRequest, reply: FastifyReply) => Promise<void>,
304
380
  parseFn: (req: FastifyRequest) => void,
381
+ opKind: OpKind,
305
382
  ) => async (request: FastifyRequest, reply: FastifyReply) => {
306
383
  try {
307
384
  parseFn(request)
308
- makeShapeHook(config, opConfig)(request)
385
+ await makeShapeHook(config, opConfig, opKind)(request)
309
386
  const { before = [], after = [] } = opConfig
310
387
  if (await runHooks(before, request, reply)) return
311
388
  await handlerFn(request, reply)
@@ -319,9 +396,10 @@ export async function ${routerFunctionName}<TCtx = unknown, TPrisma = any>(
319
396
  const handleWrite = (
320
397
  opConfig: OperationConfigLike,
321
398
  handlerFn: (req: FastifyRequest, reply: FastifyReply) => Promise<void>,
399
+ opKind: OpKind,
322
400
  ) => async (request: FastifyRequest, reply: FastifyReply) => {
323
401
  try {
324
- makeShapeHook(config, opConfig)(request)
402
+ await makeShapeHook(config, opConfig, opKind)(request)
325
403
  const { before = [], after = [] } = opConfig
326
404
  if (await runHooks(before, request, reply)) return
327
405
  await handlerFn(request, reply)
@@ -350,7 +428,7 @@ ${writeOpBlocks}
350
428
  if (!Array.isArray(request.body)) {
351
429
  throw new HttpError(400, 'updateEach body must be an array of { where, data } items')
352
430
  }
353
- makeShapeHook(config, opConfig)(request)
431
+ await makeShapeHook(config, opConfig, 'noop')(request)
354
432
  const { before = [], after = [] } = opConfig
355
433
  if (await runHooks(before, request, reply)) return
356
434
  await ${modelName}UpdateEach(request, reply)
@@ -10,24 +10,59 @@ function pathExpr(suffix: string): string {
10
10
  return `\`\${basePath}${suffix}\``
11
11
  }
12
12
 
13
+ function opKindFor(opName: string): string {
14
+ switch (opName) {
15
+ case 'findUnique':
16
+ case 'findUniqueOrThrow':
17
+ return 'readUnique'
18
+ case 'findMany':
19
+ case 'findFirst':
20
+ case 'findFirstOrThrow':
21
+ case 'findManyPaginated':
22
+ case 'count':
23
+ case 'aggregate':
24
+ case 'groupBy':
25
+ return 'read'
26
+ case 'create':
27
+ return 'create'
28
+ case 'createMany':
29
+ case 'createManyAndReturn':
30
+ return 'createMany'
31
+ case 'update':
32
+ return 'update'
33
+ case 'updateMany':
34
+ case 'updateManyAndReturn':
35
+ return 'updateMany'
36
+ case 'upsert':
37
+ return 'upsert'
38
+ case 'delete':
39
+ return 'delete'
40
+ case 'deleteMany':
41
+ return 'deleteMany'
42
+ default:
43
+ return 'noop'
44
+ }
45
+ }
46
+
13
47
  function emitReadOp(meta: (typeof OPERATION_METADATA)[number], modelName: string): string {
14
48
  const c = meta.name.charAt(0).toUpperCase() + meta.name.slice(1)
15
49
  const handlerName = `${modelName}${c}`
16
50
  const pathValue = pathExpr(meta.pathSuffix)
51
+ const opKind = opKindFor(meta.name)
17
52
 
18
53
  const postReadLine = meta.supportsPostRead
19
54
  ? meta.name === 'findMany'
20
55
  ? ` if (postReadsEnabled) {
21
56
  const postPath = basePath ? \`\${basePath}/read\` : '/read'
22
- app.post(postPath, handleRead(opConfig, ${handlerName}, parseBodyAsQueryMiddleware))
57
+ app.post(postPath, handleRead(opConfig, ${handlerName}, parseBodyAsQueryMiddleware, '${opKind}'))
23
58
  }`
24
- : ` if (postReadsEnabled) app.post(path, handleRead(opConfig, ${handlerName}, parseBodyAsQueryMiddleware))`
59
+ : ` if (postReadsEnabled) app.post(path, handleRead(opConfig, ${handlerName}, parseBodyAsQueryMiddleware, '${opKind}'))`
25
60
  : ''
26
61
 
27
62
  return ` if (isEnabled(config.${meta.configKey})) {
28
63
  const opConfig = opFor('${meta.configKey}')
29
64
  const path = ${pathValue}
30
- app.get(path, handleRead(opConfig, ${handlerName}, parseQueryMiddleware))
65
+ app.get(path, handleRead(opConfig, ${handlerName}, parseQueryMiddleware, '${opKind}'))
31
66
  ${postReadLine}
32
67
  }`
33
68
  }
@@ -36,11 +71,12 @@ function emitWriteOp(meta: (typeof OPERATION_METADATA)[number], modelName: strin
36
71
  const c = meta.name.charAt(0).toUpperCase() + meta.name.slice(1)
37
72
  const handlerName = `${modelName}${c}`
38
73
  const pathValue = pathExpr(meta.pathSuffix)
74
+ const opKind = opKindFor(meta.name)
39
75
 
40
76
  return ` if (isEnabled(config.${meta.configKey})) {
41
77
  const opConfig = opFor('${meta.configKey}')
42
78
  const path = ${pathValue}
43
- app.${meta.method}(path, handleWrite(opConfig, ${handlerName}))
79
+ app.${meta.method}(path, handleWrite(opConfig, ${handlerName}, '${opKind}'))
44
80
  }`
45
81
  }
46
82
 
@@ -92,12 +128,14 @@ import type {
92
128
  PaginationConfig,
93
129
  } from '../routeConfig.target${ext}'
94
130
  import { parseQueryParams } from '../parseQueryParams${ext}'
95
- import { normalizePrefix, getEnv, sanitizeKeys } from '../misc${ext}'
131
+ import { normalizePrefix, getEnv, sanitizeKeys, isPlainObject } from '../misc${ext}'
96
132
  import { buildModelOpenApi } from '../buildModelOpenApi${ext}'
97
133
  import { validateCountSourceWhere } from '../routeConfig${ext}'
98
134
  import { transformResult } from '../operationRuntime${ext}'
99
135
  import { mapError } from '../errorMapper${ext}'
100
136
  import { mergePaginationConfig } from '../pagination${ext}'
137
+ import { applyDroppedGuard } from '../projectionDefaults${ext}'
138
+ import type { OpKind } from '../projectionDefaults${ext}'
101
139
  import { MODEL_FIELDS, MODEL_ENUMS } from './${modelName}Metadata${ext}'
102
140
 
103
141
  ${generateRouteConfigType(modelName, 'HonoBeforeHook', guardShapesImport, importStyle, 'hono')}
@@ -176,18 +214,54 @@ async function parseUpdateEachBodyMiddleware(c: HandlerContext): Promise<void> {
176
214
  function makeShapeMiddleware<TCtx, TPrisma, TEnv extends HonoEnvBase>(
177
215
  config: ${modelName}RouteConfig<TCtx, TPrisma, TEnv>,
178
216
  opConfig: OperationConfigLike<TEnv>,
217
+ opKind: OpKind,
179
218
  ) {
180
- return (c: Context<GeneratedHonoEnv<TEnv>>): void => {
219
+ return async (c: Context<GeneratedHonoEnv<TEnv>>): Promise<void> => {
181
220
  const merged = mergePaginationConfig(config.pagination, opConfig.pagination)
182
- if (merged) {
183
- c.set('routeConfig', { pagination: merged })
184
- }
221
+ if (merged) c.set('routeConfig', { pagination: merged })
222
+
185
223
  const headerName = config.guard?.variantHeader || 'x-api-variant'
186
224
  const headerValue = c.req.header(headerName)
187
225
  const caller = config.guard?.resolveVariant?.(c) ?? headerValue ?? undefined
188
226
  if (caller) c.set('guardCaller', caller)
189
- if (opConfig.shape && !DROP_GUARD) {
190
- c.set('guardShape', opConfig.shape)
227
+
228
+ if (opConfig.shape) {
229
+ if (!DROP_GUARD) {
230
+ c.set('guardShape', opConfig.shape)
231
+ } else {
232
+ const resolveCtx = typeof config.resolveContext === 'function'
233
+ ? () => (config.resolveContext as (ctx: Context<GeneratedHonoEnv<TEnv>>) => unknown | Promise<unknown>)(c)
234
+ : undefined
235
+
236
+ await applyDroppedGuard(
237
+ opConfig.shape,
238
+ caller,
239
+ resolveCtx,
240
+ opKind,
241
+ {
242
+ readQuery: c.get('parsedQuery'),
243
+ writeBody: isPlainObject(c.get('body'))
244
+ ? (c.get('body') as Record<string, unknown>)
245
+ : undefined,
246
+ },
247
+ () => {
248
+ let target = c.get('parsedQuery')
249
+ if (!target) {
250
+ target = {}
251
+ c.set('parsedQuery', target)
252
+ }
253
+ return target
254
+ },
255
+ () => {
256
+ let target = c.get('body')
257
+ if (!isPlainObject(target)) {
258
+ target = {}
259
+ c.set('body', target)
260
+ }
261
+ return target as Record<string, unknown>
262
+ },
263
+ )
264
+ }
191
265
  }
192
266
  }
193
267
  }
@@ -306,10 +380,11 @@ export function ${routerFunctionName}<TCtx = unknown, TPrisma = any, TEnv extend
306
380
  opConfig: OperationConfigLike<TEnv>,
307
381
  handlerFn: (c: HandlerContext) => Promise<void>,
308
382
  parseFn: (c: HandlerContext) => Promise<void>,
383
+ opKind: OpKind,
309
384
  ) => async (c: Context<GeneratedHonoEnv<TEnv>>): Promise<Response> => {
310
385
  try {
311
386
  await parseFn(c as unknown as HandlerContext)
312
- makeShapeMiddleware<TCtx, TPrisma, TEnv>(config, opConfig)(c)
387
+ await makeShapeMiddleware<TCtx, TPrisma, TEnv>(config, opConfig, opKind)(c)
313
388
  const { before = [], after = [] } = opConfig
314
389
  const beforeResp = await runBeforeHooks<TEnv>(before, c)
315
390
  if (beforeResp) return beforeResp
@@ -325,10 +400,11 @@ export function ${routerFunctionName}<TCtx = unknown, TPrisma = any, TEnv extend
325
400
  const handleWrite = (
326
401
  opConfig: OperationConfigLike<TEnv>,
327
402
  handlerFn: (c: HandlerContext) => Promise<void>,
403
+ opKind: OpKind,
328
404
  ) => async (c: Context<GeneratedHonoEnv<TEnv>>): Promise<Response> => {
329
405
  try {
330
406
  await parseWriteBodyMiddleware(c as unknown as HandlerContext)
331
- makeShapeMiddleware<TCtx, TPrisma, TEnv>(config, opConfig)(c)
407
+ await makeShapeMiddleware<TCtx, TPrisma, TEnv>(config, opConfig, opKind)(c)
332
408
  const { before = [], after = [] } = opConfig
333
409
  const beforeResp = await runBeforeHooks<TEnv>(before, c)
334
410
  if (beforeResp) return beforeResp
@@ -362,7 +438,7 @@ ${writeOpBlocks}
362
438
  app.post(path, async (c: Context<GeneratedHonoEnv<TEnv>>): Promise<Response> => {
363
439
  try {
364
440
  await parseUpdateEachBodyMiddleware(c as unknown as HandlerContext)
365
- makeShapeMiddleware<TCtx, TPrisma, TEnv>(config, opConfig)(c)
441
+ await makeShapeMiddleware<TCtx, TPrisma, TEnv>(config, opConfig, 'noop')(c)
366
442
  const { before = [], after = [] } = opConfig
367
443
  const beforeResp = await runBeforeHooks<TEnv>(before, c)
368
444
  if (beforeResp) return beforeResp
@@ -20,6 +20,7 @@ const SHARED_FILES = [
20
20
  'guardHelpers.ts',
21
21
  'scalarTypes.ts',
22
22
  'concurrency.ts',
23
+ 'projectionDefaults.ts',
23
24
  ]
24
25
 
25
26
  const EXPRESS_ONLY_FILES = [