prisma-generator-express 1.57.0 → 1.58.1
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/README.md +111 -29
- package/dist/copy/misc.js +25 -6
- package/dist/copy/misc.js.map +1 -1
- package/dist/generators/generateFastifyHandler.js +11 -0
- package/dist/generators/generateFastifyHandler.js.map +1 -1
- package/dist/generators/generateHonoHandler.js +14 -20
- package/dist/generators/generateHonoHandler.js.map +1 -1
- package/dist/generators/generateImportPrismaStatement.js +7 -1
- package/dist/generators/generateImportPrismaStatement.js.map +1 -1
- package/dist/generators/generateOperationCore.js +58 -17
- package/dist/generators/generateOperationCore.js.map +1 -1
- package/dist/generators/generateRouteConfigType.js +12 -7
- package/dist/generators/generateRouteConfigType.js.map +1 -1
- package/dist/generators/generateRouter.js +57 -32
- package/dist/generators/generateRouter.js.map +1 -1
- package/dist/generators/generateRouterFastify.js +235 -191
- package/dist/generators/generateRouterFastify.js.map +1 -1
- package/dist/generators/generateRouterHono.d.ts +2 -3
- package/dist/generators/generateRouterHono.js +131 -89
- package/dist/generators/generateRouterHono.js.map +1 -1
- package/dist/index.js +8 -6
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/copy/autoIncludeRuntime.ts +9 -5
- package/src/copy/buildModelOpenApi.ts +96 -0
- package/src/copy/docsRenderer.ts +577 -174
- package/src/copy/materializedRouter.ts +40 -1
- package/src/copy/misc.ts +23 -6
- package/src/copy/operationDefinitions.ts +10 -0
- package/src/copy/operationRuntime.ts +28 -9
- package/src/copy/routeConfig.express.ts +9 -9
- package/src/copy/routeConfig.hono.ts +63 -5
- package/src/copy/routeConfig.ts +44 -22
- package/src/generators/generateFastifyHandler.ts +12 -0
- package/src/generators/generateHonoHandler.ts +15 -20
- package/src/generators/generateImportPrismaStatement.ts +10 -1
- package/src/generators/generateOperationCore.ts +58 -17
- package/src/generators/generateRouteConfigType.ts +13 -8
- package/src/generators/generateRouter.ts +57 -32
- package/src/generators/generateRouterFastify.ts +235 -191
- package/src/generators/generateRouterHono.ts +131 -91
- package/src/index.ts +11 -7
|
@@ -2,7 +2,7 @@ import { DMMF } from '@prisma/generator-helper'
|
|
|
2
2
|
import { generateRouteConfigType } from './generateRouteConfigType'
|
|
3
3
|
import { ImportStyle } from '../utils/resolveImportStyle'
|
|
4
4
|
import { importExt } from '../utils/importExt'
|
|
5
|
-
import { WriteStrategy
|
|
5
|
+
import { WriteStrategy } from '../constants'
|
|
6
6
|
|
|
7
7
|
export function generateHonoRouterFunction({
|
|
8
8
|
model,
|
|
@@ -10,7 +10,6 @@ export function generateHonoRouterFunction({
|
|
|
10
10
|
guardShapesImport,
|
|
11
11
|
importStyle,
|
|
12
12
|
writeStrategy,
|
|
13
|
-
findManyPaginatedMode,
|
|
14
13
|
dropGuard,
|
|
15
14
|
}: {
|
|
16
15
|
model: DMMF.Model
|
|
@@ -18,7 +17,6 @@ export function generateHonoRouterFunction({
|
|
|
18
17
|
guardShapesImport: string | null
|
|
19
18
|
importStyle: ImportStyle
|
|
20
19
|
writeStrategy: WriteStrategy
|
|
21
|
-
findManyPaginatedMode: FindManyPaginatedMode
|
|
22
20
|
dropGuard: boolean
|
|
23
21
|
}): string {
|
|
24
22
|
const ext = importExt(importStyle)
|
|
@@ -50,10 +48,9 @@ export function generateHonoRouterFunction({
|
|
|
50
48
|
}))
|
|
51
49
|
|
|
52
50
|
return `import { Hono } from 'hono'
|
|
53
|
-
import type { Context
|
|
51
|
+
import type { Context } from 'hono'
|
|
54
52
|
import type { ContentfulStatusCode } from 'hono/utils/http-status'
|
|
55
53
|
import { HTTPException } from 'hono/http-exception'
|
|
56
|
-
import { startQueryBuilder } from '../queryBuilder${ext}'
|
|
57
54
|
import {
|
|
58
55
|
${modelName}FindUnique,
|
|
59
56
|
${modelName}FindUniqueOrThrow,
|
|
@@ -73,41 +70,49 @@ import {
|
|
|
73
70
|
${modelName}Aggregate,
|
|
74
71
|
${modelName}Count,
|
|
75
72
|
${modelName}GroupBy,
|
|
73
|
+
${modelName}UpdateEach,
|
|
76
74
|
} from './${modelName}Handlers${ext}'
|
|
77
75
|
import type {
|
|
78
76
|
RouteConfig,
|
|
79
|
-
|
|
77
|
+
HonoBeforeHook,
|
|
78
|
+
HonoAfterHook,
|
|
80
79
|
HonoEnvBase,
|
|
81
80
|
HonoInternalVariables,
|
|
82
81
|
GeneratedHonoEnv,
|
|
83
82
|
WriteStrategy,
|
|
84
|
-
FindManyPaginatedMode,
|
|
85
83
|
PaginationConfig,
|
|
86
84
|
} from '../routeConfig.target${ext}'
|
|
87
85
|
import { parseQueryParams } from '../parseQueryParams${ext}'
|
|
88
|
-
import {
|
|
86
|
+
import { normalizePrefix, getEnv, sanitizeKeys } from '../misc${ext}'
|
|
89
87
|
import { buildModelOpenApi } from '../buildModelOpenApi${ext}'
|
|
88
|
+
import { validateCountSourceWhere } from '../routeConfig${ext}'
|
|
90
89
|
import {
|
|
91
90
|
mapError,
|
|
92
91
|
transformResult,
|
|
93
92
|
mergePaginationConfig,
|
|
94
|
-
type OperationContext,
|
|
95
93
|
} from '../operationRuntime${ext}'
|
|
96
94
|
|
|
97
|
-
${generateRouteConfigType(modelName, '
|
|
95
|
+
${generateRouteConfigType(modelName, 'HonoBeforeHook', guardShapesImport, importStyle, 'hono')}
|
|
98
96
|
const _env = getEnv()
|
|
99
97
|
|
|
100
98
|
const WRITE_STRATEGY: WriteStrategy = '${writeStrategy}'
|
|
101
|
-
const FIND_MANY_PAGINATED_MODE: FindManyPaginatedMode = '${findManyPaginatedMode}'
|
|
102
99
|
const DROP_GUARD = ${dropGuard} || _env.E2E === 'true'
|
|
103
100
|
|
|
101
|
+
type JsonLike =
|
|
102
|
+
| string
|
|
103
|
+
| number
|
|
104
|
+
| boolean
|
|
105
|
+
| null
|
|
106
|
+
| JsonLike[]
|
|
107
|
+
| { [k: string]: JsonLike }
|
|
108
|
+
|
|
104
109
|
const MODEL_FIELDS = ${JSON.stringify(fieldsMeta, null, 2)} as const
|
|
105
110
|
|
|
106
111
|
const MODEL_ENUMS = ${JSON.stringify(enumsMeta, null, 2)} as const
|
|
107
112
|
|
|
108
113
|
type OperationConfigLike<TEnv extends HonoEnvBase> = {
|
|
109
|
-
before?:
|
|
110
|
-
after?:
|
|
114
|
+
before?: HonoBeforeHook<TEnv>[]
|
|
115
|
+
after?: HonoAfterHook<TEnv>[]
|
|
111
116
|
shape?: Record<string, unknown>
|
|
112
117
|
pagination?: Partial<PaginationConfig>
|
|
113
118
|
}
|
|
@@ -117,20 +122,7 @@ const defaultOpConfig = Object.freeze({
|
|
|
117
122
|
after: Object.freeze([]),
|
|
118
123
|
}) as unknown as OperationConfigLike<HonoEnvBase>
|
|
119
124
|
|
|
120
|
-
type HandlerContext = Context<{ Variables: HonoInternalVariables
|
|
121
|
-
|
|
122
|
-
function isQueryBuilderEnabled(config: RouteConfig): boolean {
|
|
123
|
-
if (config.queryBuilder === false) return false
|
|
124
|
-
if (typeof config.queryBuilder === 'object' && config.queryBuilder.enabled === false) return false
|
|
125
|
-
if (_env.NODE_ENV === 'production') return false
|
|
126
|
-
return true
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
function getQueryBuilderConfig(config: RouteConfig) {
|
|
130
|
-
if (config.queryBuilder === false) return null
|
|
131
|
-
if (typeof config.queryBuilder === 'object') return config.queryBuilder
|
|
132
|
-
return {}
|
|
133
|
-
}
|
|
125
|
+
type HandlerContext = Context<{ Variables: HonoInternalVariables }>
|
|
134
126
|
|
|
135
127
|
async function parseQueryMiddleware(c: HandlerContext): Promise<void> {
|
|
136
128
|
const raw = c.req.query() as Record<string, unknown>
|
|
@@ -162,7 +154,20 @@ async function parseWriteBodyMiddleware(c: HandlerContext): Promise<void> {
|
|
|
162
154
|
if (!body || typeof body !== 'object' || Array.isArray(body)) {
|
|
163
155
|
throw new HTTPException(400, { message: 'Request body must be a JSON object' })
|
|
164
156
|
}
|
|
165
|
-
c.set('body',
|
|
157
|
+
c.set('body', body)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
async function parseUpdateEachBodyMiddleware(c: HandlerContext): Promise<void> {
|
|
161
|
+
let body: unknown
|
|
162
|
+
try {
|
|
163
|
+
body = await c.req.json()
|
|
164
|
+
} catch {
|
|
165
|
+
throw new HTTPException(400, { message: 'updateEach body must be an array of { where, data } items' })
|
|
166
|
+
}
|
|
167
|
+
if (!Array.isArray(body)) {
|
|
168
|
+
throw new HTTPException(400, { message: 'updateEach body must be an array of { where, data } items' })
|
|
169
|
+
}
|
|
170
|
+
c.set('body', body)
|
|
166
171
|
}
|
|
167
172
|
|
|
168
173
|
function makeShapeMiddleware<TCtx, TPrisma, TEnv extends HonoEnvBase>(
|
|
@@ -174,7 +179,6 @@ function makeShapeMiddleware<TCtx, TPrisma, TEnv extends HonoEnvBase>(
|
|
|
174
179
|
if (merged) {
|
|
175
180
|
c.set('routeConfig', { pagination: merged })
|
|
176
181
|
}
|
|
177
|
-
;(c as unknown as HandlerContext).set('findManyPaginatedMode', FIND_MANY_PAGINATED_MODE)
|
|
178
182
|
const headerName = config.guard?.variantHeader || 'x-api-variant'
|
|
179
183
|
const headerValue = c.req.header(headerName)
|
|
180
184
|
const caller = config.guard?.resolveVariant?.(c) ?? headerValue ?? undefined
|
|
@@ -185,26 +189,24 @@ function makeShapeMiddleware<TCtx, TPrisma, TEnv extends HonoEnvBase>(
|
|
|
185
189
|
}
|
|
186
190
|
}
|
|
187
191
|
|
|
188
|
-
async function
|
|
189
|
-
hooks:
|
|
192
|
+
async function runBeforeHooks<TEnv extends HonoEnvBase>(
|
|
193
|
+
hooks: HonoBeforeHook<TEnv>[],
|
|
190
194
|
c: Context<GeneratedHonoEnv<TEnv>>,
|
|
191
195
|
): Promise<Response | undefined> {
|
|
192
196
|
for (const hook of hooks) {
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
197
|
+
const result = await hook(c)
|
|
198
|
+
if (result instanceof Response) return result
|
|
199
|
+
}
|
|
200
|
+
return undefined
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
async function runAfterHooks<TEnv extends HonoEnvBase>(
|
|
204
|
+
hooks: HonoAfterHook<TEnv>[],
|
|
205
|
+
c: Context<GeneratedHonoEnv<TEnv>>,
|
|
206
|
+
): Promise<Response | undefined> {
|
|
207
|
+
for (const hook of hooks) {
|
|
208
|
+
const result = await hook(c)
|
|
198
209
|
if (result instanceof Response) return result
|
|
199
|
-
if (!advanced) {
|
|
200
|
-
if (_env.NODE_ENV !== 'production') {
|
|
201
|
-
console.warn(
|
|
202
|
-
'[hono-router] Hook returned without calling next() or returning a Response. ' +
|
|
203
|
-
'Use \`return c.json(...)\` to short-circuit, or \`await next()\` to continue.',
|
|
204
|
-
)
|
|
205
|
-
}
|
|
206
|
-
return c.body(null) ?? undefined
|
|
207
|
-
}
|
|
208
210
|
}
|
|
209
211
|
return undefined
|
|
210
212
|
}
|
|
@@ -215,73 +217,85 @@ function sendResult(c: HandlerContext): Response {
|
|
|
215
217
|
if (data === undefined) {
|
|
216
218
|
throw new HTTPException(500, { message: 'No data set by handler' })
|
|
217
219
|
}
|
|
218
|
-
return c.json(transformResult(data) as
|
|
220
|
+
return c.json(transformResult(data) as JsonLike, status as ContentfulStatusCode)
|
|
219
221
|
}
|
|
220
222
|
|
|
221
223
|
function sendError(c: HandlerContext, error: unknown): Response {
|
|
224
|
+
if (error instanceof HTTPException) {
|
|
225
|
+
return c.json({ message: error.message }, error.status as ContentfulStatusCode)
|
|
226
|
+
}
|
|
222
227
|
const httpError = mapError(error)
|
|
223
228
|
return c.json({ message: httpError.message }, httpError.status as ContentfulStatusCode)
|
|
224
229
|
}
|
|
225
230
|
|
|
226
231
|
export function ${routerFunctionName}<TCtx = unknown, TPrisma = any, TEnv extends HonoEnvBase = HonoEnvBase>(config: ${modelName}RouteConfig<TCtx, TPrisma, TEnv> = {}): Hono<GeneratedHonoEnv<TEnv>> {
|
|
232
|
+
validateCountSourceWhere(config.pagination?.countSource, '${modelName} pagination')
|
|
233
|
+
validateCountSourceWhere(
|
|
234
|
+
(config.findManyPaginated && typeof config.findManyPaginated === 'object' ? config.findManyPaginated : undefined)?.pagination?.countSource,
|
|
235
|
+
'${modelName} findManyPaginated pagination',
|
|
236
|
+
)
|
|
237
|
+
|
|
227
238
|
const app = new Hono<GeneratedHonoEnv<TEnv>>()
|
|
228
239
|
|
|
240
|
+
const isEnabled = (value: unknown): boolean => value !== false && !!(config.enableAll || value)
|
|
241
|
+
|
|
229
242
|
const customPrefix = normalizePrefix(config.customUrlPrefix || '')
|
|
230
243
|
const modelPrefix = config.addModelPrefix !== false ? '/${modelNameLower}' : ''
|
|
231
244
|
const basePath = customPrefix + modelPrefix
|
|
232
245
|
|
|
233
246
|
const openApiDisabled = config.disableOpenApi === true
|
|
234
247
|
|| (config.disableOpenApi !== false && (
|
|
235
|
-
_env.
|
|
236
|
-
|| _env.
|
|
248
|
+
_env.NODE_ENV === 'production'
|
|
249
|
+
|| _env.DISABLE_OPENAPI === 'true'
|
|
237
250
|
))
|
|
238
251
|
|
|
239
252
|
const postReadsEnabled = !config.disablePostReads
|
|
240
253
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
254
|
+
let _openApiJsonCache: unknown = undefined
|
|
255
|
+
const getOpenApiJson = (): unknown => {
|
|
256
|
+
if (_openApiJsonCache === undefined) {
|
|
257
|
+
_openApiJsonCache = buildModelOpenApi(
|
|
244
258
|
'${modelName}',
|
|
245
259
|
MODEL_FIELDS as unknown as Parameters<typeof buildModelOpenApi>[1],
|
|
246
260
|
MODEL_ENUMS as unknown as Parameters<typeof buildModelOpenApi>[2],
|
|
247
261
|
config as RouteConfig,
|
|
248
262
|
{ format: 'json', writeStrategy: WRITE_STRATEGY },
|
|
249
263
|
)
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
264
|
+
}
|
|
265
|
+
return _openApiJsonCache
|
|
266
|
+
}
|
|
267
|
+
let _openApiYamlCache: string | undefined = undefined
|
|
268
|
+
const getOpenApiYaml = (): string => {
|
|
269
|
+
if (_openApiYamlCache === undefined) {
|
|
270
|
+
_openApiYamlCache = buildModelOpenApi(
|
|
253
271
|
'${modelName}',
|
|
254
272
|
MODEL_FIELDS as unknown as Parameters<typeof buildModelOpenApi>[1],
|
|
255
273
|
MODEL_ENUMS as unknown as Parameters<typeof buildModelOpenApi>[2],
|
|
256
274
|
config as RouteConfig,
|
|
257
275
|
{ format: 'yaml', writeStrategy: WRITE_STRATEGY },
|
|
258
|
-
)
|
|
259
|
-
|
|
260
|
-
if (isQueryBuilderEnabled(config as RouteConfig)) {
|
|
261
|
-
const qbConfig = getQueryBuilderConfig(config as RouteConfig)
|
|
262
|
-
if (qbConfig) {
|
|
263
|
-
try {
|
|
264
|
-
startQueryBuilder(qbConfig)
|
|
265
|
-
} catch (err) {
|
|
266
|
-
if (_env.NODE_ENV !== 'production') console.warn('[query-builder]', err)
|
|
267
|
-
}
|
|
276
|
+
) as string
|
|
268
277
|
}
|
|
278
|
+
return _openApiYamlCache
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
if (config.queryBuilder && config.queryBuilder !== false && _env.NODE_ENV !== 'production') {
|
|
282
|
+
console.warn(
|
|
283
|
+
'[${modelName}Router] queryBuilder config is present but Hono target does not auto-start it. ' +
|
|
284
|
+
'Run \`npx prisma-query-builder-ui\` in a separate process.',
|
|
285
|
+
)
|
|
269
286
|
}
|
|
270
287
|
|
|
271
288
|
app.onError((err, c) => {
|
|
272
|
-
if (err instanceof HTTPException) {
|
|
273
|
-
return c.json({ message: err.message }, err.status as ContentfulStatusCode)
|
|
274
|
-
}
|
|
275
289
|
return sendError(c as HandlerContext, err)
|
|
276
290
|
})
|
|
277
291
|
|
|
278
292
|
if (!openApiDisabled) {
|
|
279
293
|
const openapiJsonPath = basePath ? \`\${basePath}/openapi.json\` : '/openapi.json'
|
|
280
294
|
const openapiYamlPath = basePath ? \`\${basePath}/openapi.yaml\` : '/openapi.yaml'
|
|
281
|
-
app.get(openapiJsonPath, (c) => c.json(
|
|
295
|
+
app.get(openapiJsonPath, (c) => c.json(getOpenApiJson() as JsonLike))
|
|
282
296
|
app.get(openapiYamlPath, (c) => {
|
|
283
297
|
c.header('Content-Type', 'application/yaml')
|
|
284
|
-
return c.body(
|
|
298
|
+
return c.body(getOpenApiYaml())
|
|
285
299
|
})
|
|
286
300
|
}
|
|
287
301
|
|
|
@@ -294,10 +308,10 @@ export function ${routerFunctionName}<TCtx = unknown, TPrisma = any, TEnv extend
|
|
|
294
308
|
await parseFn(c as unknown as HandlerContext)
|
|
295
309
|
makeShapeMiddleware<TCtx, TPrisma, TEnv>(config, opConfig)(c)
|
|
296
310
|
const { before = [], after = [] } = opConfig
|
|
297
|
-
const beforeResp = await
|
|
311
|
+
const beforeResp = await runBeforeHooks<TEnv>(before, c)
|
|
298
312
|
if (beforeResp) return beforeResp
|
|
299
313
|
await handlerFn(c as unknown as HandlerContext)
|
|
300
|
-
const afterResp = await
|
|
314
|
+
const afterResp = await runAfterHooks<TEnv>(after, c)
|
|
301
315
|
if (afterResp) return afterResp
|
|
302
316
|
return sendResult(c as unknown as HandlerContext)
|
|
303
317
|
} catch (error: unknown) {
|
|
@@ -313,10 +327,10 @@ export function ${routerFunctionName}<TCtx = unknown, TPrisma = any, TEnv extend
|
|
|
313
327
|
await parseWriteBodyMiddleware(c as unknown as HandlerContext)
|
|
314
328
|
makeShapeMiddleware<TCtx, TPrisma, TEnv>(config, opConfig)(c)
|
|
315
329
|
const { before = [], after = [] } = opConfig
|
|
316
|
-
const beforeResp = await
|
|
330
|
+
const beforeResp = await runBeforeHooks<TEnv>(before, c)
|
|
317
331
|
if (beforeResp) return beforeResp
|
|
318
332
|
await handlerFn(c as unknown as HandlerContext)
|
|
319
|
-
const afterResp = await
|
|
333
|
+
const afterResp = await runAfterHooks<TEnv>(after, c)
|
|
320
334
|
if (afterResp) return afterResp
|
|
321
335
|
return sendResult(c as unknown as HandlerContext)
|
|
322
336
|
} catch (error: unknown) {
|
|
@@ -329,55 +343,55 @@ export function ${routerFunctionName}<TCtx = unknown, TPrisma = any, TEnv extend
|
|
|
329
343
|
?? (defaultOpConfig as OperationConfigLike<TEnv>)
|
|
330
344
|
}
|
|
331
345
|
|
|
332
|
-
if (config.
|
|
346
|
+
if (isEnabled(config.findFirst)) {
|
|
333
347
|
const opConfig = opFor('findFirst')
|
|
334
348
|
const path = basePath ? \`\${basePath}/first\` : '/first'
|
|
335
349
|
app.get(path, handleRead(opConfig, ${modelName}FindFirst, parseQueryMiddleware))
|
|
336
350
|
if (postReadsEnabled) app.post(path, handleRead(opConfig, ${modelName}FindFirst, parseBodyAsQueryMiddleware))
|
|
337
351
|
}
|
|
338
|
-
if (config.
|
|
352
|
+
if (isEnabled(config.findFirstOrThrow)) {
|
|
339
353
|
const opConfig = opFor('findFirstOrThrow')
|
|
340
354
|
const path = basePath ? \`\${basePath}/first/strict\` : '/first/strict'
|
|
341
355
|
app.get(path, handleRead(opConfig, ${modelName}FindFirstOrThrow, parseQueryMiddleware))
|
|
342
356
|
if (postReadsEnabled) app.post(path, handleRead(opConfig, ${modelName}FindFirstOrThrow, parseBodyAsQueryMiddleware))
|
|
343
357
|
}
|
|
344
|
-
if (config.
|
|
358
|
+
if (isEnabled(config.findManyPaginated)) {
|
|
345
359
|
const opConfig = opFor('findManyPaginated')
|
|
346
360
|
const path = basePath ? \`\${basePath}/paginated\` : '/paginated'
|
|
347
361
|
app.get(path, handleRead(opConfig, ${modelName}FindManyPaginated, parseQueryMiddleware))
|
|
348
362
|
if (postReadsEnabled) app.post(path, handleRead(opConfig, ${modelName}FindManyPaginated, parseBodyAsQueryMiddleware))
|
|
349
363
|
}
|
|
350
|
-
if (config.
|
|
364
|
+
if (isEnabled(config.aggregate)) {
|
|
351
365
|
const opConfig = opFor('aggregate')
|
|
352
366
|
const path = basePath ? \`\${basePath}/aggregate\` : '/aggregate'
|
|
353
367
|
app.get(path, handleRead(opConfig, ${modelName}Aggregate, parseQueryMiddleware))
|
|
354
368
|
if (postReadsEnabled) app.post(path, handleRead(opConfig, ${modelName}Aggregate, parseBodyAsQueryMiddleware))
|
|
355
369
|
}
|
|
356
|
-
if (config.
|
|
370
|
+
if (isEnabled(config.count)) {
|
|
357
371
|
const opConfig = opFor('count')
|
|
358
372
|
const path = basePath ? \`\${basePath}/count\` : '/count'
|
|
359
373
|
app.get(path, handleRead(opConfig, ${modelName}Count, parseQueryMiddleware))
|
|
360
374
|
if (postReadsEnabled) app.post(path, handleRead(opConfig, ${modelName}Count, parseBodyAsQueryMiddleware))
|
|
361
375
|
}
|
|
362
|
-
if (config.
|
|
376
|
+
if (isEnabled(config.groupBy)) {
|
|
363
377
|
const opConfig = opFor('groupBy')
|
|
364
378
|
const path = basePath ? \`\${basePath}/groupby\` : '/groupby'
|
|
365
379
|
app.get(path, handleRead(opConfig, ${modelName}GroupBy, parseQueryMiddleware))
|
|
366
380
|
if (postReadsEnabled) app.post(path, handleRead(opConfig, ${modelName}GroupBy, parseBodyAsQueryMiddleware))
|
|
367
381
|
}
|
|
368
|
-
if (config.
|
|
382
|
+
if (isEnabled(config.findUniqueOrThrow)) {
|
|
369
383
|
const opConfig = opFor('findUniqueOrThrow')
|
|
370
384
|
const path = basePath ? \`\${basePath}/unique/strict\` : '/unique/strict'
|
|
371
385
|
app.get(path, handleRead(opConfig, ${modelName}FindUniqueOrThrow, parseQueryMiddleware))
|
|
372
386
|
if (postReadsEnabled) app.post(path, handleRead(opConfig, ${modelName}FindUniqueOrThrow, parseBodyAsQueryMiddleware))
|
|
373
387
|
}
|
|
374
|
-
if (config.
|
|
388
|
+
if (isEnabled(config.findUnique)) {
|
|
375
389
|
const opConfig = opFor('findUnique')
|
|
376
390
|
const path = basePath ? \`\${basePath}/unique\` : '/unique'
|
|
377
391
|
app.get(path, handleRead(opConfig, ${modelName}FindUnique, parseQueryMiddleware))
|
|
378
392
|
if (postReadsEnabled) app.post(path, handleRead(opConfig, ${modelName}FindUnique, parseBodyAsQueryMiddleware))
|
|
379
393
|
}
|
|
380
|
-
if (config.
|
|
394
|
+
if (isEnabled(config.findMany)) {
|
|
381
395
|
const opConfig = opFor('findMany')
|
|
382
396
|
const path = basePath || '/'
|
|
383
397
|
app.get(path, handleRead(opConfig, ${modelName}FindMany, parseQueryMiddleware))
|
|
@@ -387,52 +401,78 @@ export function ${routerFunctionName}<TCtx = unknown, TPrisma = any, TEnv extend
|
|
|
387
401
|
}
|
|
388
402
|
}
|
|
389
403
|
|
|
390
|
-
if (config.
|
|
404
|
+
if (isEnabled(config.createManyAndReturn)) {
|
|
391
405
|
const opConfig = opFor('createManyAndReturn')
|
|
392
406
|
const path = basePath ? \`\${basePath}/many/return\` : '/many/return'
|
|
393
407
|
app.post(path, handleWrite(opConfig, ${modelName}CreateManyAndReturn))
|
|
394
408
|
}
|
|
395
|
-
if (config.
|
|
409
|
+
if (isEnabled(config.createMany)) {
|
|
396
410
|
const opConfig = opFor('createMany')
|
|
397
411
|
const path = basePath ? \`\${basePath}/many\` : '/many'
|
|
398
412
|
app.post(path, handleWrite(opConfig, ${modelName}CreateMany))
|
|
399
413
|
}
|
|
400
|
-
if (config.
|
|
414
|
+
if (isEnabled(config.create)) {
|
|
401
415
|
const opConfig = opFor('create')
|
|
402
416
|
const path = basePath || '/'
|
|
403
417
|
app.post(path, handleWrite(opConfig, ${modelName}Create))
|
|
404
418
|
}
|
|
405
|
-
if (config.
|
|
419
|
+
if (isEnabled(config.updateManyAndReturn)) {
|
|
406
420
|
const opConfig = opFor('updateManyAndReturn')
|
|
407
421
|
const path = basePath ? \`\${basePath}/many/return\` : '/many/return'
|
|
408
422
|
app.put(path, handleWrite(opConfig, ${modelName}UpdateManyAndReturn))
|
|
409
423
|
}
|
|
410
|
-
if (config.
|
|
424
|
+
if (isEnabled(config.updateMany)) {
|
|
411
425
|
const opConfig = opFor('updateMany')
|
|
412
426
|
const path = basePath ? \`\${basePath}/many\` : '/many'
|
|
413
427
|
app.put(path, handleWrite(opConfig, ${modelName}UpdateMany))
|
|
414
428
|
}
|
|
415
|
-
if (config.
|
|
429
|
+
if (isEnabled(config.update)) {
|
|
416
430
|
const opConfig = opFor('update')
|
|
417
431
|
const path = basePath || '/'
|
|
418
432
|
app.put(path, handleWrite(opConfig, ${modelName}Update))
|
|
419
433
|
}
|
|
420
|
-
if (config.
|
|
434
|
+
if (isEnabled(config.upsert)) {
|
|
421
435
|
const opConfig = opFor('upsert')
|
|
422
436
|
const path = basePath || '/'
|
|
423
437
|
app.patch(path, handleWrite(opConfig, ${modelName}Upsert))
|
|
424
438
|
}
|
|
425
|
-
if (config.
|
|
439
|
+
if (isEnabled(config.deleteMany)) {
|
|
426
440
|
const opConfig = opFor('deleteMany')
|
|
427
441
|
const path = basePath ? \`\${basePath}/many\` : '/many'
|
|
428
442
|
app.delete(path, handleWrite(opConfig, ${modelName}DeleteMany))
|
|
429
443
|
}
|
|
430
|
-
if (config.
|
|
444
|
+
if (isEnabled(config.delete)) {
|
|
431
445
|
const opConfig = opFor('delete')
|
|
432
446
|
const path = basePath || '/'
|
|
433
447
|
app.delete(path, handleWrite(opConfig, ${modelName}Delete))
|
|
434
448
|
}
|
|
435
449
|
|
|
450
|
+
if (config.updateEach) {
|
|
451
|
+
const opConfig = (config.updateEach as unknown as OperationConfigLike<TEnv> | undefined) ?? (defaultOpConfig as OperationConfigLike<TEnv>)
|
|
452
|
+
if ((!opConfig.before || opConfig.before.length === 0) && _env.NODE_ENV !== 'production') {
|
|
453
|
+
console.warn(
|
|
454
|
+
'[${modelName}Router] updateEach is enabled without a before hook. ' +
|
|
455
|
+
'This endpoint bypasses guard shapes and should be protected by authentication middleware.',
|
|
456
|
+
)
|
|
457
|
+
}
|
|
458
|
+
const path = basePath ? \`\${basePath}/each\` : '/each'
|
|
459
|
+
app.post(path, async (c: Context<GeneratedHonoEnv<TEnv>>): Promise<Response> => {
|
|
460
|
+
try {
|
|
461
|
+
await parseUpdateEachBodyMiddleware(c as unknown as HandlerContext)
|
|
462
|
+
makeShapeMiddleware<TCtx, TPrisma, TEnv>(config, opConfig)(c)
|
|
463
|
+
const { before = [], after = [] } = opConfig
|
|
464
|
+
const beforeResp = await runBeforeHooks<TEnv>(before, c)
|
|
465
|
+
if (beforeResp) return beforeResp
|
|
466
|
+
await ${modelName}UpdateEach(c as unknown as HandlerContext)
|
|
467
|
+
const afterResp = await runAfterHooks<TEnv>(after, c)
|
|
468
|
+
if (afterResp) return afterResp
|
|
469
|
+
return sendResult(c as unknown as HandlerContext)
|
|
470
|
+
} catch (error: unknown) {
|
|
471
|
+
return sendError(c as unknown as HandlerContext, error)
|
|
472
|
+
}
|
|
473
|
+
})
|
|
474
|
+
}
|
|
475
|
+
|
|
436
476
|
return app
|
|
437
477
|
}
|
|
438
478
|
`
|
package/src/index.ts
CHANGED
|
@@ -32,7 +32,7 @@ import {
|
|
|
32
32
|
FindManyPaginatedMode,
|
|
33
33
|
} from './constants'
|
|
34
34
|
|
|
35
|
-
const GENERATOR_OFF_RE =
|
|
35
|
+
const GENERATOR_OFF_RE = /^\s*generator off\s*$/m
|
|
36
36
|
|
|
37
37
|
function getTarget(options: GeneratorOptions): Target {
|
|
38
38
|
const raw = String(
|
|
@@ -85,9 +85,12 @@ function getDropGuard(options: GeneratorOptions): boolean {
|
|
|
85
85
|
const lower = String(raw).toLowerCase()
|
|
86
86
|
if (lower === 'true' || lower === '1') return true
|
|
87
87
|
if (lower === 'false' || lower === '0') return false
|
|
88
|
-
|
|
89
|
-
`
|
|
88
|
+
console.warn(
|
|
89
|
+
`[prisma-generator-express] dropGuard="${raw}" is not a recognized boolean value. ` +
|
|
90
|
+
`Treating as false. Expected "true", "false", "1", "0", or "" (unset). ` +
|
|
91
|
+
`If you used env("VAR_NAME"), set VAR_NAME to "true" or "false".`,
|
|
90
92
|
)
|
|
93
|
+
return false
|
|
91
94
|
}
|
|
92
95
|
|
|
93
96
|
function validateClientGeneratorPresent(options: GeneratorOptions): void {
|
|
@@ -101,7 +104,7 @@ generatorHandler({
|
|
|
101
104
|
onManifest() {
|
|
102
105
|
return {
|
|
103
106
|
version: require('../package.json').version,
|
|
104
|
-
defaultOutput: '../generated/
|
|
107
|
+
defaultOutput: '../generated/output',
|
|
105
108
|
prettyName: GENERATOR_NAME,
|
|
106
109
|
}
|
|
107
110
|
},
|
|
@@ -116,7 +119,7 @@ generatorHandler({
|
|
|
116
119
|
__dirname,
|
|
117
120
|
'..',
|
|
118
121
|
'generated',
|
|
119
|
-
|
|
122
|
+
'output',
|
|
120
123
|
)
|
|
121
124
|
const currentOutput = options.generator.output?.value
|
|
122
125
|
const isUnsetOrManifestDefault =
|
|
@@ -136,7 +139,9 @@ generatorHandler({
|
|
|
136
139
|
console.log(` Import style: ${importStyle}`)
|
|
137
140
|
console.log(` Write strategy: ${writeStrategy}`)
|
|
138
141
|
console.log(` findManyPaginated mode: ${findManyPaginatedMode}`)
|
|
139
|
-
console.log(
|
|
142
|
+
console.log(
|
|
143
|
+
` Drop guard (generator): ${dropGuard}${dropGuard ? '' : ' (runtime E2E=true will also drop guard)'}`,
|
|
144
|
+
)
|
|
140
145
|
|
|
141
146
|
if (options.dmmf.datamodel.models.length > 0) {
|
|
142
147
|
validateClientGeneratorPresent(options)
|
|
@@ -205,7 +210,6 @@ generatorHandler({
|
|
|
205
210
|
guardShapesImport,
|
|
206
211
|
importStyle,
|
|
207
212
|
writeStrategy,
|
|
208
|
-
findManyPaginatedMode,
|
|
209
213
|
dropGuard,
|
|
210
214
|
})
|
|
211
215
|
: generateRouterFunction({
|