prisma-generator-express 1.59.0 → 1.60.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 (64) hide show
  1. package/dist/client/encodeQueryParams.js +19 -23
  2. package/dist/client/encodeQueryParams.js.map +1 -1
  3. package/dist/copy/operationDefinitions.d.ts +37 -0
  4. package/dist/copy/operationDefinitions.js +412 -0
  5. package/dist/copy/operationDefinitions.js.map +1 -0
  6. package/dist/generators/generateFastifyHandler.js +10 -41
  7. package/dist/generators/generateFastifyHandler.js.map +1 -1
  8. package/dist/generators/generateHonoHandler.js +10 -41
  9. package/dist/generators/generateHonoHandler.js.map +1 -1
  10. package/dist/generators/generateModelMetadata.d.ts +8 -0
  11. package/dist/generators/generateModelMetadata.js +77 -0
  12. package/dist/generators/generateModelMetadata.js.map +1 -0
  13. package/dist/generators/generateOperationCore.js +20 -30
  14. package/dist/generators/generateOperationCore.js.map +1 -1
  15. package/dist/generators/generateQueryBuilderHelper.js +1 -1
  16. package/dist/generators/generateQueryBuilderHelper.js.map +1 -1
  17. package/dist/generators/generateRouteConfigType.js +11 -57
  18. package/dist/generators/generateRouteConfigType.js.map +1 -1
  19. package/dist/generators/generateRouter.js +64 -177
  20. package/dist/generators/generateRouter.js.map +1 -1
  21. package/dist/generators/generateRouterFastify.js +59 -169
  22. package/dist/generators/generateRouterFastify.js.map +1 -1
  23. package/dist/generators/generateRouterHono.js +52 -152
  24. package/dist/generators/generateRouterHono.js.map +1 -1
  25. package/dist/generators/generateUnifiedHandler.js +7 -30
  26. package/dist/generators/generateUnifiedHandler.js.map +1 -1
  27. package/dist/generators/generateUnifiedScalarUI.js +9 -74
  28. package/dist/generators/generateUnifiedScalarUI.js.map +1 -1
  29. package/dist/index.js +11 -0
  30. package/dist/index.js.map +1 -1
  31. package/dist/utils/copyFiles.js +7 -0
  32. package/dist/utils/copyFiles.js.map +1 -1
  33. package/dist/utils/writeFileSafely.js +5 -12
  34. package/dist/utils/writeFileSafely.js.map +1 -1
  35. package/package.json +12 -2
  36. package/src/client/encodeQueryParams.ts +23 -36
  37. package/src/copy/autoIncludePlanner.ts +14 -23
  38. package/src/copy/autoIncludeRuntime.ts +117 -228
  39. package/src/copy/buildModelOpenApi.ts +248 -628
  40. package/src/copy/concurrency.ts +20 -0
  41. package/src/copy/docsRenderer.ts +63 -333
  42. package/src/copy/errorMapper.ts +126 -0
  43. package/src/copy/guardHelpers.ts +56 -0
  44. package/src/copy/materializedCount.ts +68 -0
  45. package/src/copy/materializedRouter.ts +33 -29
  46. package/src/copy/operationDefinitions.ts +359 -35
  47. package/src/copy/operationRuntime.ts +11 -605
  48. package/src/copy/pagination.ts +151 -0
  49. package/src/copy/scalarTypes.ts +2 -0
  50. package/src/copy/sse.ts +296 -0
  51. package/src/generators/generateFastifyHandler.ts +13 -47
  52. package/src/generators/generateHonoHandler.ts +13 -47
  53. package/src/generators/generateModelMetadata.ts +92 -0
  54. package/src/generators/generateOperationCore.ts +19 -32
  55. package/src/generators/generateQueryBuilderHelper.ts +1 -1
  56. package/src/generators/generateRouteConfigType.ts +9 -60
  57. package/src/generators/generateRouter.ts +88 -180
  58. package/src/generators/generateRouterFastify.ts +65 -172
  59. package/src/generators/generateRouterHono.ts +58 -155
  60. package/src/generators/generateUnifiedHandler.ts +8 -33
  61. package/src/generators/generateUnifiedScalarUI.ts +9 -91
  62. package/src/index.ts +13 -1
  63. package/src/utils/copyFiles.ts +7 -0
  64. 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,23 @@ import {
13
11
  sendSSEPageMeta,
14
12
  runSingleResultSSE,
15
13
  emitTerminalSSEError,
14
+ safeSendError,
16
15
  setByPath,
17
- getDelegate,
18
- getExtendedClient,
16
+ withSSE,
17
+ } from './sse'
18
+ import {
19
19
  applyPaginationLimits,
20
20
  countForPagination,
21
- mapError,
22
- HttpError,
21
+ } from './pagination'
22
+ import {
23
+ getDelegate,
24
+ getExtendedClient,
23
25
  type OperationContext,
24
26
  type PrismaDelegate,
25
27
  type FindManyPaginatedMode,
26
28
  } from './operationRuntime'
