prisma-generator-express 1.60.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/package.json +1 -1
- package/src/copy/autoIncludePlannerGuarded.ts +477 -0
- package/src/copy/autoIncludeRuntime.ts +203 -84
- package/src/copy/autoIncludeRuntimeGuarded.ts +379 -0
|
@@ -15,10 +15,7 @@ import {
|
|
|
15
15
|
setByPath,
|
|
16
16
|
withSSE,
|
|
17
17
|
} from './sse'
|
|
18
|
-
import {
|
|
19
|
-
applyPaginationLimits,
|
|
20
|
-
countForPagination,
|
|
21
|
-
} from './pagination'
|
|
18
|
+
import { applyPaginationLimits, countForPagination } from './pagination'
|
|
22
19
|
import {
|
|
23
20
|
getDelegate,
|
|
24
21
|
getExtendedClient,
|
|
@@ -35,6 +32,10 @@ import {
|
|
|
35
32
|
type AutoIncludeStage,
|
|
36
33
|
} from './autoIncludePlanner'
|
|
37
34
|
import type { AutoIncludeProgressiveVariantConfig } from './routeConfig'
|
|
35
|
+
import {
|
|
36
|
+
isGuardedAutoIncludeBaseOp,
|
|
37
|
+
runGuardedAutoIncludeProgressive,
|
|
38
|
+
} from './autoIncludeRuntimeGuarded'
|
|
38
39
|
|
|
39
40
|
const STAGE_CONCURRENCY = 4
|
|
40
41
|
const MAX_IN_CHUNK = 1000
|
|
@@ -70,7 +71,10 @@ type ParentEntry = RowPair & {
|
|
|
70
71
|
locator: Array<number | string>
|
|
71
72
|
}
|
|
72
73
|
|
|
73
|
-
function createClientGoneChecker(
|
|
74
|
+
function createClientGoneChecker(
|
|
75
|
+
res: Response,
|
|
76
|
+
signal?: AbortSignal,
|
|
77
|
+
): () => boolean {
|
|
74
78
|
return () => signal?.aborted === true || res.writableEnded || res.destroyed
|
|
75
79
|
}
|
|
76
80
|
|
|
@@ -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
|
|
|
@@ -269,7 +274,16 @@ async function runOneStageSingle(options: {
|
|
|
269
274
|
res: Response
|
|
270
275
|
isAborted: () => boolean
|
|
271
276
|
}): Promise<void> {
|
|
272
|
-
const {
|
|
277
|
+
const {
|
|
278
|
+
extended,
|
|
279
|
+
models,
|
|
280
|
+
stage,
|
|
281
|
+
internal,
|
|
282
|
+
publicState,
|
|
283
|
+
internalFieldPaths,
|
|
284
|
+
res,
|
|
285
|
+
isAborted,
|
|
286
|
+
} = options
|
|
273
287
|
if (isAborted()) return
|
|
274
288
|
|
|
275
289
|
const parentRaw = readPath(internal, stage.parentPath)
|
|
@@ -294,28 +308,46 @@ async function runOneStageSingle(options: {
|
|
|
294
308
|
|
|
295
309
|
const targetModel = models[stage.relationField.type]
|
|
296
310
|
if (!targetModel) {
|
|
297
|
-
throw new HttpError(
|
|
311
|
+
throw new HttpError(
|
|
312
|
+
500,
|
|
313
|
+
'Target model not in relation metadata: ' + stage.relationField.type,
|
|
314
|
+
)
|
|
298
315
|
}
|
|
299
316
|
|
|
300
317
|
const finalArgs: Record<string, unknown> = { ...stage.stageArgs }
|
|
301
318
|
finalArgs.where = mergeWhere(stage.stageArgs.where, linkFilter)
|
|
302
319
|
|
|
303
|
-
const delegate: PrismaDelegate = getDelegate(
|
|
304
|
-
|
|
320
|
+
const delegate: PrismaDelegate = getDelegate(
|
|
321
|
+
extended,
|
|
322
|
+
targetModel.delegateKey,
|
|
323
|
+
)
|
|
324
|
+
const method: 'findMany' | 'findFirst' = stage.relationField.isList
|
|
325
|
+
? 'findMany'
|
|
326
|
+
: 'findFirst'
|
|
305
327
|
const result = await delegate[method](finalArgs)
|
|
306
328
|
|
|
307
329
|
if (isAborted()) return
|
|
308
330
|
|
|
309
331
|
const appliedInternal = setByPath(internal, stage.relationPath, result)
|
|
310
332
|
if (!appliedInternal) {
|
|
311
|
-
throw new HttpError(
|
|
333
|
+
throw new HttpError(
|
|
334
|
+
500,
|
|
335
|
+
'Failed to apply internal patch for ' + stage.relationPath,
|
|
336
|
+
)
|
|
312
337
|
}
|
|
313
338
|
|
|
314
|
-
const publicResult = buildPublicForStage(
|
|
339
|
+
const publicResult = buildPublicForStage(
|
|
340
|
+
result,
|
|
341
|
+
internalFieldPaths,
|
|
342
|
+
stage.relationPath,
|
|
343
|
+
)
|
|
315
344
|
|
|
316
345
|
const appliedPublic = setByPath(publicState, stage.relationPath, publicResult)
|
|
317
346
|
if (!appliedPublic) {
|
|
318
|
-
throw new HttpError(
|
|
347
|
+
throw new HttpError(
|
|
348
|
+
500,
|
|
349
|
+
'Failed to apply public patch for ' + stage.relationPath,
|
|
350
|
+
)
|
|
319
351
|
}
|
|
320
352
|
|
|
321
353
|
sendSSEField(res, stage.relationPath, publicResult)
|
|
@@ -336,7 +368,9 @@ async function runAutoIncludeSingle(
|
|
|
336
368
|
|
|
337
369
|
let rootResult: unknown
|
|
338
370
|
try {
|
|
339
|
-
rootResult = await rootDelegate[
|
|
371
|
+
rootResult = await rootDelegate[
|
|
372
|
+
baseOp as Exclude<AutoIncludeBaseOp, 'findMany' | 'findManyPaginated'>
|
|
373
|
+
](plan.rootArgs)
|
|
340
374
|
} catch (err) {
|
|
341
375
|
if (isClientGone()) return
|
|
342
376
|
console.error(LOG_PREFIX, 'root query failed:', err)
|
|
@@ -400,7 +434,12 @@ async function runAutoIncludeSingle(
|
|
|
400
434
|
}
|
|
401
435
|
if (isAborted()) return
|
|
402
436
|
completed++
|
|
403
|
-
const ok = sendSSEProgress(
|
|
437
|
+
const ok = sendSSEProgress(
|
|
438
|
+
res,
|
|
439
|
+
stage.relationPath,
|
|
440
|
+
completed,
|
|
441
|
+
plan.stages.length,
|
|
442
|
+
)
|
|
404
443
|
if (!ok) return
|
|
405
444
|
})
|
|
406
445
|
}
|
|
@@ -430,7 +469,9 @@ function buildStageQueryArgs(
|
|
|
430
469
|
: null
|
|
431
470
|
|
|
432
471
|
const finalArgs: Record<string, unknown> = { ...stage.stageArgs }
|
|
433
|
-
finalArgs.where = mergeWhere(stage.stageArgs.where, {
|
|
472
|
+
finalArgs.where = mergeWhere(stage.stageArgs.where, {
|
|
473
|
+
[childKey]: { in: inChunk },
|
|
474
|
+
})
|
|
434
475
|
|
|
435
476
|
let injectedChildPath: string | null = null
|
|
436
477
|
|
|
@@ -500,7 +541,15 @@ async function runOneStageMany(options: {
|
|
|
500
541
|
res: Response
|
|
501
542
|
isAborted: () => boolean
|
|
502
543
|
}): Promise<void> {
|
|
503
|
-
const {
|
|
544
|
+
const {
|
|
545
|
+
extended,
|
|
546
|
+
models,
|
|
547
|
+
stage,
|
|
548
|
+
parentEntries,
|
|
549
|
+
internalFieldPaths,
|
|
550
|
+
res,
|
|
551
|
+
isAborted,
|
|
552
|
+
} = options
|
|
504
553
|
|
|
505
554
|
if (isAborted()) return
|
|
506
555
|
|
|
@@ -510,7 +559,10 @@ async function runOneStageMany(options: {
|
|
|
510
559
|
|
|
511
560
|
const targetModel = models[rel.type]
|
|
512
561
|
if (!targetModel) {
|
|
513
|
-
throw new HttpError(
|
|
562
|
+
throw new HttpError(
|
|
563
|
+
500,
|
|
564
|
+
'Target model not in relation metadata: ' + rel.type,
|
|
565
|
+
)
|
|
514
566
|
}
|
|
515
567
|
|
|
516
568
|
if (parentEntries.length === 0) {
|
|
@@ -524,7 +576,10 @@ async function runOneStageMany(options: {
|
|
|
524
576
|
|
|
525
577
|
const internalParents = parentEntries.map((p) => p.internal)
|
|
526
578
|
const distinctValues = collectDistinctParentValues(internalParents, parentKey)
|
|
527
|
-
const delegate: PrismaDelegate = getDelegate(
|
|
579
|
+
const delegate: PrismaDelegate = getDelegate(
|
|
580
|
+
extended,
|
|
581
|
+
targetModel.delegateKey,
|
|
582
|
+
)
|
|
528
583
|
|
|
529
584
|
const children: unknown[] = []
|
|
530
585
|
let injectedChildPath: string | null = null
|
|
@@ -532,7 +587,11 @@ async function runOneStageMany(options: {
|
|
|
532
587
|
for (let i = 0; i < distinctValues.length; i += MAX_IN_CHUNK) {
|
|
533
588
|
if (isAborted()) return
|
|
534
589
|
const chunk = distinctValues.slice(i, i + MAX_IN_CHUNK)
|
|
535
|
-
const { args, injectedChildPath: ip } = buildStageQueryArgs(
|
|
590
|
+
const { args, injectedChildPath: ip } = buildStageQueryArgs(
|
|
591
|
+
stage,
|
|
592
|
+
childKey,
|
|
593
|
+
chunk,
|
|
594
|
+
)
|
|
536
595
|
if (ip) injectedChildPath = ip
|
|
537
596
|
const partial = await delegate.findMany(args)
|
|
538
597
|
if (isAborted()) return
|
|
@@ -564,7 +623,11 @@ async function runOneStageMany(options: {
|
|
|
564
623
|
}
|
|
565
624
|
}
|
|
566
625
|
|
|
567
|
-
const publicVal = buildPublicForStage(
|
|
626
|
+
const publicVal = buildPublicForStage(
|
|
627
|
+
internalVal,
|
|
628
|
+
effectivePaths,
|
|
629
|
+
stage.relationPath,
|
|
630
|
+
)
|
|
568
631
|
|
|
569
632
|
entry.internal[stage.relationName] = internalVal
|
|
570
633
|
entry.public[stage.relationName] = publicVal
|
|
@@ -629,7 +692,8 @@ async function processFindManyStages(args: {
|
|
|
629
692
|
res.destroyed
|
|
630
693
|
|
|
631
694
|
for (const group of groups) {
|
|
632
|
-
if (signal?.aborted === true || res.writableEnded || res.destroyed)
|
|
695
|
+
if (signal?.aborted === true || res.writableEnded || res.destroyed)
|
|
696
|
+
return stageErrorMessage
|
|
633
697
|
if (stageErrorMessage) break
|
|
634
698
|
|
|
635
699
|
await mapLimited(group, STAGE_CONCURRENCY, async (stage) => {
|
|
@@ -653,7 +717,12 @@ async function processFindManyStages(args: {
|
|
|
653
717
|
}
|
|
654
718
|
if (isAborted()) return
|
|
655
719
|
completed++
|
|
656
|
-
const ok = sendSSEProgress(
|
|
720
|
+
const ok = sendSSEProgress(
|
|
721
|
+
res,
|
|
722
|
+
stage.relationPath,
|
|
723
|
+
completed,
|
|
724
|
+
plan.stages.length,
|
|
725
|
+
)
|
|
657
726
|
if (!ok) return
|
|
658
727
|
})
|
|
659
728
|
}
|
|
@@ -665,9 +734,12 @@ async function fetchRootAndCount(args: {
|
|
|
665
734
|
rawClient: unknown
|
|
666
735
|
rootArgs: Record<string, unknown>
|
|
667
736
|
distinctCountLimit: number | undefined
|
|
668
|
-
countSource:
|
|
737
|
+
countSource:
|
|
738
|
+
| NonNullable<OperationContext['paginationConfig']>['countSource']
|
|
739
|
+
| undefined
|
|
669
740
|
}): Promise<{ data: unknown[]; count: number }> {
|
|
670
|
-
const { delegate, rawClient, rootArgs, distinctCountLimit, countSource } =
|
|
741
|
+
const { delegate, rawClient, rootArgs, distinctCountLimit, countSource } =
|
|
742
|
+
args
|
|
671
743
|
const [data, count] = await Promise.all([
|
|
672
744
|
delegate.findMany(rootArgs),
|
|
673
745
|
countForPagination(
|
|
@@ -732,83 +804,130 @@ async function runAutoIncludeManyOrPaginated(
|
|
|
732
804
|
const { res, ctx, delegateKey, models, signal } = options
|
|
733
805
|
const isClientGone = createClientGoneChecker(res, signal)
|
|
734
806
|
|
|
735
|
-
await withSSE(
|
|
736
|
-
|
|
737
|
-
|
|
807
|
+
await withSSE(
|
|
808
|
+
{ res, signal, label: isPaginated ? 'paginated' : 'many' },
|
|
809
|
+
async () => {
|
|
810
|
+
const extended = await getExtendedClient(ctx)
|
|
811
|
+
if (isClientGone()) return
|
|
738
812
|
|
|
739
|
-
|
|
813
|
+
const rootArgs = applyPaginationLimits(
|
|
814
|
+
plan.rootArgs,
|
|
815
|
+
ctx.paginationConfig,
|
|
816
|
+
!!ctx.guardShape,
|
|
817
|
+
)
|
|
740
818
|
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
819
|
+
let rootRows: unknown[]
|
|
820
|
+
let total = 0
|
|
821
|
+
let hasMore = false
|
|
744
822
|
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
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
|
|
761
855
|
}
|
|
762
|
-
|
|
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
|
|
763
867
|
}
|
|
764
|
-
} catch (err) {
|
|
765
|
-
if (isClientGone()) return
|
|
766
|
-
console.error(
|
|
767
|
-
LOG_PREFIX,
|
|
768
|
-
isPaginated ? 'root findManyPaginated failed:' : 'root findMany failed:',
|
|
769
|
-
err,
|
|
770
|
-
)
|
|
771
|
-
sendSSEError(res, mapError(err).message)
|
|
772
|
-
return
|
|
773
|
-
}
|
|
774
868
|
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
const { publicRows, rootPairs } = buildRootPairs(rootRows, plan.internalFieldPaths)
|
|
869
|
+
if (isClientGone()) return
|
|
778
870
|
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
sendSSEProgress(res, 'root', 0, plan.stages.length)
|
|
871
|
+
const { publicRows, rootPairs } = buildRootPairs(
|
|
872
|
+
rootRows,
|
|
873
|
+
plan.internalFieldPaths,
|
|
874
|
+
)
|
|
784
875
|
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
876
|
+
if (isPaginated) {
|
|
877
|
+
sendSSEPageMeta(res, total, hasMore)
|
|
878
|
+
}
|
|
879
|
+
sendSSERootArray(res, publicRows)
|
|
880
|
+
sendSSEProgress(res, 'root', 0, plan.stages.length)
|
|
881
|
+
|
|
882
|
+
const stageError = await processFindManyStages({
|
|
883
|
+
extended,
|
|
884
|
+
models,
|
|
885
|
+
plan,
|
|
886
|
+
rootPairs,
|
|
887
|
+
res,
|
|
888
|
+
signal,
|
|
889
|
+
})
|
|
788
890
|
|
|
789
|
-
|
|
891
|
+
if (isClientGone()) return
|
|
790
892
|
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
893
|
+
if (stageError) {
|
|
894
|
+
safeSendError(res, stageError)
|
|
895
|
+
return
|
|
896
|
+
}
|
|
795
897
|
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
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
|
+
)
|
|
803
906
|
}
|
|
804
907
|
|
|
805
908
|
export async function runAutoIncludeProgressive(
|
|
806
909
|
options: RunAutoIncludeOptions,
|
|
807
910
|
): Promise<void> {
|
|
808
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
|
+
}
|
|
809
927
|
return handleAutoIncludeFallback(
|
|
810
928
|
options,
|
|
811
|
-
'auto-progressive fallback: guard shape disables auto-include'
|
|
929
|
+
'auto-progressive fallback: guard shape disables auto-include for baseOp=' +
|
|
930
|
+
options.baseOp,
|
|
812
931
|
)
|
|
813
932
|
}
|
|
814
933
|
|
|
@@ -845,4 +964,4 @@ export async function runAutoIncludeProgressive(
|
|
|
845
964
|
return runAutoIncludeManyOrPaginated(options, plan, true)
|
|
846
965
|
}
|
|
847
966
|
return runAutoIncludeSingle(options, plan)
|
|
848
|
-
}
|
|
967
|
+
}
|