prisma-generator-express 1.59.0 → 1.61.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 +150 -14
- package/dist/client/encodeQueryParams.js +19 -23
- package/dist/client/encodeQueryParams.js.map +1 -1
- package/dist/copy/operationDefinitions.d.ts +37 -0
- package/dist/copy/operationDefinitions.js +412 -0
- package/dist/copy/operationDefinitions.js.map +1 -0
- package/dist/generators/generateFastifyHandler.js +10 -41
- package/dist/generators/generateFastifyHandler.js.map +1 -1
- package/dist/generators/generateHonoHandler.js +10 -41
- package/dist/generators/generateHonoHandler.js.map +1 -1
- package/dist/generators/generateModelMetadata.d.ts +8 -0
- package/dist/generators/generateModelMetadata.js +77 -0
- package/dist/generators/generateModelMetadata.js.map +1 -0
- package/dist/generators/generateOperationCore.js +20 -30
- package/dist/generators/generateOperationCore.js.map +1 -1
- package/dist/generators/generateQueryBuilderHelper.js +1 -1
- package/dist/generators/generateQueryBuilderHelper.js.map +1 -1
- package/dist/generators/generateRouteConfigType.js +11 -57
- package/dist/generators/generateRouteConfigType.js.map +1 -1
- package/dist/generators/generateRouter.js +64 -177
- package/dist/generators/generateRouter.js.map +1 -1
- package/dist/generators/generateRouterFastify.js +59 -169
- package/dist/generators/generateRouterFastify.js.map +1 -1
- package/dist/generators/generateRouterHono.js +52 -152
- package/dist/generators/generateRouterHono.js.map +1 -1
- package/dist/generators/generateUnifiedHandler.js +7 -30
- package/dist/generators/generateUnifiedHandler.js.map +1 -1
- package/dist/generators/generateUnifiedScalarUI.js +9 -74
- package/dist/generators/generateUnifiedScalarUI.js.map +1 -1
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -1
- package/dist/utils/copyFiles.js +7 -0
- package/dist/utils/copyFiles.js.map +1 -1
- package/dist/utils/writeFileSafely.js +5 -12
- package/dist/utils/writeFileSafely.js.map +1 -1
- package/package.json +12 -2
- package/src/client/encodeQueryParams.ts +23 -36
- package/src/copy/autoIncludePlanner.ts +14 -23
- package/src/copy/autoIncludePlannerGuarded.ts +477 -0
- package/src/copy/autoIncludeRuntime.ts +269 -261
- package/src/copy/autoIncludeRuntimeGuarded.ts +379 -0
- package/src/copy/buildModelOpenApi.ts +248 -628
- package/src/copy/concurrency.ts +20 -0
- package/src/copy/docsRenderer.ts +63 -333
- package/src/copy/errorMapper.ts +126 -0
- package/src/copy/guardHelpers.ts +56 -0
- package/src/copy/materializedCount.ts +68 -0
- package/src/copy/materializedRouter.ts +33 -29
- package/src/copy/operationDefinitions.ts +359 -35
- package/src/copy/operationRuntime.ts +11 -605
- package/src/copy/pagination.ts +151 -0
- package/src/copy/scalarTypes.ts +2 -0
- package/src/copy/sse.ts +296 -0
- package/src/generators/generateFastifyHandler.ts +13 -47
- package/src/generators/generateHonoHandler.ts +13 -47
- package/src/generators/generateModelMetadata.ts +92 -0
- package/src/generators/generateOperationCore.ts +19 -32
- package/src/generators/generateQueryBuilderHelper.ts +1 -1
- package/src/generators/generateRouteConfigType.ts +9 -60
- package/src/generators/generateRouter.ts +88 -180
- package/src/generators/generateRouterFastify.ts +65 -172
- package/src/generators/generateRouterHono.ts +58 -155
- package/src/generators/generateUnifiedHandler.ts +8 -33
- package/src/generators/generateUnifiedScalarUI.ts +9 -91
- package/src/index.ts +13 -1
- package/src/utils/copyFiles.ts +7 -0
- package/src/utils/writeFileSafely.ts +5 -11
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import type { Request, Response } from 'express'
|
|
2
|
+
import { HttpError, LOG_PREFIX, mapError } from './errorMapper'
|
|
2
3
|
import {
|
|
3
|
-
initSSE,
|
|
4
|
-
endSSE,
|
|
5
|
-
startSSEKeepalive,
|
|
6
4
|
sendSSEField,
|
|
7
5
|
sendSSEResult,
|
|
8
6
|
sendSSEError,
|
|
@@ -13,18 +11,20 @@ import {
|
|
|
13
11
|
sendSSEPageMeta,
|
|
14
12
|
runSingleResultSSE,
|
|
15
13
|
emitTerminalSSEError,
|
|
14
|
+
safeSendError,
|
|
16
15
|
setByPath,
|
|
16
|
+
withSSE,
|
|
17
|
+
} from './sse'
|
|
18
|
+
import { applyPaginationLimits, countForPagination } from './pagination'
|
|
19
|
+
import {
|
|
17
20
|
getDelegate,
|
|
18
21
|
getExtendedClient,
|
|
19
|
-
applyPaginationLimits,
|
|
20
|
-
countForPagination,
|
|
21
|
-
mapError,
|
|
22
|
-
HttpError,
|
|
23
22
|
type OperationContext,
|
|
24
23
|
type PrismaDelegate,
|
|
25
24
|
type FindManyPaginatedMode,
|
|
26
25
|
} from './operationRuntime'
|
|
27
26
|
import { isPlainObject } from './misc'
|
|
27
|
+
import { mapLimited } from './concurrency'
|
|
28
28
|
import {
|
|
29
29
|
planAutoInclude,
|
|
30
30
|
type AutoIncludePlan,
|
|
@@ -32,12 +32,14 @@ import {
|
|
|
32
32
|
type AutoIncludeStage,
|
|
33
33
|
} from './autoIncludePlanner'
|
|
34
34
|
import type { AutoIncludeProgressiveVariantConfig } from './routeConfig'
|
|
35
|
+
import {
|
|
36
|
+
isGuardedAutoIncludeBaseOp,
|
|
37
|
+
runGuardedAutoIncludeProgressive,
|
|
38
|
+
} from './autoIncludeRuntimeGuarded'
|
|
35
39
|
|
|
36
40
|
const STAGE_CONCURRENCY = 4
|
|
37
41
|
const MAX_IN_CHUNK = 1000
|
|
38
42
|
|
|
39
|
-
type IntervalHandle = ReturnType<typeof setInterval>
|
|
40
|
-
|
|
41
43
|
export type AutoIncludeBaseOp =
|
|
42
44
|
| 'findUnique'
|
|
43
45
|
| 'findUniqueOrThrow'
|
|
@@ -69,6 +71,13 @@ type ParentEntry = RowPair & {
|
|
|
69
71
|
locator: Array<number | string>
|
|
70
72
|
}
|
|
71
73
|
|
|
74
|
+
function createClientGoneChecker(
|
|
75
|
+
res: Response,
|
|
76
|
+
signal?: AbortSignal,
|
|
77
|
+
): () => boolean {
|
|
78
|
+
return () => signal?.aborted === true || res.writableEnded || res.destroyed
|
|
79
|
+
}
|
|
80
|
+
|
|
72
81
|
function readPath(source: Record<string, unknown>, path: string): unknown {
|
|
73
82
|
if (path === '') return source
|
|
74
83
|
const parts = path.split('.')
|
|
@@ -85,16 +94,11 @@ function stripInternalAtScope(
|
|
|
85
94
|
internalPaths: string[],
|
|
86
95
|
scopePath: string,
|
|
87
96
|
): void {
|
|
97
|
+
const prefix = scopePath === '' ? '' : scopePath + '.'
|
|
88
98
|
for (const fullPath of internalPaths) {
|
|
89
|
-
if (
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
}
|
|
93
|
-
continue
|
|
94
|
-
}
|
|
95
|
-
if (!fullPath.startsWith(scopePath + '.')) continue
|
|
96
|
-
const relative = fullPath.slice(scopePath.length + 1)
|
|
97
|
-
if (relative.includes('.')) continue
|
|
99
|
+
if (!fullPath.startsWith(prefix)) continue
|
|
100
|
+
const relative = fullPath.slice(prefix.length)
|
|
101
|
+
if (relative === '' || relative.includes('.')) continue
|
|
98
102
|
delete target[relative]
|
|
99
103
|
}
|
|
100
104
|
}
|
|
@@ -103,7 +107,8 @@ function mergeWhere(
|
|
|
103
107
|
userWhere: unknown,
|
|
104
108
|
linkFilter: Record<string, unknown>,
|
|
105
109
|
): Record<string, unknown> {
|
|
106
|
-
if (!isPlainObject(userWhere) || Object.keys(userWhere).length === 0)
|
|
110
|
+
if (!isPlainObject(userWhere) || Object.keys(userWhere).length === 0)
|
|
111
|
+
return linkFilter
|
|
107
112
|
return { AND: [userWhere, linkFilter] }
|
|
108
113
|
}
|
|
109
114
|
|
|
@@ -132,22 +137,14 @@ function buildPublicForStage(
|
|
|
132
137
|
internalFieldPaths: string[],
|
|
133
138
|
scopePath: string,
|
|
134
139
|
): unknown {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
const copy: Record<string, unknown> = { ...item }
|
|
139
|
-
stripInternalAtScope(copy, internalFieldPaths, scopePath)
|
|
140
|
-
return copy
|
|
141
|
-
}
|
|
142
|
-
return item
|
|
143
|
-
})
|
|
144
|
-
}
|
|
145
|
-
if (isPlainObject(result)) {
|
|
146
|
-
const copy: Record<string, unknown> = { ...result }
|
|
140
|
+
const process = (item: unknown): unknown => {
|
|
141
|
+
if (!isPlainObject(item)) return item
|
|
142
|
+
const copy: Record<string, unknown> = { ...item }
|
|
147
143
|
stripInternalAtScope(copy, internalFieldPaths, scopePath)
|
|
148
144
|
return copy
|
|
149
145
|
}
|
|
150
|
-
return result
|
|
146
|
+
if (Array.isArray(result)) return result.map(process)
|
|
147
|
+
return process(result)
|
|
151
148
|
}
|
|
152
149
|
|
|
153
150
|
function normalizeKey(v: unknown): string {
|
|
@@ -170,26 +167,6 @@ function groupStagesByDepth(stages: AutoIncludeStage[]): AutoIncludeStage[][] {
|
|
|
170
167
|
.map((d) => byDepth.get(d) as AutoIncludeStage[])
|
|
171
168
|
}
|
|
172
169
|
|
|
173
|
-
async function runConcurrent<T>(
|
|
174
|
-
items: T[],
|
|
175
|
-
limit: number,
|
|
176
|
-
fn: (item: T) => Promise<void>,
|
|
177
|
-
): Promise<void> {
|
|
178
|
-
let index = 0
|
|
179
|
-
const workers: Promise<void>[] = []
|
|
180
|
-
const workerCount = Math.min(limit, items.length)
|
|
181
|
-
for (let w = 0; w < workerCount; w++) {
|
|
182
|
-
workers.push((async () => {
|
|
183
|
-
for (;;) {
|
|
184
|
-
const i = index++
|
|
185
|
-
if (i >= items.length) return
|
|
186
|
-
await fn(items[i])
|
|
187
|
-
}
|
|
188
|
-
})())
|
|
189
|
-
}
|
|
190
|
-
await Promise.all(workers)
|
|
191
|
-
}
|
|
192
|
-
|
|
193
170
|
function handleAutoIncludeFallback(
|
|
194
171
|
options: RunAutoIncludeOptions,
|
|
195
172
|
message: string,
|
|
@@ -297,7 +274,16 @@ async function runOneStageSingle(options: {
|
|
|
297
274
|
res: Response
|
|
298
275
|
isAborted: () => boolean
|
|
299
276
|
}): Promise<void> {
|
|
300
|
-
const {
|
|
277
|
+
const {
|
|
278
|
+
extended,
|
|
279
|
+
models,
|
|
280
|
+
stage,
|
|
281
|
+
internal,
|
|
282
|
+
publicState,
|
|
283
|
+
internalFieldPaths,
|
|
284
|
+
res,
|
|
285
|
+
isAborted,
|
|
286
|
+
} = options
|
|
301
287
|
if (isAborted()) return
|
|
302
288
|
|
|
303
289
|
const parentRaw = readPath(internal, stage.parentPath)
|
|
@@ -322,28 +308,46 @@ async function runOneStageSingle(options: {
|
|
|
322
308
|
|
|
323
309
|
const targetModel = models[stage.relationField.type]
|
|
324
310
|
if (!targetModel) {
|
|
325
|
-
throw new
|
|
311
|
+
throw new HttpError(
|
|
312
|
+
500,
|
|
313
|
+
'Target model not in relation metadata: ' + stage.relationField.type,
|
|
314
|
+
)
|
|
326
315
|
}
|
|
327
316
|
|
|
328
317
|
const finalArgs: Record<string, unknown> = { ...stage.stageArgs }
|
|
329
318
|
finalArgs.where = mergeWhere(stage.stageArgs.where, linkFilter)
|
|
330
319
|
|
|
331
|
-
const delegate: PrismaDelegate = getDelegate(
|
|
332
|
-
|
|
320
|
+
const delegate: PrismaDelegate = getDelegate(
|
|
321
|
+
extended,
|
|
322
|
+
targetModel.delegateKey,
|
|
323
|
+
)
|
|
324
|
+
const method: 'findMany' | 'findFirst' = stage.relationField.isList
|
|
325
|
+
? 'findMany'
|
|
326
|
+
: 'findFirst'
|
|
333
327
|
const result = await delegate[method](finalArgs)
|
|
334
328
|
|
|
335
329
|
if (isAborted()) return
|
|
336
330
|
|
|
337
331
|
const appliedInternal = setByPath(internal, stage.relationPath, result)
|
|
338
332
|
if (!appliedInternal) {
|
|
339
|
-
throw new
|
|
333
|
+
throw new HttpError(
|
|
334
|
+
500,
|
|
335
|
+
'Failed to apply internal patch for ' + stage.relationPath,
|
|
336
|
+
)
|
|
340
337
|
}
|
|
341
338
|
|
|
342
|
-
const publicResult = buildPublicForStage(
|
|
339
|
+
const publicResult = buildPublicForStage(
|
|
340
|
+
result,
|
|
341
|
+
internalFieldPaths,
|
|
342
|
+
stage.relationPath,
|
|
343
|
+
)
|
|
343
344
|
|
|
344
345
|
const appliedPublic = setByPath(publicState, stage.relationPath, publicResult)
|
|
345
346
|
if (!appliedPublic) {
|
|
346
|
-
throw new
|
|
347
|
+
throw new HttpError(
|
|
348
|
+
500,
|
|
349
|
+
'Failed to apply public patch for ' + stage.relationPath,
|
|
350
|
+
)
|
|
347
351
|
}
|
|
348
352
|
|
|
349
353
|
sendSSEField(res, stage.relationPath, publicResult)
|
|
@@ -354,16 +358,9 @@ async function runAutoIncludeSingle(
|
|
|
354
358
|
plan: AutoIncludePlan,
|
|
355
359
|
): Promise<void> {
|
|
356
360
|
const { res, ctx, baseOp, delegateKey, models, signal } = options
|
|
361
|
+
const isClientGone = createClientGoneChecker(res, signal)
|
|
357
362
|
|
|
358
|
-
|
|
359
|
-
signal?.aborted === true || res.writableEnded || res.destroyed
|
|
360
|
-
|
|
361
|
-
let keepalive: IntervalHandle | null = null
|
|
362
|
-
try {
|
|
363
|
-
initSSE(res)
|
|
364
|
-
keepalive = startSSEKeepalive(res)
|
|
365
|
-
if (isClientGone()) return
|
|
366
|
-
|
|
363
|
+
await withSSE({ res, signal, label: 'single' }, async () => {
|
|
367
364
|
const extended = await getExtendedClient(ctx)
|
|
368
365
|
if (isClientGone()) return
|
|
369
366
|
|
|
@@ -371,10 +368,12 @@ async function runAutoIncludeSingle(
|
|
|
371
368
|
|
|
372
369
|
let rootResult: unknown
|
|
373
370
|
try {
|
|
374
|
-
rootResult = await rootDelegate[
|
|
371
|
+
rootResult = await rootDelegate[
|
|
372
|
+
baseOp as Exclude<AutoIncludeBaseOp, 'findMany' | 'findManyPaginated'>
|
|
373
|
+
](plan.rootArgs)
|
|
375
374
|
} catch (err) {
|
|
376
375
|
if (isClientGone()) return
|
|
377
|
-
console.error('
|
|
376
|
+
console.error(LOG_PREFIX, 'root query failed:', err)
|
|
378
377
|
sendSSEError(res, mapError(err).message)
|
|
379
378
|
return
|
|
380
379
|
}
|
|
@@ -414,7 +413,7 @@ async function runAutoIncludeSingle(
|
|
|
414
413
|
if (isClientGone()) return
|
|
415
414
|
if (stageErrorMessage) break
|
|
416
415
|
|
|
417
|
-
await
|
|
416
|
+
await mapLimited(group, STAGE_CONCURRENCY, async (stage) => {
|
|
418
417
|
if (isAborted()) return
|
|
419
418
|
try {
|
|
420
419
|
await runOneStageSingle({
|
|
@@ -429,13 +428,18 @@ async function runAutoIncludeSingle(
|
|
|
429
428
|
})
|
|
430
429
|
} catch (err) {
|
|
431
430
|
if (isAborted()) return
|
|
432
|
-
console.error('
|
|
431
|
+
console.error(LOG_PREFIX, 'stage failed:', stage.relationPath, err)
|
|
433
432
|
stageErrorMessage = mapError(err).message
|
|
434
433
|
return
|
|
435
434
|
}
|
|
436
435
|
if (isAborted()) return
|
|
437
436
|
completed++
|
|
438
|
-
const ok = sendSSEProgress(
|
|
437
|
+
const ok = sendSSEProgress(
|
|
438
|
+
res,
|
|
439
|
+
stage.relationPath,
|
|
440
|
+
completed,
|
|
441
|
+
plan.stages.length,
|
|
442
|
+
)
|
|
439
443
|
if (!ok) return
|
|
440
444
|
})
|
|
441
445
|
}
|
|
@@ -443,23 +447,13 @@ async function runAutoIncludeSingle(
|
|
|
443
447
|
if (isClientGone()) return
|
|
444
448
|
|
|
445
449
|
if (stageErrorMessage) {
|
|
446
|
-
|
|
447
|
-
sendSSEError(res, stageErrorMessage)
|
|
448
|
-
}
|
|
450
|
+
safeSendError(res, stageErrorMessage)
|
|
449
451
|
return
|
|
450
452
|
}
|
|
451
453
|
|
|
452
454
|
if (res.writableEnded || res.destroyed) return
|
|
453
455
|
sendSSEResult(res, publicState)
|
|
454
|
-
}
|
|
455
|
-
if (isClientGone()) return
|
|
456
|
-
console.error('[auto-progressive] dispatch error:', err)
|
|
457
|
-
if (!res.writableEnded && !res.destroyed) {
|
|
458
|
-
sendSSEError(res, mapError(err).message)
|
|
459
|
-
}
|
|
460
|
-
} finally {
|
|
461
|
-
endSSE(res, keepalive)
|
|
462
|
-
}
|
|
456
|
+
})
|
|
463
457
|
}
|
|
464
458
|
|
|
465
459
|
function buildStageQueryArgs(
|
|
@@ -475,7 +469,9 @@ function buildStageQueryArgs(
|
|
|
475
469
|
: null
|
|
476
470
|
|
|
477
471
|
const finalArgs: Record<string, unknown> = { ...stage.stageArgs }
|
|
478
|
-
finalArgs.where = mergeWhere(stage.stageArgs.where, {
|
|
472
|
+
finalArgs.where = mergeWhere(stage.stageArgs.where, {
|
|
473
|
+
[childKey]: { in: inChunk },
|
|
474
|
+
})
|
|
479
475
|
|
|
480
476
|
let injectedChildPath: string | null = null
|
|
481
477
|
|
|
@@ -487,11 +483,7 @@ function buildStageQueryArgs(
|
|
|
487
483
|
}
|
|
488
484
|
|
|
489
485
|
if (baseOmit && baseOmit[childKey] === true) {
|
|
490
|
-
const
|
|
491
|
-
for (const [k, v] of Object.entries(baseOmit)) {
|
|
492
|
-
if (k === childKey) continue
|
|
493
|
-
nextOmit[k] = v
|
|
494
|
-
}
|
|
486
|
+
const { [childKey]: _drop, ...nextOmit } = baseOmit
|
|
495
487
|
if (Object.keys(nextOmit).length > 0) {
|
|
496
488
|
finalArgs.omit = nextOmit
|
|
497
489
|
} else {
|
|
@@ -549,7 +541,15 @@ async function runOneStageMany(options: {
|
|
|
549
541
|
res: Response
|
|
550
542
|
isAborted: () => boolean
|
|
551
543
|
}): Promise<void> {
|
|
552
|
-
const {
|
|
544
|
+
const {
|
|
545
|
+
extended,
|
|
546
|
+
models,
|
|
547
|
+
stage,
|
|
548
|
+
parentEntries,
|
|
549
|
+
internalFieldPaths,
|
|
550
|
+
res,
|
|
551
|
+
isAborted,
|
|
552
|
+
} = options
|
|
553
553
|
|
|
554
554
|
if (isAborted()) return
|
|
555
555
|
|
|
@@ -559,7 +559,10 @@ async function runOneStageMany(options: {
|
|
|
559
559
|
|
|
560
560
|
const targetModel = models[rel.type]
|
|
561
561
|
if (!targetModel) {
|
|
562
|
-
throw new
|
|
562
|
+
throw new HttpError(
|
|
563
|
+
500,
|
|
564
|
+
'Target model not in relation metadata: ' + rel.type,
|
|
565
|
+
)
|
|
563
566
|
}
|
|
564
567
|
|
|
565
568
|
if (parentEntries.length === 0) {
|
|
@@ -573,7 +576,10 @@ async function runOneStageMany(options: {
|
|
|
573
576
|
|
|
574
577
|
const internalParents = parentEntries.map((p) => p.internal)
|
|
575
578
|
const distinctValues = collectDistinctParentValues(internalParents, parentKey)
|
|
576
|
-
const delegate: PrismaDelegate = getDelegate(
|
|
579
|
+
const delegate: PrismaDelegate = getDelegate(
|
|
580
|
+
extended,
|
|
581
|
+
targetModel.delegateKey,
|
|
582
|
+
)
|
|
577
583
|
|
|
578
584
|
const children: unknown[] = []
|
|
579
585
|
let injectedChildPath: string | null = null
|
|
@@ -581,7 +587,11 @@ async function runOneStageMany(options: {
|
|
|
581
587
|
for (let i = 0; i < distinctValues.length; i += MAX_IN_CHUNK) {
|
|
582
588
|
if (isAborted()) return
|
|
583
589
|
const chunk = distinctValues.slice(i, i + MAX_IN_CHUNK)
|
|
584
|
-
const { args, injectedChildPath: ip } = buildStageQueryArgs(
|
|
590
|
+
const { args, injectedChildPath: ip } = buildStageQueryArgs(
|
|
591
|
+
stage,
|
|
592
|
+
childKey,
|
|
593
|
+
chunk,
|
|
594
|
+
)
|
|
585
595
|
if (ip) injectedChildPath = ip
|
|
586
596
|
const partial = await delegate.findMany(args)
|
|
587
597
|
if (isAborted()) return
|
|
@@ -613,7 +623,11 @@ async function runOneStageMany(options: {
|
|
|
613
623
|
}
|
|
614
624
|
}
|
|
615
625
|
|
|
616
|
-
const publicVal = buildPublicForStage(
|
|
626
|
+
const publicVal = buildPublicForStage(
|
|
627
|
+
internalVal,
|
|
628
|
+
effectivePaths,
|
|
629
|
+
stage.relationPath,
|
|
630
|
+
)
|
|
617
631
|
|
|
618
632
|
entry.internal[stage.relationName] = internalVal
|
|
619
633
|
entry.public[stage.relationName] = publicVal
|
|
@@ -678,10 +692,11 @@ async function processFindManyStages(args: {
|
|
|
678
692
|
res.destroyed
|
|
679
693
|
|
|
680
694
|
for (const group of groups) {
|
|
681
|
-
if (signal?.aborted === true || res.writableEnded || res.destroyed)
|
|
695
|
+
if (signal?.aborted === true || res.writableEnded || res.destroyed)
|
|
696
|
+
return stageErrorMessage
|
|
682
697
|
if (stageErrorMessage) break
|
|
683
698
|
|
|
684
|
-
await
|
|
699
|
+
await mapLimited(group, STAGE_CONCURRENCY, async (stage) => {
|
|
685
700
|
if (isAborted()) return
|
|
686
701
|
const parentEntries = collectParentEntries(rootPairs, stage.parentPath)
|
|
687
702
|
try {
|
|
@@ -696,19 +711,50 @@ async function processFindManyStages(args: {
|
|
|
696
711
|
})
|
|
697
712
|
} catch (err) {
|
|
698
713
|
if (isAborted()) return
|
|
699
|
-
console.error('
|
|
714
|
+
console.error(LOG_PREFIX, 'stage failed:', stage.relationPath, err)
|
|
700
715
|
stageErrorMessage = mapError(err).message
|
|
701
716
|
return
|
|
702
717
|
}
|
|
703
718
|
if (isAborted()) return
|
|
704
719
|
completed++
|
|
705
|
-
const ok = sendSSEProgress(
|
|
720
|
+
const ok = sendSSEProgress(
|
|
721
|
+
res,
|
|
722
|
+
stage.relationPath,
|
|
723
|
+
completed,
|
|
724
|
+
plan.stages.length,
|
|
725
|
+
)
|
|
706
726
|
if (!ok) return
|
|
707
727
|
})
|
|
708
728
|
}
|
|
709
729
|
return stageErrorMessage
|
|
710
730
|
}
|
|
711
731
|
|
|
732
|
+
async function fetchRootAndCount(args: {
|
|
733
|
+
delegate: PrismaDelegate
|
|
734
|
+
rawClient: unknown
|
|
735
|
+
rootArgs: Record<string, unknown>
|
|
736
|
+
distinctCountLimit: number | undefined
|
|
737
|
+
countSource:
|
|
738
|
+
| NonNullable<OperationContext['paginationConfig']>['countSource']
|
|
739
|
+
| undefined
|
|
740
|
+
}): Promise<{ data: unknown[]; count: number }> {
|
|
741
|
+
const { delegate, rawClient, rootArgs, distinctCountLimit, countSource } =
|
|
742
|
+
args
|
|
743
|
+
const [data, count] = await Promise.all([
|
|
744
|
+
delegate.findMany(rootArgs),
|
|
745
|
+
countForPagination(
|
|
746
|
+
delegate,
|
|
747
|
+
rootArgs,
|
|
748
|
+
undefined,
|
|
749
|
+
undefined,
|
|
750
|
+
distinctCountLimit,
|
|
751
|
+
countSource,
|
|
752
|
+
rawClient,
|
|
753
|
+
),
|
|
754
|
+
])
|
|
755
|
+
return { data: data as unknown[], count }
|
|
756
|
+
}
|
|
757
|
+
|
|
712
758
|
async function runPaginatedRoot(args: {
|
|
713
759
|
extended: unknown
|
|
714
760
|
delegateKey: string
|
|
@@ -730,196 +776,158 @@ async function runPaginatedRoot(args: {
|
|
|
730
776
|
'findManyPaginatedMode="transaction" requires transaction support on the Prisma client',
|
|
731
777
|
)
|
|
732
778
|
}
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
distinctCountLimit,
|
|
743
|
-
countSource,
|
|
744
|
-
tx,
|
|
745
|
-
),
|
|
746
|
-
])
|
|
747
|
-
return { data, count }
|
|
748
|
-
})
|
|
749
|
-
return { data: result.data as unknown[], count: result.count }
|
|
779
|
+
return txClient.$transaction((tx: unknown) =>
|
|
780
|
+
fetchRootAndCount({
|
|
781
|
+
delegate: getDelegate(tx, delegateKey),
|
|
782
|
+
rawClient: tx,
|
|
783
|
+
rootArgs,
|
|
784
|
+
distinctCountLimit,
|
|
785
|
+
countSource,
|
|
786
|
+
}),
|
|
787
|
+
)
|
|
750
788
|
}
|
|
751
789
|
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
undefined,
|
|
760
|
-
distinctCountLimit,
|
|
761
|
-
countSource,
|
|
762
|
-
extended,
|
|
763
|
-
),
|
|
764
|
-
])
|
|
765
|
-
return { data: data as unknown[], count }
|
|
790
|
+
return fetchRootAndCount({
|
|
791
|
+
delegate: getDelegate(extended, delegateKey),
|
|
792
|
+
rawClient: extended,
|
|
793
|
+
rootArgs,
|
|
794
|
+
distinctCountLimit,
|
|
795
|
+
countSource,
|
|
796
|
+
})
|
|
766
797
|
}
|
|
767
798
|
|
|
768
|
-
async function
|
|
799
|
+
async function runAutoIncludeManyOrPaginated(
|
|
769
800
|
options: RunAutoIncludeOptions,
|
|
770
801
|
plan: AutoIncludePlan,
|
|
802
|
+
isPaginated: boolean,
|
|
771
803
|
): Promise<void> {
|
|
772
804
|
const { res, ctx, delegateKey, models, signal } = options
|
|
805
|
+
const isClientGone = createClientGoneChecker(res, signal)
|
|
773
806
|
|
|
774
|
-
|
|
775
|
-
signal
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
try {
|
|
779
|
-
initSSE(res)
|
|
780
|
-
keepalive = startSSEKeepalive(res)
|
|
781
|
-
if (isClientGone()) return
|
|
782
|
-
|
|
783
|
-
const extended = await getExtendedClient(ctx)
|
|
784
|
-
if (isClientGone()) return
|
|
785
|
-
|
|
786
|
-
const rootDelegate = getDelegate(extended, delegateKey)
|
|
787
|
-
const rootArgs = applyPaginationLimits(plan.rootArgs, ctx.paginationConfig, !!ctx.guardShape)
|
|
788
|
-
|
|
789
|
-
let rootResult: unknown
|
|
790
|
-
try {
|
|
791
|
-
rootResult = await rootDelegate.findMany(rootArgs)
|
|
792
|
-
} catch (err) {
|
|
807
|
+
await withSSE(
|
|
808
|
+
{ res, signal, label: isPaginated ? 'paginated' : 'many' },
|
|
809
|
+
async () => {
|
|
810
|
+
const extended = await getExtendedClient(ctx)
|
|
793
811
|
if (isClientGone()) return
|
|
794
|
-
console.error('[auto-progressive] root findMany failed:', err)
|
|
795
|
-
sendSSEError(res, mapError(err).message)
|
|
796
|
-
return
|
|
797
|
-
}
|
|
798
|
-
|
|
799
|
-
if (isClientGone()) return
|
|
800
|
-
|
|
801
|
-
if (!Array.isArray(rootResult)) {
|
|
802
|
-
sendSSEError(res, 'auto-progressive: unexpected non-array root result for findMany')
|
|
803
|
-
return
|
|
804
|
-
}
|
|
805
812
|
|
|
806
|
-
|
|
813
|
+
const rootArgs = applyPaginationLimits(
|
|
814
|
+
plan.rootArgs,
|
|
815
|
+
ctx.paginationConfig,
|
|
816
|
+
!!ctx.guardShape,
|
|
817
|
+
)
|
|
807
818
|
|
|
808
|
-
|
|
809
|
-
|
|
819
|
+
let rootRows: unknown[]
|
|
820
|
+
let total = 0
|
|
821
|
+
let hasMore = false
|
|
810
822
|
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
823
|
+
try {
|
|
824
|
+
if (isPaginated) {
|
|
825
|
+
const mode: FindManyPaginatedMode =
|
|
826
|
+
ctx.findManyPaginatedMode ?? 'promiseAll'
|
|
827
|
+
const r = await runPaginatedRoot({
|
|
828
|
+
extended,
|
|
829
|
+
delegateKey,
|
|
830
|
+
rootArgs,
|
|
831
|
+
ctx,
|
|
832
|
+
mode,
|
|
833
|
+
})
|
|
834
|
+
rootRows = r.data
|
|
835
|
+
total = r.count
|
|
836
|
+
const skip = typeof rootArgs.skip === 'number' ? rootArgs.skip : 0
|
|
837
|
+
const takeRaw =
|
|
838
|
+
typeof rootArgs.take === 'number' ? rootArgs.take : rootRows.length
|
|
839
|
+
const absTake = Math.abs(takeRaw)
|
|
840
|
+
hasMore =
|
|
841
|
+
absTake > 0 &&
|
|
842
|
+
rootRows.length >= absTake &&
|
|
843
|
+
skip + rootRows.length < total
|
|
844
|
+
} else {
|
|
845
|
+
const rootDelegate = getDelegate(extended, delegateKey)
|
|
846
|
+
const result = await rootDelegate.findMany(rootArgs)
|
|
847
|
+
if (!Array.isArray(result)) {
|
|
848
|
+
safeSendError(
|
|
849
|
+
res,
|
|
850
|
+
'auto-progressive: unexpected non-array root result for findMany',
|
|
851
|
+
)
|
|
852
|
+
return
|
|
853
|
+
}
|
|
854
|
+
rootRows = result
|
|
855
|
+
}
|
|
856
|
+
} catch (err) {
|
|
857
|
+
if (isClientGone()) return
|
|
858
|
+
console.error(
|
|
859
|
+
LOG_PREFIX,
|
|
860
|
+
isPaginated
|
|
861
|
+
? 'root findManyPaginated failed:'
|
|
862
|
+
: 'root findMany failed:',
|
|
863
|
+
err,
|
|
864
|
+
)
|
|
865
|
+
sendSSEError(res, mapError(err).message)
|
|
866
|
+
return
|
|
820
867
|
}
|
|
821
|
-
return
|
|
822
|
-
}
|
|
823
|
-
|
|
824
|
-
if (res.writableEnded || res.destroyed) return
|
|
825
|
-
sendSSEResult(res, publicRows)
|
|
826
|
-
} catch (err) {
|
|
827
|
-
if (isClientGone()) return
|
|
828
|
-
console.error('[auto-progressive] many dispatch error:', err)
|
|
829
|
-
if (!res.writableEnded && !res.destroyed) {
|
|
830
|
-
sendSSEError(res, mapError(err).message)
|
|
831
|
-
}
|
|
832
|
-
} finally {
|
|
833
|
-
endSSE(res, keepalive)
|
|
834
|
-
}
|
|
835
|
-
}
|
|
836
|
-
|
|
837
|
-
async function runAutoIncludePaginated(
|
|
838
|
-
options: RunAutoIncludeOptions,
|
|
839
|
-
plan: AutoIncludePlan,
|
|
840
|
-
): Promise<void> {
|
|
841
|
-
const { res, ctx, delegateKey, models, signal } = options
|
|
842
868
|
|
|
843
|
-
|
|
844
|
-
signal?.aborted === true || res.writableEnded || res.destroyed
|
|
845
|
-
|
|
846
|
-
let keepalive: IntervalHandle | null = null
|
|
847
|
-
try {
|
|
848
|
-
initSSE(res)
|
|
849
|
-
keepalive = startSSEKeepalive(res)
|
|
850
|
-
if (isClientGone()) return
|
|
869
|
+
if (isClientGone()) return
|
|
851
870
|
|
|
852
|
-
|
|
853
|
-
|
|
871
|
+
const { publicRows, rootPairs } = buildRootPairs(
|
|
872
|
+
rootRows,
|
|
873
|
+
plan.internalFieldPaths,
|
|
874
|
+
)
|
|
854
875
|
|
|
855
|
-
|
|
856
|
-
|
|
876
|
+
if (isPaginated) {
|
|
877
|
+
sendSSEPageMeta(res, total, hasMore)
|
|
878
|
+
}
|
|
879
|
+
sendSSERootArray(res, publicRows)
|
|
880
|
+
sendSSEProgress(res, 'root', 0, plan.stages.length)
|
|
857
881
|
|
|
858
|
-
|
|
859
|
-
let total: number
|
|
860
|
-
try {
|
|
861
|
-
const r = await runPaginatedRoot({
|
|
882
|
+
const stageError = await processFindManyStages({
|
|
862
883
|
extended,
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
884
|
+
models,
|
|
885
|
+
plan,
|
|
886
|
+
rootPairs,
|
|
887
|
+
res,
|
|
888
|
+
signal,
|
|
867
889
|
})
|
|
868
|
-
rootRows = r.data
|
|
869
|
-
total = r.count
|
|
870
|
-
} catch (err) {
|
|
871
|
-
if (isClientGone()) return
|
|
872
|
-
console.error('[auto-progressive] root findManyPaginated failed:', err)
|
|
873
|
-
sendSSEError(res, mapError(err).message)
|
|
874
|
-
return
|
|
875
|
-
}
|
|
876
|
-
|
|
877
|
-
if (isClientGone()) return
|
|
878
|
-
|
|
879
|
-
const skip = typeof rootArgs.skip === 'number' ? rootArgs.skip : 0
|
|
880
|
-
const takeRaw = typeof rootArgs.take === 'number' ? rootArgs.take : rootRows.length
|
|
881
|
-
const absTake = Math.abs(takeRaw)
|
|
882
|
-
const hasMore = absTake > 0 && rootRows.length >= absTake && skip + rootRows.length < total
|
|
883
|
-
|
|
884
|
-
const { publicRows, rootPairs } = buildRootPairs(rootRows, plan.internalFieldPaths)
|
|
885
890
|
|
|
886
|
-
|
|
887
|
-
sendSSERootArray(res, publicRows)
|
|
888
|
-
sendSSEProgress(res, 'root', 0, plan.stages.length)
|
|
889
|
-
|
|
890
|
-
const stageError = await processFindManyStages({
|
|
891
|
-
extended, models, plan, rootPairs, res, signal,
|
|
892
|
-
})
|
|
893
|
-
|
|
894
|
-
if (isClientGone()) return
|
|
891
|
+
if (isClientGone()) return
|
|
895
892
|
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
893
|
+
if (stageError) {
|
|
894
|
+
safeSendError(res, stageError)
|
|
895
|
+
return
|
|
899
896
|
}
|
|
900
|
-
return
|
|
901
|
-
}
|
|
902
897
|
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
} finally {
|
|
912
|
-
endSSE(res, keepalive)
|
|
913
|
-
}
|
|
898
|
+
if (res.writableEnded || res.destroyed) return
|
|
899
|
+
if (isPaginated) {
|
|
900
|
+
sendSSEResult(res, { data: publicRows, total, hasMore })
|
|
901
|
+
} else {
|
|
902
|
+
sendSSEResult(res, publicRows)
|
|
903
|
+
}
|
|
904
|
+
},
|
|
905
|
+
)
|
|
914
906
|
}
|
|
915
907
|
|
|
916
908
|
export async function runAutoIncludeProgressive(
|
|
917
909
|
options: RunAutoIncludeOptions,
|
|
918
910
|
): Promise<void> {
|
|
919
911
|
if (options.ctx.guardShape) {
|
|
912
|
+
if (isGuardedAutoIncludeBaseOp(options.baseOp)) {
|
|
913
|
+
return runGuardedAutoIncludeProgressive({
|
|
914
|
+
req: options.req,
|
|
915
|
+
res: options.res,
|
|
916
|
+
ctx: options.ctx,
|
|
917
|
+
args: options.args,
|
|
918
|
+
baseOp: options.baseOp,
|
|
919
|
+
modelName: options.modelName,
|
|
920
|
+
delegateKey: options.delegateKey,
|
|
921
|
+
models: options.models,
|
|
922
|
+
variantConfig: options.variantConfig,
|
|
923
|
+
coreQueryFn: options.coreQueryFn,
|
|
924
|
+
signal: options.signal,
|
|
925
|
+
})
|
|
926
|
+
}
|
|
920
927
|
return handleAutoIncludeFallback(
|
|
921
928
|
options,
|
|
922
|
-
'auto-progressive fallback: guard shape disables auto-include'
|
|
929
|
+
'auto-progressive fallback: guard shape disables auto-include for baseOp=' +
|
|
930
|
+
options.baseOp,
|
|
923
931
|
)
|
|
924
932
|
}
|
|
925
933
|
|
|
@@ -950,10 +958,10 @@ export async function runAutoIncludeProgressive(
|
|
|
950
958
|
}
|
|
951
959
|
|
|
952
960
|
if (options.baseOp === 'findMany') {
|
|
953
|
-
return
|
|
961
|
+
return runAutoIncludeManyOrPaginated(options, plan, false)
|
|
954
962
|
}
|
|
955
963
|
if (options.baseOp === 'findManyPaginated') {
|
|
956
|
-
return
|
|
964
|
+
return runAutoIncludeManyOrPaginated(options, plan, true)
|
|
957
965
|
}
|
|
958
966
|
return runAutoIncludeSingle(options, plan)
|
|
959
|
-
}
|
|
967
|
+
}
|