prisma-generator-express 1.62.3 → 1.63.0

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.
@@ -130,9 +130,17 @@ import type {
130
130
  import { parseQueryParams } from '../parseQueryParams${ext}'
131
131
  import { normalizePrefix, getEnv, sanitizeKeys, isPlainObject } from '../misc${ext}'
132
132
  import { buildModelOpenApi } from '../buildModelOpenApi${ext}'
133
- import { validateCountSourceWhere } from '../routeConfig${ext}'
133
+ import {
134
+ normalizeOperation,
135
+ resolveOperationVariantKey,
136
+ validateCountSourceWhere,
137
+ validateOperationConfig,
138
+ validateUpdateEachConfig,
139
+ } from '../routeConfig${ext}'
140
+ import type { NormalizedOperationConfig } from '../routeConfig${ext}'
134
141
  import { transformResult } from '../operationRuntime${ext}'
135
142
  import { mapError } from '../errorMapper${ext}'
143
+ import { formatGuardVariantResolutionError } from '../guardVariantError${ext}'
136
144
  import { mergePaginationConfig } from '../pagination${ext}'
137
145
  import { applyDroppedGuard } from '../projectionDefaults${ext}'
138
146
  import type { OpKind } from '../projectionDefaults${ext}'
@@ -154,14 +162,28 @@ type JsonLike =
154
162
  type OperationConfigLike<TEnv extends HonoEnvBase> = {
155
163
  before?: HonoBeforeHook<TEnv>[]
156
164
  after?: HonoAfterHook<TEnv>[]
157
- shape?: Record<string, unknown>
165
+ shape?: unknown
166
+ variants?: Record<
167
+ string,
168
+ {
169
+ shape?: unknown
170
+ before?: HonoBeforeHook<TEnv>[]
171
+ after?: HonoAfterHook<TEnv>[]
172
+ }
173
+ >
158
174
  pagination?: Partial<PaginationConfig>
159
175
  }
160
176
 
161
- const defaultOpConfig = Object.freeze({
162
- before: Object.freeze([]),
163
- after: Object.freeze([]),
164
- }) as unknown as OperationConfigLike<HonoEnvBase>
177
+ type NormalizedOp<TEnv extends HonoEnvBase> = NormalizedOperationConfig<
178
+ HonoBeforeHook<TEnv>,
179
+ HonoAfterHook<TEnv>
180
+ >
181
+
182
+ function normalizeHonoOperation<TEnv extends HonoEnvBase>(
183
+ config: OperationConfigLike<TEnv> | undefined,
184
+ ): NormalizedOp<TEnv> {
185
+ return normalizeOperation<HonoBeforeHook<TEnv>, HonoAfterHook<TEnv>>(config)
186
+ }
165
187
 
166
188
  type HandlerContext = Context<{ Variables: HonoInternalVariables }>
167
189
 
@@ -213,7 +235,7 @@ async function parseUpdateEachBodyMiddleware(c: HandlerContext): Promise<void> {
213
235
 
214
236
  function makeShapeMiddleware<TCtx, TPrisma, TEnv extends HonoEnvBase>(
215
237
  config: ${modelName}RouteConfig<TCtx, TPrisma, TEnv>,
216
- opConfig: OperationConfigLike<TEnv>,
238
+ opConfig: NormalizedOp<TEnv>,
217
239
  opKind: OpKind,
218
240
  ) {
219
241
  return async (c: Context<GeneratedHonoEnv<TEnv>>): Promise<void> => {
@@ -223,19 +245,31 @@ function makeShapeMiddleware<TCtx, TPrisma, TEnv extends HonoEnvBase>(
223
245
  const headerName = config.guard?.variantHeader || 'x-api-variant'
224
246
  const headerValue = c.req.header(headerName)
225
247
  const caller = config.guard?.resolveVariant?.(c) ?? headerValue ?? undefined
226
- if (caller) c.set('guardCaller', caller)
248
+ if (typeof caller === 'string') c.set('guardCaller', caller)
249
+
250
+ const resolution = resolveOperationVariantKey(opConfig.guardRouting, caller)
251
+ if (!resolution.ok) {
252
+ c.set('guardVariantFailure', resolution)
253
+ return
254
+ }
255
+
256
+ const resolvedKey =
257
+ opConfig.guardRouting.kind === 'named'
258
+ ? resolution.key
259
+ : undefined
260
+ if (resolvedKey !== undefined) c.set('guardVariantKey', resolvedKey)
227
261
 
228
- if (opConfig.shape) {
262
+ if (opConfig.guardShape) {
229
263
  if (!DROP_GUARD) {
230
- c.set('guardShape', opConfig.shape)
264
+ c.set('guardShape', opConfig.guardShape)
231
265
  } else {
232
266
  const resolveCtx = typeof config.resolveContext === 'function'
233
267
  ? () => (config.resolveContext as (ctx: Context<GeneratedHonoEnv<TEnv>>) => unknown | Promise<unknown>)(c)
234
268
  : undefined
235
269
 
236
270
  await applyDroppedGuard(
237
- opConfig.shape,
238
- caller,
271
+ opConfig.guardShape,
272
+ resolvedKey,
239
273
  resolveCtx,
240
274
  opKind,
241
275
  {
@@ -267,7 +301,7 @@ function makeShapeMiddleware<TCtx, TPrisma, TEnv extends HonoEnvBase>(
267
301
  }
268
302
 
269
303
  async function runBeforeHooks<TEnv extends HonoEnvBase>(
270
- hooks: HonoBeforeHook<TEnv>[],
304
+ hooks: readonly HonoBeforeHook<TEnv>[],
271
305
  c: Context<GeneratedHonoEnv<TEnv>>,
272
306
  ): Promise<Response | undefined> {
273
307
  for (const hook of hooks) {
@@ -278,7 +312,7 @@ async function runBeforeHooks<TEnv extends HonoEnvBase>(
278
312
  }
279
313
 
280
314
  async function runAfterHooks<TEnv extends HonoEnvBase>(
281
- hooks: HonoAfterHook<TEnv>[],
315
+ hooks: readonly HonoAfterHook<TEnv>[],
282
316
  c: Context<GeneratedHonoEnv<TEnv>>,
283
317
  ): Promise<Response | undefined> {
284
318
  for (const hook of hooks) {
@@ -377,7 +411,7 @@ export function ${routerFunctionName}<TCtx = unknown, TPrisma = any, TEnv extend
377
411
  }
378
412
 
379
413
  const handleRead = (
380
- opConfig: OperationConfigLike<TEnv>,
414
+ opConfig: NormalizedOp<TEnv>,
381
415
  handlerFn: (c: HandlerContext) => Promise<void>,
382
416
  parseFn: (c: HandlerContext) => Promise<void>,
383
417
  opKind: OpKind,
@@ -385,12 +419,27 @@ export function ${routerFunctionName}<TCtx = unknown, TPrisma = any, TEnv extend
385
419
  try {
386
420
  await parseFn(c as unknown as HandlerContext)
387
421
  await makeShapeMiddleware<TCtx, TPrisma, TEnv>(config, opConfig, opKind)(c)
388
- const { before = [], after = [] } = opConfig
389
- const beforeResp = await runBeforeHooks<TEnv>(before, c)
390
- if (beforeResp) return beforeResp
422
+ const operationBefore = await runBeforeHooks<TEnv>(opConfig.operationBefore, c)
423
+ if (operationBefore) return operationBefore
424
+
425
+ const failure = c.get('guardVariantFailure')
426
+ if (failure) {
427
+ throw new HTTPException(400, {
428
+ message: formatGuardVariantResolutionError(failure),
429
+ })
430
+ }
431
+
432
+ const key = c.get('guardVariantKey')
433
+ const variantHooks =
434
+ key !== undefined ? opConfig.variantHooks[key] : undefined
435
+
436
+ const variantBefore = await runBeforeHooks<TEnv>(variantHooks?.before ?? [], c)
437
+ if (variantBefore) return variantBefore
391
438
  await handlerFn(c as unknown as HandlerContext)
392
- const afterResp = await runAfterHooks<TEnv>(after, c)
393
- if (afterResp) return afterResp
439
+ const variantAfter = await runAfterHooks<TEnv>(variantHooks?.after ?? [], c)
440
+ if (variantAfter) return variantAfter
441
+ const operationAfter = await runAfterHooks<TEnv>(opConfig.operationAfter, c)
442
+ if (operationAfter) return operationAfter
394
443
  return sendResult(c as unknown as HandlerContext)
395
444
  } catch (error: unknown) {
396
445
  return sendError(c as unknown as HandlerContext, error)
@@ -398,28 +447,46 @@ export function ${routerFunctionName}<TCtx = unknown, TPrisma = any, TEnv extend
398
447
  }
399
448
 
400
449
  const handleWrite = (
401
- opConfig: OperationConfigLike<TEnv>,
450
+ opConfig: NormalizedOp<TEnv>,
402
451
  handlerFn: (c: HandlerContext) => Promise<void>,
403
452
  opKind: OpKind,
404
453
  ) => async (c: Context<GeneratedHonoEnv<TEnv>>): Promise<Response> => {
405
454
  try {
406
455
  await parseWriteBodyMiddleware(c as unknown as HandlerContext)
407
456
  await makeShapeMiddleware<TCtx, TPrisma, TEnv>(config, opConfig, opKind)(c)
408
- const { before = [], after = [] } = opConfig
409
- const beforeResp = await runBeforeHooks<TEnv>(before, c)
410
- if (beforeResp) return beforeResp
457
+ const operationBefore = await runBeforeHooks<TEnv>(opConfig.operationBefore, c)
458
+ if (operationBefore) return operationBefore
459
+
460
+ const failure = c.get('guardVariantFailure')
461
+ if (failure) {
462
+ throw new HTTPException(400, {
463
+ message: formatGuardVariantResolutionError(failure),
464
+ })
465
+ }
466
+
467
+ const key = c.get('guardVariantKey')
468
+ const variantHooks =
469
+ key !== undefined ? opConfig.variantHooks[key] : undefined
470
+
471
+ const variantBefore = await runBeforeHooks<TEnv>(variantHooks?.before ?? [], c)
472
+ if (variantBefore) return variantBefore
411
473
  await handlerFn(c as unknown as HandlerContext)
412
- const afterResp = await runAfterHooks<TEnv>(after, c)
413
- if (afterResp) return afterResp
474
+ const variantAfter = await runAfterHooks<TEnv>(variantHooks?.after ?? [], c)
475
+ if (variantAfter) return variantAfter
476
+ const operationAfter = await runAfterHooks<TEnv>(opConfig.operationAfter, c)
477
+ if (operationAfter) return operationAfter
414
478
  return sendResult(c as unknown as HandlerContext)
415
479
  } catch (error: unknown) {
416
480
  return sendError(c as unknown as HandlerContext, error)
417
481
  }
418
482
  }
419
483
 
420
- const opFor = <K extends keyof ${modelName}RouteConfig<TCtx, TPrisma, TEnv>>(key: K): OperationConfigLike<TEnv> => {
421
- return (config[key] as unknown as OperationConfigLike<TEnv> | undefined)
422
- ?? (defaultOpConfig as OperationConfigLike<TEnv>)
484
+ const opFor = <K extends keyof ${modelName}RouteConfig<TCtx, TPrisma, TEnv>>(
485
+ key: K,
486
+ ): NormalizedOp<TEnv> => {
487
+ const raw = config[key] as unknown as OperationConfigLike<TEnv> | undefined
488
+ validateOperationConfig(raw, '${modelName}.' + String(key))
489
+ return normalizeHonoOperation(raw)
423
490
  }
424
491
 
425
492
  ${readOpBlocks}
@@ -427,8 +494,10 @@ ${readOpBlocks}
427
494
  ${writeOpBlocks}
428
495
 
429
496
  if (config.updateEach) {
430
- const opConfig = (config.updateEach as unknown as OperationConfigLike<TEnv> | undefined) ?? (defaultOpConfig as OperationConfigLike<TEnv>)
431
- if ((!opConfig.before || opConfig.before.length === 0) && _env.NODE_ENV !== 'production') {
497
+ const rawUpdateEach = config.updateEach as unknown as OperationConfigLike<TEnv>
498
+ validateUpdateEachConfig(rawUpdateEach, '${modelName}.updateEach')
499
+ const opConfig = normalizeHonoOperation(rawUpdateEach)
500
+ if (opConfig.operationBefore.length === 0 && _env.NODE_ENV !== 'production') {
432
501
  console.warn(
433
502
  '[${modelName}Router] updateEach is enabled without a before hook. ' +
434
503
  'This endpoint bypasses guard shapes and should be protected by authentication middleware.',
@@ -439,12 +508,11 @@ ${writeOpBlocks}
439
508
  try {
440
509
  await parseUpdateEachBodyMiddleware(c as unknown as HandlerContext)
441
510
  await makeShapeMiddleware<TCtx, TPrisma, TEnv>(config, opConfig, 'noop')(c)
442
- const { before = [], after = [] } = opConfig
443
- const beforeResp = await runBeforeHooks<TEnv>(before, c)
444
- if (beforeResp) return beforeResp
511
+ const beforeResponse = await runBeforeHooks<TEnv>(opConfig.operationBefore, c)
512
+ if (beforeResponse) return beforeResponse
445
513
  await ${modelName}UpdateEach(c as unknown as HandlerContext)
446
- const afterResp = await runAfterHooks<TEnv>(after, c)
447
- if (afterResp) return afterResp
514
+ const afterResponse = await runAfterHooks<TEnv>(opConfig.operationAfter, c)
515
+ if (afterResponse) return afterResponse
448
516
  return sendResult(c as unknown as HandlerContext)
449
517
  } catch (error: unknown) {
450
518
  return sendError(c as unknown as HandlerContext, error)
@@ -21,6 +21,8 @@ const SHARED_FILES = [
21
21
  'scalarTypes.ts',
22
22
  'concurrency.ts',
23
23
  'projectionDefaults.ts',
24
+ 'guardVariantRouting.ts',
25
+ 'guardVariantError.ts',
24
26
  ]
25
27
 
26
28
  const EXPRESS_ONLY_FILES = [