prisma-generator-express 1.67.0 → 1.69.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.
Files changed (68) hide show
  1. package/README.md +223 -28
  2. package/dist/copy/operationDefinitions.d.ts +4 -1
  3. package/dist/copy/operationDefinitions.js +64 -3
  4. package/dist/copy/operationDefinitions.js.map +1 -1
  5. package/dist/generators/generateFastifyHandler.js +55 -9
  6. package/dist/generators/generateFastifyHandler.js.map +1 -1
  7. package/dist/generators/generateHonoHandler.js +44 -4
  8. package/dist/generators/generateHonoHandler.js.map +1 -1
  9. package/dist/generators/generateHonoOpenApi.d.ts +13 -0
  10. package/dist/generators/generateHonoOpenApi.js +70 -0
  11. package/dist/generators/generateHonoOpenApi.js.map +1 -0
  12. package/dist/generators/generateMcpApp.d.ts +10 -0
  13. package/dist/generators/generateMcpApp.js +113 -0
  14. package/dist/generators/generateMcpApp.js.map +1 -0
  15. package/dist/generators/generateModelMcp.d.ts +9 -0
  16. package/dist/generators/generateModelMcp.js +145 -0
  17. package/dist/generators/generateModelMcp.js.map +1 -0
  18. package/dist/generators/generateModelMetadata.js +9 -5
  19. package/dist/generators/generateModelMetadata.js.map +1 -1
  20. package/dist/generators/generateRouter.js +62 -56
  21. package/dist/generators/generateRouter.js.map +1 -1
  22. package/dist/generators/generateRouterFastify.js +56 -64
  23. package/dist/generators/generateRouterFastify.js.map +1 -1
  24. package/dist/generators/generateRouterHono.js +43 -411
  25. package/dist/generators/generateRouterHono.js.map +1 -1
  26. package/dist/generators/generateRouterPartsHono.d.ts +14 -0
  27. package/dist/generators/generateRouterPartsHono.js +180 -0
  28. package/dist/generators/generateRouterPartsHono.js.map +1 -0
  29. package/dist/generators/generateUnifiedHandler.js +36 -20
  30. package/dist/generators/generateUnifiedHandler.js.map +1 -1
  31. package/dist/index.d.ts +2 -0
  32. package/dist/index.js +101 -3
  33. package/dist/index.js.map +1 -1
  34. package/dist/utils/copyFiles.d.ts +1 -1
  35. package/dist/utils/copyFiles.js +20 -1
  36. package/dist/utils/copyFiles.js.map +1 -1
  37. package/dist/utils/mcpGate.d.ts +10 -0
  38. package/dist/utils/mcpGate.js +118 -0
  39. package/dist/utils/mcpGate.js.map +1 -0
  40. package/dist/utils/writeFileSafely.js +6 -0
  41. package/dist/utils/writeFileSafely.js.map +1 -1
  42. package/package.json +18 -3
  43. package/src/copy/buildModelOpenApi.ts +76 -126
  44. package/src/copy/docsRenderer.ts +14 -15
  45. package/src/copy/mcpRuntime.ts +1017 -0
  46. package/src/copy/operationDefinitions.ts +78 -4
  47. package/src/copy/operationPipeline.ts +267 -0
  48. package/src/copy/operationRuntime.ts +24 -14
  49. package/src/copy/operationSchemas.ts +2773 -0
  50. package/src/copy/pagination.ts +10 -2
  51. package/src/copy/routeConfig.hono.ts +34 -5
  52. package/src/copy/routeConfig.ts +1 -1
  53. package/src/copy/routerParts.ts +524 -0
  54. package/src/generators/generateFastifyHandler.ts +55 -9
  55. package/src/generators/generateHonoHandler.ts +44 -4
  56. package/src/generators/generateHonoOpenApi.ts +93 -0
  57. package/src/generators/generateMcpApp.ts +133 -0
  58. package/src/generators/generateModelMcp.ts +208 -0
  59. package/src/generators/generateModelMetadata.ts +16 -10
  60. package/src/generators/generateRouter.ts +62 -56
  61. package/src/generators/generateRouterFastify.ts +56 -64
  62. package/src/generators/generateRouterHono.ts +49 -459
  63. package/src/generators/generateRouterPartsHono.ts +220 -0
  64. package/src/generators/generateUnifiedHandler.ts +36 -21
  65. package/src/index.ts +116 -3
  66. package/src/utils/copyFiles.ts +36 -0
  67. package/src/utils/mcpGate.ts +157 -0
  68. package/src/utils/writeFileSafely.ts +6 -0
