prisma-generator-express 1.59.0 → 1.60.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/client/encodeQueryParams.js +19 -23
- package/dist/client/encodeQueryParams.js.map +1 -1
- package/dist/copy/operationDefinitions.d.ts +37 -0
- package/dist/copy/operationDefinitions.js +412 -0
- package/dist/copy/operationDefinitions.js.map +1 -0
- package/dist/generators/generateFastifyHandler.js +10 -41
- package/dist/generators/generateFastifyHandler.js.map +1 -1
- package/dist/generators/generateHonoHandler.js +10 -41
- package/dist/generators/generateHonoHandler.js.map +1 -1
- package/dist/generators/generateModelMetadata.d.ts +8 -0
- package/dist/generators/generateModelMetadata.js +77 -0
- package/dist/generators/generateModelMetadata.js.map +1 -0
- package/dist/generators/generateOperationCore.js +20 -30
- package/dist/generators/generateOperationCore.js.map +1 -1
- package/dist/generators/generateQueryBuilderHelper.js +1 -1
- package/dist/generators/generateQueryBuilderHelper.js.map +1 -1
- package/dist/generators/generateRouteConfigType.js +11 -57
- package/dist/generators/generateRouteConfigType.js.map +1 -1
- package/dist/generators/generateRouter.js +64 -177
- package/dist/generators/generateRouter.js.map +1 -1
- package/dist/generators/generateRouterFastify.js +59 -169
- package/dist/generators/generateRouterFastify.js.map +1 -1
- package/dist/generators/generateRouterHono.js +52 -152
- package/dist/generators/generateRouterHono.js.map +1 -1
- package/dist/generators/generateUnifiedHandler.js +7 -30
- package/dist/generators/generateUnifiedHandler.js.map +1 -1
- package/dist/generators/generateUnifiedScalarUI.js +9 -74
- package/dist/generators/generateUnifiedScalarUI.js.map +1 -1
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -1
- package/dist/utils/copyFiles.js +7 -0
- package/dist/utils/copyFiles.js.map +1 -1
- package/dist/utils/writeFileSafely.js +5 -12
- package/dist/utils/writeFileSafely.js.map +1 -1
- package/package.json +12 -2
- package/src/client/encodeQueryParams.ts +23 -36
- package/src/copy/autoIncludePlanner.ts +14 -23
- package/src/copy/autoIncludeRuntime.ts +117 -228
- package/src/copy/buildModelOpenApi.ts +248 -628
- package/src/copy/concurrency.ts +20 -0
- package/src/copy/docsRenderer.ts +63 -333
- package/src/copy/errorMapper.ts +126 -0
- package/src/copy/guardHelpers.ts +56 -0
- package/src/copy/materializedCount.ts +68 -0
- package/src/copy/materializedRouter.ts +33 -29
- package/src/copy/operationDefinitions.ts +359 -35
- package/src/copy/operationRuntime.ts +11 -605
- package/src/copy/pagination.ts +151 -0
- package/src/copy/scalarTypes.ts +2 -0
- package/src/copy/sse.ts +296 -0
- package/src/generators/generateFastifyHandler.ts +13 -47
- package/src/generators/generateHonoHandler.ts +13 -47
- package/src/generators/generateModelMetadata.ts +92 -0
- package/src/generators/generateOperationCore.ts +19 -32
- package/src/generators/generateQueryBuilderHelper.ts +1 -1
- package/src/generators/generateRouteConfigType.ts +9 -60
- package/src/generators/generateRouter.ts +88 -180
- package/src/generators/generateRouterFastify.ts +65 -172
- package/src/generators/generateRouterHono.ts +58 -155
- package/src/generators/generateUnifiedHandler.ts +8 -33
- package/src/generators/generateUnifiedScalarUI.ts +9 -91
- package/src/index.ts +13 -1
- package/src/utils/copyFiles.ts +7 -0
- package/src/utils/writeFileSafely.ts +5 -11
|
@@ -3,6 +3,46 @@ import { generateRouteConfigType } from './generateRouteConfigType'
|
|
|
3
3
|
import { ImportStyle } from '../utils/resolveImportStyle'
|
|
4
4
|
import { importExt } from '../utils/importExt'
|
|
5
5
|
import { WriteStrategy, FindManyPaginatedMode } from '../constants'
|
|
6
|
+
import { OPERATION_METADATA } from '../copy/operationDefinitions'
|
|
7
|
+
|
|
8
|
+
function pathExpr(suffix: string): string {
|
|
9
|
+
if (!suffix) return `basePath || '/'`
|
|
10
|
+
return `\`\${basePath}${suffix}\``
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function emitReadOp(meta: (typeof OPERATION_METADATA)[number], modelName: string): string {
|
|
14
|
+
const c = meta.name.charAt(0).toUpperCase() + meta.name.slice(1)
|
|
15
|
+
const handlerName = `${modelName}${c}`
|
|
16
|
+
const pathValue = pathExpr(meta.pathSuffix)
|
|
17
|
+
|
|
18
|
+
const postReadLine = meta.supportsPostRead
|
|
19
|
+
? meta.name === 'findMany'
|
|
20
|
+
? ` if (postReadsEnabled) {
|
|
21
|
+
const postPath = basePath ? \`\${basePath}/read\` : '/read'
|
|
22
|
+
instance.post(postPath, handleRead(opConfig, ${handlerName}, parseBodyAsQueryHook))
|
|
23
|
+
}`
|
|
24
|
+
: ` if (postReadsEnabled) instance.post(path, handleRead(opConfig, ${handlerName}, parseBodyAsQueryHook))`
|
|
25
|
+
: ''
|
|
26
|
+
|
|
27
|
+
return ` if (isEnabled(config.${meta.configKey})) {
|
|
28
|
+
const opConfig: OperationConfigLike = (config.${meta.configKey} as OperationConfigLike | undefined) ?? defaultOpConfig
|
|
29
|
+
const path = ${pathValue}
|
|
30
|
+
instance.get(path, handleRead(opConfig, ${handlerName}, parseQueryHook))
|
|
31
|
+
${postReadLine}
|
|
32
|
+
}`
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function emitWriteOp(meta: (typeof OPERATION_METADATA)[number], modelName: string): string {
|
|
36
|
+
const c = meta.name.charAt(0).toUpperCase() + meta.name.slice(1)
|
|
37
|
+
const handlerName = `${modelName}${c}`
|
|
38
|
+
const pathValue = pathExpr(meta.pathSuffix)
|
|
39
|
+
|
|
40
|
+
return ` if (isEnabled(config.${meta.configKey})) {
|
|
41
|
+
const opConfig: OperationConfigLike = (config.${meta.configKey} as OperationConfigLike | undefined) ?? defaultOpConfig
|
|
42
|
+
const path = ${pathValue}
|
|
43
|
+
instance.${meta.method}(path, handleWrite(opConfig, ${handlerName}))
|
|
44
|
+
}`
|
|
45
|
+
}
|
|
6
46
|
|
|
7
47
|
export function generateFastifyRouterFunction({
|
|
8
48
|
model,
|
|
@@ -26,56 +66,25 @@ export function generateFastifyRouterFunction({
|
|
|
26
66
|
const modelNameLower = modelName.toLowerCase()
|
|
27
67
|
const routerFunctionName = `${modelName}Router`
|
|
28
68
|
|
|
29
|
-
const
|
|
30
|
-
name
|
|
31
|
-
|
|
32
|
-
type: f.type,
|
|
33
|
-
isList: f.isList,
|
|
34
|
-
isRequired: f.isRequired,
|
|
35
|
-
hasDefaultValue: f.hasDefaultValue,
|
|
36
|
-
isUpdatedAt: f.isUpdatedAt ?? false,
|
|
37
|
-
documentation: f.documentation,
|
|
38
|
-
relationFromFields: f.relationFromFields,
|
|
39
|
-
}))
|
|
40
|
-
|
|
41
|
-
const referencedEnumTypes = new Set(
|
|
42
|
-
model.fields.filter((f) => f.kind === 'enum').map((f) => f.type),
|
|
43
|
-
)
|
|
69
|
+
const handlerImports = OPERATION_METADATA
|
|
70
|
+
.map((m) => ` ${modelName}${m.name.charAt(0).toUpperCase() + m.name.slice(1)},`)
|
|
71
|
+
.join('\n')
|
|
44
72
|
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
.
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
73
|
+
const readOps = OPERATION_METADATA.filter((m) => m.kind === 'read')
|
|
74
|
+
const writeOps = OPERATION_METADATA.filter((m) => m.kind === 'write' || m.kind === 'batch')
|
|
75
|
+
.filter((m) => m.name !== 'updateEach')
|
|
76
|
+
|
|
77
|
+
const readOpBlocks = readOps.map((m) => emitReadOp(m, modelName)).join('\n\n')
|
|
78
|
+
const writeOpBlocks = writeOps.map((m) => emitWriteOp(m, modelName)).join('\n\n')
|
|
51
79
|
|
|
52
80
|
return `import type { FastifyInstance, FastifyRequest, FastifyReply, FastifyError } from 'fastify'
|
|
53
81
|
import { startQueryBuilder } from '../queryBuilder${ext}'
|
|
54
82
|
import {
|
|
55
|
-
|
|
56
|
-
${modelName}FindUniqueOrThrow,
|
|
57
|
-
${modelName}FindFirst,
|
|
58
|
-
${modelName}FindFirstOrThrow,
|
|
59
|
-
${modelName}FindMany,
|
|
60
|
-
${modelName}FindManyPaginated,
|
|
61
|
-
${modelName}Create,
|
|
62
|
-
${modelName}CreateMany,
|
|
63
|
-
${modelName}CreateManyAndReturn,
|
|
64
|
-
${modelName}Update,
|
|
65
|
-
${modelName}UpdateMany,
|
|
66
|
-
${modelName}UpdateManyAndReturn,
|
|
67
|
-
${modelName}Upsert,
|
|
68
|
-
${modelName}Delete,
|
|
69
|
-
${modelName}DeleteMany,
|
|
70
|
-
${modelName}Aggregate,
|
|
71
|
-
${modelName}Count,
|
|
72
|
-
${modelName}GroupBy,
|
|
73
|
-
${modelName}UpdateEach,
|
|
83
|
+
${handlerImports}
|
|
74
84
|
} from './${modelName}Handlers${ext}'
|
|
75
85
|
import type {
|
|
76
86
|
RouteConfig,
|
|
77
87
|
FastifyHookHandler,
|
|
78
|
-
WriteStrategy,
|
|
79
88
|
FindManyPaginatedMode,
|
|
80
89
|
PaginationConfig,
|
|
81
90
|
} from '../routeConfig.target${ext}'
|
|
@@ -83,19 +92,18 @@ import { parseQueryParams } from '../parseQueryParams${ext}'
|
|
|
83
92
|
import { sanitizeKeys, normalizePrefix, getEnv } from '../misc${ext}'
|
|
84
93
|
import { buildModelOpenApi } from '../buildModelOpenApi${ext}'
|
|
85
94
|
import { validateCountSourceWhere } from '../routeConfig${ext}'
|
|
86
|
-
import {
|
|
95
|
+
import type { OperationContext } from '../operationRuntime${ext}'
|
|
96
|
+
import { transformResult } from '../operationRuntime${ext}'
|
|
97
|
+
import { HttpError, mapError } from '../errorMapper${ext}'
|
|
98
|
+
import { mergePaginationConfig } from '../pagination${ext}'
|
|
99
|
+
import { MODEL_FIELDS, MODEL_ENUMS } from './${modelName}Metadata${ext}'
|
|
87
100
|
|
|
88
101
|
${generateRouteConfigType(modelName, 'FastifyHookHandler', guardShapesImport, importStyle, 'fastify')}
|
|
89
102
|
const _env = getEnv()
|
|
90
103
|
|
|
91
|
-
const WRITE_STRATEGY: WriteStrategy = '${writeStrategy}'
|
|
92
104
|
const FIND_MANY_PAGINATED_MODE: FindManyPaginatedMode = '${findManyPaginatedMode}'
|
|
93
105
|
const DROP_GUARD = ${dropGuard} || _env.E2E === 'true'
|
|
94
106
|
|
|
95
|
-
const MODEL_FIELDS = ${JSON.stringify(fieldsMeta, null, 2)} as const
|
|
96
|
-
|
|
97
|
-
const MODEL_ENUMS = ${JSON.stringify(enumsMeta, null, 2)} as const
|
|
98
|
-
|
|
99
107
|
type OperationConfigLike = {
|
|
100
108
|
before?: FastifyHookHandler[]
|
|
101
109
|
after?: FastifyHookHandler[]
|
|
@@ -234,7 +242,7 @@ export async function ${routerFunctionName}<TCtx = unknown, TPrisma = any>(
|
|
|
234
242
|
MODEL_FIELDS as unknown as Parameters<typeof buildModelOpenApi>[1],
|
|
235
243
|
MODEL_ENUMS as unknown as Parameters<typeof buildModelOpenApi>[2],
|
|
236
244
|
config,
|
|
237
|
-
{ format: 'json', writeStrategy:
|
|
245
|
+
{ format: 'json', writeStrategy: '${writeStrategy}' },
|
|
238
246
|
)
|
|
239
247
|
}
|
|
240
248
|
return _openApiJsonCache
|
|
@@ -247,7 +255,7 @@ export async function ${routerFunctionName}<TCtx = unknown, TPrisma = any>(
|
|
|
247
255
|
MODEL_FIELDS as unknown as Parameters<typeof buildModelOpenApi>[1],
|
|
248
256
|
MODEL_ENUMS as unknown as Parameters<typeof buildModelOpenApi>[2],
|
|
249
257
|
config,
|
|
250
|
-
{ format: 'yaml', writeStrategy:
|
|
258
|
+
{ format: 'yaml', writeStrategy: '${writeStrategy}' },
|
|
251
259
|
) as string
|
|
252
260
|
}
|
|
253
261
|
return _openApiYamlCache
|
|
@@ -271,11 +279,9 @@ export async function ${routerFunctionName}<TCtx = unknown, TPrisma = any>(
|
|
|
271
279
|
})
|
|
272
280
|
|
|
273
281
|
instance.setErrorHandler((error: FastifyError, _request: FastifyRequest, reply: FastifyReply) => {
|
|
274
|
-
const
|
|
275
|
-
const status = e.status ?? e.statusCode ?? 500
|
|
276
|
-
const message = error.message || 'Internal server error'
|
|
282
|
+
const httpError = mapError(error)
|
|
277
283
|
if (!reply.sent) {
|
|
278
|
-
reply.code(status).send({ message })
|
|
284
|
+
reply.code(httpError.status).send({ message: httpError.message })
|
|
279
285
|
}
|
|
280
286
|
})
|
|
281
287
|
|
|
@@ -292,7 +298,7 @@ export async function ${routerFunctionName}<TCtx = unknown, TPrisma = any>(
|
|
|
292
298
|
})
|
|
293
299
|
}
|
|
294
300
|
|
|
295
|
-
const
|
|
301
|
+
const handleRead = (
|
|
296
302
|
opConfig: OperationConfigLike,
|
|
297
303
|
handlerFn: (req: FastifyRequest, reply: FastifyReply) => Promise<void>,
|
|
298
304
|
parseFn: (req: FastifyRequest) => void,
|
|
@@ -326,125 +332,9 @@ export async function ${routerFunctionName}<TCtx = unknown, TPrisma = any>(
|
|
|
326
332
|
}
|
|
327
333
|
}
|
|
328
334
|
|
|
329
|
-
|
|
330
|
-
const opConfig: OperationConfigLike = (config.findFirst as OperationConfigLike | undefined) ?? defaultOpConfig
|
|
331
|
-
const path = basePath ? \`\${basePath}/first\` : '/first'
|
|
332
|
-
instance.get(path, handleGet(opConfig, ${modelName}FindFirst, parseQueryHook))
|
|
333
|
-
if (postReadsEnabled) instance.post(path, handleGet(opConfig, ${modelName}FindFirst, parseBodyAsQueryHook))
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
if (isEnabled(config.findFirstOrThrow)) {
|
|
337
|
-
const opConfig: OperationConfigLike = (config.findFirstOrThrow as OperationConfigLike | undefined) ?? defaultOpConfig
|
|
338
|
-
const path = basePath ? \`\${basePath}/first/strict\` : '/first/strict'
|
|
339
|
-
instance.get(path, handleGet(opConfig, ${modelName}FindFirstOrThrow, parseQueryHook))
|
|
340
|
-
if (postReadsEnabled) instance.post(path, handleGet(opConfig, ${modelName}FindFirstOrThrow, parseBodyAsQueryHook))
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
if (isEnabled(config.findManyPaginated)) {
|
|
344
|
-
const opConfig: OperationConfigLike = (config.findManyPaginated as OperationConfigLike | undefined) ?? defaultOpConfig
|
|
345
|
-
const path = basePath ? \`\${basePath}/paginated\` : '/paginated'
|
|
346
|
-
instance.get(path, handleGet(opConfig, ${modelName}FindManyPaginated, parseQueryHook))
|
|
347
|
-
if (postReadsEnabled) instance.post(path, handleGet(opConfig, ${modelName}FindManyPaginated, parseBodyAsQueryHook))
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
if (isEnabled(config.aggregate)) {
|
|
351
|
-
const opConfig: OperationConfigLike = (config.aggregate as OperationConfigLike | undefined) ?? defaultOpConfig
|
|
352
|
-
const path = basePath ? \`\${basePath}/aggregate\` : '/aggregate'
|
|
353
|
-
instance.get(path, handleGet(opConfig, ${modelName}Aggregate, parseQueryHook))
|
|
354
|
-
if (postReadsEnabled) instance.post(path, handleGet(opConfig, ${modelName}Aggregate, parseBodyAsQueryHook))
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
if (isEnabled(config.count)) {
|
|
358
|
-
const opConfig: OperationConfigLike = (config.count as OperationConfigLike | undefined) ?? defaultOpConfig
|
|
359
|
-
const path = basePath ? \`\${basePath}/count\` : '/count'
|
|
360
|
-
instance.get(path, handleGet(opConfig, ${modelName}Count, parseQueryHook))
|
|
361
|
-
if (postReadsEnabled) instance.post(path, handleGet(opConfig, ${modelName}Count, parseBodyAsQueryHook))
|
|
362
|
-
}
|
|
335
|
+
${readOpBlocks}
|
|
363
336
|
|
|
364
|
-
|
|
365
|
-
const opConfig: OperationConfigLike = (config.groupBy as OperationConfigLike | undefined) ?? defaultOpConfig
|
|
366
|
-
const path = basePath ? \`\${basePath}/groupby\` : '/groupby'
|
|
367
|
-
instance.get(path, handleGet(opConfig, ${modelName}GroupBy, parseQueryHook))
|
|
368
|
-
if (postReadsEnabled) instance.post(path, handleGet(opConfig, ${modelName}GroupBy, parseBodyAsQueryHook))
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
if (isEnabled(config.findUniqueOrThrow)) {
|
|
372
|
-
const opConfig: OperationConfigLike = (config.findUniqueOrThrow as OperationConfigLike | undefined) ?? defaultOpConfig
|
|
373
|
-
const path = basePath ? \`\${basePath}/unique/strict\` : '/unique/strict'
|
|
374
|
-
instance.get(path, handleGet(opConfig, ${modelName}FindUniqueOrThrow, parseQueryHook))
|
|
375
|
-
if (postReadsEnabled) instance.post(path, handleGet(opConfig, ${modelName}FindUniqueOrThrow, parseBodyAsQueryHook))
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
if (isEnabled(config.findUnique)) {
|
|
379
|
-
const opConfig: OperationConfigLike = (config.findUnique as OperationConfigLike | undefined) ?? defaultOpConfig
|
|
380
|
-
const path = basePath ? \`\${basePath}/unique\` : '/unique'
|
|
381
|
-
instance.get(path, handleGet(opConfig, ${modelName}FindUnique, parseQueryHook))
|
|
382
|
-
if (postReadsEnabled) instance.post(path, handleGet(opConfig, ${modelName}FindUnique, parseBodyAsQueryHook))
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
if (isEnabled(config.findMany)) {
|
|
386
|
-
const opConfig: OperationConfigLike = (config.findMany as OperationConfigLike | undefined) ?? defaultOpConfig
|
|
387
|
-
const path = basePath || '/'
|
|
388
|
-
instance.get(path, handleGet(opConfig, ${modelName}FindMany, parseQueryHook))
|
|
389
|
-
if (postReadsEnabled) {
|
|
390
|
-
const postPath = basePath ? \`\${basePath}/read\` : '/read'
|
|
391
|
-
instance.post(postPath, handleGet(opConfig, ${modelName}FindMany, parseBodyAsQueryHook))
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
if (isEnabled(config.createManyAndReturn)) {
|
|
396
|
-
const opConfig: OperationConfigLike = (config.createManyAndReturn as OperationConfigLike | undefined) ?? defaultOpConfig
|
|
397
|
-
const path = basePath ? \`\${basePath}/many/return\` : '/many/return'
|
|
398
|
-
instance.post(path, handleWrite(opConfig, ${modelName}CreateManyAndReturn))
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
if (isEnabled(config.createMany)) {
|
|
402
|
-
const opConfig: OperationConfigLike = (config.createMany as OperationConfigLike | undefined) ?? defaultOpConfig
|
|
403
|
-
const path = basePath ? \`\${basePath}/many\` : '/many'
|
|
404
|
-
instance.post(path, handleWrite(opConfig, ${modelName}CreateMany))
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
if (isEnabled(config.create)) {
|
|
408
|
-
const opConfig: OperationConfigLike = (config.create as OperationConfigLike | undefined) ?? defaultOpConfig
|
|
409
|
-
const path = basePath || '/'
|
|
410
|
-
instance.post(path, handleWrite(opConfig, ${modelName}Create))
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
if (isEnabled(config.updateManyAndReturn)) {
|
|
414
|
-
const opConfig: OperationConfigLike = (config.updateManyAndReturn as OperationConfigLike | undefined) ?? defaultOpConfig
|
|
415
|
-
const path = basePath ? \`\${basePath}/many/return\` : '/many/return'
|
|
416
|
-
instance.put(path, handleWrite(opConfig, ${modelName}UpdateManyAndReturn))
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
if (isEnabled(config.updateMany)) {
|
|
420
|
-
const opConfig: OperationConfigLike = (config.updateMany as OperationConfigLike | undefined) ?? defaultOpConfig
|
|
421
|
-
const path = basePath ? \`\${basePath}/many\` : '/many'
|
|
422
|
-
instance.put(path, handleWrite(opConfig, ${modelName}UpdateMany))
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
if (isEnabled(config.update)) {
|
|
426
|
-
const opConfig: OperationConfigLike = (config.update as OperationConfigLike | undefined) ?? defaultOpConfig
|
|
427
|
-
const path = basePath || '/'
|
|
428
|
-
instance.put(path, handleWrite(opConfig, ${modelName}Update))
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
if (isEnabled(config.upsert)) {
|
|
432
|
-
const opConfig: OperationConfigLike = (config.upsert as OperationConfigLike | undefined) ?? defaultOpConfig
|
|
433
|
-
const path = basePath || '/'
|
|
434
|
-
instance.patch(path, handleWrite(opConfig, ${modelName}Upsert))
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
if (isEnabled(config.deleteMany)) {
|
|
438
|
-
const opConfig: OperationConfigLike = (config.deleteMany as OperationConfigLike | undefined) ?? defaultOpConfig
|
|
439
|
-
const path = basePath ? \`\${basePath}/many\` : '/many'
|
|
440
|
-
instance.delete(path, handleWrite(opConfig, ${modelName}DeleteMany))
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
if (isEnabled(config.delete)) {
|
|
444
|
-
const opConfig: OperationConfigLike = (config.delete as OperationConfigLike | undefined) ?? defaultOpConfig
|
|
445
|
-
const path = basePath || '/'
|
|
446
|
-
instance.delete(path, handleWrite(opConfig, ${modelName}Delete))
|
|
447
|
-
}
|
|
337
|
+
${writeOpBlocks}
|
|
448
338
|
|
|
449
339
|
if (config.updateEach) {
|
|
450
340
|
const opConfig: OperationConfigLike = (config.updateEach as OperationConfigLike | undefined) ?? defaultOpConfig
|
|
@@ -457,6 +347,9 @@ export async function ${routerFunctionName}<TCtx = unknown, TPrisma = any>(
|
|
|
457
347
|
const path = basePath ? \`\${basePath}/each\` : '/each'
|
|
458
348
|
instance.post(path, async (request: FastifyRequest, reply: FastifyReply) => {
|
|
459
349
|
try {
|
|
350
|
+
if (!Array.isArray(request.body)) {
|
|
351
|
+
throw new HttpError(400, 'updateEach body must be an array of { where, data } items')
|
|
352
|
+
}
|
|
460
353
|
makeShapeHook(config, opConfig)(request)
|
|
461
354
|
const { before = [], after = [] } = opConfig
|
|
462
355
|
if (await runHooks(before, request, reply)) return
|
|
@@ -3,6 +3,46 @@ import { generateRouteConfigType } from './generateRouteConfigType'
|
|
|
3
3
|
import { ImportStyle } from '../utils/resolveImportStyle'
|
|
4
4
|
import { importExt } from '../utils/importExt'
|
|
5
5
|
import { WriteStrategy } from '../constants'
|
|
6
|
+
import { OPERATION_METADATA } from '../copy/operationDefinitions'
|
|
7
|
+
|
|
8
|
+
function pathExpr(suffix: string): string {
|
|
9
|
+
if (!suffix) return `basePath || '/'`
|
|
10
|
+
return `\`\${basePath}${suffix}\``
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function emitReadOp(meta: (typeof OPERATION_METADATA)[number], modelName: string): string {
|
|
14
|
+
const c = meta.name.charAt(0).toUpperCase() + meta.name.slice(1)
|
|
15
|
+
const handlerName = `${modelName}${c}`
|
|
16
|
+
const pathValue = pathExpr(meta.pathSuffix)
|
|
17
|
+
|
|
18
|
+
const postReadLine = meta.supportsPostRead
|
|
19
|
+
? meta.name === 'findMany'
|
|
20
|
+
? ` if (postReadsEnabled) {
|
|
21
|
+
const postPath = basePath ? \`\${basePath}/read\` : '/read'
|
|
22
|
+
app.post(postPath, handleRead(opConfig, ${handlerName}, parseBodyAsQueryMiddleware))
|
|
23
|
+
}`
|
|
24
|
+
: ` if (postReadsEnabled) app.post(path, handleRead(opConfig, ${handlerName}, parseBodyAsQueryMiddleware))`
|
|
25
|
+
: ''
|
|
26
|
+
|
|
27
|
+
return ` if (isEnabled(config.${meta.configKey})) {
|
|
28
|
+
const opConfig = opFor('${meta.configKey}')
|
|
29
|
+
const path = ${pathValue}
|
|
30
|
+
app.get(path, handleRead(opConfig, ${handlerName}, parseQueryMiddleware))
|
|
31
|
+
${postReadLine}
|
|
32
|
+
}`
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function emitWriteOp(meta: (typeof OPERATION_METADATA)[number], modelName: string): string {
|
|
36
|
+
const c = meta.name.charAt(0).toUpperCase() + meta.name.slice(1)
|
|
37
|
+
const handlerName = `${modelName}${c}`
|
|
38
|
+
const pathValue = pathExpr(meta.pathSuffix)
|
|
39
|
+
|
|
40
|
+
return ` if (isEnabled(config.${meta.configKey})) {
|
|
41
|
+
const opConfig = opFor('${meta.configKey}')
|
|
42
|
+
const path = ${pathValue}
|
|
43
|
+
app.${meta.method}(path, handleWrite(opConfig, ${handlerName}))
|
|
44
|
+
}`
|
|
45
|
+
}
|
|
6
46
|
|
|
7
47
|
export function generateHonoRouterFunction({
|
|
8
48
|
model,
|
|
@@ -24,53 +64,23 @@ export function generateHonoRouterFunction({
|
|
|
24
64
|
const modelNameLower = modelName.toLowerCase()
|
|
25
65
|
const routerFunctionName = `${modelName}Router`
|
|
26
66
|
|
|
27
|
-
const
|
|
28
|
-
name
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
isUpdatedAt: f.isUpdatedAt ?? false,
|
|
35
|
-
documentation: f.documentation,
|
|
36
|
-
relationFromFields: f.relationFromFields,
|
|
37
|
-
}))
|
|
38
|
-
|
|
39
|
-
const referencedEnumTypes = new Set(
|
|
40
|
-
model.fields.filter((f) => f.kind === 'enum').map((f) => f.type),
|
|
41
|
-
)
|
|
67
|
+
const handlerImports = OPERATION_METADATA
|
|
68
|
+
.map((m) => ` ${modelName}${m.name.charAt(0).toUpperCase() + m.name.slice(1)},`)
|
|
69
|
+
.join('\n')
|
|
70
|
+
|
|
71
|
+
const readOps = OPERATION_METADATA.filter((m) => m.kind === 'read')
|
|
72
|
+
const writeOps = OPERATION_METADATA.filter((m) => m.kind === 'write' || m.kind === 'batch')
|
|
73
|
+
.filter((m) => m.name !== 'updateEach')
|
|
42
74
|
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
.map((e) => ({
|
|
46
|
-
name: e.name,
|
|
47
|
-
values: e.values.map((v) => ({ name: v.name })),
|
|
48
|
-
}))
|
|
75
|
+
const readOpBlocks = readOps.map((m) => emitReadOp(m, modelName)).join('\n\n')
|
|
76
|
+
const writeOpBlocks = writeOps.map((m) => emitWriteOp(m, modelName)).join('\n\n')
|
|
49
77
|
|
|
50
78
|
return `import { Hono } from 'hono'
|
|
51
79
|
import type { Context } from 'hono'
|
|
52
80
|
import type { ContentfulStatusCode } from 'hono/utils/http-status'
|
|
53
81
|
import { HTTPException } from 'hono/http-exception'
|
|
54
82
|
import {
|
|
55
|
-
|
|
56
|
-
${modelName}FindUniqueOrThrow,
|
|
57
|
-
${modelName}FindFirst,
|
|
58
|
-
${modelName}FindFirstOrThrow,
|
|
59
|
-
${modelName}FindMany,
|
|
60
|
-
${modelName}FindManyPaginated,
|
|
61
|
-
${modelName}Create,
|
|
62
|
-
${modelName}CreateMany,
|
|
63
|
-
${modelName}CreateManyAndReturn,
|
|
64
|
-
${modelName}Update,
|
|
65
|
-
${modelName}UpdateMany,
|
|
66
|
-
${modelName}UpdateManyAndReturn,
|
|
67
|
-
${modelName}Upsert,
|
|
68
|
-
${modelName}Delete,
|
|
69
|
-
${modelName}DeleteMany,
|
|
70
|
-
${modelName}Aggregate,
|
|
71
|
-
${modelName}Count,
|
|
72
|
-
${modelName}GroupBy,
|
|
73
|
-
${modelName}UpdateEach,
|
|
83
|
+
${handlerImports}
|
|
74
84
|
} from './${modelName}Handlers${ext}'
|
|
75
85
|
import type {
|
|
76
86
|
RouteConfig,
|
|
@@ -79,23 +89,20 @@ import type {
|
|
|
79
89
|
HonoEnvBase,
|
|
80
90
|
HonoInternalVariables,
|
|
81
91
|
GeneratedHonoEnv,
|
|
82
|
-
WriteStrategy,
|
|
83
92
|
PaginationConfig,
|
|
84
93
|
} from '../routeConfig.target${ext}'
|
|
85
94
|
import { parseQueryParams } from '../parseQueryParams${ext}'
|
|
86
95
|
import { normalizePrefix, getEnv, sanitizeKeys } from '../misc${ext}'
|
|
87
96
|
import { buildModelOpenApi } from '../buildModelOpenApi${ext}'
|
|
88
97
|
import { validateCountSourceWhere } from '../routeConfig${ext}'
|
|
89
|
-
import {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
} from '../operationRuntime${ext}'
|
|
98
|
+
import { transformResult } from '../operationRuntime${ext}'
|
|
99
|
+
import { mapError } from '../errorMapper${ext}'
|
|
100
|
+
import { mergePaginationConfig } from '../pagination${ext}'
|
|
101
|
+
import { MODEL_FIELDS, MODEL_ENUMS } from './${modelName}Metadata${ext}'
|
|
94
102
|
|
|
95
103
|
${generateRouteConfigType(modelName, 'HonoBeforeHook', guardShapesImport, importStyle, 'hono')}
|
|
96
104
|
const _env = getEnv()
|
|
97
105
|
|
|
98
|
-
const WRITE_STRATEGY: WriteStrategy = '${writeStrategy}'
|
|
99
106
|
const DROP_GUARD = ${dropGuard} || _env.E2E === 'true'
|
|
100
107
|
|
|
101
108
|
type JsonLike =
|
|
@@ -106,10 +113,6 @@ type JsonLike =
|
|
|
106
113
|
| JsonLike[]
|
|
107
114
|
| { [k: string]: JsonLike }
|
|
108
115
|
|
|
109
|
-
const MODEL_FIELDS = ${JSON.stringify(fieldsMeta, null, 2)} as const
|
|
110
|
-
|
|
111
|
-
const MODEL_ENUMS = ${JSON.stringify(enumsMeta, null, 2)} as const
|
|
112
|
-
|
|
113
116
|
type OperationConfigLike<TEnv extends HonoEnvBase> = {
|
|
114
117
|
before?: HonoBeforeHook<TEnv>[]
|
|
115
118
|
after?: HonoAfterHook<TEnv>[]
|
|
@@ -259,7 +262,7 @@ export function ${routerFunctionName}<TCtx = unknown, TPrisma = any, TEnv extend
|
|
|
259
262
|
MODEL_FIELDS as unknown as Parameters<typeof buildModelOpenApi>[1],
|
|
260
263
|
MODEL_ENUMS as unknown as Parameters<typeof buildModelOpenApi>[2],
|
|
261
264
|
config as RouteConfig,
|
|
262
|
-
{ format: 'json', writeStrategy:
|
|
265
|
+
{ format: 'json', writeStrategy: '${writeStrategy}' },
|
|
263
266
|
)
|
|
264
267
|
}
|
|
265
268
|
return _openApiJsonCache
|
|
@@ -272,7 +275,7 @@ export function ${routerFunctionName}<TCtx = unknown, TPrisma = any, TEnv extend
|
|
|
272
275
|
MODEL_FIELDS as unknown as Parameters<typeof buildModelOpenApi>[1],
|
|
273
276
|
MODEL_ENUMS as unknown as Parameters<typeof buildModelOpenApi>[2],
|
|
274
277
|
config as RouteConfig,
|
|
275
|
-
{ format: 'yaml', writeStrategy:
|
|
278
|
+
{ format: 'yaml', writeStrategy: '${writeStrategy}' },
|
|
276
279
|
) as string
|
|
277
280
|
}
|
|
278
281
|
return _openApiYamlCache
|
|
@@ -343,109 +346,9 @@ export function ${routerFunctionName}<TCtx = unknown, TPrisma = any, TEnv extend
|
|
|
343
346
|
?? (defaultOpConfig as OperationConfigLike<TEnv>)
|
|
344
347
|
}
|
|
345
348
|
|
|
346
|
-
|
|
347
|
-
const opConfig = opFor('findFirst')
|
|
348
|
-
const path = basePath ? \`\${basePath}/first\` : '/first'
|
|
349
|
-
app.get(path, handleRead(opConfig, ${modelName}FindFirst, parseQueryMiddleware))
|
|
350
|
-
if (postReadsEnabled) app.post(path, handleRead(opConfig, ${modelName}FindFirst, parseBodyAsQueryMiddleware))
|
|
351
|
-
}
|
|
352
|
-
if (isEnabled(config.findFirstOrThrow)) {
|
|
353
|
-
const opConfig = opFor('findFirstOrThrow')
|
|
354
|
-
const path = basePath ? \`\${basePath}/first/strict\` : '/first/strict'
|
|
355
|
-
app.get(path, handleRead(opConfig, ${modelName}FindFirstOrThrow, parseQueryMiddleware))
|
|
356
|
-
if (postReadsEnabled) app.post(path, handleRead(opConfig, ${modelName}FindFirstOrThrow, parseBodyAsQueryMiddleware))
|
|
357
|
-
}
|
|
358
|
-
if (isEnabled(config.findManyPaginated)) {
|
|
359
|
-
const opConfig = opFor('findManyPaginated')
|
|
360
|
-
const path = basePath ? \`\${basePath}/paginated\` : '/paginated'
|
|
361
|
-
app.get(path, handleRead(opConfig, ${modelName}FindManyPaginated, parseQueryMiddleware))
|
|
362
|
-
if (postReadsEnabled) app.post(path, handleRead(opConfig, ${modelName}FindManyPaginated, parseBodyAsQueryMiddleware))
|
|
363
|
-
}
|
|
364
|
-
if (isEnabled(config.aggregate)) {
|
|
365
|
-
const opConfig = opFor('aggregate')
|
|
366
|
-
const path = basePath ? \`\${basePath}/aggregate\` : '/aggregate'
|
|
367
|
-
app.get(path, handleRead(opConfig, ${modelName}Aggregate, parseQueryMiddleware))
|
|
368
|
-
if (postReadsEnabled) app.post(path, handleRead(opConfig, ${modelName}Aggregate, parseBodyAsQueryMiddleware))
|
|
369
|
-
}
|
|
370
|
-
if (isEnabled(config.count)) {
|
|
371
|
-
const opConfig = opFor('count')
|
|
372
|
-
const path = basePath ? \`\${basePath}/count\` : '/count'
|
|
373
|
-
app.get(path, handleRead(opConfig, ${modelName}Count, parseQueryMiddleware))
|
|
374
|
-
if (postReadsEnabled) app.post(path, handleRead(opConfig, ${modelName}Count, parseBodyAsQueryMiddleware))
|
|
375
|
-
}
|
|
376
|
-
if (isEnabled(config.groupBy)) {
|
|
377
|
-
const opConfig = opFor('groupBy')
|
|
378
|
-
const path = basePath ? \`\${basePath}/groupby\` : '/groupby'
|
|
379
|
-
app.get(path, handleRead(opConfig, ${modelName}GroupBy, parseQueryMiddleware))
|
|
380
|
-
if (postReadsEnabled) app.post(path, handleRead(opConfig, ${modelName}GroupBy, parseBodyAsQueryMiddleware))
|
|
381
|
-
}
|
|
382
|
-
if (isEnabled(config.findUniqueOrThrow)) {
|
|
383
|
-
const opConfig = opFor('findUniqueOrThrow')
|
|
384
|
-
const path = basePath ? \`\${basePath}/unique/strict\` : '/unique/strict'
|
|
385
|
-
app.get(path, handleRead(opConfig, ${modelName}FindUniqueOrThrow, parseQueryMiddleware))
|
|
386
|
-
if (postReadsEnabled) app.post(path, handleRead(opConfig, ${modelName}FindUniqueOrThrow, parseBodyAsQueryMiddleware))
|
|
387
|
-
}
|
|
388
|
-
if (isEnabled(config.findUnique)) {
|
|
389
|
-
const opConfig = opFor('findUnique')
|
|
390
|
-
const path = basePath ? \`\${basePath}/unique\` : '/unique'
|
|
391
|
-
app.get(path, handleRead(opConfig, ${modelName}FindUnique, parseQueryMiddleware))
|
|
392
|
-
if (postReadsEnabled) app.post(path, handleRead(opConfig, ${modelName}FindUnique, parseBodyAsQueryMiddleware))
|
|
393
|
-
}
|
|
394
|
-
if (isEnabled(config.findMany)) {
|
|
395
|
-
const opConfig = opFor('findMany')
|
|
396
|
-
const path = basePath || '/'
|
|
397
|
-
app.get(path, handleRead(opConfig, ${modelName}FindMany, parseQueryMiddleware))
|
|
398
|
-
if (postReadsEnabled) {
|
|
399
|
-
const postPath = basePath ? \`\${basePath}/read\` : '/read'
|
|
400
|
-
app.post(postPath, handleRead(opConfig, ${modelName}FindMany, parseBodyAsQueryMiddleware))
|
|
401
|
-
}
|
|
402
|
-
}
|
|
349
|
+
${readOpBlocks}
|
|
403
350
|
|
|
404
|
-
|
|
405
|
-
const opConfig = opFor('createManyAndReturn')
|
|
406
|
-
const path = basePath ? \`\${basePath}/many/return\` : '/many/return'
|
|
407
|
-
app.post(path, handleWrite(opConfig, ${modelName}CreateManyAndReturn))
|
|
408
|
-
}
|
|
409
|
-
if (isEnabled(config.createMany)) {
|
|
410
|
-
const opConfig = opFor('createMany')
|
|
411
|
-
const path = basePath ? \`\${basePath}/many\` : '/many'
|
|
412
|
-
app.post(path, handleWrite(opConfig, ${modelName}CreateMany))
|
|
413
|
-
}
|
|
414
|
-
if (isEnabled(config.create)) {
|
|
415
|
-
const opConfig = opFor('create')
|
|
416
|
-
const path = basePath || '/'
|
|
417
|
-
app.post(path, handleWrite(opConfig, ${modelName}Create))
|
|
418
|
-
}
|
|
419
|
-
if (isEnabled(config.updateManyAndReturn)) {
|
|
420
|
-
const opConfig = opFor('updateManyAndReturn')
|
|
421
|
-
const path = basePath ? \`\${basePath}/many/return\` : '/many/return'
|
|
422
|
-
app.put(path, handleWrite(opConfig, ${modelName}UpdateManyAndReturn))
|
|
423
|
-
}
|
|
424
|
-
if (isEnabled(config.updateMany)) {
|
|
425
|
-
const opConfig = opFor('updateMany')
|
|
426
|
-
const path = basePath ? \`\${basePath}/many\` : '/many'
|
|
427
|
-
app.put(path, handleWrite(opConfig, ${modelName}UpdateMany))
|
|
428
|
-
}
|
|
429
|
-
if (isEnabled(config.update)) {
|
|
430
|
-
const opConfig = opFor('update')
|
|
431
|
-
const path = basePath || '/'
|
|
432
|
-
app.put(path, handleWrite(opConfig, ${modelName}Update))
|
|
433
|
-
}
|
|
434
|
-
if (isEnabled(config.upsert)) {
|
|
435
|
-
const opConfig = opFor('upsert')
|
|
436
|
-
const path = basePath || '/'
|
|
437
|
-
app.patch(path, handleWrite(opConfig, ${modelName}Upsert))
|
|
438
|
-
}
|
|
439
|
-
if (isEnabled(config.deleteMany)) {
|
|
440
|
-
const opConfig = opFor('deleteMany')
|
|
441
|
-
const path = basePath ? \`\${basePath}/many\` : '/many'
|
|
442
|
-
app.delete(path, handleWrite(opConfig, ${modelName}DeleteMany))
|
|
443
|
-
}
|
|
444
|
-
if (isEnabled(config.delete)) {
|
|
445
|
-
const opConfig = opFor('delete')
|
|
446
|
-
const path = basePath || '/'
|
|
447
|
-
app.delete(path, handleWrite(opConfig, ${modelName}Delete))
|
|
448
|
-
}
|
|
351
|
+
${writeOpBlocks}
|
|
449
352
|
|
|
450
353
|
if (config.updateEach) {
|
|
451
354
|
const opConfig = (config.updateEach as unknown as OperationConfigLike<TEnv> | undefined) ?? (defaultOpConfig as OperationConfigLike<TEnv>)
|
|
@@ -1,47 +1,21 @@
|
|
|
1
1
|
import { DMMF } from '@prisma/generator-helper'
|
|
2
2
|
import { ImportStyle } from '../utils/resolveImportStyle'
|
|
3
3
|
import { importExt } from '../utils/importExt'
|
|
4
|
+
import { OPERATION_METADATA } from '../copy/operationDefinitions'
|
|
4
5
|
|
|
5
6
|
export interface UnifiedHandlerOptions {
|
|
6
7
|
model: DMMF.Model
|
|
7
8
|
importStyle: ImportStyle
|
|
8
9
|
}
|
|
9
10
|
|
|
10
|
-
const CORE_NAME_MAP: Record<string, string> = {
|
|
11
|
-
delete: 'deleteUnique',
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
function coreFnName(op: string): string {
|
|
15
|
-
return CORE_NAME_MAP[op] || op
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const ALL_OPS = [
|
|
19
|
-
'findMany',
|
|
20
|
-
'findFirst',
|
|
21
|
-
'findFirstOrThrow',
|
|
22
|
-
'findUnique',
|
|
23
|
-
'findUniqueOrThrow',
|
|
24
|
-
'findManyPaginated',
|
|
25
|
-
'create',
|
|
26
|
-
'createMany',
|
|
27
|
-
'createManyAndReturn',
|
|
28
|
-
'update',
|
|
29
|
-
'updateMany',
|
|
30
|
-
'updateManyAndReturn',
|
|
31
|
-
'upsert',
|
|
32
|
-
'delete',
|
|
33
|
-
'deleteMany',
|
|
34
|
-
'aggregate',
|
|
35
|
-
'count',
|
|
36
|
-
'groupBy',
|
|
37
|
-
]
|
|
38
|
-
|
|
39
11
|
export function generateUnifiedHandler(options: UnifiedHandlerOptions): string {
|
|
40
12
|
const ext = importExt(options.importStyle)
|
|
41
13
|
const modelName = options.model.name
|
|
42
14
|
|
|
43
|
-
const
|
|
44
|
-
|
|
15
|
+
const dispatchOps = OPERATION_METADATA.filter((m) => m.name !== 'updateEach')
|
|
16
|
+
|
|
17
|
+
const handlers = dispatchOps.map((meta) => {
|
|
18
|
+
const exportName = `${modelName}${meta.name.charAt(0).toUpperCase() + meta.name.slice(1)}`
|
|
45
19
|
|
|
46
20
|
return `
|
|
47
21
|
export async function ${exportName}(
|
|
@@ -50,7 +24,7 @@ export async function ${exportName}(
|
|
|
50
24
|
next: NextFunction,
|
|
51
25
|
) {
|
|
52
26
|
try {
|
|
53
|
-
;(res.locals as LocalsBag).data = await core.${
|
|
27
|
+
;(res.locals as LocalsBag).data = await core.${meta.coreName}(buildContext(req, res))
|
|
54
28
|
next()
|
|
55
29
|
} catch (error: unknown) {
|
|
56
30
|
next(mapError(error))
|
|
@@ -60,7 +34,8 @@ export async function ${exportName}(
|
|
|
60
34
|
|
|
61
35
|
return `import type { Request, Response, NextFunction } from 'express'
|
|
62
36
|
import * as core from './${modelName}Core${ext}'
|
|
63
|
-
import { OperationContext
|
|
37
|
+
import { OperationContext } from '../operationRuntime${ext}'
|
|
38
|
+
import { mapError } from '../errorMapper${ext}'
|
|
64
39
|
|
|
65
40
|
type ExtendedRequest = Request & {
|
|
66
41
|
prisma?: unknown
|