prisma-generator-express 1.40.0 → 1.42.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/README.md +444 -12
- package/dist/generators/generateOperationCore.d.ts +3 -1
- package/dist/generators/generateOperationCore.js +266 -160
- package/dist/generators/generateOperationCore.js.map +1 -1
- package/dist/generators/generateRouteConfigType.d.ts +3 -1
- package/dist/generators/generateRouteConfigType.js +36 -31
- package/dist/generators/generateRouteConfigType.js.map +1 -1
- package/dist/generators/generateRouter.d.ts +4 -2
- package/dist/generators/generateRouter.js +186 -132
- package/dist/generators/generateRouter.js.map +1 -1
- package/dist/generators/generateRouterFastify.d.ts +3 -1
- package/dist/generators/generateRouterFastify.js +12 -10
- package/dist/generators/generateRouterFastify.js.map +1 -1
- package/dist/generators/generateRouterHono.d.ts +3 -1
- package/dist/generators/generateRouterHono.js +12 -9
- package/dist/generators/generateRouterHono.js.map +1 -1
- package/dist/generators/generateUnifiedDocs.d.ts +2 -1
- package/dist/generators/generateUnifiedDocs.js +6 -4
- package/dist/generators/generateUnifiedDocs.js.map +1 -1
- package/dist/index.js +16 -21
- package/dist/index.js.map +1 -1
- package/dist/utils/copyFiles.d.ts +2 -1
- package/dist/utils/copyFiles.js +39 -34
- package/dist/utils/copyFiles.js.map +1 -1
- package/dist/utils/importExt.d.ts +2 -0
- package/dist/utils/importExt.js +11 -0
- package/dist/utils/importExt.js.map +1 -0
- package/dist/utils/resolveImportStyle.d.ts +3 -0
- package/dist/utils/resolveImportStyle.js +211 -0
- package/dist/utils/resolveImportStyle.js.map +1 -0
- package/dist/utils/writeFileSafely.js +6 -9
- package/dist/utils/writeFileSafely.js.map +1 -1
- package/package.json +1 -1
- package/src/copy/routeConfig.express.ts +39 -5
- package/src/copy/routeConfig.fastify.ts +8 -4
- package/src/copy/routeConfig.hono.ts +7 -3
- package/src/copy/routeConfig.ts +42 -2
- package/src/generators/generateOperationCore.ts +273 -169
- package/src/generators/generateRouteConfigType.ts +43 -36
- package/src/generators/generateRouter.ts +189 -133
- package/src/generators/generateRouterFastify.ts +14 -9
- package/src/generators/generateRouterHono.ts +14 -8
- package/src/generators/generateUnifiedDocs.ts +8 -3
- package/src/index.ts +25 -47
- package/src/utils/copyFiles.ts +45 -45
- package/src/utils/importExt.ts +7 -0
- package/src/utils/resolveImportStyle.ts +187 -0
- package/src/utils/writeFileSafely.ts +6 -22
|
@@ -2,8 +2,25 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.generateOperationRuntime = generateOperationRuntime;
|
|
4
4
|
exports.generateModelCore = generateModelCore;
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
const importExt_1 = require("../utils/importExt");
|
|
6
|
+
function generateOperationRuntime(importStyle) {
|
|
7
|
+
const ext = (0, importExt_1.importExt)(importStyle);
|
|
8
|
+
return `import { sanitizeKeys } from './misc${ext}'
|
|
9
|
+
import type {
|
|
10
|
+
ProgressivePatch,
|
|
11
|
+
ProgressiveStopResult,
|
|
12
|
+
ProgressiveStageResult,
|
|
13
|
+
ProgressiveStageContext,
|
|
14
|
+
ProgressiveStage,
|
|
15
|
+
} from './routeConfig${ext}'
|
|
16
|
+
|
|
17
|
+
export type {
|
|
18
|
+
ProgressivePatch,
|
|
19
|
+
ProgressiveStopResult,
|
|
20
|
+
ProgressiveStageResult,
|
|
21
|
+
ProgressiveStageContext,
|
|
22
|
+
ProgressiveStage,
|
|
23
|
+
}
|
|
7
24
|
|
|
8
25
|
export interface PaginationConfig {
|
|
9
26
|
defaultLimit?: number
|
|
@@ -69,88 +86,41 @@ const PRISMA_ERROR_MAP: Record<string, { status: number; message: string }> = {
|
|
|
69
86
|
|
|
70
87
|
export function mapError(error: unknown): HttpError {
|
|
71
88
|
if (error instanceof HttpError) return error
|
|
72
|
-
|
|
73
|
-
if (
|
|
74
|
-
error &&
|
|
75
|
-
typeof error === 'object' &&
|
|
76
|
-
'name' in error &&
|
|
77
|
-
error.name === 'ShapeError'
|
|
78
|
-
) {
|
|
89
|
+
if (error && typeof error === 'object' && 'name' in error && (error as any).name === 'ShapeError') {
|
|
79
90
|
return new HttpError(400, (error as any).message)
|
|
80
91
|
}
|
|
81
|
-
|
|
82
|
-
if (
|
|
83
|
-
error &&
|
|
84
|
-
typeof error === 'object' &&
|
|
85
|
-
'name' in error &&
|
|
86
|
-
error.name === 'CallerError'
|
|
87
|
-
) {
|
|
92
|
+
if (error && typeof error === 'object' && 'name' in error && (error as any).name === 'CallerError') {
|
|
88
93
|
return new HttpError(400, (error as any).message)
|
|
89
94
|
}
|
|
90
|
-
|
|
91
|
-
if (
|
|
92
|
-
error &&
|
|
93
|
-
typeof error === 'object' &&
|
|
94
|
-
'name' in error &&
|
|
95
|
-
error.name === 'PolicyError'
|
|
96
|
-
) {
|
|
95
|
+
if (error && typeof error === 'object' && 'name' in error && (error as any).name === 'PolicyError') {
|
|
97
96
|
return new HttpError(403, (error as any).message)
|
|
98
97
|
}
|
|
99
|
-
|
|
100
|
-
if (
|
|
101
|
-
error &&
|
|
102
|
-
typeof error === 'object' &&
|
|
103
|
-
'issues' in error &&
|
|
104
|
-
'name' in error &&
|
|
105
|
-
(error as any).name === 'ZodError'
|
|
106
|
-
) {
|
|
98
|
+
if (error && typeof error === 'object' && 'issues' in error && 'name' in error && (error as any).name === 'ZodError') {
|
|
107
99
|
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
|
|
100
|
+
const message = Array.isArray(issues) ? issues.map((i: any) => i.message).join('; ') : (error as any).message
|
|
111
101
|
return new HttpError(400, message)
|
|
112
102
|
}
|
|
113
|
-
|
|
114
103
|
if (error && typeof error === 'object' && 'code' in error) {
|
|
115
104
|
const code = (error as any).code as string
|
|
116
105
|
const mapped = PRISMA_ERROR_MAP[code]
|
|
117
106
|
if (mapped) {
|
|
118
107
|
const detail = (error as any).message
|
|
119
|
-
|
|
120
|
-
? mapped.message + ': ' + detail
|
|
121
|
-
: mapped.message
|
|
122
|
-
return new HttpError(mapped.status, message)
|
|
108
|
+
return new HttpError(mapped.status, detail ? mapped.message + ': ' + detail : mapped.message)
|
|
123
109
|
}
|
|
124
110
|
if (typeof code === 'string' && code.startsWith('P')) {
|
|
125
111
|
const msg = (error as any).message || 'Database operation failed'
|
|
126
|
-
console.warn(
|
|
127
|
-
'[prisma-generator-express] Unmapped Prisma error code:',
|
|
128
|
-
code,
|
|
129
|
-
msg,
|
|
130
|
-
)
|
|
112
|
+
console.warn('[prisma-generator-express] Unmapped Prisma error code:', code, msg)
|
|
131
113
|
return new HttpError(500, msg)
|
|
132
114
|
}
|
|
133
115
|
}
|
|
134
|
-
|
|
135
116
|
if (error && typeof error === 'object' && 'name' in error) {
|
|
136
117
|
const name = (error as any).name
|
|
137
|
-
if (name === 'PrismaClientValidationError')
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
if (name === '
|
|
141
|
-
|
|
142
|
-
}
|
|
143
|
-
if (name === 'PrismaClientInitializationError') {
|
|
144
|
-
return new HttpError(503, (error as any).message || 'Database connection failed')
|
|
145
|
-
}
|
|
146
|
-
if (name === 'PrismaClientRustPanicError') {
|
|
147
|
-
return new HttpError(500, (error as any).message || 'Internal database engine error')
|
|
148
|
-
}
|
|
149
|
-
if (name === 'PrismaClientUnknownRequestError') {
|
|
150
|
-
return new HttpError(500, (error as any).message || 'Unknown database error')
|
|
151
|
-
}
|
|
118
|
+
if (name === 'PrismaClientValidationError') return new HttpError(400, (error as any).message || 'Invalid query parameters')
|
|
119
|
+
if (name === 'PrismaClientKnownRequestError') return new HttpError(400, (error as any).message || 'Database request error')
|
|
120
|
+
if (name === 'PrismaClientInitializationError') return new HttpError(503, (error as any).message || 'Database connection failed')
|
|
121
|
+
if (name === 'PrismaClientRustPanicError') return new HttpError(500, (error as any).message || 'Internal database engine error')
|
|
122
|
+
if (name === 'PrismaClientUnknownRequestError') return new HttpError(500, (error as any).message || 'Unknown database error')
|
|
152
123
|
}
|
|
153
|
-
|
|
154
124
|
const msg = error instanceof Error ? error.message : String(error)
|
|
155
125
|
console.error('[prisma-generator-express] Unhandled error:', error)
|
|
156
126
|
return new HttpError(500, msg || 'Internal server error')
|
|
@@ -178,14 +148,10 @@ export async function getExtendedClient(ctx: OperationContext): Promise<any> {
|
|
|
178
148
|
if (!base) {
|
|
179
149
|
throw new HttpError(500, 'PrismaClient not found on request. Set req.prisma in middleware.')
|
|
180
150
|
}
|
|
181
|
-
|
|
182
151
|
await _prismasqlReady
|
|
183
|
-
|
|
184
152
|
if (!_speedExtension) return base
|
|
185
|
-
|
|
186
153
|
const connector = ctx.postgres || ctx.sqlite
|
|
187
154
|
if (!connector) return base
|
|
188
|
-
|
|
189
155
|
if (typeof connector === 'object' && connector !== null) {
|
|
190
156
|
const innerMap = _extendedClients.get(connector)
|
|
191
157
|
if (innerMap) {
|
|
@@ -193,14 +159,12 @@ export async function getExtendedClient(ctx: OperationContext): Promise<any> {
|
|
|
193
159
|
if (cached) return cached
|
|
194
160
|
}
|
|
195
161
|
}
|
|
196
|
-
|
|
197
162
|
try {
|
|
198
163
|
const extended = base.$extends(_speedExtension({
|
|
199
164
|
postgres: ctx.postgres,
|
|
200
165
|
sqlite: ctx.sqlite,
|
|
201
166
|
debug: process.env.DEBUG === 'true',
|
|
202
167
|
}))
|
|
203
|
-
|
|
204
168
|
if (typeof connector === 'object' && connector !== null) {
|
|
205
169
|
let innerMap = _extendedClients.get(connector)
|
|
206
170
|
if (!innerMap) {
|
|
@@ -209,7 +173,6 @@ export async function getExtendedClient(ctx: OperationContext): Promise<any> {
|
|
|
209
173
|
}
|
|
210
174
|
innerMap.set(base, extended)
|
|
211
175
|
}
|
|
212
|
-
|
|
213
176
|
return extended
|
|
214
177
|
} catch (error) {
|
|
215
178
|
console.warn('[speedExtension] Failed to initialize, using base client:', error)
|
|
@@ -230,25 +193,18 @@ export function requireBodyField(body: Record<string, any>, field: string): void
|
|
|
230
193
|
}
|
|
231
194
|
}
|
|
232
195
|
|
|
233
|
-
export function applyPaginationLimits(
|
|
234
|
-
query: Record<string, any>,
|
|
235
|
-
config?: PaginationConfig,
|
|
236
|
-
): Record<string, any> {
|
|
196
|
+
export function applyPaginationLimits(query: Record<string, any>, config?: PaginationConfig): Record<string, any> {
|
|
237
197
|
if (!config) return query
|
|
238
|
-
|
|
239
198
|
const result = { ...query }
|
|
240
|
-
|
|
241
199
|
if (result.take === undefined && config.defaultLimit !== undefined) {
|
|
242
200
|
result.take = config.defaultLimit
|
|
243
201
|
}
|
|
244
|
-
|
|
245
202
|
if (config.maxLimit !== undefined && result.take !== undefined) {
|
|
246
203
|
const takeNum = Number(result.take)
|
|
247
204
|
if (Math.abs(takeNum) > config.maxLimit) {
|
|
248
205
|
result.take = takeNum < 0 ? -config.maxLimit : config.maxLimit
|
|
249
206
|
}
|
|
250
207
|
}
|
|
251
|
-
|
|
252
208
|
return result
|
|
253
209
|
}
|
|
254
210
|
|
|
@@ -260,10 +216,7 @@ export function normalizeDistinct(value: unknown): string[] {
|
|
|
260
216
|
|
|
261
217
|
export function assertGuard(delegate: any): void {
|
|
262
218
|
if (typeof delegate.guard !== 'function') {
|
|
263
|
-
throw new HttpError(
|
|
264
|
-
500,
|
|
265
|
-
'Guard shapes require prisma-guard extension on PrismaClient. Install: npm install prisma-guard, then extend your client with guardExtension().',
|
|
266
|
-
)
|
|
219
|
+
throw new HttpError(500, 'Guard shapes require prisma-guard extension on PrismaClient.')
|
|
267
220
|
}
|
|
268
221
|
}
|
|
269
222
|
|
|
@@ -283,14 +236,9 @@ export function buildCountShape(shape: Record<string, any>): Record<string, any>
|
|
|
283
236
|
if (typeof shape === 'function') {
|
|
284
237
|
return (...args: any[]) => keepWhereOnly((shape as Function)(...args))
|
|
285
238
|
}
|
|
286
|
-
|
|
287
239
|
const keys = Object.keys(shape)
|
|
288
240
|
const isSingleShape = keys.length === 0 || keys.every((k) => GUARD_SHAPE_CONFIG_KEYS.has(k))
|
|
289
|
-
|
|
290
|
-
if (isSingleShape) {
|
|
291
|
-
return keepWhereOnly(shape)
|
|
292
|
-
}
|
|
293
|
-
|
|
241
|
+
if (isSingleShape) return keepWhereOnly(shape)
|
|
294
242
|
const result: Record<string, any> = {}
|
|
295
243
|
for (const [key, variant] of Object.entries(shape)) {
|
|
296
244
|
if (typeof variant === 'function') {
|
|
@@ -314,7 +262,6 @@ export async function countForPagination(
|
|
|
314
262
|
const distinctFields = normalizeDistinct(query.distinct)
|
|
315
263
|
const hasDistinct = distinctFields.length > 0
|
|
316
264
|
const effectiveLimit = distinctCountLimit ?? DISTINCT_COUNT_LIMIT
|
|
317
|
-
|
|
318
265
|
const countShape = shape ? buildCountShape(shape) : undefined
|
|
319
266
|
|
|
320
267
|
if (hasDistinct) {
|
|
@@ -325,30 +272,22 @@ export async function countForPagination(
|
|
|
325
272
|
select: { [selectField]: true },
|
|
326
273
|
take: effectiveLimit + 1,
|
|
327
274
|
}
|
|
328
|
-
|
|
329
275
|
const results = shape
|
|
330
276
|
? await delegate.guard(shape, caller).findMany(distinctArgs)
|
|
331
277
|
: await delegate.findMany(distinctArgs)
|
|
332
|
-
|
|
333
278
|
if (results.length > effectiveLimit) {
|
|
334
|
-
console.warn(
|
|
335
|
-
'[prisma-generator-express] Distinct count exceeds ' +
|
|
336
|
-
effectiveLimit +
|
|
337
|
-
', falling back to approximate total',
|
|
338
|
-
)
|
|
279
|
+
console.warn('[prisma-generator-express] Distinct count exceeds ' + effectiveLimit + ', falling back to approximate total')
|
|
339
280
|
const countArgs: Record<string, any> = {}
|
|
340
281
|
if (query.where) countArgs.where = query.where
|
|
341
282
|
return countShape
|
|
342
283
|
? await delegate.guard(countShape, caller).count(countArgs)
|
|
343
284
|
: await delegate.count(countArgs)
|
|
344
285
|
}
|
|
345
|
-
|
|
346
286
|
return results.length
|
|
347
287
|
}
|
|
348
288
|
|
|
349
289
|
const countArgs: Record<string, any> = {}
|
|
350
290
|
if (query.where) countArgs.where = query.where
|
|
351
|
-
|
|
352
291
|
return countShape
|
|
353
292
|
? await delegate.guard(countShape, caller).count(countArgs)
|
|
354
293
|
: await delegate.count(countArgs)
|
|
@@ -357,12 +296,8 @@ export async function countForPagination(
|
|
|
357
296
|
export function transformResult(value: unknown): unknown {
|
|
358
297
|
if (value === null || value === undefined) return value
|
|
359
298
|
if (typeof value === 'bigint') return value.toString()
|
|
360
|
-
if (typeof Buffer !== 'undefined' && Buffer.isBuffer(value))
|
|
361
|
-
|
|
362
|
-
}
|
|
363
|
-
if (value instanceof Uint8Array) {
|
|
364
|
-
return Buffer.from(value).toString('base64')
|
|
365
|
-
}
|
|
299
|
+
if (typeof Buffer !== 'undefined' && Buffer.isBuffer(value)) return value.toString('base64')
|
|
300
|
+
if (value instanceof Uint8Array) return Buffer.from(value).toString('base64')
|
|
366
301
|
if (value instanceof Date) return value
|
|
367
302
|
if (Array.isArray(value)) return value.map(transformResult)
|
|
368
303
|
if (typeof value === 'object') {
|
|
@@ -376,19 +311,227 @@ export function transformResult(value: unknown): unknown {
|
|
|
376
311
|
}
|
|
377
312
|
return value
|
|
378
313
|
}
|
|
314
|
+
|
|
315
|
+
export function acceptsEventStream(accept: string | undefined): boolean {
|
|
316
|
+
if (!accept) return false
|
|
317
|
+
return accept.toLowerCase().includes('text/event-stream')
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
const UNSAFE_PATH_SEGMENTS = new Set(['__proto__', 'constructor', 'prototype'])
|
|
321
|
+
|
|
322
|
+
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
323
|
+
if (value === null || typeof value !== 'object') return false
|
|
324
|
+
if (Array.isArray(value)) return false
|
|
325
|
+
const proto = Object.getPrototypeOf(value)
|
|
326
|
+
return proto === Object.prototype || proto === null
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
export function setByPath(target: Record<string, unknown>, path: string, value: unknown): boolean {
|
|
330
|
+
const parts = path.split('.')
|
|
331
|
+
if (parts.length === 0) return false
|
|
332
|
+
for (const p of parts) {
|
|
333
|
+
if (p === '' || UNSAFE_PATH_SEGMENTS.has(p)) return false
|
|
334
|
+
}
|
|
335
|
+
let cursor: Record<string, unknown> = target
|
|
336
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
337
|
+
const part = parts[i]
|
|
338
|
+
const next = cursor[part]
|
|
339
|
+
if (!isPlainObject(next)) {
|
|
340
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
341
|
+
console.warn(
|
|
342
|
+
'[progressive] Dropping patch for "' + path +
|
|
343
|
+
'": cannot traverse non-plain-object at segment "' + part + '"',
|
|
344
|
+
)
|
|
345
|
+
}
|
|
346
|
+
return false
|
|
347
|
+
}
|
|
348
|
+
cursor = next
|
|
349
|
+
}
|
|
350
|
+
cursor[parts[parts.length - 1]] = value
|
|
351
|
+
return true
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function removeReqCloseListener(req: any, listener: () => void): void {
|
|
355
|
+
if (typeof req.off === 'function') {
|
|
356
|
+
req.off('close', listener)
|
|
357
|
+
} else if (typeof req.removeListener === 'function') {
|
|
358
|
+
req.removeListener('close', listener)
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
export function initSSE(res: any): void {
|
|
363
|
+
res.statusCode = 200
|
|
364
|
+
res.setHeader('Content-Type', 'text/event-stream')
|
|
365
|
+
res.setHeader('Cache-Control', 'no-cache, no-transform')
|
|
366
|
+
res.setHeader('Connection', 'keep-alive')
|
|
367
|
+
res.setHeader('X-Accel-Buffering', 'no')
|
|
368
|
+
if (typeof res.flushHeaders === 'function') res.flushHeaders()
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
export function flushSSE(res: any): void {
|
|
372
|
+
if (typeof res.flush === 'function') {
|
|
373
|
+
try { res.flush() } catch {}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
export function sendSSE(res: any, payload: unknown): boolean {
|
|
378
|
+
if (res.writableEnded || res.destroyed) return false
|
|
379
|
+
try {
|
|
380
|
+
res.write('data: ' + JSON.stringify(transformResult(payload)) + '\\n\\n')
|
|
381
|
+
flushSSE(res)
|
|
382
|
+
return true
|
|
383
|
+
} catch (err) {
|
|
384
|
+
console.error('[progressive] failed to send SSE event:', err)
|
|
385
|
+
return false
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
export function sendSSEProgress(res: any, stage: string, completed: number, total: number): boolean {
|
|
390
|
+
return sendSSE(res, { type: 'progress', stage, completed, total })
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
export function sendSSEField(res: any, key: string, value: unknown): boolean {
|
|
394
|
+
return sendSSE(res, { type: 'field', key, value })
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
export function sendSSEResult(res: any, data: unknown): boolean {
|
|
398
|
+
return sendSSE(res, { type: 'result', data })
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
export function sendSSEError(res: any, message: string): boolean {
|
|
402
|
+
if (res.writableEnded || res.destroyed) return false
|
|
403
|
+
try {
|
|
404
|
+
res.write('data: ' + JSON.stringify({ type: 'error', message }) + '\\n\\n')
|
|
405
|
+
flushSSE(res)
|
|
406
|
+
return true
|
|
407
|
+
} catch (err) {
|
|
408
|
+
console.error('[progressive] failed to send SSE error event:', err)
|
|
409
|
+
return false
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
export function startSSEKeepalive(res: any, intervalMs: number = 15000): any {
|
|
414
|
+
const handle = setInterval(() => {
|
|
415
|
+
if (res.writableEnded || res.destroyed) return
|
|
416
|
+
try {
|
|
417
|
+
res.write(': keepalive\\n\\n')
|
|
418
|
+
flushSSE(res)
|
|
419
|
+
} catch {}
|
|
420
|
+
}, intervalMs)
|
|
421
|
+
if (typeof (handle as any).unref === 'function') (handle as any).unref()
|
|
422
|
+
return handle
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
export function endSSE(res: any, keepaliveHandle: any): void {
|
|
426
|
+
if (keepaliveHandle) {
|
|
427
|
+
try { clearInterval(keepaliveHandle) } catch {}
|
|
428
|
+
}
|
|
429
|
+
if (!res.writableEnded && !res.destroyed) {
|
|
430
|
+
try { res.end() } catch {}
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
export interface RunSingleResultSSEOptions {
|
|
435
|
+
req: any
|
|
436
|
+
res: any
|
|
437
|
+
coreQueryFn: () => Promise<unknown>
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
export async function runSingleResultSSE(options: RunSingleResultSSEOptions): Promise<void> {
|
|
441
|
+
const { req, res, coreQueryFn } = options
|
|
442
|
+
let keepalive: any = null
|
|
443
|
+
try {
|
|
444
|
+
initSSE(res)
|
|
445
|
+
keepalive = startSSEKeepalive(res)
|
|
446
|
+
if (req.destroyed) return
|
|
447
|
+
const data = await coreQueryFn()
|
|
448
|
+
if (res.writableEnded || res.destroyed) return
|
|
449
|
+
sendSSEResult(res, data)
|
|
450
|
+
} catch (err) {
|
|
451
|
+
console.error('[progressive] single-result error:', err)
|
|
452
|
+
if (!res.writableEnded && !res.destroyed) {
|
|
453
|
+
sendSSEError(res, 'Internal server error')
|
|
454
|
+
}
|
|
455
|
+
} finally {
|
|
456
|
+
endSSE(res, keepalive)
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
function isStopResult(value: unknown): value is ProgressiveStopResult<unknown> {
|
|
461
|
+
return typeof value === 'object' && value !== null && (value as any).stop === true
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
export interface RunProgressiveOptions {
|
|
465
|
+
req: any
|
|
466
|
+
res: any
|
|
467
|
+
ctx: unknown
|
|
468
|
+
prisma: any
|
|
469
|
+
variant: string
|
|
470
|
+
stages: string[]
|
|
471
|
+
stageRegistry: Record<string, ProgressiveStage<any, any>>
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
export async function runProgressiveEndpoint(options: RunProgressiveOptions): Promise<void> {
|
|
475
|
+
const { req, res, ctx, prisma, variant, stages, stageRegistry } = options
|
|
476
|
+
let keepalive: any = null
|
|
477
|
+
const controller = new AbortController()
|
|
478
|
+
const onClose = () => controller.abort()
|
|
479
|
+
if (typeof req.on === 'function') req.on('close', onClose)
|
|
480
|
+
|
|
481
|
+
const accumulated: Record<string, unknown> = {}
|
|
482
|
+
const signal = controller.signal
|
|
483
|
+
|
|
484
|
+
try {
|
|
485
|
+
initSSE(res)
|
|
486
|
+
keepalive = startSSEKeepalive(res)
|
|
487
|
+
sendSSEProgress(res, 'start', 0, stages.length)
|
|
488
|
+
|
|
489
|
+
for (let i = 0; i < stages.length; i++) {
|
|
490
|
+
if (res.writableEnded || res.destroyed || signal.aborted) return
|
|
491
|
+
const stageName = stages[i]
|
|
492
|
+
const stage = stageRegistry[stageName]
|
|
493
|
+
if (!stage) throw new Error('Missing progressive stage: ' + stageName)
|
|
494
|
+
|
|
495
|
+
const result = await stage({ ctx, req, res, prisma, variant, accumulated, signal })
|
|
496
|
+
if (res.writableEnded || res.destroyed) return
|
|
497
|
+
|
|
498
|
+
if (isStopResult(result)) {
|
|
499
|
+
sendSSEResult(res, result.data)
|
|
500
|
+
return
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
const patches = Array.isArray(result) ? result : result ? [result] : []
|
|
504
|
+
for (const patch of patches) {
|
|
505
|
+
if (!patch || typeof patch !== 'object') continue
|
|
506
|
+
if (typeof (patch as any).key !== 'string') continue
|
|
507
|
+
if (!('value' in patch)) continue
|
|
508
|
+
const p = patch as ProgressivePatch
|
|
509
|
+
const applied = setByPath(accumulated, p.key, p.value)
|
|
510
|
+
if (applied) sendSSEField(res, p.key, p.value)
|
|
511
|
+
}
|
|
512
|
+
sendSSEProgress(res, stageName, i + 1, stages.length)
|
|
513
|
+
}
|
|
514
|
+
if (res.writableEnded || res.destroyed) return
|
|
515
|
+
sendSSEResult(res, accumulated)
|
|
516
|
+
} catch (err) {
|
|
517
|
+
console.error('[progressive] stage error:', err)
|
|
518
|
+
if (!res.writableEnded && !res.destroyed) {
|
|
519
|
+
sendSSEError(res, 'Could not load progressive response')
|
|
520
|
+
}
|
|
521
|
+
} finally {
|
|
522
|
+
removeReqCloseListener(req, onClose)
|
|
523
|
+
endSSE(res, keepalive)
|
|
524
|
+
}
|
|
525
|
+
}
|
|
379
526
|
`;
|
|
380
527
|
}
|
|
381
528
|
function generateModelCore(options) {
|
|
529
|
+
const ext = (0, importExt_1.importExt)(options.importStyle);
|
|
382
530
|
const modelName = options.model.name;
|
|
383
531
|
const modelNameLower = modelName.charAt(0).toLowerCase() + modelName.slice(1);
|
|
384
532
|
const standardReadOps = [
|
|
385
|
-
'findFirst',
|
|
386
|
-
'
|
|
387
|
-
'findUniqueOrThrow',
|
|
388
|
-
'findFirstOrThrow',
|
|
389
|
-
'count',
|
|
390
|
-
'aggregate',
|
|
391
|
-
'groupBy',
|
|
533
|
+
'findFirst', 'findUnique', 'findUniqueOrThrow', 'findFirstOrThrow',
|
|
534
|
+
'count', 'aggregate', 'groupBy',
|
|
392
535
|
];
|
|
393
536
|
const standardReadHandlers = standardReadOps
|
|
394
537
|
.map((op) => `
|
|
@@ -405,43 +548,16 @@ export async function ${op}(ctx: OperationContext): Promise<unknown> {
|
|
|
405
548
|
const writeOps = [
|
|
406
549
|
{ name: 'create', method: 'create', requiredFields: ['data'] },
|
|
407
550
|
{ name: 'createMany', method: 'createMany', requiredFields: ['data'] },
|
|
408
|
-
{
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
},
|
|
413
|
-
{
|
|
414
|
-
name: 'update',
|
|
415
|
-
method: 'update',
|
|
416
|
-
requiredFields: ['where', 'data'],
|
|
417
|
-
},
|
|
418
|
-
{
|
|
419
|
-
name: 'updateMany',
|
|
420
|
-
method: 'updateMany',
|
|
421
|
-
requiredFields: ['where', 'data'],
|
|
422
|
-
},
|
|
423
|
-
{
|
|
424
|
-
name: 'updateManyAndReturn',
|
|
425
|
-
method: 'updateManyAndReturn',
|
|
426
|
-
requiredFields: ['where', 'data'],
|
|
427
|
-
},
|
|
551
|
+
{ name: 'createManyAndReturn', method: 'createManyAndReturn', requiredFields: ['data'] },
|
|
552
|
+
{ name: 'update', method: 'update', requiredFields: ['where', 'data'] },
|
|
553
|
+
{ name: 'updateMany', method: 'updateMany', requiredFields: ['where', 'data'] },
|
|
554
|
+
{ name: 'updateManyAndReturn', method: 'updateManyAndReturn', requiredFields: ['where', 'data'] },
|
|
428
555
|
{ name: 'deleteUnique', method: 'delete', requiredFields: ['where'] },
|
|
429
|
-
{
|
|
430
|
-
|
|
431
|
-
method: 'deleteMany',
|
|
432
|
-
requiredFields: ['where'],
|
|
433
|
-
},
|
|
434
|
-
{
|
|
435
|
-
name: 'upsert',
|
|
436
|
-
method: 'upsert',
|
|
437
|
-
requiredFields: ['where', 'create', 'update'],
|
|
438
|
-
},
|
|
556
|
+
{ name: 'deleteMany', method: 'deleteMany', requiredFields: ['where'] },
|
|
557
|
+
{ name: 'upsert', method: 'upsert', requiredFields: ['where', 'create', 'update'] },
|
|
439
558
|
];
|
|
440
|
-
const writeHandlers = writeOps
|
|
441
|
-
.map((
|
|
442
|
-
const validationLines = op.requiredFields
|
|
443
|
-
.map((field) => ` requireBodyField(body, '${field}')`)
|
|
444
|
-
.join('\n');
|
|
559
|
+
const writeHandlers = writeOps.map((op) => {
|
|
560
|
+
const validationLines = op.requiredFields.map((field) => ` requireBodyField(body, '${field}')`).join('\n');
|
|
445
561
|
return `
|
|
446
562
|
export async function ${op.name}(ctx: OperationContext): Promise<unknown> {
|
|
447
563
|
const body = validateBody(ctx.body)
|
|
@@ -453,8 +569,7 @@ ${validationLines}
|
|
|
453
569
|
}
|
|
454
570
|
return (extended as any).${modelNameLower}.${op.method}(body)
|
|
455
571
|
}`;
|
|
456
|
-
})
|
|
457
|
-
.join('\n');
|
|
572
|
+
}).join('\n');
|
|
458
573
|
return `import {
|
|
459
574
|
OperationContext,
|
|
460
575
|
getExtendedClient,
|
|
@@ -463,7 +578,7 @@ ${validationLines}
|
|
|
463
578
|
applyPaginationLimits,
|
|
464
579
|
assertGuard,
|
|
465
580
|
countForPagination,
|
|
466
|
-
} from '../operationRuntime'
|
|
581
|
+
} from '../operationRuntime${ext}'
|
|
467
582
|
|
|
468
583
|
export async function findMany(ctx: OperationContext): Promise<unknown> {
|
|
469
584
|
const rawQuery = ctx.parsedQuery || {}
|
|
@@ -489,18 +604,14 @@ export async function findManyPaginated(
|
|
|
489
604
|
const distinctCountLimit = ctx.paginationConfig?.distinctCountLimit
|
|
490
605
|
const delegate = (extended as any).${modelNameLower}
|
|
491
606
|
|
|
492
|
-
if (shape)
|
|
493
|
-
assertGuard(delegate)
|
|
494
|
-
}
|
|
607
|
+
if (shape) assertGuard(delegate)
|
|
495
608
|
|
|
496
609
|
let items: any[]
|
|
497
610
|
let total: number
|
|
498
611
|
|
|
499
612
|
if (shape || typeof extended.$transaction !== 'function') {
|
|
500
613
|
const [data, count] = await Promise.all([
|
|
501
|
-
shape
|
|
502
|
-
? delegate.guard(shape, caller).findMany(query)
|
|
503
|
-
: delegate.findMany(query),
|
|
614
|
+
shape ? delegate.guard(shape, caller).findMany(query) : delegate.findMany(query),
|
|
504
615
|
countForPagination(delegate, query, shape, caller, distinctCountLimit),
|
|
505
616
|
])
|
|
506
617
|
items = data
|
|
@@ -515,13 +626,8 @@ export async function findManyPaginated(
|
|
|
515
626
|
items = txResult.d
|
|
516
627
|
total = txResult.t
|
|
517
628
|
} catch (txError: any) {
|
|
518
|
-
if (
|
|
519
|
-
|
|
520
|
-
txError?.code === 'P2028'
|
|
521
|
-
) {
|
|
522
|
-
console.warn(
|
|
523
|
-
'[prisma-generator-express] Interactive transactions not available, pagination queries are non-atomic',
|
|
524
|
-
)
|
|
629
|
+
if (txError?.message?.includes?.('interactive transactions') || txError?.code === 'P2028') {
|
|
630
|
+
console.warn('[prisma-generator-express] Interactive transactions not available, pagination queries are non-atomic')
|
|
525
631
|
items = await delegate.findMany(query)
|
|
526
632
|
total = await countForPagination(delegate, query, undefined, undefined, distinctCountLimit)
|
|
527
633
|
} else {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generateOperationCore.js","sourceRoot":"","sources":["../../src/generators/generateOperationCore.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"generateOperationCore.js","sourceRoot":"","sources":["../../src/generators/generateOperationCore.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1,3 @@
|
|
|
1
|
-
|
|
1
|
+
import { ImportStyle } from '../utils/resolveImportStyle';
|
|
2
|
+
import type { Target } from '../constants';
|
|
3
|
+
export declare function generateRouteConfigType(modelName: string, hookHandlerType: string, guardShapesImport: string | null, importStyle: ImportStyle, target: Target): string;
|