27
29
  import { isPlainObject } from './misc'
30
+ import { mapLimited } from './concurrency'
28
31
  import {
29
32
  planAutoInclude,
30
33
  type AutoIncludePlan,
@@ -36,8 +39,6 @@ import type { AutoIncludeProgressiveVariantConfig } from './routeConfig'
36
39
  const STAGE_CONCURRENCY = 4
37
40
  const MAX_IN_CHUNK = 1000
38
41
 
39
- type IntervalHandle = ReturnType<typeof setInterval>
40
-
41
42
  export type AutoIncludeBaseOp =
42
43
  | 'findUnique'
43
44
  | 'findUniqueOrThrow'
@@ -69,6 +70,10 @@ type ParentEntry = RowPair & {
69
70
  locator: Array<number | string>
70
71
  }
71
72
 
73
+ function createClientGoneChecker(res: Response, signal?: AbortSignal): () => boolean {
74
+ return () => signal?.aborted === true || res.writableEnded || res.destroyed
75
+ }
76
+
72
77
  function readPath(source: Record<string, unknown>, path: string): unknown {
73
78
  if (path === '') return source
74
79
  const parts = path.split('.')
@@ -85,16 +90,11 @@ function stripInternalAtScope(
85
90
  internalPaths: string[],
86
91
  scopePath: string,
87
92
  ): void {
93
+ const prefix = scopePath === '' ? '' : scopePath + '.'
88
94
  for (const fullPath of internalPaths) {
89
- if (scopePath === '') {
90
- if (!fullPath.includes('.')) {
91
- delete target[fullPath]
92
- }
93
- continue
94
- }
95
- if (!fullPath.startsWith(scopePath + '.')) continue
96
- const relative = fullPath.slice(scopePath.length + 1)
97
- if (relative.includes('.')) continue
95
+ if (!fullPath.startsWith(prefix)) continue
96
+ const relative = fullPath.slice(prefix.length)
97
+ if (relative === '' || relative.includes('.')) continue
98
98
  delete target[relative]
99
99
  }
100
100
  }
@@ -132,22 +132,14 @@ function buildPublicForStage(
132
132
  internalFieldPaths: string[],
133
133
  scopePath: string,
134
134
  ): unknown {
135
- if (Array.isArray(result)) {
136
- return result.map((item) => {
137
- if (isPlainObject(item)) {
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 }
135
+ const process = (item: unknown): unknown => {
136
+ if (!isPlainObject(item)) return item
137
+ const copy: Record<string, unknown> = { ...item }
147
138
  stripInternalAtScope(copy, internalFieldPaths, scopePath)
148
139
  return copy
149
140
  }
150
- return result
141
+ if (Array.isArray(result)) return result.map(process)
142
+ return process(result)
151
143
  }
152
144
 
153
145
  function normalizeKey(v: unknown): string {
@@ -170,26 +162,6 @@ function groupStagesByDepth(stages: AutoIncludeStage[]): AutoIncludeStage[][] {
170
162
  .map((d) => byDepth.get(d) as AutoIncludeStage[])
171
163
  }
172
164
 
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
165
  function handleAutoIncludeFallback(
194
166
  options: RunAutoIncludeOptions,
195
167
  message: string,
@@ -322,7 +294,7 @@ async function runOneStageSingle(options: {
322
294
 
323
295
  const targetModel = models[stage.relationField.type]
324
296
  if (!targetModel) {
325
- throw new Error('Target model not in relation metadata: ' + stage.relationField.type)
297
+ throw new HttpError(500, 'Target model not in relation metadata: ' + stage.relationField.type)
326
298
  }
327
299
 
328
300
  const finalArgs: Record<string, unknown> = { ...stage.stageArgs }
@@ -336,14 +308,14 @@ async function runOneStageSingle(options: {
336
308
 
337
309
  const appliedInternal = setByPath(internal, stage.relationPath, result)
338
310
  if (!appliedInternal) {
339
- throw new Error('Failed to apply internal patch for ' + stage.relationPath)
311
+ throw new HttpError(500, 'Failed to apply internal patch for ' + stage.relationPath)
340
312
  }
341
313
 
342
314
  const publicResult = buildPublicForStage(result, internalFieldPaths, stage.relationPath)
343
315
 
344
316
  const appliedPublic = setByPath(publicState, stage.relationPath, publicResult)
345
317
  if (!appliedPublic) {
346
- throw new Error('Failed to apply public patch for ' + stage.relationPath)
318
+ throw new HttpError(500, 'Failed to apply public patch for ' + stage.relationPath)
347
319
  }
348
320
 
349
321
  sendSSEField(res, stage.relationPath, publicResult)
@@ -354,16 +326,9 @@ async function runAutoIncludeSingle(
354
326
  plan: AutoIncludePlan,
355
327
  ): Promise<void> {
356
328
  const { res, ctx, baseOp, delegateKey, models, signal } = options
329
+ const isClientGone = createClientGoneChecker(res, signal)
357
330
 
358
- const isClientGone = () =>
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
-
331
+ await withSSE({ res, signal, label: 'single' }, async () => {
367
332
  const extended = await getExtendedClient(ctx)
368
333
  if (isClientGone()) return
369
334
 
@@ -374,7 +339,7 @@ async function runAutoIncludeSingle(
374
339
  rootResult = await rootDelegate[baseOp as Exclude<AutoIncludeBaseOp, 'findMany' | 'findManyPaginated'>](plan.rootArgs)
375
340
  } catch (err) {
376
341
  if (isClientGone()) return
377
- console.error('[auto-progressive] root query failed:', err)
342
+ console.error(LOG_PREFIX, 'root query failed:', err)
378
343
  sendSSEError(res, mapError(err).message)
379
344
  return
380
345
  }
@@ -414,7 +379,7 @@ async function runAutoIncludeSingle(
414
379
  if (isClientGone()) return
415
380
  if (stageErrorMessage) break
416
381
 
417
- await runConcurrent(group, STAGE_CONCURRENCY, async (stage) => {
382
+ await mapLimited(group, STAGE_CONCURRENCY, async (stage) => {
418
383
  if (isAborted()) return
419
384
  try {
420
385
  await runOneStageSingle({
@@ -429,7 +394,7 @@ async function runAutoIncludeSingle(
429
394
  })
430
395
  } catch (err) {
431
396
  if (isAborted()) return
432
- console.error('[auto-progressive] stage failed:', stage.relationPath, err)
397
+ console.error(LOG_PREFIX, 'stage failed:', stage.relationPath, err)
433
398
  stageErrorMessage = mapError(err).message
434
399
  return
435
400
  }
@@ -443,23 +408,13 @@ async function runAutoIncludeSingle(
443
408
  if (isClientGone()) return
444
409
 
445
410
  if (stageErrorMessage) {
446
- if (!res.writableEnded && !res.destroyed) {
447
- sendSSEError(res, stageErrorMessage)
448
- }
411
+ safeSendError(res, stageErrorMessage)
449
412
  return
450
413
  }
451
414
 
452
415
  if (res.writableEnded || res.destroyed) return
453
416
  sendSSEResult(res, publicState)
454
- } catch (err) {
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
- }
417
+ })
463
418
  }
464
419
 
465
420
  function buildStageQueryArgs(
@@ -487,11 +442,7 @@ function buildStageQueryArgs(
487
442
  }
488
443
 
489
444
  if (baseOmit && baseOmit[childKey] === true) {
490
- const nextOmit: Record<string, unknown> = {}
491
- for (const [k, v] of Object.entries(baseOmit)) {
492
- if (k === childKey) continue
493
- nextOmit[k] = v
494
- }
445
+ const { [childKey]: _drop, ...nextOmit } = baseOmit
495
446
  if (Object.keys(nextOmit).length > 0) {
496
447
  finalArgs.omit = nextOmit
497
448
  } else {
@@ -559,7 +510,7 @@ async function runOneStageMany(options: {
559
510
 
560
511
  const targetModel = models[rel.type]
561
512
  if (!targetModel) {
562
- throw new Error('Target model not in relation metadata: ' + rel.type)
513
+ throw new HttpError(500, 'Target model not in relation metadata: ' + rel.type)
563
514
  }
564
515
 
565
516
  if (parentEntries.length === 0) {
@@ -681,7 +632,7 @@ async function processFindManyStages(args: {
681
632
  if (signal?.aborted === true || res.writableEnded || res.destroyed) return stageErrorMessage
682
633
  if (stageErrorMessage) break
683
634
 
684
- await runConcurrent(group, STAGE_CONCURRENCY, async (stage) => {
635
+ await mapLimited(group, STAGE_CONCURRENCY, async (stage) => {
685
636
  if (isAborted()) return
686
637
  const parentEntries = collectParentEntries(rootPairs, stage.parentPath)
687
638
  try {
@@ -696,7 +647,7 @@ async function processFindManyStages(args: {
696
647
  })
697
648
  } catch (err) {
698
649
  if (isAborted()) return
699
- console.error('[auto-progressive] stage failed:', stage.relationPath, err)
650
+ console.error(LOG_PREFIX, 'stage failed:', stage.relationPath, err)
700
651
  stageErrorMessage = mapError(err).message
701
652
  return
702
653
  }
@@ -709,6 +660,29 @@ async function processFindManyStages(args: {
709
660
  return stageErrorMessage
710
661
  }
711
662
 
663
+ async function fetchRootAndCount(args: {
664
+ delegate: PrismaDelegate
665
+ rawClient: unknown
666
+ rootArgs: Record<string, unknown>
667
+ distinctCountLimit: number | undefined
668
+ countSource: NonNullable<OperationContext['paginationConfig']>['countSource'] | undefined
669
+ }): Promise<{ data: unknown[]; count: number }> {
670
+ const { delegate, rawClient, rootArgs, distinctCountLimit, countSource } = args
671
+ const [data, count] = await Promise.all([
672
+ delegate.findMany(rootArgs),
673
+ countForPagination(
674
+ delegate,
675
+ rootArgs,
676
+ undefined,
677
+ undefined,
678
+ distinctCountLimit,
679
+ countSource,
680
+ rawClient,
681
+ ),
682
+ ])
683
+ return { data: data as unknown[], count }
684
+ }
685
+
712
686
  async function runPaginatedRoot(args: {
713
687
  extended: unknown
714
688
  delegateKey: string
@@ -730,160 +704,81 @@ async function runPaginatedRoot(args: {
730
704
  'findManyPaginatedMode="transaction" requires transaction support on the Prisma client',
731
705
  )
732
706
  }
733
- const result = await txClient.$transaction(async (tx: unknown) => {
734
- const txDelegate = getDelegate(tx, delegateKey)
735
- const [data, count] = await Promise.all([
736
- txDelegate.findMany(rootArgs),
737
- countForPagination(
738
- txDelegate,
739
- rootArgs,
740
- undefined,
741
- undefined,
742
- distinctCountLimit,
743
- countSource,
744
- tx,
745
- ),
746
- ])
747
- return { data, count }
748
- })
749
- return { data: result.data as unknown[], count: result.count }
707
+ return txClient.$transaction((tx: unknown) =>
708
+ fetchRootAndCount({
709
+ delegate: getDelegate(tx, delegateKey),
710
+ rawClient: tx,
711
+ rootArgs,
712
+ distinctCountLimit,
713
+ countSource,
714
+ }),
715
+ )
750
716
  }
751
717
 
752
- const rootDelegate = getDelegate(extended, delegateKey)
753
- const [data, count] = await Promise.all([
754
- rootDelegate.findMany(rootArgs),
755
- countForPagination(
756
- rootDelegate,
757
- rootArgs,
758
- undefined,
759
- undefined,
760
- distinctCountLimit,
761
- countSource,
762
- extended,
763
- ),
764
- ])
765
- return { data: data as unknown[], count }
766
- }
767
-
768
- async function runAutoIncludeMany(
769
- options: RunAutoIncludeOptions,
770
- plan: AutoIncludePlan,
771
- ): Promise<void> {
772
- const { res, ctx, delegateKey, models, signal } = options
773
-
774
- const isClientGone = () =>
775
- signal?.aborted === true || res.writableEnded || res.destroyed
776
-
777
- let keepalive: IntervalHandle | null = null
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) {
793
- 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
-
806
- const { publicRows, rootPairs } = buildRootPairs(rootResult, plan.internalFieldPaths)
807
-
808
- sendSSERootArray(res, publicRows)
809
- sendSSEProgress(res, 'root', 0, plan.stages.length)
810
-
811
- const stageError = await processFindManyStages({
812
- extended, models, plan, rootPairs, res, signal,
813
- })
814
-
815
- if (isClientGone()) return
816
-
817
- if (stageError) {
818
- if (!res.writableEnded && !res.destroyed) {
819
- sendSSEError(res, stageError)
820
- }
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
- }
718
+ return fetchRootAndCount({
719
+ delegate: getDelegate(extended, delegateKey),
720
+ rawClient: extended,
721
+ rootArgs,
722
+ distinctCountLimit,
723
+ countSource,
724
+ })
835
725
  }
836
726
 
837
- async function runAutoIncludePaginated(
727
+ async function runAutoIncludeManyOrPaginated(
838
728
  options: RunAutoIncludeOptions,
839
729
  plan: AutoIncludePlan,
730
+ isPaginated: boolean,
840
731
  ): Promise<void> {
841
732
  const { res, ctx, delegateKey, models, signal } = options
733
+ const isClientGone = createClientGoneChecker(res, signal)
842
734
 
843
- const isClientGone = () =>
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
851
-
735
+ await withSSE({ res, signal, label: isPaginated ? 'paginated' : 'many' }, async () => {
852
736
  const extended = await getExtendedClient(ctx)
853
737
  if (isClientGone()) return
854
738
 
855
739
  const rootArgs = applyPaginationLimits(plan.rootArgs, ctx.paginationConfig, !!ctx.guardShape)
856
- const mode: FindManyPaginatedMode = ctx.findManyPaginatedMode ?? 'promiseAll'
857
740
 
858
741
  let rootRows: unknown[]
859
- let total: number
742
+ let total = 0
743
+ let hasMore = false
744
+
860
745
  try {
861
- const r = await runPaginatedRoot({
862
- extended,
863
- delegateKey,
864
- rootArgs,
865
- ctx,
866
- mode,
867
- })
868
- rootRows = r.data
869
- total = r.count
746
+ if (isPaginated) {
747
+ const mode: FindManyPaginatedMode = ctx.findManyPaginatedMode ?? 'promiseAll'
748
+ const r = await runPaginatedRoot({ extended, delegateKey, rootArgs, ctx, mode })
749
+ rootRows = r.data
750
+ total = r.count
751
+ const skip = typeof rootArgs.skip === 'number' ? rootArgs.skip : 0
752
+ const takeRaw = typeof rootArgs.take === 'number' ? rootArgs.take : rootRows.length
753
+ const absTake = Math.abs(takeRaw)
754
+ hasMore = absTake > 0 && rootRows.length >= absTake && skip + rootRows.length < total
755
+ } else {
756
+ const rootDelegate = getDelegate(extended, delegateKey)
757
+ const result = await rootDelegate.findMany(rootArgs)
758
+ if (!Array.isArray(result)) {
759
+ safeSendError(res, 'auto-progressive: unexpected non-array root result for findMany')
760
+ return
761
+ }
762
+ rootRows = result
763
+ }
870
764
  } catch (err) {
871
765
  if (isClientGone()) return
872
- console.error('[auto-progressive] root findManyPaginated failed:', err)
766
+ console.error(
767
+ LOG_PREFIX,
768
+ isPaginated ? 'root findManyPaginated failed:' : 'root findMany failed:',
769
+ err,
770
+ )
873
771
  sendSSEError(res, mapError(err).message)
874
772
  return
875
773
  }
876
774
 
877
775
  if (isClientGone()) return
878
776
 
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
777
  const { publicRows, rootPairs } = buildRootPairs(rootRows, plan.internalFieldPaths)
885
778
 
886
- sendSSEPageMeta(res, total, hasMore)
779
+ if (isPaginated) {
780
+ sendSSEPageMeta(res, total, hasMore)
781
+ }
887
782
  sendSSERootArray(res, publicRows)
888
783
  sendSSEProgress(res, 'root', 0, plan.stages.length)
889
784
 
@@ -894,23 +789,17 @@ async function runAutoIncludePaginated(
894
789
  if (isClientGone()) return
895
790
 
896
791
  if (stageError) {
897
- if (!res.writableEnded && !res.destroyed) {
898
- sendSSEError(res, stageError)
899
- }
792
+ safeSendError(res, stageError)
900
793
  return
901
794
  }
902
795
 
903
796
  if (res.writableEnded || res.destroyed) return
904
- sendSSEResult(res, { data: publicRows, total, hasMore })
905
- } catch (err) {
906
- if (isClientGone()) return
907
- console.error('[auto-progressive] paginated dispatch error:', err)
908
- if (!res.writableEnded && !res.destroyed) {
909
- sendSSEError(res, mapError(err).message)
797
+ if (isPaginated) {
798
+ sendSSEResult(res, { data: publicRows, total, hasMore })
799
+ } else {
800
+ sendSSEResult(res, publicRows)
910
801
  }
911
- } finally {
912
- endSSE(res, keepalive)
913
- }
802
+ })
914
803
  }
915
804
 
916
805
  export async function runAutoIncludeProgressive(
@@ -950,10 +839,10 @@ export async function runAutoIncludeProgressive(
950
839
  }
951
840
 
952
841
  if (options.baseOp === 'findMany') {
953
- return runAutoIncludeMany(options, plan)
842
+ return runAutoIncludeManyOrPaginated(options, plan, false)
954
843
  }
955
844
  if (options.baseOp === 'findManyPaginated') {
956
- return runAutoIncludePaginated(options, plan)
845
+ return runAutoIncludeManyOrPaginated(options, plan, true)
957
846
  }
958
847
  return runAutoIncludeSingle(options, plan)
959
848
  }