prisma-generator-express 1.27.0 → 1.29.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.
Files changed (49) hide show
  1. package/README.md +255 -16
  2. package/dist/constants.d.ts +1 -0
  3. package/dist/generators/generateFastifyHandler.d.ts +4 -0
  4. package/dist/generators/generateFastifyHandler.js +78 -0
  5. package/dist/generators/generateFastifyHandler.js.map +1 -0
  6. package/dist/generators/generateOperationCore.d.ts +6 -0
  7. package/dist/generators/generateOperationCore.js +534 -0
  8. package/dist/generators/generateOperationCore.js.map +1 -0
  9. package/dist/generators/generateQueryBuilderHelper.js +85 -69
  10. package/dist/generators/generateQueryBuilderHelper.js.map +1 -1
  11. package/dist/generators/generateRouter.js +1 -25
  12. package/dist/generators/generateRouter.js.map +1 -1
  13. package/dist/generators/generateRouterFastify.d.ts +5 -0
  14. package/dist/generators/generateRouterFastify.js +512 -0
  15. package/dist/generators/generateRouterFastify.js.map +1 -0
  16. package/dist/generators/generateUnifiedDocs.d.ts +2 -1
  17. package/dist/generators/generateUnifiedDocs.js +147 -82
  18. package/dist/generators/generateUnifiedDocs.js.map +1 -1
  19. package/dist/generators/generateUnifiedHandler.d.ts +0 -1
  20. package/dist/generators/generateUnifiedHandler.js +47 -516
  21. package/dist/generators/generateUnifiedHandler.js.map +1 -1
  22. package/dist/generators/generateUnifiedScalarUI.d.ts +2 -0
  23. package/dist/generators/generateUnifiedScalarUI.js +127 -1324
  24. package/dist/generators/generateUnifiedScalarUI.js.map +1 -1
  25. package/dist/index.js +33 -8
  26. package/dist/index.js.map +1 -1
  27. package/dist/utils/copyFiles.d.ts +2 -1
  28. package/dist/utils/copyFiles.js +64 -38
  29. package/dist/utils/copyFiles.js.map +1 -1
  30. package/dist/utils/writeFileSafely.js +3 -0
  31. package/dist/utils/writeFileSafely.js.map +1 -1
  32. package/package.json +4 -1
  33. package/src/client/encodeQueryParams.ts +1 -1
  34. package/src/constants.ts +2 -0
  35. package/src/copy/createOutputValidatorMiddleware.ts +9 -12
  36. package/src/copy/docsRenderer.ts +1285 -0
  37. package/src/copy/parseQueryParams.ts +4 -8
  38. package/src/copy/routeConfig.ts +10 -4
  39. package/src/generators/generateFastifyHandler.ts +86 -0
  40. package/src/generators/generateOperationCore.ts +545 -0
  41. package/src/generators/generateQueryBuilderHelper.ts +86 -70
  42. package/src/generators/generateRouter.ts +1 -25
  43. package/src/generators/generateRouterFastify.ts +522 -0
  44. package/src/generators/generateUnifiedDocs.ts +164 -81
  45. package/src/generators/generateUnifiedHandler.ts +45 -533
  46. package/src/generators/generateUnifiedScalarUI.ts +134 -1323
  47. package/src/index.ts +45 -9
  48. package/src/utils/copyFiles.ts +79 -45
  49. package/src/utils/writeFileSafely.ts +4 -0
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateFastifyHandler = generateFastifyHandler;
4
+ const READ_OPS = [
5
+ 'findMany',
6
+ 'findFirst',
7
+ 'findFirstOrThrow',
8
+ 'findUnique',
9
+ 'findUniqueOrThrow',
10
+ 'findManyPaginated',
11
+ 'aggregate',
12
+ 'count',
13
+ 'groupBy',
14
+ ];
15
+ const WRITE_OPS = [
16
+ 'create',
17
+ 'createMany',
18
+ 'createManyAndReturn',
19
+ 'update',
20
+ 'updateMany',
21
+ 'updateManyAndReturn',
22
+ 'upsert',
23
+ 'delete',
24
+ 'deleteMany',
25
+ ];
26
+ const CREATED_OPS = new Set([
27
+ 'create',
28
+ 'createMany',
29
+ 'createManyAndReturn',
30
+ ]);
31
+ function generateFastifyHandler(options) {
32
+ const modelName = options.model.name;
33
+ const readHandlers = READ_OPS.map((op) => {
34
+ const exportName = `${modelName}${op.charAt(0).toUpperCase() + op.slice(1)}`;
35
+ return `
36
+ export async function ${exportName}(
37
+ request: FastifyRequest,
38
+ _reply: FastifyReply,
39
+ ): Promise<void> {
40
+ const data = await core.${op}(buildContext(request))
41
+ ;(request as any).resultData = data
42
+ }`;
43
+ }).join('\n');
44
+ const writeHandlers = WRITE_OPS.map((op) => {
45
+ const exportName = `${modelName}${op.charAt(0).toUpperCase() + op.slice(1)}`;
46
+ const statusCode = CREATED_OPS.has(op) ? 201 : 200;
47
+ return `
48
+ export async function ${exportName}(
49
+ request: FastifyRequest,
50
+ _reply: FastifyReply,
51
+ ): Promise<void> {
52
+ const data = await core.${op}(buildContext(request))
53
+ ;(request as any).resultData = data
54
+ ;(request as any).resultStatus = ${statusCode}
55
+ }`;
56
+ }).join('\n');
57
+ return `import type { FastifyRequest, FastifyReply } from 'fastify'
58
+ import * as core from './${modelName}Core.js'
59
+ import type { OperationContext } from '../operationRuntime.js'
60
+
61
+ function buildContext(request: FastifyRequest): OperationContext {
62
+ const req = request as any
63
+ return {
64
+ prisma: req.prisma,
65
+ postgres: req.postgres,
66
+ sqlite: req.sqlite,
67
+ parsedQuery: req.parsedQuery,
68
+ body: request.body,
69
+ guardShape: req.guardShape,
70
+ guardCaller: req.guardCaller,
71
+ paginationConfig: req.routeConfig?.pagination,
72
+ }
73
+ }
74
+ ${readHandlers}
75
+ ${writeHandlers}
76
+ `;
77
+ }
78
+ //# sourceMappingURL=generateFastifyHandler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generateFastifyHandler.js","sourceRoot":"","sources":["../../src/generators/generateFastifyHandler.ts"],"names":[],"mappings":";;AAgCA,wDAqDC;AAnFD,MAAM,QAAQ,GAAG;IACf,UAAU;IACV,WAAW;IACX,kBAAkB;IAClB,YAAY;IACZ,mBAAmB;IACnB,mBAAmB;IACnB,WAAW;IACX,OAAO;IACP,SAAS;CACV,CAAA;AAED,MAAM,SAAS,GAAG;IAChB,QAAQ;IACR,YAAY;IACZ,qBAAqB;IACrB,QAAQ;IACR,YAAY;IACZ,qBAAqB;IACrB,QAAQ;IACR,QAAQ;IACR,YAAY;CACb,CAAA;AAED,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC;IAC1B,QAAQ;IACR,YAAY;IACZ,qBAAqB;CACtB,CAAC,CAAA;AAEF,SAAgB,sBAAsB,CAAC,OAEtC;IACC,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAA;IAEpC,MAAM,YAAY,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE;QACvC,MAAM,UAAU,GAAG,GAAG,SAAS,GAAG,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAA;QAE5E,OAAO;wBACa,UAAU;;;;4BAIN,EAAE;;EAE5B,CAAA;IACA,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAEb,MAAM,aAAa,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE;QACzC,MAAM,UAAU,GAAG,GAAG,SAAS,GAAG,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAA;QAC5E,MAAM,UAAU,GAAG,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAA;QAElD,OAAO;wBACa,UAAU;;;;4BAIN,EAAE;;qCAEO,UAAU;EAC7C,CAAA;IACA,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAEb,OAAO;2BACkB,SAAS;;;;;;;;;;;;;;;;EAgBlC,YAAY;EACZ,aAAa;CACd,CAAA;AACD,CAAC"}
@@ -0,0 +1,6 @@
1
+ import { DMMF } from '@prisma/generator-helper';
2
+ export declare function generateOperationRuntime(): string;
3
+ export interface ModelCoreOptions {
4
+ model: DMMF.Model;
5
+ }
6
+ export declare function generateModelCore(options: ModelCoreOptions): string;
@@ -0,0 +1,534 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateOperationRuntime = generateOperationRuntime;
4
+ exports.generateModelCore = generateModelCore;
5
+ function generateOperationRuntime() {
6
+ return `import { sanitizeKeys } from './misc.js'
7
+
8
+ export interface PaginationConfig {
9
+ defaultLimit?: number
10
+ maxLimit?: number
11
+ distinctCountLimit?: number
12
+ }
13
+
14
+ export interface OperationContext {
15
+ prisma: any
16
+ postgres?: any
17
+ sqlite?: any
18
+ parsedQuery?: Record<string, unknown>
19
+ body?: unknown
20
+ guardShape?: Record<string, unknown>
21
+ guardCaller?: string
22
+ paginationConfig?: PaginationConfig
23
+ }
24
+
25
+ export const DISTINCT_COUNT_LIMIT = 100000
26
+
27
+ export class HttpError extends Error {
28
+ status: number
29
+ constructor(status: number, message: string) {
30
+ super(message)
31
+ this.name = 'HttpError'
32
+ this.status = status
33
+ }
34
+ }
35
+
36
+ const PRISMA_ERROR_MAP: Record<string, { status: number; message: string }> = {
37
+ P2000: { status: 400, message: 'Value too long for column' },
38
+ P2001: { status: 404, message: 'Record not found' },
39
+ P2002: { status: 409, message: 'Unique constraint violation' },
40
+ P2003: { status: 400, message: 'Foreign key constraint failed' },
41
+ P2004: { status: 400, message: 'Constraint failed on the database' },
42
+ P2005: { status: 400, message: 'Invalid field value' },
43
+ P2006: { status: 400, message: 'Invalid value provided' },
44
+ P2007: { status: 400, message: 'Data validation error' },
45
+ P2008: { status: 400, message: 'Failed to parse the query' },
46
+ P2009: { status: 400, message: 'Failed to validate the query' },
47
+ P2010: { status: 500, message: 'Raw query failed' },
48
+ P2011: { status: 400, message: 'Null constraint violation' },
49
+ P2012: { status: 400, message: 'Missing required value' },
50
+ P2013: { status: 400, message: 'Missing required argument' },
51
+ P2014: { status: 400, message: 'Required relation violation' },
52
+ P2015: { status: 404, message: 'Related record not found' },
53
+ P2016: { status: 400, message: 'Query interpretation error' },
54
+ P2017: { status: 400, message: 'Records not connected' },
55
+ P2018: { status: 404, message: 'Required connected record not found' },
56
+ P2019: { status: 400, message: 'Input error' },
57
+ P2020: { status: 400, message: 'Value out of range for the field type' },
58
+ P2021: { status: 500, message: 'Table does not exist in the database' },
59
+ P2022: { status: 500, message: 'Column does not exist in the database' },
60
+ P2023: { status: 500, message: 'Inconsistent column data' },
61
+ P2024: { status: 503, message: 'Connection pool timeout' },
62
+ P2025: { status: 404, message: 'Record not found' },
63
+ P2026: { status: 501, message: 'Feature not supported by the current database provider' },
64
+ P2028: { status: 500, message: 'Transaction API error' },
65
+ P2030: { status: 400, message: 'Cannot find a fulltext index for the search' },
66
+ P2033: { status: 400, message: 'Number out of range for the field type' },
67
+ P2034: { status: 409, message: 'Transaction conflict, please retry' },
68
+ }
69
+
70
+ export function mapError(error: unknown): HttpError {
71
+ if (error instanceof HttpError) return error
72
+
73
+ if (
74
+ error &&
75
+ typeof error === 'object' &&
76
+ 'name' in error &&
77
+ error.name === 'ShapeError'
78
+ ) {
79
+ return new HttpError(400, (error as any).message)
80
+ }
81
+
82
+ if (
83
+ error &&
84
+ typeof error === 'object' &&
85
+ 'name' in error &&
86
+ error.name === 'CallerError'
87
+ ) {
88
+ return new HttpError(400, (error as any).message)
89
+ }
90
+
91
+ if (
92
+ error &&
93
+ typeof error === 'object' &&
94
+ 'name' in error &&
95
+ error.name === 'PolicyError'
96
+ ) {
97
+ return new HttpError(403, (error as any).message)
98
+ }
99
+
100
+ if (
101
+ error &&
102
+ typeof error === 'object' &&
103
+ 'issues' in error &&
104
+ 'name' in error &&
105
+ (error as any).name === 'ZodError'
106
+ ) {
107
+ const issues = (error as any).issues
108
+ const message = Array.isArray(issues)
109
+ ? issues.map((i: any) => i.message).join('; ')
110
+ : (error as any).message
111
+ return new HttpError(400, message)
112
+ }
113
+
114
+ if (error && typeof error === 'object' && 'code' in error) {
115
+ const code = (error as any).code as string
116
+ const mapped = PRISMA_ERROR_MAP[code]
117
+ if (mapped) {
118
+ return new HttpError(mapped.status, mapped.message)
119
+ }
120
+ if (typeof code === 'string' && code.startsWith('P')) {
121
+ console.warn(
122
+ '[prisma-generator-express] Unmapped Prisma error code:',
123
+ code,
124
+ (error as any).message || '',
125
+ )
126
+ return new HttpError(500, 'Database operation failed')
127
+ }
128
+ }
129
+
130
+ if (error && typeof error === 'object' && 'name' in error) {
131
+ const name = (error as any).name
132
+ if (name === 'PrismaClientValidationError') {
133
+ return new HttpError(400, 'Invalid query parameters')
134
+ }
135
+ }
136
+
137
+ console.error('[prisma-generator-express] Unhandled error:', error)
138
+ return new HttpError(500, 'Internal server error')
139
+ }
140
+
141
+ let _speedExtension: ((opts: any) => any) | null = null
142
+
143
+ const _prismasqlModule = 'prisma-' + 'sql'
144
+ const _prismasqlReady = (async () => {
145
+ try {
146
+ const mod = await import(_prismasqlModule)
147
+ _speedExtension = mod.speedExtension ?? mod.default?.speedExtension ?? null
148
+ } catch (err: any) {
149
+ const code = err?.code
150
+ if (code !== 'MODULE_NOT_FOUND' && code !== 'ERR_MODULE_NOT_FOUND') {
151
+ console.warn('[prisma-generator-express] prisma-sql initialization failed:', err)
152
+ }
153
+ }
154
+ })()
155
+
156
+ const _extendedClients = new WeakMap<object, WeakMap<object, any>>()
157
+
158
+ export async function getExtendedClient(ctx: OperationContext): Promise<any> {
159
+ const base = ctx.prisma
160
+ if (!base) {
161
+ throw new HttpError(500, 'PrismaClient not found on request. Set req.prisma in middleware.')
162
+ }
163
+
164
+ await _prismasqlReady
165
+
166
+ if (!_speedExtension) return base
167
+
168
+ const connector = ctx.postgres || ctx.sqlite
169
+ if (!connector) return base
170
+
171
+ if (typeof connector === 'object' && connector !== null) {
172
+ const innerMap = _extendedClients.get(connector)
173
+ if (innerMap) {
174
+ const cached = innerMap.get(base)
175
+ if (cached) return cached
176
+ }
177
+ }
178
+
179
+ try {
180
+ const extended = base.$extends(_speedExtension({
181
+ postgres: ctx.postgres,
182
+ sqlite: ctx.sqlite,
183
+ debug: process.env.DEBUG === 'true',
184
+ }))
185
+
186
+ if (typeof connector === 'object' && connector !== null) {
187
+ let innerMap = _extendedClients.get(connector)
188
+ if (!innerMap) {
189
+ innerMap = new WeakMap<object, any>()
190
+ _extendedClients.set(connector, innerMap)
191
+ }
192
+ innerMap.set(base, extended)
193
+ }
194
+
195
+ return extended
196
+ } catch (error) {
197
+ console.warn('[speedExtension] Failed to initialize, using base client:', error)
198
+ return base
199
+ }
200
+ }
201
+
202
+ export function validateBody(body: unknown): Record<string, any> {
203
+ if (!body || typeof body !== 'object' || Array.isArray(body)) {
204
+ throw new HttpError(400, 'Request body must be a JSON object')
205
+ }
206
+ return sanitizeKeys(body as Record<string, any>)
207
+ }
208
+
209
+ export function requireBodyField(body: Record<string, any>, field: string): void {
210
+ if (!(field in body) || body[field] === undefined) {
211
+ throw new HttpError(400, 'Missing required field: ' + field)
212
+ }
213
+ }
214
+
215
+ export function applyPaginationLimits(
216
+ query: Record<string, any>,
217
+ config?: PaginationConfig,
218
+ ): Record<string, any> {
219
+ if (!config) return query
220
+
221
+ const result = { ...query }
222
+
223
+ if (result.take === undefined && config.defaultLimit !== undefined) {
224
+ result.take = config.defaultLimit
225
+ }
226
+
227
+ if (config.maxLimit !== undefined && result.take !== undefined) {
228
+ const takeNum = Number(result.take)
229
+ if (Math.abs(takeNum) > config.maxLimit) {
230
+ result.take = takeNum < 0 ? -config.maxLimit : config.maxLimit
231
+ }
232
+ }
233
+
234
+ return result
235
+ }
236
+
237
+ export function normalizeDistinct(value: unknown): string[] {
238
+ if (typeof value === 'string') return [value]
239
+ if (Array.isArray(value)) return value.filter((v): v is string => typeof v === 'string')
240
+ return []
241
+ }
242
+
243
+ export function assertGuard(delegate: any): void {
244
+ if (typeof delegate.guard !== 'function') {
245
+ throw new HttpError(
246
+ 500,
247
+ 'Guard shapes require prisma-guard extension on PrismaClient. Install: npm install prisma-guard, then extend your client with guardExtension().',
248
+ )
249
+ }
250
+ }
251
+
252
+ const GUARD_SHAPE_CONFIG_KEYS = new Set([
253
+ 'data', 'create', 'update', 'where', 'include', 'select', 'orderBy',
254
+ 'cursor', 'take', 'skip', 'distinct', 'having', '_count', '_avg',
255
+ '_sum', '_min', '_max', 'by',
256
+ ])
257
+
258
+ function keepWhereOnly(obj: Record<string, any>): Record<string, any> {
259
+ const result: Record<string, any> = {}
260
+ if ('where' in obj) result.where = obj.where
261
+ return result
262
+ }
263
+
264
+ export function buildCountShape(shape: Record<string, any>): Record<string, any> {
265
+ if (typeof shape === 'function') {
266
+ return (...args: any[]) => keepWhereOnly((shape as Function)(...args))
267
+ }
268
+
269
+ const keys = Object.keys(shape)
270
+ const isSingleShape = keys.length === 0 || keys.every((k) => GUARD_SHAPE_CONFIG_KEYS.has(k))
271
+
272
+ if (isSingleShape) {
273
+ return keepWhereOnly(shape)
274
+ }
275
+
276
+ const result: Record<string, any> = {}
277
+ for (const [key, variant] of Object.entries(shape)) {
278
+ if (typeof variant === 'function') {
279
+ result[key] = (...args: any[]) => keepWhereOnly(variant(...args))
280
+ } else if (typeof variant === 'object' && variant !== null) {
281
+ result[key] = keepWhereOnly(variant)
282
+ } else {
283
+ result[key] = variant
284
+ }
285
+ }
286
+ return result
287
+ }
288
+
289
+ export async function countForPagination(
290
+ delegate: any,
291
+ query: Record<string, any>,
292
+ shape: Record<string, any> | undefined,
293
+ caller: string | undefined,
294
+ distinctCountLimit?: number,
295
+ ): Promise<number> {
296
+ const distinctFields = normalizeDistinct(query.distinct)
297
+ const hasDistinct = distinctFields.length > 0
298
+ const effectiveLimit = distinctCountLimit ?? DISTINCT_COUNT_LIMIT
299
+
300
+ const countShape = shape ? buildCountShape(shape) : undefined
301
+
302
+ if (hasDistinct) {
303
+ const selectField = distinctFields[0]
304
+ const distinctArgs: Record<string, any> = {
305
+ where: query.where,
306
+ distinct: distinctFields,
307
+ select: { [selectField]: true },
308
+ take: effectiveLimit + 1,
309
+ }
310
+
311
+ const results = shape
312
+ ? await delegate.guard(shape, caller).findMany(distinctArgs)
313
+ : await delegate.findMany(distinctArgs)
314
+
315
+ if (results.length > effectiveLimit) {
316
+ console.warn(
317
+ '[prisma-generator-express] Distinct count exceeds ' +
318
+ effectiveLimit +
319
+ ', falling back to approximate total',
320
+ )
321
+ const countArgs: Record<string, any> = {}
322
+ if (query.where) countArgs.where = query.where
323
+ return countShape
324
+ ? await delegate.guard(countShape, caller).count(countArgs)
325
+ : await delegate.count(countArgs)
326
+ }
327
+
328
+ return results.length
329
+ }
330
+
331
+ const countArgs: Record<string, any> = {}
332
+ if (query.where) countArgs.where = query.where
333
+
334
+ return countShape
335
+ ? await delegate.guard(countShape, caller).count(countArgs)
336
+ : await delegate.count(countArgs)
337
+ }
338
+
339
+ export function transformResult(value: unknown): unknown {
340
+ if (value === null || value === undefined) return value
341
+ if (typeof value === 'bigint') return value.toString()
342
+ if (typeof Buffer !== 'undefined' && Buffer.isBuffer(value)) {
343
+ return value.toString('base64')
344
+ }
345
+ if (value instanceof Uint8Array) {
346
+ return Buffer.from(value).toString('base64')
347
+ }
348
+ if (value instanceof Date) return value
349
+ if (Array.isArray(value)) return value.map(transformResult)
350
+ if (typeof value === 'object') {
351
+ const proto = Object.getPrototypeOf(value)
352
+ if (proto !== Object.prototype && proto !== null) return value
353
+ const out: Record<string, unknown> = {}
354
+ for (const [k, v] of Object.entries(value as Record<string, unknown>)) {
355
+ out[k] = transformResult(v)
356
+ }
357
+ return out
358
+ }
359
+ return value
360
+ }
361
+ `;
362
+ }
363
+ function generateModelCore(options) {
364
+ const modelName = options.model.name;
365
+ const modelNameLower = modelName.charAt(0).toLowerCase() + modelName.slice(1);
366
+ const standardReadOps = [
367
+ 'findFirst',
368
+ 'findUnique',
369
+ 'findUniqueOrThrow',
370
+ 'findFirstOrThrow',
371
+ 'count',
372
+ 'aggregate',
373
+ 'groupBy',
374
+ ];
375
+ const standardReadHandlers = standardReadOps
376
+ .map((op) => `
377
+ export async function ${op}(ctx: OperationContext): Promise<unknown> {
378
+ const query = ctx.parsedQuery || {}
379
+ const extended = await getExtendedClient(ctx)
380
+ if (ctx.guardShape) {
381
+ assertGuard((extended as any).${modelNameLower})
382
+ return (extended as any).${modelNameLower}.guard(ctx.guardShape, ctx.guardCaller).${op}(query)
383
+ }
384
+ return (extended as any).${modelNameLower}.${op}(query)
385
+ }`)
386
+ .join('\n');
387
+ const writeOps = [
388
+ { name: 'create', method: 'create', requiredFields: ['data'] },
389
+ { name: 'createMany', method: 'createMany', requiredFields: ['data'] },
390
+ {
391
+ name: 'createManyAndReturn',
392
+ method: 'createManyAndReturn',
393
+ requiredFields: ['data'],
394
+ },
395
+ {
396
+ name: 'update',
397
+ method: 'update',
398
+ requiredFields: ['where', 'data'],
399
+ },
400
+ {
401
+ name: 'updateMany',
402
+ method: 'updateMany',
403
+ requiredFields: ['where', 'data'],
404
+ },
405
+ {
406
+ name: 'updateManyAndReturn',
407
+ method: 'updateManyAndReturn',
408
+ requiredFields: ['where', 'data'],
409
+ },
410
+ { name: 'delete', method: 'delete', requiredFields: ['where'] },
411
+ {
412
+ name: 'deleteMany',
413
+ method: 'deleteMany',
414
+ requiredFields: ['where'],
415
+ },
416
+ {
417
+ name: 'upsert',
418
+ method: 'upsert',
419
+ requiredFields: ['where', 'create', 'update'],
420
+ },
421
+ ];
422
+ const writeHandlers = writeOps
423
+ .map((op) => {
424
+ const validationLines = op.requiredFields
425
+ .map((field) => ` requireBodyField(body, '${field}')`)
426
+ .join('\n');
427
+ return `
428
+ export async function ${op.name}(ctx: OperationContext): Promise<unknown> {
429
+ const body = validateBody(ctx.body)
430
+ ${validationLines}
431
+ const extended = await getExtendedClient(ctx)
432
+ if (ctx.guardShape) {
433
+ assertGuard((extended as any).${modelNameLower})
434
+ return (extended as any).${modelNameLower}.guard(ctx.guardShape, ctx.guardCaller).${op.method}(body)
435
+ }
436
+ return (extended as any).${modelNameLower}.${op.method}(body)
437
+ }`;
438
+ })
439
+ .join('\n');
440
+ return `import {
441
+ OperationContext,
442
+ getExtendedClient,
443
+ validateBody,
444
+ requireBodyField,
445
+ applyPaginationLimits,
446
+ assertGuard,
447
+ countForPagination,
448
+ } from '../operationRuntime.js'
449
+
450
+ export async function findMany(ctx: OperationContext): Promise<unknown> {
451
+ const rawQuery = ctx.parsedQuery || {}
452
+ const query = applyPaginationLimits(rawQuery, ctx.paginationConfig)
453
+ const extended = await getExtendedClient(ctx)
454
+ if (ctx.guardShape) {
455
+ assertGuard((extended as any).${modelNameLower})
456
+ return (extended as any).${modelNameLower}.guard(ctx.guardShape, ctx.guardCaller).findMany(query)
457
+ }
458
+ return (extended as any).${modelNameLower}.findMany(query)
459
+ }
460
+ ${standardReadHandlers}
461
+ ${writeHandlers}
462
+
463
+ export async function findManyPaginated(
464
+ ctx: OperationContext,
465
+ ): Promise<{ data: unknown[]; total: number; hasMore: boolean }> {
466
+ const rawQuery = ctx.parsedQuery || {}
467
+ const query = applyPaginationLimits(rawQuery, ctx.paginationConfig)
468
+ const extended = await getExtendedClient(ctx)
469
+ const shape = ctx.guardShape
470
+ const caller = ctx.guardCaller
471
+ const distinctCountLimit = ctx.paginationConfig?.distinctCountLimit
472
+
473
+ if (shape) {
474
+ assertGuard((extended as any).${modelNameLower})
475
+ }
476
+
477
+ let items: any[]
478
+ let total: number
479
+
480
+ if (typeof extended.$transaction === 'function') {
481
+ try {
482
+ const txResult = await extended.$transaction(async (tx: any) => {
483
+ const d = shape
484
+ ? await tx.${modelNameLower}.guard(shape, caller).findMany(query)
485
+ : await tx.${modelNameLower}.findMany(query)
486
+ const t = await countForPagination(tx.${modelNameLower}, query, shape, caller, distinctCountLimit)
487
+ return { d, t }
488
+ })
489
+ items = txResult.d
490
+ total = txResult.t
491
+ } catch (txError: any) {
492
+ if (
493
+ txError?.message?.includes?.('interactive transactions') ||
494
+ txError?.code === 'P2028'
495
+ ) {
496
+ console.warn(
497
+ '[prisma-generator-express] Interactive transactions not available, pagination queries are non-atomic',
498
+ )
499
+ items = shape
500
+ ? await (extended as any).${modelNameLower}.guard(shape, caller).findMany(query)
501
+ : await (extended as any).${modelNameLower}.findMany(query)
502
+ total = await countForPagination(
503
+ (extended as any).${modelNameLower},
504
+ query,
505
+ shape,
506
+ caller,
507
+ distinctCountLimit,
508
+ )
509
+ } else {
510
+ throw txError
511
+ }
512
+ }
513
+ } else {
514
+ items = shape
515
+ ? await (extended as any).${modelNameLower}.guard(shape, caller).findMany(query)
516
+ : await (extended as any).${modelNameLower}.findMany(query)
517
+ total = await countForPagination(
518
+ (extended as any).${modelNameLower},
519
+ query,
520
+ shape,
521
+ caller,
522
+ distinctCountLimit,
523
+ )
524
+ }
525
+
526
+ const skip = (query.skip as number) ?? 0
527
+ const absTake = Math.abs((query.take as number) ?? items.length)
528
+ const hasMore = items.length >= absTake && skip + items.length < total
529
+
530
+ return { data: items, total, hasMore }
531
+ }
532
+ `;
533
+ }
534
+ //# sourceMappingURL=generateOperationCore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generateOperationCore.js","sourceRoot":"","sources":["../../src/generators/generateOperationCore.ts"],"names":[],"mappings":"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"}