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.
@@ -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 requiredScalars = fields
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 (requiredScalars.length > 0) {
239
- createInputSchema.required = [...requiredScalars]
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 (requiredScalars.length > 0) {
256
- createManyInputSchema.required = [...requiredScalars]
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(field: ModelField): SchemaObject | RefObject {
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 schema: SchemaObject | RefObject
1446
+ let baseSchema: SchemaObject | RefObject
1393
1447
 
1394
1448
  switch (field.kind) {
1395
1449
  case 'scalar':
1396
- schema = mapScalarType(field.type)
1450
+ baseSchema = mapScalarType(field.type)
1397
1451
  break
1398
1452
  case 'enum':
1399
- schema = { $ref: `#/components/schemas/${field.type}` }
1453
+ baseSchema = { $ref: `#/components/schemas/${field.type}` }
1400
1454
  break
1401
1455
  default:
1402
- schema = { type: 'string' }
1456
+ baseSchema = { type: 'string' }
1403
1457
  }
1404
1458
 
1459
+ let schema: SchemaObject | RefObject
1460
+
1405
1461
  if (field.isList) {
1406
- schema = { type: 'array', items: schema }
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
+ }
@@ -1,4 +1,4 @@
1
- import { isObject, isSafeKey, sanitizeKeys } from './misc'
1
+ import { isObject, isSafeKey, sanitizeKeys } from './misc.js'
2
2
 
3
3
  type QueryParams =
4
4
  | string
@@ -75,4 +75,4 @@ export function getRelativeClientPath(
75
75
  const routerDirPath = path.join(outputValue, modelName)
76
76
 
77
77
  return getRelativeImportPath(routerDirPath, clientGenerator.output.value)
78
- }
78
+ }
@@ -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
- ${models.map((model) => ` ${model}: ${model}Docs`).join(',\n')}
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
- .replaceAll('&', '&amp;')
37
- .replaceAll('<', '&lt;')
38
- .replaceAll('>', '&gt;')
39
- .replaceAll('"', '&quot;')
40
- .replaceAll("'", '&#039;')
40
+ .replace(/&/g, '&amp;')
41
+ .replace(/</g, '&lt;')
42
+ .replace(/>/g, '&gt;')
43
+ .replace(/"/g, '&quot;')
44
+ .replace(/'/g, '&#039;')
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 html = \`<!DOCTYPE html>
81
- <html lang="en">
82
- <head>
83
- <meta charset="utf-8" />
84
- <meta name="viewport" content="width=device-width, initial-scale=1" />
85
- <title>\${escapeHtml(title)}</title>
86
- <script src="https://cdn.tailwindcss.com"></script>
87
- </head>
88
- <body class="m-0 bg-white text-gray-900 font-serif leading-normal">
89
- <div class="max-w-[980px] mx-auto px-7 pt-10 pb-16">
90
- <div class="border-b-2 border-gray-900 pb-3.5 mb-[18px]">
91
- <div class="text-[28px] font-bold tracking-wide">\${escapeHtml(title)}</div>
92
- \${description ? '<div class="mt-1.5 text-gray-500 text-sm">' + escapeHtml(description) + '</div>' : ''}
93
- <div class="mt-3 flex gap-x-5 text-[13px] text-gray-500">
94
- \${version ? '<div>Version: ' + escapeHtml(version) + '</div>' : ''}
95
- <div>Generated: \${escapeHtml(generatedAt)}</div>
96
- </div>
97
- </div>
98
-
99
- <div class="mt-[22px]">
100
- <h2 class="m-0 mb-2.5 text-lg border-t border-gray-300 pt-3.5">Models</h2>
101
- <table class="w-full border-collapse text-[13px]">
102
- <thead>
103
- <tr>
104
- <th class="text-left py-2 px-2 border-b border-gray-300 align-top font-bold">Model</th>
105
- <th class="text-left py-2 px-2 border-b border-gray-300 align-top font-bold">Documentation</th>
106
- <th class="text-left py-2 px-2 border-b border-gray-300 align-top font-bold">Views</th>
107
- </tr>
108
- </thead>
109
- <tbody>
110
- \${registeredModels.map((m) => {
111
- const lower = m.toLowerCase()
112
- const docsUrl = \`\${basePath}/\${lower}\`
113
- const scalarUrl = \`\${basePath}/\${lower}?ui=scalar\`
114
- const jsonUrl = \`\${basePath}/\${lower}?ui=json\`
115
- const yamlUrl = \`\${basePath}/\${lower}?ui=yaml\`
116
- const playgroundUrl = \`\${basePath}/\${lower}?ui=playground\`
117
- const modelCfg = config.modelConfigs[m]
118
- const modelPlayground = isPlaygroundAvailable(modelCfg)
119
- const playgroundLink = modelPlayground
120
- ? \`, <a href="\${playgroundUrl}" class="text-inherit underline">playground</a>\`
121
- : ''
122
- return \`
123
- <tr>
124
- <td class="text-left py-2 px-2 border-b border-gray-300 align-top">\${escapeHtml(m)}</td>
125
- <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>
126
- <td class="text-left py-2 px-2 border-b border-gray-300 align-top">
127
- <a href="\${scalarUrl}" class="text-inherit underline">scalar</a>,
128
- <a href="\${jsonUrl}" class="text-inherit underline">json</a>,
129
- <a href="\${yamlUrl}" class="text-inherit underline">yaml</a>\${playgroundLink}
130
- </td>
131
- </tr>
132
- \`
133
- }).join('')}
134
- </tbody>
135
- </table>
136
- </div>
137
- </div>
138
- </body>
139
- </html>\`
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 path = \`\${normalizedBase}/\${model.toLowerCase()}\`
163
- console.log(\` Registered docs: \${path}\`)
164
- app.get(path, handler(cfg))
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 = shape
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 countShape
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
- export const MODEL_FIELDS = ${JSON.stringify(fieldsMeta, null, 2)} as const
97
- export const MODEL_ENUMS = ${JSON.stringify(enumsMeta, null, 2)} as const
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
- .replaceAll('&', '&amp;')
369
- .replaceAll('<', '&lt;')
370
- .replaceAll('>', '&gt;')
371
- .replaceAll('"', '&quot;')
372
- .replaceAll("'", '&#039;')
385
+ .replace(/&/g, '&amp;')
386
+ .replace(/</g, '&lt;')
387
+ .replace(/>/g, '&gt;')
388
+ .replace(/"/g, '&quot;')
389
+ .replace(/'/g, '&#039;')
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() {