@@ -189,7 +189,6 @@ import { sanitizeKeys, normalizePrefix, getEnv, isPlainObject, resolveDropGuardE
189
189
  import { buildModelOpenApi } from '../buildModelOpenApi${ext}'
190
190
  import {
191
191
  normalizeOperation,
192
- resolveOperationVariantKey,
193
192
  validateCountSourceWhere,
194
193
  validateOperationConfig,
195
194
  validateUpdateEachConfig,
@@ -201,8 +200,14 @@ import type { NormalizedOperationConfig } from '../routeConfig${ext}'
201
200
  import type { OperationContext } from '../operationRuntime${ext}'
202
201
  import { transformResult } from '../operationRuntime${ext}'
203
202
  import { HttpError, mapError } from '../errorMapper${ext}'
204
- import { formatGuardVariantResolutionError } from '../guardVariantError${ext}'
205
- import type { GuardVariantResolution } from '../guardVariantRouting${ext}'
203
+ import {
204
+ routeOperation,
205
+ prepareGuardOperation,
206
+ settleStage,
207
+ classifyError,
208
+ memoizeContext,
209
+ type ArgsChannel,
210
+ } from '../operationPipeline${ext}'
206
211
  import { mergePaginationConfig } from '../pagination${ext}'
207
212
  import { acceptsNdjson, runNdjsonFindMany } from '../ndjson${ext}'
208
213
  import {
@@ -214,7 +219,6 @@ import {
214
219
  } from '../sse${ext}'
215
220
  import { relationModels } from '../relationModels${ext}'
216
221
  import { runAutoIncludeProgressive } from '../autoIncludeRuntime${ext}'
217
- import { applyDroppedGuard } from '../projectionDefaults${ext}'
218
222
  import type { OpKind } from '../projectionDefaults${ext}'
219
223
  import { MODEL_FIELDS, MODEL_ENUMS } from './${modelName}Metadata${ext}'
220
224
 
@@ -263,12 +267,34 @@ type LocalsBag = {
263
267
  guardShape?: Record<string, unknown>
264
268
  guardCaller?: string
265
269
  operationOverride?: RuntimeOperationOverride
266
- resolveOperationContext?: () => unknown | Promise<unknown>
270
+ getContext?: () => Promise<unknown>
267
271
  guardVariantKey?: string
268
- guardVariantFailure?: Extract<GuardVariantResolution, { ok: false }>
272
+ routeFailure?: { ok: false; failure: HttpError }
273
+ guardFailure?: { ok: false; failure: HttpError }
269
274
  data?: unknown
270
275
  }
271
276
 
277
+ function queryChannel(res: Response): ArgsChannel {
278
+ return {
279
+ read: () => readLocals(res).parsedQuery,
280
+ write: (next) => {
281
+ readLocals(res).parsedQuery = next
282
+ },
283
+ }
284
+ }
285
+
286
+ function bodyChannel(req: Request): ArgsChannel {
287
+ return {
288
+ read: () =>
289
+ isPlainObject(req.body)
290
+ ? (req.body as Record<string, unknown>)
291
+ : undefined,
292
+ write: (next) => {
293
+ req.body = next
294
+ },
295
+ }
296
+ }
297
+
272
298
  function normalizeExpressOperation(
273
299
  config: OperationConfigLike | undefined,
274
300
  ): NormalizedOp {
@@ -372,12 +398,15 @@ function variantAfterDispatcher(opConfig: NormalizedOp): RequestHandler {
372
398
 
373
399
  function requireVariantKey(): RequestHandler {
374
400
  return (_req, res, next) => {
375
- const failure = readLocals(res).guardVariantFailure
376
- if (!failure) {
377
- next()
401
+ const locals = readLocals(res)
402
+ try {
403
+ if (locals.routeFailure) settleStage(locals.routeFailure)
404
+ if (locals.guardFailure) settleStage(locals.guardFailure)
405
+ } catch (err) {
406
+ next(err)
378
407
  return
379
408
  }
380
- next(new HttpError(400, formatGuardVariantResolutionError(failure)))
409
+ next()
381
410
  }
382
411
  }
383
412
 
@@ -451,7 +480,7 @@ export function ${routerFunctionName}<TCtx = unknown, TPrisma extends PrismaClie
451
480
  const locals = readLocals(res)
452
481
  return {
453
482
  operationOverride: locals.operationOverride,
454
- resolveOperationContext: locals.resolveOperationContext,
483
+ resolveOperationContext: locals.getContext,
455
484
  prisma: extReq.prisma,
456
485
  postgres: extReq.postgres,
457
486
  sqlite: extReq.sqlite,
@@ -465,11 +494,6 @@ export function ${routerFunctionName}<TCtx = unknown, TPrisma extends PrismaClie
465
494
  }
466
495
  }
467
496
 
468
- const buildResolveContext = (req: Request): (() => unknown | Promise<unknown>) | undefined => {
469
- if (typeof config.resolveContext !== 'function') return undefined
470
- return () => (config.resolveContext as (r: Request) => unknown | Promise<unknown>)(req)
471
- }
472
-
473
497
  const parseQuery: RequestHandler = (req, res, next) => {
474
498
  const rawQuery = req.query
475
499
  if (rawQuery && Object.keys(rawQuery).length > 0) {
@@ -501,11 +525,10 @@ export function ${routerFunctionName}<TCtx = unknown, TPrisma extends PrismaClie
501
525
 
502
526
  const setShape = (opConfig: NormalizedOp, opKind: OpKind): RequestHandler => {
503
527
  return async (req, res, next) => {
528
+ const locals = readLocals(res)
504
529
  try {
505
- const locals = readLocals(res)
506
530
  locals.operationOverride = opConfig.override
507
- let context: Promise<unknown> | undefined
508
- locals.resolveOperationContext = () => context ??= Promise.resolve(config.resolveContext?.(req))
531
+ locals.getContext = memoizeContext(() => config.resolveContext?.(req))
509
532
  const merged = mergePaginationConfig(config.pagination, opConfig.pagination)
510
533
  if (merged) {
511
534
  locals.routeConfig = { pagination: merged }
@@ -516,51 +539,34 @@ export function ${routerFunctionName}<TCtx = unknown, TPrisma extends PrismaClie
516
539
  const caller = config.guard?.resolveVariant?.(req) ?? headerValue ?? undefined
517
540
  if (typeof caller === 'string') locals.guardCaller = caller
518
541
 
519
- const resolution = resolveOperationVariantKey(opConfig.guardRouting, caller)
520
- if (!resolution.ok) {
521
- locals.guardVariantFailure = resolution
542
+ const routed = routeOperation({ guardRouting: opConfig.guardRouting, caller })
543
+ if (!routed.ok) {
544
+ locals.routeFailure = routed
522
545
  next()
523
546
  return
524
547
  }
525
548
 
526
- const resolvedKey =
527
- opConfig.guardRouting.kind === 'named'
528
- ? resolution.key
529
- : undefined
530
- if (resolvedKey !== undefined) locals.guardVariantKey = resolvedKey
531
-
532
- if (opConfig.guardShape) {
533
- if (!dropGuard) {
534
- locals.guardShape = opConfig.guardShape
535
- } else {
536
- await applyDroppedGuard(
537
- opConfig.guardShape,
538
- resolvedKey,
539
- buildResolveContext(req),
540
- opKind,
541
- {
542
- readQuery: locals.parsedQuery,
543
- writeBody: isPlainObject(req.body)
544
- ? (req.body as Record<string, unknown>)
545
- : undefined,
546
- },
547
- () => {
548
- if (!locals.parsedQuery) locals.parsedQuery = {}
549
- return locals.parsedQuery
550
- },
551
- () => {
552
- if (!isPlainObject(req.body)) {
553
- req.body = {}
554
- }
555
- return req.body as Record<string, unknown>
556
- },
557
- )
558
- }
549
+ if (routed.variantKey !== undefined) locals.guardVariantKey = routed.variantKey
550
+
551
+ const guard = await prepareGuardOperation(routed, {
552
+ guardShape: opConfig.guardShape,
553
+ opKind,
554
+ policy: { dropGuard, validateResolvedShapes: false },
555
+ getContext: typeof config.resolveContext === 'function' ? locals.getContext : undefined,
556
+ args: queryChannel(res),
557
+ writeArgs: bodyChannel(req),
558
+ })
559
+ if (!guard.ok) {
560
+ locals.guardFailure = guard
561
+ next()
562
+ return
559
563
  }
560
564
 
565
+ locals.guardShape = guard.guardShape
566
+
561
567
  next()
562
568
  } catch (err) {
563
- next(mapError(err))
569
+ next(classifyError(err))
564
570
  }
565
571
  }
566
572
  }
@@ -145,7 +145,6 @@ import { sanitizeKeys, normalizePrefix, getEnv, isPlainObject, resolveDropGuardE
145
145
  import { buildModelOpenApi } from '../buildModelOpenApi${ext}'
146
146
  import {
147
147
  normalizeOperation,
148
- resolveOperationVariantKey,
149
148
  validateCountSourceWhere,
150
149
  validateOperationConfig,
151
150
  validateUpdateEachConfig,
@@ -157,10 +156,15 @@ import type { NormalizedOperationConfig } from '../routeConfig${ext}'
157
156
  import type { OperationContext } from '../operationRuntime${ext}'
158
157
  import { transformResult } from '../operationRuntime${ext}'
159
158
  import { HttpError, mapError } from '../errorMapper${ext}'
160
- import { formatGuardVariantResolutionError } from '../guardVariantError${ext}'
161
- import type { GuardVariantResolution } from '../guardVariantRouting${ext}'
162
159
  import { mergePaginationConfig } from '../pagination${ext}'
163
- import { applyDroppedGuard } from '../projectionDefaults${ext}'
160
+ import {
161
+ routeOperation,
162
+ prepareGuardOperation,
163
+ settleStage,
164
+ classifyError,
165
+ memoizeContext,
166
+ type ArgsChannel,
167
+ } from '../operationPipeline${ext}'
164
168
  import type { OpKind } from '../projectionDefaults${ext}'
165
169
  import { MODEL_FIELDS, MODEL_ENUMS } from './${modelName}Metadata${ext}'
166
170
 
@@ -204,13 +208,36 @@ type FastifyExtended = FastifyRequest & {
204
208
  guardShape?: Record<string, unknown>
205
209
  guardCaller?: string
206
210
  operationOverride?: RuntimeOperationOverride
207
- resolveOperationContext?: () => unknown | Promise<unknown>
211
+ getContext?: () => Promise<unknown>
208
212
  guardVariantKey?: string
209
- guardVariantFailure?: Extract<GuardVariantResolution, { ok: false }>
213
+ routeFailure?: { ok: false; failure: HttpError }
214
+ guardFailure?: { ok: false; failure: HttpError }
210
215
  resultData?: unknown
211
216
  resultStatus?: number
212
217
  }
213
218
 
219
+ function queryChannel(request: FastifyRequest): ArgsChannel {
220
+ const fx = request as FastifyExtended
221
+ return {
222
+ read: () => fx.parsedQuery,
223
+ write: (next) => {
224
+ fx.parsedQuery = next
225
+ },
226
+ }
227
+ }
228
+
229
+ function bodyChannel(request: FastifyRequest): ArgsChannel {
230
+ return {
231
+ read: () =>
232
+ isPlainObject(request.body)
233
+ ? (request.body as Record<string, unknown>)
234
+ : undefined,
235
+ write: (next) => {
236
+ ;(request as unknown as { body: unknown }).body = next
237
+ },
238
+ }
239
+ }
240
+
214
241
  function normalizeFastifyOperation(
215
242
  config: OperationConfigLike | undefined,
216
243
  ): NormalizedOp {
@@ -245,14 +272,6 @@ function parseBodyAsQueryHook(request: FastifyRequest): void {
245
272
  (request as FastifyExtended).parsedQuery = sanitizeKeys(body as Record<string, unknown>)
246
273
  }
247
274
 
248
- function buildResolveContext<TCtx, TPrisma extends PrismaClientLike>(
249
- config: ${modelName}RouteConfig<TCtx, TPrisma>,
250
- request: FastifyRequest,
251
- ): (() => unknown | Promise<unknown>) | undefined {
252
- if (typeof config.resolveContext !== 'function') return undefined
253
- return () => (config.resolveContext as (r: FastifyRequest) => unknown | Promise<unknown>)(request)
254
- }
255
-
256
275
  function makeShapeHook<TCtx, TPrisma extends PrismaClientLike>(
257
276
  config: ${modelName}RouteConfig<TCtx, TPrisma>,
258
277
  opConfig: NormalizedOp,
@@ -272,8 +291,7 @@ function makeShapeHook<TCtx, TPrisma extends PrismaClientLike>(
272
291
  return async (request: FastifyRequest) => {
273
292
  const fx = request as FastifyExtended
274
293
  fx.operationOverride = opConfig.override
275
- let context: Promise<unknown> | undefined
276
- fx.resolveOperationContext = () => context ??= Promise.resolve(config.resolveContext?.(request))
294
+ fx.getContext = memoizeContext(() => config.resolveContext?.(request))
277
295
  const merged = mergePaginationConfig(config.pagination, opConfig.pagination)
278
296
  if (merged) fx.routeConfig = { pagination: merged }
279
297
 
@@ -284,46 +302,28 @@ function makeShapeHook<TCtx, TPrisma extends PrismaClientLike>(
284
302
  ?? undefined
285
303
  if (typeof caller === 'string') fx.guardCaller = caller
286
304
 
287
- const resolution = resolveOperationVariantKey(opConfig.guardRouting, caller)
288
- if (!resolution.ok) {
289
- fx.guardVariantFailure = resolution
305
+ const routed = routeOperation({ guardRouting: opConfig.guardRouting, caller })
306
+ if (!routed.ok) {
307
+ fx.routeFailure = routed
290
308
  return
291
309
  }
292
310
 
293
- const resolvedKey =
294
- opConfig.guardRouting.kind === 'named'
295
- ? resolution.key
296
- : undefined
297
- if (resolvedKey !== undefined) fx.guardVariantKey = resolvedKey
298
-
299
- if (opConfig.guardShape) {
300
- if (!dropGuard) {
301
- fx.guardShape = opConfig.guardShape
302
- } else {
303
- await applyDroppedGuard(
304
- opConfig.guardShape,
305
- resolvedKey,
306
- buildResolveContext(config, request),
307
- opKind,
308
- {
309
- readQuery: fx.parsedQuery,
310
- writeBody: isPlainObject(request.body)
311
- ? (request.body as Record<string, unknown>)
312
- : undefined,
313
- },
314
- () => {
315
- if (!fx.parsedQuery) fx.parsedQuery = {}
316
- return fx.parsedQuery
317
- },
318
- () => {
319
- if (!isPlainObject(request.body)) {
320
- ;(request as unknown as { body: unknown }).body = {}
321
- }
322
- return request.body as Record<string, unknown>
323
- },
324
- )
325
- }
311
+ if (routed.variantKey !== undefined) fx.guardVariantKey = routed.variantKey
312
+
313
+ const guard = await prepareGuardOperation(routed, {
314
+ guardShape: opConfig.guardShape,
315
+ opKind,
316
+ policy: { dropGuard, validateResolvedShapes: false },
317
+ getContext: typeof config.resolveContext === 'function' ? fx.getContext : undefined,
318
+ args: queryChannel(request),
319
+ writeArgs: bodyChannel(request),
320
+ })
321
+ if (!guard.ok) {
322
+ fx.guardFailure = guard
323
+ return
326
324
  }
325
+
326
+ fx.guardShape = guard.guardShape
327
327
  }
328
328
  }
329
329
 
@@ -464,12 +464,8 @@ export async function ${routerFunctionName}<TCtx = unknown, TPrisma extends Pris
464
464
  if (await runHooks(opConfig.operationBefore, request, reply)) return
465
465
 
466
466
  const fx = request as FastifyExtended
467
- if (fx.guardVariantFailure) {
468
- throw new HttpError(
469
- 400,
470
- formatGuardVariantResolutionError(fx.guardVariantFailure),
471
- )
472
- }
467
+ if (fx.routeFailure) settleStage(fx.routeFailure)
468
+ if (fx.guardFailure) settleStage(fx.guardFailure)
473
469
 
474
470
  const key = fx.guardVariantKey
475
471
  const variantHooks =
@@ -496,12 +492,8 @@ export async function ${routerFunctionName}<TCtx = unknown, TPrisma extends Pris
496
492
  if (await runHooks(opConfig.operationBefore, request, reply)) return
497
493
 
498
494
  const fx = request as FastifyExtended
499
- if (fx.guardVariantFailure) {
500
- throw new HttpError(
501
- 400,
502
- formatGuardVariantResolutionError(fx.guardVariantFailure),
503
- )
504
- }
495
+ if (fx.routeFailure) settleStage(fx.routeFailure)
496
+ if (fx.guardFailure) settleStage(fx.guardFailure)
505
497
 
506
498
  const key = fx.guardVariantKey
507
499
  const variantHooks =