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