prisma-generator-express 1.40.0 → 1.42.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 (48) hide show
  1. package/README.md +444 -12
  2. package/dist/generators/generateOperationCore.d.ts +3 -1
  3. package/dist/generators/generateOperationCore.js +266 -160
  4. package/dist/generators/generateOperationCore.js.map +1 -1
  5. package/dist/generators/generateRouteConfigType.d.ts +3 -1
  6. package/dist/generators/generateRouteConfigType.js +36 -31
  7. package/dist/generators/generateRouteConfigType.js.map +1 -1
  8. package/dist/generators/generateRouter.d.ts +4 -2
  9. package/dist/generators/generateRouter.js +186 -132
  10. package/dist/generators/generateRouter.js.map +1 -1
  11. package/dist/generators/generateRouterFastify.d.ts +3 -1
  12. package/dist/generators/generateRouterFastify.js +12 -10
  13. package/dist/generators/generateRouterFastify.js.map +1 -1
  14. package/dist/generators/generateRouterHono.d.ts +3 -1
  15. package/dist/generators/generateRouterHono.js +12 -9
  16. package/dist/generators/generateRouterHono.js.map +1 -1
  17. package/dist/generators/generateUnifiedDocs.d.ts +2 -1
  18. package/dist/generators/generateUnifiedDocs.js +6 -4
  19. package/dist/generators/generateUnifiedDocs.js.map +1 -1
  20. package/dist/index.js +16 -21
  21. package/dist/index.js.map +1 -1
  22. package/dist/utils/copyFiles.d.ts +2 -1
  23. package/dist/utils/copyFiles.js +39 -34
  24. package/dist/utils/copyFiles.js.map +1 -1
  25. package/dist/utils/importExt.d.ts +2 -0
  26. package/dist/utils/importExt.js +11 -0
  27. package/dist/utils/importExt.js.map +1 -0
  28. package/dist/utils/resolveImportStyle.d.ts +3 -0
  29. package/dist/utils/resolveImportStyle.js +211 -0
  30. package/dist/utils/resolveImportStyle.js.map +1 -0
  31. package/dist/utils/writeFileSafely.js +6 -9
  32. package/dist/utils/writeFileSafely.js.map +1 -1
  33. package/package.json +1 -1
  34. package/src/copy/routeConfig.express.ts +39 -5
  35. package/src/copy/routeConfig.fastify.ts +8 -4
  36. package/src/copy/routeConfig.hono.ts +7 -3
  37. package/src/copy/routeConfig.ts +42 -2
  38. package/src/generators/generateOperationCore.ts +273 -169
  39. package/src/generators/generateRouteConfigType.ts +43 -36
  40. package/src/generators/generateRouter.ts +189 -133
  41. package/src/generators/generateRouterFastify.ts +14 -9
  42. package/src/generators/generateRouterHono.ts +14 -8
  43. package/src/generators/generateUnifiedDocs.ts +8 -3
  44. package/src/index.ts +25 -47
  45. package/src/utils/copyFiles.ts +45 -45
  46. package/src/utils/importExt.ts +7 -0
  47. package/src/utils/resolveImportStyle.ts +187 -0
  48. package/src/utils/writeFileSafely.ts +6 -22
@@ -1,26 +1,22 @@
1
+ import { ImportStyle } from '../utils/resolveImportStyle'
2
+ import { importExt } from '../utils/importExt'
3
+ import type { Target } from '../constants'
4
+
1
5
  const ROUTER_OPERATIONS = [
2
- 'findUnique',
3
- 'findUniqueOrThrow',
4
- 'findFirst',
5
- 'findFirstOrThrow',
6
- 'findMany',
7
- 'findManyPaginated',
8
- 'count',
9
- 'aggregate',
10
- 'groupBy',
11
- 'create',
12
- 'createMany',
13
- 'createManyAndReturn',
14
- 'update',
15
- 'updateMany',
16
- 'updateManyAndReturn',
17
- 'upsert',
18
- 'delete',
19
- 'deleteMany',
6
+ 'findUnique', 'findUniqueOrThrow', 'findFirst', 'findFirstOrThrow',
7
+ 'findMany', 'findManyPaginated', 'count', 'aggregate', 'groupBy',
8
+ 'create', 'createMany', 'createManyAndReturn',
9
+ 'update', 'updateMany', 'updateManyAndReturn',
10
+ 'upsert', 'delete', 'deleteMany',
20
11
  ] as const
21
12
 
22
13
  type RouterOperation = (typeof ROUTER_OPERATIONS)[number]
23
14
 
