prisma-generator-express 1.24.0 → 1.25.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/dist/bin.d.ts +1 -1
- package/dist/bin.js +1 -1
- package/dist/bin.js.map +1 -1
- package/dist/generators/generateRouter.js +5 -5
- package/dist/generators/generateUnifiedDocs.js +78 -69
- package/dist/generators/generateUnifiedDocs.js.map +1 -1
- package/dist/generators/generateUnifiedHandler.js +27 -22
- package/dist/generators/generateUnifiedHandler.js.map +1 -1
- package/dist/generators/generateUnifiedScalarUI.js +30 -13
- package/dist/generators/generateUnifiedScalarUI.js.map +1 -1
- package/dist/index.js +23 -23
- package/dist/index.js.map +1 -1
- package/dist/utils/writeFileSafely.js +0 -6
- package/dist/utils/writeFileSafely.js.map +1 -1
- package/package.json +1 -1
- package/src/bin.ts +1 -1
- package/src/client/encodeQueryParams.ts +1 -1
- package/src/copy/buildModelOpenApi.ts +83 -19
- package/src/copy/parseQueryParams.ts +1 -1
- package/src/generators/generateImportPrismaStatement.ts +1 -1
- package/src/generators/generateRouter.ts +5 -5
- package/src/generators/generateUnifiedDocs.ts +79 -69
- package/src/generators/generateUnifiedHandler.ts +28 -23
- package/src/generators/generateUnifiedScalarUI.ts +31 -14
- package/src/index.ts +9 -9
- package/src/utils/writeFileSafely.ts +1 -9
|
@@ -123,6 +123,45 @@ function queryParam(
|
|
|
123
123
|
return param
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
+
function scalarUpdateOperations(
|
|
127
|
+
baseSchema: SchemaObject | RefObject,
|
|
128
|
+
fieldType: string,
|
|
129
|
+
fieldKind: string,
|
|
130
|
+
): SchemaObject {
|
|
131
|
+
const ops: Record<string, SchemaObject | RefObject> = { set: baseSchema }
|
|
132
|
+
|
|
133
|
+
if (fieldKind === 'scalar' && NUMERIC_SCALAR_TYPES.has(fieldType)) {
|
|
134
|
+
ops.increment = baseSchema
|
|
135
|
+
ops.decrement = baseSchema
|
|
136
|
+
ops.multiply = baseSchema
|
|
137
|
+
ops.divide = baseSchema
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return {
|
|
141
|
+
oneOf: [
|
|
142
|
+
baseSchema,
|
|
143
|
+
{ type: 'object', properties: ops },
|
|
144
|
+
],
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function listScalarUpdateOperations(
|
|
149
|
+
itemSchema: SchemaObject | RefObject,
|
|
150
|
+
): SchemaObject {
|
|
151
|
+
return {
|
|
152
|
+
type: 'object',
|
|
153
|
+
properties: {
|
|
154
|
+
set: { type: 'array', items: itemSchema },
|
|
155
|
+
push: {
|
|
156
|
+
oneOf: [
|
|
157
|
+
itemSchema as SchemaObject,
|
|
158
|
+
{ type: 'array', items: itemSchema },
|
|
159
|
+
],
|
|
160
|
+
},
|
|
161
|
+
},
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
126
165
|
export function buildModelOpenApi(
|
|
127
166
|
modelName: string,
|
|
128
167
|
modelFields: ModelField[],
|
|
@@ -220,7 +259,7 @@ function generateOperationSchemas(
|
|
|
220
259
|
.flatMap((f) => f.relationFromFields!),
|
|
221
260
|
)
|
|
222
261
|
|
|
223
|
-
const
|
|
262
|
+
const requiredCreateScalars = fields
|
|
224
263
|
.filter(
|
|
225
264
|
(f) =>
|
|
226
265
|
(f.kind === 'scalar' || f.kind === 'enum') &&
|
|
@@ -231,36 +270,46 @@ function generateOperationSchemas(
|
|
|
231
270
|
)
|
|
232
271
|
.map((f) => f.name)
|
|
233
272
|
|
|
273
|
+
const requiredCreateManyScalars = fields
|
|
274
|
+
.filter(
|
|
275
|
+
(f) =>
|
|
276
|
+
(f.kind === 'scalar' || f.kind === 'enum') &&
|
|
277
|
+
f.isRequired &&
|
|
278
|
+
!f.hasDefaultValue &&
|
|
279
|
+
!f.isUpdatedAt,
|
|
280
|
+
)
|
|
281
|
+
.map((f) => f.name)
|
|
282
|
+
|
|
234
283
|
const createInputSchema: SchemaObject = {
|
|
235
284
|
type: 'object',
|
|
236
|
-
properties: fieldsToWriteProperties(fields),
|
|
285
|
+
properties: fieldsToWriteProperties(fields, 'create'),
|
|
237
286
|
}
|
|
238
|
-
if (
|
|
239
|
-
createInputSchema.required = [...
|
|
287
|
+
if (requiredCreateScalars.length > 0) {
|
|
288
|
+
createInputSchema.required = [...requiredCreateScalars]
|
|
240
289
|
}
|
|
241
290
|
|
|
242
291
|
spec.components.schemas[`${modelName}CreateInput`] = createInputSchema
|
|
243
292
|
|
|
244
293
|
spec.components.schemas[`${modelName}UpdateInput`] = {
|
|
245
294
|
type: 'object',
|
|
246
|
-
properties: fieldsToWriteProperties(fields),
|
|
295
|
+
properties: fieldsToWriteProperties(fields, 'update'),
|
|
247
296
|
}
|
|
248
297
|
|
|
249
298
|
const createManyInputSchema: SchemaObject = {
|
|
250
299
|
type: 'object',
|
|
251
|
-
properties: fieldsToBulkWriteProperties(fields),
|
|
300
|
+
properties: fieldsToBulkWriteProperties(fields, 'create'),
|
|
252
301
|
description:
|
|
253
302
|
'Scalar-only input for bulk create. Nested relation writes are not supported in createMany operations.',
|
|
254
303
|
}
|
|
255
|
-
if (
|
|
256
|
-
createManyInputSchema.required = [...
|
|
304
|
+
if (requiredCreateManyScalars.length > 0) {
|
|
305
|
+
createManyInputSchema.required = [...requiredCreateManyScalars]
|
|
257
306
|
}
|
|
258
307
|
|
|
259
308
|
spec.components.schemas[`${modelName}CreateManyInput`] = createManyInputSchema
|
|
260
309
|
|
|
261
310
|
spec.components.schemas[`${modelName}UpdateManyMutationInput`] = {
|
|
262
311
|
type: 'object',
|
|
263
|
-
properties: fieldsToBulkWriteProperties(fields),
|
|
312
|
+
properties: fieldsToBulkWriteProperties(fields, 'update'),
|
|
264
313
|
description:
|
|
265
314
|
'Scalar-only input for bulk update. Nested relation writes are not supported in updateMany operations.',
|
|
266
315
|
}
|
|
@@ -1092,21 +1141,23 @@ function fieldsToProperties(
|
|
|
1092
1141
|
|
|
1093
1142
|
function fieldsToWriteProperties(
|
|
1094
1143
|
fields: ModelField[],
|
|
1144
|
+
mode: 'create' | 'update',
|
|
1095
1145
|
): Record<string, SchemaObject | RefObject> {
|
|
1096
1146
|
const props: Record<string, SchemaObject | RefObject> = {}
|
|
1097
1147
|
for (const field of fields) {
|
|
1098
|
-
props[field.name] = mapFieldToWriteSchema(field)
|
|
1148
|
+
props[field.name] = mapFieldToWriteSchema(field, mode)
|
|
1099
1149
|
}
|
|
1100
1150
|
return props
|
|
1101
1151
|
}
|
|
1102
1152
|
|
|
1103
1153
|
function fieldsToBulkWriteProperties(
|
|
1104
1154
|
fields: ModelField[],
|
|
1155
|
+
mode: 'create' | 'update',
|
|
1105
1156
|
): Record<string, SchemaObject | RefObject> {
|
|
1106
1157
|
const props: Record<string, SchemaObject | RefObject> = {}
|
|
1107
1158
|
for (const field of fields) {
|
|
1108
1159
|
if (field.kind === 'object') continue
|
|
1109
|
-
props[field.name] = mapFieldToWriteSchema(field)
|
|
1160
|
+
props[field.name] = mapFieldToWriteSchema(field, mode)
|
|
1110
1161
|
}
|
|
1111
1162
|
return props
|
|
1112
1163
|
}
|
|
@@ -1171,7 +1222,10 @@ function mapFieldToSchema(field: ModelField): SchemaObject | RefObject {
|
|
|
1171
1222
|
return schema
|
|
1172
1223
|
}
|
|
1173
1224
|
|
|
1174
|
-
function mapFieldToWriteSchema(
|
|
1225
|
+
function mapFieldToWriteSchema(
|
|
1226
|
+
field: ModelField,
|
|
1227
|
+
mode: 'create' | 'update',
|
|
1228
|
+
): SchemaObject | RefObject {
|
|
1175
1229
|
if (field.kind === 'object') {
|
|
1176
1230
|
if (field.isList) {
|
|
1177
1231
|
return {
|
|
@@ -1389,21 +1443,31 @@ function mapFieldToWriteSchema(field: ModelField): SchemaObject | RefObject {
|
|
|
1389
1443
|
}
|
|
1390
1444
|
}
|
|
1391
1445
|
|
|
1392
|
-
let
|
|
1446
|
+
let baseSchema: SchemaObject | RefObject
|
|
1393
1447
|
|
|
1394
1448
|
switch (field.kind) {
|
|
1395
1449
|
case 'scalar':
|
|
1396
|
-
|
|
1450
|
+
baseSchema = mapScalarType(field.type)
|
|
1397
1451
|
break
|
|
1398
1452
|
case 'enum':
|
|
1399
|
-
|
|
1453
|
+
baseSchema = { $ref: `#/components/schemas/${field.type}` }
|
|
1400
1454
|
break
|
|
1401
1455
|
default:
|
|
1402
|
-
|
|
1456
|
+
baseSchema = { type: 'string' }
|
|
1403
1457
|
}
|
|
1404
1458
|
|
|
1459
|
+
let schema: SchemaObject | RefObject
|
|
1460
|
+
|
|
1405
1461
|
if (field.isList) {
|
|
1406
|
-
|
|
1462
|
+
if (mode === 'update') {
|
|
1463
|
+
schema = listScalarUpdateOperations(baseSchema)
|
|
1464
|
+
} else {
|
|
1465
|
+
schema = { type: 'array', items: baseSchema }
|
|
1466
|
+
}
|
|
1467
|
+
} else if (mode === 'update') {
|
|
1468
|
+
schema = scalarUpdateOperations(baseSchema, field.type, field.kind)
|
|
1469
|
+
} else {
|
|
1470
|
+
schema = baseSchema
|
|
1407
1471
|
}
|
|
1408
1472
|
|
|
1409
1473
|
if (!field.isRequired && !field.isList) {
|
|
@@ -1417,7 +1481,7 @@ function mapFieldToWriteSchema(field: ModelField): SchemaObject | RefObject {
|
|
|
1417
1481
|
description: field.documentation,
|
|
1418
1482
|
}
|
|
1419
1483
|
} else if (!('$ref' in schema)) {
|
|
1420
|
-
schema.description = field.documentation
|
|
1484
|
+
(schema as SchemaObject).description = field.documentation
|
|
1421
1485
|
}
|
|
1422
1486
|
}
|
|
1423
1487
|
|
|
@@ -1573,4 +1637,4 @@ function toYaml(obj: any, indent = 0): string {
|
|
|
1573
1637
|
}
|
|
1574
1638
|
|
|
1575
1639
|
return yaml
|
|
1576
|
-
}
|
|
1640
|
+
}
|
|
@@ -57,10 +57,10 @@ import {
|
|
|
57
57
|
${modelName}Aggregate,
|
|
58
58
|
${modelName}Count,
|
|
59
59
|
${modelName}GroupBy
|
|
60
|
-
} from './${modelName}Handlers'
|
|
61
|
-
import type { RouteConfig } from '../routeConfig'
|
|
62
|
-
import { parseQueryParams } from '../parseQueryParams'
|
|
63
|
-
import { buildModelOpenApi } from '../buildModelOpenApi'
|
|
60
|
+
} from './${modelName}Handlers.js'
|
|
61
|
+
import type { RouteConfig } from '../routeConfig.js'
|
|
62
|
+
import { parseQueryParams } from '../parseQueryParams.js'
|
|
63
|
+
import { buildModelOpenApi } from '../buildModelOpenApi.js'
|
|
64
64
|
|
|
65
65
|
const _env = typeof process !== 'undefined' && process.env ? process.env : {} as Record<string, string | undefined>
|
|
66
66
|
|
|
@@ -140,7 +140,7 @@ export function ${routerFunctionName}(config: RouteConfig = {}) {
|
|
|
140
140
|
if (qbEnabled) {
|
|
141
141
|
const qbConfig = getQueryBuilderConfig(config)
|
|
142
142
|
if (qbConfig) {
|
|
143
|
-
import('../queryBuilder').then(mod => mod.startQueryBuilder(qbConfig)).catch(() => {})
|
|
143
|
+
import('../queryBuilder.js').then(mod => mod.startQueryBuilder(qbConfig)).catch((err) => { if (_env.NODE_ENV !== 'production') console.warn('[query-builder]', err) })
|
|
144
144
|
}
|
|
145
145
|
}
|
|
146
146
|
|
|
@@ -3,6 +3,10 @@ export function generateUnifiedDocs(models: string[]): string {
|
|
|
3
3
|
.map((model) => `import { ${model}Docs } from './${model}/${model}Docs'`)
|
|
4
4
|
.join('\n')
|
|
5
5
|
|
|
6
|
+
const handlersEntries = models
|
|
7
|
+
.map((model) => ` ${model}: ${model}Docs`)
|
|
8
|
+
.join(',\n')
|
|
9
|
+
|
|
6
10
|
return `${imports}
|
|
7
11
|
import { Request, Response } from 'express'
|
|
8
12
|
import type { RouteConfig } from './routeConfig'
|
|
@@ -10,7 +14,7 @@ import type { RouteConfig } from './routeConfig'
|
|
|
10
14
|
const _env = typeof process !== 'undefined' && process.env ? process.env : {} as Record<string, string | undefined>
|
|
11
15
|
|
|
12
16
|
const docsHandlers: Record<string, (config: any) => (req: Request, res: Response) => any> = {
|
|
13
|
-
${
|
|
17
|
+
${handlersEntries}
|
|
14
18
|
}
|
|
15
19
|
|
|
16
20
|
type DocsUI = 'docs' | 'scalar' | 'json' | 'yaml' | 'playground'
|
|
@@ -33,11 +37,11 @@ export interface CombinedDocsConfig {
|
|
|
33
37
|
|
|
34
38
|
function escapeHtml(input: string) {
|
|
35
39
|
return input
|
|
36
|
-
.
|
|
37
|
-
.
|
|
38
|
-
.
|
|
39
|
-
.
|
|
40
|
-
.
|
|
40
|
+
.replace(/&/g, '&')
|
|
41
|
+
.replace(/</g, '<')
|
|
42
|
+
.replace(/>/g, '>')
|
|
43
|
+
.replace(/"/g, '"')
|
|
44
|
+
.replace(/'/g, ''')
|
|
41
45
|
}
|
|
42
46
|
|
|
43
47
|
function removeTrailingSlash(p: string): string {
|
|
@@ -77,66 +81,72 @@ export function generateCombinedDocs(config: CombinedDocsConfig) {
|
|
|
77
81
|
const basePath = removeTrailingSlash(config.basePath || '/docs')
|
|
78
82
|
const generatedAt = new Date().toISOString()
|
|
79
83
|
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
<
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
</
|
|
139
|
-
</
|
|
84
|
+
const modelRows = registeredModels.map((m) => {
|
|
85
|
+
const lower = m.toLowerCase()
|
|
86
|
+
const docsUrl = basePath + '/' + lower
|
|
87
|
+
const scalarUrl = docsUrl + '?ui=scalar'
|
|
88
|
+
const jsonUrl = docsUrl + '?ui=json'
|
|
89
|
+
const yamlUrl = docsUrl + '?ui=yaml'
|
|
90
|
+
const playgroundUrl = docsUrl + '?ui=playground'
|
|
91
|
+
const modelCfg = config.modelConfigs[m]
|
|
92
|
+
const modelPlayground = isPlaygroundAvailable(modelCfg)
|
|
93
|
+
const playgroundLink = modelPlayground
|
|
94
|
+
? ', <a href="' + playgroundUrl + '" class="text-inherit underline">playground</a>'
|
|
95
|
+
: ''
|
|
96
|
+
return '<tr>' +
|
|
97
|
+
'<td class="text-left py-2 px-2 border-b border-gray-300 align-top">' + escapeHtml(m) + '</td>' +
|
|
98
|
+
'<td class="text-left py-2 px-2 border-b border-gray-300 align-top"><a href="' + docsUrl + '" class="text-inherit underline">' + escapeHtml(docsUrl) + '</a></td>' +
|
|
99
|
+
'<td class="text-left py-2 px-2 border-b border-gray-300 align-top">' +
|
|
100
|
+
'<a href="' + scalarUrl + '" class="text-inherit underline">scalar</a>, ' +
|
|
101
|
+
'<a href="' + jsonUrl + '" class="text-inherit underline">json</a>, ' +
|
|
102
|
+
'<a href="' + yamlUrl + '" class="text-inherit underline">yaml</a>' +
|
|
103
|
+
playgroundLink +
|
|
104
|
+
'</td>' +
|
|
105
|
+
'</tr>'
|
|
106
|
+
}).join('')
|
|
107
|
+
|
|
108
|
+
const descriptionHtml = description
|
|
109
|
+
? '<div class="mt-1.5 text-gray-500 text-sm">' + escapeHtml(description) + '</div>'
|
|
110
|
+
: ''
|
|
111
|
+
const versionHtml = version
|
|
112
|
+
? '<div>Version: ' + escapeHtml(version) + '</div>'
|
|
113
|
+
: ''
|
|
114
|
+
|
|
115
|
+
const html =
|
|
116
|
+
'<!DOCTYPE html>' +
|
|
117
|
+
'<html lang="en">' +
|
|
118
|
+
'<head>' +
|
|
119
|
+
'<meta charset="utf-8" />' +
|
|
120
|
+
'<meta name="viewport" content="width=device-width, initial-scale=1" />' +
|
|
121
|
+
'<title>' + escapeHtml(title) + '</title>' +
|
|
122
|
+
'<script src="https://cdn.tailwindcss.com"></' + 'script>' +
|
|
123
|
+
'</head>' +
|
|
124
|
+
'<body class="m-0 bg-white text-gray-900 font-serif leading-normal">' +
|
|
125
|
+
'<div class="max-w-[980px] mx-auto px-7 pt-10 pb-16">' +
|
|
126
|
+
'<div class="border-b-2 border-gray-900 pb-3.5 mb-[18px]">' +
|
|
127
|
+
'<div class="text-[28px] font-bold tracking-wide">' + escapeHtml(title) + '</div>' +
|
|
128
|
+
descriptionHtml +
|
|
129
|
+
'<div class="mt-3 flex gap-x-5 text-[13px] text-gray-500">' +
|
|
130
|
+
versionHtml +
|
|
131
|
+
'<div>Generated: ' + escapeHtml(generatedAt) + '</div>' +
|
|
132
|
+
'</div>' +
|
|
133
|
+
'</div>' +
|
|
134
|
+
'<div class="mt-[22px]">' +
|
|
135
|
+
'<h2 class="m-0 mb-2.5 text-lg border-t border-gray-300 pt-3.5">Models</h2>' +
|
|
136
|
+
'<table class="w-full border-collapse text-[13px]">' +
|
|
137
|
+
'<thead>' +
|
|
138
|
+
'<tr>' +
|
|
139
|
+
'<th class="text-left py-2 px-2 border-b border-gray-300 align-top font-bold">Model</th>' +
|
|
140
|
+
'<th class="text-left py-2 px-2 border-b border-gray-300 align-top font-bold">Documentation</th>' +
|
|
141
|
+
'<th class="text-left py-2 px-2 border-b border-gray-300 align-top font-bold">Views</th>' +
|
|
142
|
+
'</tr>' +
|
|
143
|
+
'</thead>' +
|
|
144
|
+
'<tbody>' + modelRows + '</tbody>' +
|
|
145
|
+
'</table>' +
|
|
146
|
+
'</div>' +
|
|
147
|
+
'</div>' +
|
|
148
|
+
'</body>' +
|
|
149
|
+
'</html>'
|
|
140
150
|
|
|
141
151
|
res.type('html').send(html)
|
|
142
152
|
}
|
|
@@ -159,9 +169,9 @@ export function registerModelDocs(
|
|
|
159
169
|
registeredModels.forEach((model) => {
|
|
160
170
|
const handler = docsHandlers[model]
|
|
161
171
|
const cfg = configs[model] || {}
|
|
162
|
-
const
|
|
163
|
-
console.log(
|
|
164
|
-
app.get(
|
|
172
|
+
const docPath = normalizedBase + '/' + model.toLowerCase()
|
|
173
|
+
console.log(' Registered docs: ' + docPath)
|
|
174
|
+
app.get(docPath, handler(cfg))
|
|
165
175
|
})
|
|
166
176
|
}
|
|
167
177
|
`
|
|
@@ -152,6 +152,7 @@ function handleError(error: unknown, next: NextFunction): void {
|
|
|
152
152
|
return
|
|
153
153
|
}
|
|
154
154
|
if (typeof code === 'string' && code.startsWith('P')) {
|
|
155
|
+
console.warn('[prisma-generator-express] Unmapped Prisma error code:', code, (error as any).message || '')
|
|
155
156
|
next(new HttpError(500, 'Database operation failed'))
|
|
156
157
|
return
|
|
157
158
|
}
|
|
@@ -290,11 +291,11 @@ export async function ${functionName}(
|
|
|
290
291
|
|
|
291
292
|
let data
|
|
292
293
|
if (shape) {
|
|
293
|
-
assertGuard(extended.${modelNameLower})
|
|
294
|
+
assertGuard((extended as any).${modelNameLower})
|
|
294
295
|
const caller = res.locals.guardCaller
|
|
295
|
-
data = await extended.${modelNameLower}.guard(shape, caller).${op}(query)
|
|
296
|
+
data = await (extended as any).${modelNameLower}.guard(shape, caller).${op}(query)
|
|
296
297
|
} else {
|
|
297
|
-
data = await extended.${modelNameLower}.${op}(query)
|
|
298
|
+
data = await (extended as any).${modelNameLower}.${op}(query)
|
|
298
299
|
}
|
|
299
300
|
|
|
300
301
|
res.locals.data = data
|
|
@@ -321,11 +322,11 @@ export async function ${modelName}FindMany(
|
|
|
321
322
|
|
|
322
323
|
let data
|
|
323
324
|
if (shape) {
|
|
324
|
-
assertGuard(extended.${modelNameLower})
|
|
325
|
+
assertGuard((extended as any).${modelNameLower})
|
|
325
326
|
const caller = res.locals.guardCaller
|
|
326
|
-
data = await extended.${modelNameLower}.guard(shape, caller).findMany(query)
|
|
327
|
+
data = await (extended as any).${modelNameLower}.guard(shape, caller).findMany(query)
|
|
327
328
|
} else {
|
|
328
|
-
data = await extended.${modelNameLower}.findMany(query)
|
|
329
|
+
data = await (extended as any).${modelNameLower}.findMany(query)
|
|
329
330
|
}
|
|
330
331
|
|
|
331
332
|
res.locals.data = data
|
|
@@ -392,11 +393,11 @@ ${validationLines ? validationLines + '\n' : ''} const extended = await getEx
|
|
|
392
393
|
|
|
393
394
|
let data
|
|
394
395
|
if (shape) {
|
|
395
|
-
assertGuard(extended.${modelNameLower})
|
|
396
|
+
assertGuard((extended as any).${modelNameLower})
|
|
396
397
|
const caller = res.locals.guardCaller
|
|
397
|
-
data = await extended.${modelNameLower}.guard(shape, caller).${op.method}(body)
|
|
398
|
+
data = await (extended as any).${modelNameLower}.guard(shape, caller).${op.method}(body)
|
|
398
399
|
} else {
|
|
399
|
-
data = await extended.${modelNameLower}.${op.method}(body)
|
|
400
|
+
data = await (extended as any).${modelNameLower}.${op.method}(body)
|
|
400
401
|
}
|
|
401
402
|
|
|
402
403
|
res.locals.data = data
|
|
@@ -422,6 +423,14 @@ async function countForPagination(
|
|
|
422
423
|
const countShape = shape ? buildCountShape(shape) : undefined
|
|
423
424
|
|
|
424
425
|
if (hasDistinct) {
|
|
426
|
+
if (shape) {
|
|
427
|
+
const countArgs: Record<string, any> = {}
|
|
428
|
+
if (query.where) countArgs.where = query.where
|
|
429
|
+
return countShape
|
|
430
|
+
? await delegate.guard(countShape, caller).count(countArgs)
|
|
431
|
+
: await delegate.count(countArgs)
|
|
432
|
+
}
|
|
433
|
+
|
|
425
434
|
const selectField = distinctFields[0]
|
|
426
435
|
const distinctArgs: Record<string, any> = {
|
|
427
436
|
where: query.where,
|
|
@@ -430,17 +439,13 @@ async function countForPagination(
|
|
|
430
439
|
take: DISTINCT_COUNT_LIMIT + 1,
|
|
431
440
|
}
|
|
432
441
|
|
|
433
|
-
const results =
|
|
434
|
-
? await delegate.guard(shape, caller).findMany(distinctArgs)
|
|
435
|
-
: await delegate.findMany(distinctArgs)
|
|
442
|
+
const results = await delegate.findMany(distinctArgs)
|
|
436
443
|
|
|
437
444
|
if (results.length > DISTINCT_COUNT_LIMIT) {
|
|
438
445
|
console.warn('[prisma-generator-express] Distinct count exceeds ' + DISTINCT_COUNT_LIMIT + ', falling back to approximate total')
|
|
439
446
|
const countArgs: Record<string, any> = {}
|
|
440
447
|
if (query.where) countArgs.where = query.where
|
|
441
|
-
return
|
|
442
|
-
? await delegate.guard(countShape, caller).count(countArgs)
|
|
443
|
-
: await delegate.count(countArgs)
|
|
448
|
+
return await delegate.count(countArgs)
|
|
444
449
|
}
|
|
445
450
|
|
|
446
451
|
return results.length
|
|
@@ -463,7 +468,7 @@ export async function ${modelName}FindManyPaginated(req: Request, res: Response,
|
|
|
463
468
|
const caller = res.locals.guardCaller
|
|
464
469
|
|
|
465
470
|
if (shape) {
|
|
466
|
-
assertGuard(extended.${modelNameLower})
|
|
471
|
+
assertGuard((extended as any).${modelNameLower})
|
|
467
472
|
}
|
|
468
473
|
|
|
469
474
|
let items: any[]
|
|
@@ -487,18 +492,18 @@ export async function ${modelName}FindManyPaginated(req: Request, res: Response,
|
|
|
487
492
|
) {
|
|
488
493
|
console.warn('[prisma-generator-express] Interactive transactions not available, pagination queries are non-atomic')
|
|
489
494
|
items = shape
|
|
490
|
-
? await extended.${modelNameLower}.guard(shape, caller).findMany(query)
|
|
491
|
-
: await extended.${modelNameLower}.findMany(query)
|
|
492
|
-
total = await countForPagination(extended.${modelNameLower}, query, shape, caller)
|
|
495
|
+
? await (extended as any).${modelNameLower}.guard(shape, caller).findMany(query)
|
|
496
|
+
: await (extended as any).${modelNameLower}.findMany(query)
|
|
497
|
+
total = await countForPagination((extended as any).${modelNameLower}, query, shape, caller)
|
|
493
498
|
} else {
|
|
494
499
|
throw txError
|
|
495
500
|
}
|
|
496
501
|
}
|
|
497
502
|
} else {
|
|
498
503
|
items = shape
|
|
499
|
-
? await extended.${modelNameLower}.guard(shape, caller).findMany(query)
|
|
500
|
-
: await extended.${modelNameLower}.findMany(query)
|
|
501
|
-
total = await countForPagination(extended.${modelNameLower}, query, shape, caller)
|
|
504
|
+
? await (extended as any).${modelNameLower}.guard(shape, caller).findMany(query)
|
|
505
|
+
: await (extended as any).${modelNameLower}.findMany(query)
|
|
506
|
+
total = await countForPagination((extended as any).${modelNameLower}, query, shape, caller)
|
|
502
507
|
}
|
|
503
508
|
|
|
504
509
|
const skip = (query.skip as number) ?? 0
|
|
@@ -513,4 +518,4 @@ export async function ${modelName}FindManyPaginated(req: Request, res: Response,
|
|
|
513
518
|
}
|
|
514
519
|
`
|
|
515
520
|
)
|
|
516
|
-
}
|
|
521
|
+
}
|
|
@@ -40,7 +40,7 @@ export function generateScalarUIHandler(options: {
|
|
|
40
40
|
isRequired: f.isRequired,
|
|
41
41
|
hasDefaultValue: f.hasDefaultValue,
|
|
42
42
|
isUpdatedAt: Boolean(f.isUpdatedAt),
|
|
43
|
-
documentation: f.documentation,
|
|
43
|
+
documentation: f.documentation ?? null,
|
|
44
44
|
isId: Boolean(f.isId),
|
|
45
45
|
isUnique: Boolean(f.isUnique),
|
|
46
46
|
relationFromFields: f.relationFromFields,
|
|
@@ -87,14 +87,33 @@ export function generateScalarUIHandler(options: {
|
|
|
87
87
|
}))
|
|
88
88
|
|
|
89
89
|
return `import { Request, Response } from 'express'
|
|
90
|
-
import { buildModelOpenApi } from '../buildModelOpenApi'
|
|
91
|
-
import type { RouteConfig } from '../routeConfig'
|
|
92
|
-
import { OPERATION_DEFS, isOperationEnabled } from '../operationDefinitions'
|
|
90
|
+
import { buildModelOpenApi } from '../buildModelOpenApi.js'
|
|
91
|
+
import type { RouteConfig } from '../routeConfig.js'
|
|
92
|
+
import { OPERATION_DEFS, isOperationEnabled } from '../operationDefinitions.js'
|
|
93
93
|
|
|
94
94
|
const _env = typeof process !== 'undefined' && process.env ? process.env : {} as Record<string, string | undefined>
|
|
95
95
|
|
|
96
|
-
|
|
97
|
-
|
|
96
|
+
interface FieldMeta {
|
|
97
|
+
name: string
|
|
98
|
+
kind: string
|
|
99
|
+
type: string
|
|
100
|
+
isList: boolean
|
|
101
|
+
isRequired: boolean
|
|
102
|
+
hasDefaultValue: boolean
|
|
103
|
+
isUpdatedAt: boolean
|
|
104
|
+
documentation: string | null
|
|
105
|
+
isId: boolean
|
|
106
|
+
isUnique: boolean
|
|
107
|
+
relationFromFields?: string[]
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
interface EnumMeta {
|
|
111
|
+
name: string
|
|
112
|
+
values: { name: string }[]
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export const MODEL_FIELDS: FieldMeta[] = ${JSON.stringify(fieldsMeta, null, 2)}
|
|
116
|
+
export const MODEL_ENUMS: EnumMeta[] = ${JSON.stringify(enumsMeta, null, 2)}
|
|
98
117
|
|
|
99
118
|
const COMPOUND_ID: { fields: string[] } | null = ${JSON.stringify(compoundIdMeta)}
|
|
100
119
|
|
|
@@ -114,8 +133,6 @@ type DocsConfig = RouteConfig & {
|
|
|
114
133
|
docsUi?: DocsUI
|
|
115
134
|
}
|
|
116
135
|
|
|
117
|
-
type FieldMeta = typeof MODEL_FIELDS[number]
|
|
118
|
-
|
|
119
136
|
interface OpDetail {
|
|
120
137
|
transport: string
|
|
121
138
|
required: string[]
|
|
@@ -365,11 +382,11 @@ function isPlaygroundAvailable(config: DocsConfig) {
|
|
|
365
382
|
|
|
366
383
|
function escapeHtml(input: string) {
|
|
367
384
|
return input
|
|
368
|
-
.
|
|
369
|
-
.
|
|
370
|
-
.
|
|
371
|
-
.
|
|
372
|
-
.
|
|
385
|
+
.replace(/&/g, '&')
|
|
386
|
+
.replace(/</g, '<')
|
|
387
|
+
.replace(/>/g, '>')
|
|
388
|
+
.replace(/"/g, '"')
|
|
389
|
+
.replace(/'/g, ''')
|
|
373
390
|
}
|
|
374
391
|
|
|
375
392
|
function safeJsonForHtml(obj: unknown): string {
|
|
@@ -1406,4 +1423,4 @@ export function ${modelName}Docs(config: DocsConfig = {}) {
|
|
|
1406
1423
|
}
|
|
1407
1424
|
}
|
|
1408
1425
|
`
|
|
1409
|
-
}
|
|
1426
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -3,18 +3,18 @@ import {
|
|
|
3
3
|
GeneratorOptions,
|
|
4
4
|
DMMF,
|
|
5
5
|
} from '@prisma/generator-helper'
|
|
6
|
-
import { generateUnifiedHandler } from './generators/generateUnifiedHandler'
|
|
7
|
-
import { generateRouterFunction } from './generators/generateRouter'
|
|
8
|
-
import { generateScalarUIHandler } from './generators/generateUnifiedScalarUI'
|
|
9
|
-
import { generateUnifiedDocs } from './generators/generateUnifiedDocs'
|
|
10
|
-
import { generateQueryBuilderHelper } from './generators/generateQueryBuilderHelper'
|
|
6
|
+
import { generateUnifiedHandler } from './generators/generateUnifiedHandler.js'
|
|
7
|
+
import { generateRouterFunction } from './generators/generateRouter.js'
|
|
8
|
+
import { generateScalarUIHandler } from './generators/generateUnifiedScalarUI.js'
|
|
9
|
+
import { generateUnifiedDocs } from './generators/generateUnifiedDocs.js'
|
|
10
|
+
import { generateQueryBuilderHelper } from './generators/generateQueryBuilderHelper.js'
|
|
11
11
|
import {
|
|
12
12
|
generateImportPrismaStatement,
|
|
13
13
|
getRelativeClientPath,
|
|
14
|
-
} from './generators/generateImportPrismaStatement'
|
|
15
|
-
import { writeFileSafely } from './utils/writeFileSafely'
|
|
16
|
-
import { copyFiles } from './utils/copyFiles'
|
|
17
|
-
import { GENERATOR_NAME } from './constants'
|
|
14
|
+
} from './generators/generateImportPrismaStatement.js'
|
|
15
|
+
import { writeFileSafely } from './utils/writeFileSafely.js'
|
|
16
|
+
import { copyFiles } from './utils/copyFiles.js'
|
|
17
|
+
import { GENERATOR_NAME } from './constants.js'
|
|
18
18
|
|
|
19
19
|
generatorHandler({
|
|
20
20
|
onManifest() {
|