15
+ const READ_OPERATIONS: ReadonlySet<RouterOperation> = new Set<RouterOperation>([
16
+ 'findUnique', 'findUniqueOrThrow', 'findFirst', 'findFirstOrThrow',
17
+ 'findMany', 'findManyPaginated', 'count', 'aggregate', 'groupBy',
18
+ ])
19
+
24
20
  const ROUTER_OP_TO_SHAPE_OP: Record<RouterOperation, string> = {
25
21
  findUnique: 'findUnique',
26
22
  findUniqueOrThrow: 'findUniqueOrThrow',
@@ -50,40 +46,51 @@ export function generateRouteConfigType(
50
46
  modelName: string,
51
47
  hookHandlerType: string,
52
48
  guardShapesImport: string | null,
49
+ importStyle: ImportStyle,
50
+ target: Target,
53
51
  ): string {
52
+ const ext = importExt(importStyle)
54
53
  const m = modelName
54
+ const supportsProgressive = target === 'express'
55
55
 
56
56
  if (!guardShapesImport) {
57
- return `export type ${m}RouteConfig<TCtx = unknown> = RouteConfig\n`
57
+ return `export type ${m}RouteConfig<TCtx = unknown> = RouteConfig<Record<string, unknown>, TCtx>\n`
58
58
  }
59
59
 
60
- const shapeOps = Object.values(ROUTER_OP_TO_SHAPE_OP).filter(
61
- (v, i, a) => a.indexOf(v) === i,
62
- )
63
-
64
- const opShapeImports = shapeOps
65
- .map((op) => `${m}${capitalize(op)}ShapeInput`)
66
- .join(',\n ')
60
+ const shapeOps = Object.values(ROUTER_OP_TO_SHAPE_OP).filter((v, i, a) => a.indexOf(v) === i)
61
+ const opShapeImports = shapeOps.map((op) => `${m}${capitalize(op)}ShapeInput`).join(',\n ')
67
62
 
68
63
  const overrides = ROUTER_OPERATIONS.map((routerOp) => {
69
64
  const shapeOp = ROUTER_OP_TO_SHAPE_OP[routerOp]
70
65
  const c = capitalize(shapeOp)
71
- return (
72
- ` ${routerOp}?: {\n` +
73
- ` before?: ${hookHandlerType}[]\n` +
74
- ` after?: ${hookHandlerType}[]\n` +
75
- ` shape?: ${m}${c}ShapeInput<TCtx>\n` +
76
- ` }`
77
- )
66
+ const isRead = READ_OPERATIONS.has(routerOp)
67
+ const lines = [
68
+ ` before?: ${hookHandlerType}[]`,
69
+ ` after?: ${hookHandlerType}[]`,
70
+ ` shape?: ${m}${c}ShapeInput<TCtx>`,
71
+ ]
72
+ if (isRead && supportsProgressive) {
73
+ lines.push(` progressive?: Record<string, ProgressiveVariantConfig>`)
74
+ lines.push(` progressiveStages?: Record<string, ProgressiveStage<TCtx>>`)
75
+ }
76
+ return ` ${routerOp}?: {\n${lines.join('\n')}\n }`
78
77
  }).join('\n')
79
78
 
80
79
  const omitKeys = ROUTER_OPERATIONS.map((k) => `'${k}'`).join('\n | ')
81
80
 
81
+ const progressiveTypeImport = supportsProgressive
82
+ ? `import type { ProgressiveVariantConfig, ProgressiveStage } from '../routeConfig.target${ext}'\n\n`
83
+ : ''
84
+
82
85
  return (
83
- `import type {\n ${opShapeImports}\n} from '${guardShapesImport}'\n\n` +
86
+ progressiveTypeImport +
87
+ `import type {\n ${opShapeImports}\n} from '${guardShapesImport}${ext}'\n\n` +
84
88
  `export type ${m}RouteConfig<TCtx = unknown> = Omit<\n` +
85
- ` RouteConfig,\n` +
89
+ ` RouteConfig<Record<string, unknown>, TCtx>,\n` +
86
90
  ` | ${omitKeys}\n` +
87
- `> & {\n${overrides}\n}\n`
91
+ ` | 'resolveContext'\n` +
92
+ `> & {\n` +
93
+ ` resolveContext?: (request: import('express').Request) => TCtx | Promise<TCtx>\n` +
94
+ `${overrides}\n}\n`
88
95
  )
89
96
  }
@@ -1,18 +1,22 @@
1
1
  import { DMMF } from '@prisma/generator-helper'
2
2
  import { toCamelCase } from '../utils/strings'
3
3
  import { generateRouteConfigType } from './generateRouteConfigType'
4
+ import { ImportStyle } from '../utils/resolveImportStyle'
5
+ import { importExt } from '../utils/importExt'
4
6
 
5
7
  export function generateRouterFunction({
6
8
  model,
7
9
  enums,
8
- relativeClientPath,
9
10
  guardShapesImport,
11
+ importStyle,
10
12
  }: {
11
13
  model: DMMF.Model
12
14
  enums: DMMF.DatamodelEnum[]
13
- relativeClientPath: string
15
+ relativeClientPath?: string
14
16
  guardShapesImport: string | null
17
+ importStyle: ImportStyle
15
18
  }): string {
19
+ const ext = importExt(importStyle)
16
20
  const modelName = model.name
17
21
  const prefix = toCamelCase(modelName)
18
22
  const modelNameLower = modelName.toLowerCase()
@@ -41,8 +45,9 @@ export function generateRouterFunction({
41
45
  values: e.values.map((v) => ({ name: v.name })),
42
46
  }))
43
47
 
44
- return `import express, { Request, Response, NextFunction, RequestHandler } from 'express'
45
- import type { PrismaClient } from '${relativeClientPath}'
48
+ return `import express from 'express'
49
+ import type { Request, Response, NextFunction, RequestHandler } from 'express'
50
+ import { startQueryBuilder } from '../queryBuilder${ext}'
46
51
  import {
47
52
  ${prefix}FindUnique,
48
53
  ${prefix}FindUniqueOrThrow,
@@ -61,24 +66,52 @@ import {
61
66
  ${prefix}DeleteMany,
62
67
  ${prefix}Aggregate,
63
68
  ${prefix}Count,
64
- ${prefix}GroupBy
65
- } from './${modelName}Handlers'
66
- import type { RouteConfig } from '../routeConfig.target'
67
- import { parseQueryParams } from '../parseQueryParams'
68
- import { sanitizeKeys } from '../misc'
69
- import { buildModelOpenApi } from '../buildModelOpenApi'
70
- import { transformResult } from '../operationRuntime'
71
-
72
- ${generateRouteConfigType(modelName, 'RequestHandler', guardShapesImport)}
69
+ ${prefix}GroupBy,
70
+ } from './${modelName}Handlers${ext}'
71
+ import * as core from './${modelName}Core${ext}'
72
+ import type { RouteConfig, ProgressiveVariantConfig, ProgressiveStage } from '../routeConfig.target${ext}'
73
+ import { parseQueryParams } from '../parseQueryParams${ext}'
74
+ import { sanitizeKeys } from '../misc${ext}'
75
+ import { buildModelOpenApi } from '../buildModelOpenApi${ext}'
76
+ import type { OperationContext } from '../operationRuntime${ext}'
77
+ import {
78
+ transformResult,
79
+ acceptsEventStream,
80
+ runProgressiveEndpoint,
81
+ runSingleResultSSE,
82
+ } from '../operationRuntime${ext}'
83
+
84
+ ${generateRouteConfigType(modelName, 'RequestHandler', guardShapesImport, importStyle, 'express')}
73
85
  const _env = typeof process !== 'undefined' && process.env ? process.env : {} as Record<string, string | undefined>
74
86
 
75
87
  const MODEL_FIELDS = ${JSON.stringify(fieldsMeta, null, 2)} as const
76
-
77
88
  const MODEL_ENUMS = ${JSON.stringify(enumsMeta, null, 2)} as const
78
89
 
79
- const defaultOpConfig = {
80
- before: [] as RequestHandler[],
81
- after: [] as RequestHandler[],
90
+ type OperationConfigLike = {
91
+ before?: RequestHandler[]
92
+ after?: RequestHandler[]
93
+ shape?: Record<string, unknown>
94
+ progressive?: Record<string, ProgressiveVariantConfig>
95
+ progressiveStages?: Record<string, ProgressiveStage<unknown>>
96
+ }
97
+
98
+ type ExtendedRequest = Request & {
99
+ prisma?: unknown
100
+ postgres?: unknown
101
+ sqlite?: unknown
102
+ }
103
+
104
+ type LocalsBag = {
105
+ parsedQuery?: Record<string, unknown>
106
+ routeConfig?: ${modelName}RouteConfig
107
+ guardShape?: Record<string, unknown>
108
+ guardCaller?: string
109
+ data?: unknown
110
+ }
111
+
112
+ const defaultOpConfig: OperationConfigLike = {
113
+ before: [],
114
+ after: [],
82
115
  }
83
116
 
84
117
  function normalizePrefix(p: string): string {
@@ -103,9 +136,12 @@ function getQueryBuilderConfig(config: RouteConfig) {
103
136
  return {}
104
137
  }
105
138
 
139
+ function readLocals(res: Response): LocalsBag {
140
+ return res.locals as LocalsBag
141
+ }
142
+
106
143
  export function ${routerFunctionName}<TCtx = unknown>(config: ${modelName}RouteConfig<TCtx> = {}) {
107
144
  const router = express.Router()
108
-
109
145
  router.use(express.json())
110
146
 
111
147
  const customPrefix = normalizePrefix(config.customUrlPrefix || '')
@@ -113,26 +149,42 @@ export function ${routerFunctionName}<TCtx = unknown>(config: ${modelName}RouteC
113
149
  const basePath = customPrefix + modelPrefix
114
150
 
115
151
  const openApiDisabled = config.disableOpenApi === true
116
- || (config.disableOpenApi !== false && (
117
- _env.DISABLE_OPENAPI === 'true'
118
- || _env.NODE_ENV === 'production'
119
- ))
152
+ || (config.disableOpenApi !== false && (_env.DISABLE_OPENAPI === 'true' || _env.NODE_ENV === 'production'))
120
153
 
121
154
  const postReadsEnabled = !config.disablePostReads
122
155
 
123
156
  const qbEnabled = isQueryBuilderEnabled(config)
124
-
125
157
  if (qbEnabled) {
126
158
  const qbConfig = getQueryBuilderConfig(config)
127
159
  if (qbConfig) {
128
- try { require('../queryBuilder').startQueryBuilder(qbConfig) } catch (err) { if (_env.NODE_ENV !== 'production') console.warn('[query-builder]', err) }
160
+ try {
161
+ startQueryBuilder(qbConfig)
162
+ } catch (err) {
163
+ if (_env.NODE_ENV !== 'production') console.warn('[query-builder]', err)
164
+ }
165
+ }
166
+ }
167
+
168
+ const buildContext = (req: Request, res: Response): OperationContext => {
169
+ const extReq = req as ExtendedRequest
170
+ const locals = readLocals(res)
171
+ return {
172
+ prisma: extReq.prisma,
173
+ postgres: extReq.postgres,
174
+ sqlite: extReq.sqlite,
175
+ parsedQuery: locals.parsedQuery,
176
+ body: req.body,
177
+ guardShape: locals.guardShape,
178
+ guardCaller: locals.guardCaller,
179
+ paginationConfig: locals.routeConfig?.pagination,
129
180
  }
130
181
  }
131
182
 
132
183
  const parseQuery: RequestHandler = (req, res, next) => {
133
184
  const rawQuery = req.query
134
185
  if (rawQuery && Object.keys(rawQuery).length > 0) {
135
- res.locals.parsedQuery = parseQueryParams(rawQuery as Record<string, unknown>)
186
+ const parsed = parseQueryParams(rawQuery as Record<string, unknown>) as Record<string, unknown>
187
+ readLocals(res).parsedQuery = parsed
136
188
  }
137
189
  next()
138
190
  }
@@ -141,154 +193,167 @@ export function ${routerFunctionName}<TCtx = unknown>(config: ${modelName}RouteC
141
193
  if (!req.body || typeof req.body !== 'object' || Array.isArray(req.body)) {
142
194
  return next({ status: 400, message: 'Request body must be a JSON object' })
143
195
  }
144
- res.locals.parsedQuery = sanitizeKeys(req.body)
196
+ readLocals(res).parsedQuery = sanitizeKeys(req.body as Record<string, unknown>)
145
197
  next()
146
198
  }
147
199
 
148
- const setShape = (opConfig: any): RequestHandler => {
200
+ const setShape = (opConfig: OperationConfigLike): RequestHandler => {
149
201
  return (req, res, next) => {
150
- res.locals.routeConfig = config
151
- if (opConfig.shape) {
152
- res.locals.guardShape = opConfig.shape
153
- const caller = config.guard?.resolveVariant?.(req)
154
- ?? req.get(config.guard?.variantHeader || 'x-api-variant')
155
- ?? undefined
156
- if (caller) {
157
- res.locals.guardCaller = caller
202
+ const locals = readLocals(res)
203
+ locals.routeConfig = config
204
+ const headerName = config.guard?.variantHeader || 'x-api-variant'
205
+ const headerValue = req.get(headerName)
206
+ const caller = config.guard?.resolveVariant?.(req) ?? headerValue ?? undefined
207
+ if (caller) locals.guardCaller = caller
208
+ if (opConfig.shape) locals.guardShape = opConfig.shape
209
+ next()
210
+ }
211
+ }
212
+
213
+ const maybeProgressiveSSE = (
214
+ opConfig: OperationConfigLike,
215
+ coreFn: (ctx: OperationContext) => Promise<unknown>,
216
+ ): RequestHandler => {
217
+ return async (req, res, next) => {
218
+ if (res.headersSent || res.writableEnded) return next()
219
+ if (req.method !== 'GET') return next()
220
+ if (!acceptsEventStream(req.headers.accept)) return next()
221
+
222
+ const locals = readLocals(res)
223
+ const variant = locals.guardCaller
224
+ const progressiveConfig = variant ? opConfig.progressive?.[variant] : undefined
225
+
226
+ try {
227
+ if (!progressiveConfig || progressiveConfig.enabled === false) {
228
+ await runSingleResultSSE({
229
+ req,
230
+ res,
231
+ coreQueryFn: () => coreFn(buildContext(req, res)),
232
+ })
233
+ return
234
+ }
235
+
236
+ if (!Array.isArray(progressiveConfig.stages)) {
237
+ return next({ status: 500, message: 'Progressive endpoint requires stages array' })
238
+ }
239
+
240
+ const stageRegistry = opConfig.progressiveStages ?? {}
241
+ const missingStage = progressiveConfig.stages.find(
242
+ (name) => typeof stageRegistry[name] !== 'function',
243
+ )
244
+ if (missingStage) {
245
+ return next({ status: 500, message: 'Missing progressive stage: ' + missingStage })
246
+ }
247
+
248
+ if (typeof config.resolveContext !== 'function') {
249
+ return next({ status: 500, message: 'Progressive endpoint requires config.resolveContext' })
250
+ }
251
+
252
+ const ctx = await config.resolveContext(req)
253
+ await runProgressiveEndpoint({
254
+ req,
255
+ res,
256
+ ctx,
257
+ prisma: (req as ExtendedRequest).prisma,
258
+ variant: variant as string,
259
+ stages: progressiveConfig.stages,
260
+ stageRegistry,
261
+ })
262
+ } catch (err) {
263
+ console.error('[progressive] dispatch error:', err)
264
+ if (!res.headersSent) {
265
+ return next({ status: 500, message: 'Internal server error' })
158
266
  }
159
267
  }
160
- next()
161
268
  }
162
269
  }
163
270
 
164
271
  const respond: RequestHandler = (_req, res) => {
165
- const data = res.locals.data
166
- if (data === undefined) {
167
- return res.status(500).json({ message: 'No data set by handler' })
168
- }
272
+ const data = readLocals(res).data
273
+ if (data === undefined) return res.status(500).json({ message: 'No data set by handler' })
169
274
  return res.json(transformResult(data))
170
275
  }
171
276
 
172
277
  const respondCreated: RequestHandler = (_req, res) => {
173
- const data = res.locals.data
174
- if (data === undefined) {
175
- return res.status(500).json({ message: 'No data set by handler' })
176
- }
278
+ const data = readLocals(res).data
279
+ if (data === undefined) return res.status(500).json({ message: 'No data set by handler' })
177
280
  return res.status(201).json(transformResult(data))
178
281
  }
179
282
 
180
283
  if (!openApiDisabled) {
181
284
  const openapiJsonPath = basePath ? \`\${basePath}/openapi.json\` : '/openapi.json'
182
285
  const openapiYamlPath = basePath ? \`\${basePath}/openapi.yaml\` : '/openapi.yaml'
183
-
184
286
  router.get(openapiJsonPath, (_req, res) => {
185
- const spec = buildModelOpenApi(
186
- '${modelName}',
187
- MODEL_FIELDS as any,
188
- MODEL_ENUMS as any,
189
- config,
190
- { format: 'json' }
191
- )
287
+ const spec = buildModelOpenApi('${modelName}', MODEL_FIELDS as unknown as Parameters<typeof buildModelOpenApi>[1], MODEL_ENUMS as unknown as Parameters<typeof buildModelOpenApi>[2], config, { format: 'json' })
192
288
  res.json(spec)
193
289
  })
194
-
195
290
  router.get(openapiYamlPath, (_req, res) => {
196
- const spec = buildModelOpenApi(
197
- '${modelName}',
198
- MODEL_FIELDS as any,
199
- MODEL_ENUMS as any,
200
- config,
201
- { format: 'yaml' }
202
- )
291
+ const spec = buildModelOpenApi('${modelName}', MODEL_FIELDS as unknown as Parameters<typeof buildModelOpenApi>[1], MODEL_ENUMS as unknown as Parameters<typeof buildModelOpenApi>[2], config, { format: 'yaml' })
203
292
  res.type('application/yaml').send(spec as string)
204
293
  })
205
294
  }
206
295
 
207
296
  if (config.enableAll || config.findFirst) {
208
- const opConfig = config.findFirst || defaultOpConfig
297
+ const opConfig: OperationConfigLike = (config.findFirst as OperationConfigLike | undefined) ?? defaultOpConfig
209
298
  const { before = [], after = [] } = opConfig
210
299
  const path = basePath ? \`\${basePath}/first\` : '/first'
211
- router.get(path, parseQuery, setShape(opConfig), ...before, ${prefix}FindFirst as RequestHandler, ...after, respond)
212
- if (postReadsEnabled) {
213
- router.post(path, parseBodyAsQuery, setShape(opConfig), ...before, ${prefix}FindFirst as RequestHandler, ...after, respond)
214
- }
300
+ router.get(path, parseQuery, setShape(opConfig), ...before, maybeProgressiveSSE(opConfig, core.findFirst), ${prefix}FindFirst as RequestHandler, ...after, respond)
301
+ if (postReadsEnabled) router.post(path, parseBodyAsQuery, setShape(opConfig), ...before, ${prefix}FindFirst as RequestHandler, ...after, respond)
215
302
  }
216
-
217
303
  if (config.enableAll || config.findFirstOrThrow) {
218
- const opConfig = config.findFirstOrThrow || defaultOpConfig
304
+ const opConfig: OperationConfigLike = (config.findFirstOrThrow as OperationConfigLike | undefined) ?? defaultOpConfig
219
305
  const { before = [], after = [] } = opConfig
220
306
  const path = basePath ? \`\${basePath}/first/strict\` : '/first/strict'
221
- router.get(path, parseQuery, setShape(opConfig), ...before, ${prefix}FindFirstOrThrow as RequestHandler, ...after, respond)
222
- if (postReadsEnabled) {
223
- router.post(path, parseBodyAsQuery, setShape(opConfig), ...before, ${prefix}FindFirstOrThrow as RequestHandler, ...after, respond)
224
- }
307
+ router.get(path, parseQuery, setShape(opConfig), ...before, maybeProgressiveSSE(opConfig, core.findFirstOrThrow), ${prefix}FindFirstOrThrow as RequestHandler, ...after, respond)
308
+ if (postReadsEnabled) router.post(path, parseBodyAsQuery, setShape(opConfig), ...before, ${prefix}FindFirstOrThrow as RequestHandler, ...after, respond)
225
309
  }
226
-
227
310
  if (config.enableAll || config.findManyPaginated) {
228
- const opConfig = config.findManyPaginated || defaultOpConfig
311
+ const opConfig: OperationConfigLike = (config.findManyPaginated as OperationConfigLike | undefined) ?? defaultOpConfig
229
312
  const { before = [], after = [] } = opConfig
230
313
  const path = basePath ? \`\${basePath}/paginated\` : '/paginated'
231
- router.get(path, parseQuery, setShape(opConfig), ...before, ${prefix}FindManyPaginated as RequestHandler, ...after, respond)
232
- if (postReadsEnabled) {
233
- router.post(path, parseBodyAsQuery, setShape(opConfig), ...before, ${prefix}FindManyPaginated as RequestHandler, ...after, respond)
234
- }
314
+ router.get(path, parseQuery, setShape(opConfig), ...before, maybeProgressiveSSE(opConfig, core.findManyPaginated), ${prefix}FindManyPaginated as RequestHandler, ...after, respond)
315
+ if (postReadsEnabled) router.post(path, parseBodyAsQuery, setShape(opConfig), ...before, ${prefix}FindManyPaginated as RequestHandler, ...after, respond)
235
316
  }
236
-
237
317
  if (config.enableAll || config.aggregate) {
238
- const opConfig = config.aggregate || defaultOpConfig
318
+ const opConfig: OperationConfigLike = (config.aggregate as OperationConfigLike | undefined) ?? defaultOpConfig
239
319
  const { before = [], after = [] } = opConfig
240
320
  const path = basePath ? \`\${basePath}/aggregate\` : '/aggregate'
241
- router.get(path, parseQuery, setShape(opConfig), ...before, ${prefix}Aggregate as RequestHandler, ...after, respond)
242
- if (postReadsEnabled) {
243
- router.post(path, parseBodyAsQuery, setShape(opConfig), ...before, ${prefix}Aggregate as RequestHandler, ...after, respond)
244
- }
321
+ router.get(path, parseQuery, setShape(opConfig), ...before, maybeProgressiveSSE(opConfig, core.aggregate), ${prefix}Aggregate as RequestHandler, ...after, respond)
322
+ if (postReadsEnabled) router.post(path, parseBodyAsQuery, setShape(opConfig), ...before, ${prefix}Aggregate as RequestHandler, ...after, respond)
245
323
  }
246
-
247
324
  if (config.enableAll || config.count) {
248
- const opConfig = config.count || defaultOpConfig
325
+ const opConfig: OperationConfigLike = (config.count as OperationConfigLike | undefined) ?? defaultOpConfig
249
326
  const { before = [], after = [] } = opConfig
250
327
  const path = basePath ? \`\${basePath}/count\` : '/count'
251
- router.get(path, parseQuery, setShape(opConfig), ...before, ${prefix}Count as RequestHandler, ...after, respond)
252
- if (postReadsEnabled) {
253
- router.post(path, parseBodyAsQuery, setShape(opConfig), ...before, ${prefix}Count as RequestHandler, ...after, respond)
254
- }
328
+ router.get(path, parseQuery, setShape(opConfig), ...before, maybeProgressiveSSE(opConfig, core.count), ${prefix}Count as RequestHandler, ...after, respond)
329
+ if (postReadsEnabled) router.post(path, parseBodyAsQuery, setShape(opConfig), ...before, ${prefix}Count as RequestHandler, ...after, respond)
255
330
  }
256
-
257
331
  if (config.enableAll || config.groupBy) {
258
- const opConfig = config.groupBy || defaultOpConfig
332
+ const opConfig: OperationConfigLike = (config.groupBy as OperationConfigLike | undefined) ?? defaultOpConfig
259
333
  const { before = [], after = [] } = opConfig
260
334
  const path = basePath ? \`\${basePath}/groupby\` : '/groupby'
261
- router.get(path, parseQuery, setShape(opConfig), ...before, ${prefix}GroupBy as RequestHandler, ...after, respond)
262
- if (postReadsEnabled) {
263
- router.post(path, parseBodyAsQuery, setShape(opConfig), ...before, ${prefix}GroupBy as RequestHandler, ...after, respond)
264
- }
335
+ router.get(path, parseQuery, setShape(opConfig), ...before, maybeProgressiveSSE(opConfig, core.groupBy), ${prefix}GroupBy as RequestHandler, ...after, respond)
336
+ if (postReadsEnabled) router.post(path, parseBodyAsQuery, setShape(opConfig), ...before, ${prefix}GroupBy as RequestHandler, ...after, respond)
265
337
  }
266
-
267
338
  if (config.enableAll || config.findUniqueOrThrow) {
268
- const opConfig = config.findUniqueOrThrow || defaultOpConfig
339
+ const opConfig: OperationConfigLike = (config.findUniqueOrThrow as OperationConfigLike | undefined) ?? defaultOpConfig
269
340
  const { before = [], after = [] } = opConfig
270
341
  const path = basePath ? \`\${basePath}/unique/strict\` : '/unique/strict'
271
- router.get(path, parseQuery, setShape(opConfig), ...before, ${prefix}FindUniqueOrThrow as RequestHandler, ...after, respond)
272
- if (postReadsEnabled) {
273
- router.post(path, parseBodyAsQuery, setShape(opConfig), ...before, ${prefix}FindUniqueOrThrow as RequestHandler, ...after, respond)
274
- }
342
+ router.get(path, parseQuery, setShape(opConfig), ...before, maybeProgressiveSSE(opConfig, core.findUniqueOrThrow), ${prefix}FindUniqueOrThrow as RequestHandler, ...after, respond)
343
+ if (postReadsEnabled) router.post(path, parseBodyAsQuery, setShape(opConfig), ...before, ${prefix}FindUniqueOrThrow as RequestHandler, ...after, respond)
275
344
  }
276
-
277
345
  if (config.enableAll || config.findUnique) {
278
- const opConfig = config.findUnique || defaultOpConfig
346
+ const opConfig: OperationConfigLike = (config.findUnique as OperationConfigLike | undefined) ?? defaultOpConfig
279
347
  const { before = [], after = [] } = opConfig
280
348
  const path = basePath ? \`\${basePath}/unique\` : '/unique'
281
- router.get(path, parseQuery, setShape(opConfig), ...before, ${prefix}FindUnique as RequestHandler, ...after, respond)
282
- if (postReadsEnabled) {
283
- router.post(path, parseBodyAsQuery, setShape(opConfig), ...before, ${prefix}FindUnique as RequestHandler, ...after, respond)
284
- }
349
+ router.get(path, parseQuery, setShape(opConfig), ...before, maybeProgressiveSSE(opConfig, core.findUnique), ${prefix}FindUnique as RequestHandler, ...after, respond)
350
+ if (postReadsEnabled) router.post(path, parseBodyAsQuery, setShape(opConfig), ...before, ${prefix}FindUnique as RequestHandler, ...after, respond)
285
351
  }
286
-
287
352
  if (config.enableAll || config.findMany) {
288
- const opConfig = config.findMany || defaultOpConfig
353
+ const opConfig: OperationConfigLike = (config.findMany as OperationConfigLike | undefined) ?? defaultOpConfig
289
354
  const { before = [], after = [] } = opConfig
290
355
  const path = basePath || '/'
291
- router.get(path, parseQuery, setShape(opConfig), ...before, ${prefix}FindMany as RequestHandler, ...after, respond)
356
+ router.get(path, parseQuery, setShape(opConfig), ...before, maybeProgressiveSSE(opConfig, core.findMany), ${prefix}FindMany as RequestHandler, ...after, respond)
292
357
  if (postReadsEnabled) {
293
358
  const postPath = basePath ? \`\${basePath}/read\` : '/read'
294
359
  router.post(postPath, parseBodyAsQuery, setShape(opConfig), ...before, ${prefix}FindMany as RequestHandler, ...after, respond)
@@ -296,74 +361,65 @@ export function ${routerFunctionName}<TCtx = unknown>(config: ${modelName}RouteC
296
361
  }
297
362
 
298
363
  if (config.enableAll || config.createManyAndReturn) {
299
- const opConfig = config.createManyAndReturn || defaultOpConfig
364
+ const opConfig: OperationConfigLike = (config.createManyAndReturn as OperationConfigLike | undefined) ?? defaultOpConfig
300
365
  const { before = [], after = [] } = opConfig
301
366
  const path = basePath ? \`\${basePath}/many/return\` : '/many/return'
302
367
  router.post(path, setShape(opConfig), ...before, ${prefix}CreateManyAndReturn as RequestHandler, ...after, respondCreated)
303
368
  }
304
-
305
369
  if (config.enableAll || config.createMany) {
306
- const opConfig = config.createMany || defaultOpConfig
370
+ const opConfig: OperationConfigLike = (config.createMany as OperationConfigLike | undefined) ?? defaultOpConfig
307
371
  const { before = [], after = [] } = opConfig
308
372
  const path = basePath ? \`\${basePath}/many\` : '/many'
309
373
  router.post(path, setShape(opConfig), ...before, ${prefix}CreateMany as RequestHandler, ...after, respondCreated)
310
374
  }
311
-
312
375
  if (config.enableAll || config.create) {
313
- const opConfig = config.create || defaultOpConfig
376
+ const opConfig: OperationConfigLike = (config.create as OperationConfigLike | undefined) ?? defaultOpConfig
314
377
  const { before = [], after = [] } = opConfig
315
378
  const path = basePath || '/'
316
379
  router.post(path, setShape(opConfig), ...before, ${prefix}Create as RequestHandler, ...after, respondCreated)
317
380
  }
318
-
319
381
  if (config.enableAll || config.updateManyAndReturn) {
320
- const opConfig = config.updateManyAndReturn || defaultOpConfig
382
+ const opConfig: OperationConfigLike = (config.updateManyAndReturn as OperationConfigLike | undefined) ?? defaultOpConfig
321
383
  const { before = [], after = [] } = opConfig
322
384
  const path = basePath ? \`\${basePath}/many/return\` : '/many/return'
323
385
  router.put(path, setShape(opConfig), ...before, ${prefix}UpdateManyAndReturn as RequestHandler, ...after, respond)
324
386
  }
325
-
326
387
  if (config.enableAll || config.updateMany) {
327
- const opConfig = config.updateMany || defaultOpConfig
388
+ const opConfig: OperationConfigLike = (config.updateMany as OperationConfigLike | undefined) ?? defaultOpConfig
328
389
  const { before = [], after = [] } = opConfig
329
390
  const path = basePath ? \`\${basePath}/many\` : '/many'
330
391
  router.put(path, setShape(opConfig), ...before, ${prefix}UpdateMany as RequestHandler, ...after, respond)
331
392
  }
332
-
333
393
  if (config.enableAll || config.update) {
334
- const opConfig = config.update || defaultOpConfig
394
+ const opConfig: OperationConfigLike = (config.update as OperationConfigLike | undefined) ?? defaultOpConfig
335
395
  const { before = [], after = [] } = opConfig
336
396
  const path = basePath || '/'
337
397
  router.put(path, setShape(opConfig), ...before, ${prefix}Update as RequestHandler, ...after, respond)
338
398
  }
339
-
340
399
  if (config.enableAll || config.upsert) {
341
- const opConfig = config.upsert || defaultOpConfig
400
+ const opConfig: OperationConfigLike = (config.upsert as OperationConfigLike | undefined) ?? defaultOpConfig
342
401
  const { before = [], after = [] } = opConfig
343
402
  const path = basePath || '/'
344
403
  router.patch(path, setShape(opConfig), ...before, ${prefix}Upsert as RequestHandler, ...after, respond)
345
404
  }
346
-
347
405
  if (config.enableAll || config.deleteMany) {
348
- const opConfig = config.deleteMany || defaultOpConfig
406
+ const opConfig: OperationConfigLike = (config.deleteMany as OperationConfigLike | undefined) ?? defaultOpConfig
349
407
  const { before = [], after = [] } = opConfig
350
408
  const path = basePath ? \`\${basePath}/many\` : '/many'
351
409
  router.delete(path, setShape(opConfig), ...before, ${prefix}DeleteMany as RequestHandler, ...after, respond)
352
410
  }
353
-
354
411
  if (config.enableAll || config.delete) {
355
- const opConfig = config.delete || defaultOpConfig
412
+ const opConfig: OperationConfigLike = (config.delete as OperationConfigLike | undefined) ?? defaultOpConfig
356
413
  const { before = [], after = [] } = opConfig
357
414
  const path = basePath || '/'
358
415
  router.delete(path, setShape(opConfig), ...before, ${prefix}Delete as RequestHandler, ...after, respond)
359
416
  }
360
417
 
361
- router.use((err: any, _req: Request, res: Response, next: NextFunction) => {
362
- const status = typeof err.status === 'number' ? err.status : 500
363
- const message = err.message || 'Internal server error'
364
- if (!res.headersSent) {
365
- return res.status(status).json({ message })
366
- }
418
+ router.use((err: unknown, _req: Request, res: Response, next: NextFunction) => {
419
+ const e = err as { status?: number; message?: string }
420
+ const status = typeof e.status === 'number' ? e.status : 500
421
+ const message = e.message || 'Internal server error'
422
+ if (!res.headersSent) return res.status(status).json({ message })
367
423
  next(err)
368
424
  })
369
425