prisma-generator-express 1.66.0 → 1.68.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 +188 -3432
- package/dist/client/encodeQueryParams.js +3 -1
- package/dist/client/encodeQueryParams.js.map +1 -1
- package/dist/copy/misc.d.ts +1 -0
- package/dist/copy/misc.js +14 -0
- package/dist/copy/misc.js.map +1 -1
- package/dist/copy/operationDefinitions.d.ts +4 -1
- package/dist/copy/operationDefinitions.js +133 -9
- package/dist/copy/operationDefinitions.js.map +1 -1
- package/dist/generators/generateFastifyHandler.d.ts +2 -2
- package/dist/generators/generateFastifyHandler.js +68 -12
- package/dist/generators/generateFastifyHandler.js.map +1 -1
- package/dist/generators/generateHonoHandler.d.ts +2 -2
- package/dist/generators/generateHonoHandler.js +53 -6
- package/dist/generators/generateHonoHandler.js.map +1 -1
- package/dist/generators/generateHonoOpenApi.d.ts +13 -0
- package/dist/generators/generateHonoOpenApi.js +70 -0
- package/dist/generators/generateHonoOpenApi.js.map +1 -0
- package/dist/generators/generateImportPrismaStatement.d.ts +1 -0
- package/dist/generators/generateImportPrismaStatement.js +6 -0
- package/dist/generators/generateImportPrismaStatement.js.map +1 -1
- package/dist/generators/generateMcpApp.d.ts +10 -0
- package/dist/generators/generateMcpApp.js +113 -0
- package/dist/generators/generateMcpApp.js.map +1 -0
- package/dist/generators/generateModelMcp.d.ts +9 -0
- package/dist/generators/generateModelMcp.js +145 -0
- package/dist/generators/generateModelMcp.js.map +1 -0
- package/dist/generators/generateModelMetadata.js +29 -15
- package/dist/generators/generateModelMetadata.js.map +1 -1
- package/dist/generators/generateOperationCore.d.ts +3 -3
- package/dist/generators/generateOperationCore.js +51 -16
- package/dist/generators/generateOperationCore.js.map +1 -1
- package/dist/generators/generateRelationMeta.js +2 -2
- package/dist/generators/generateRelationMeta.js.map +1 -1
- package/dist/generators/generateRouteConfigType.d.ts +2 -2
- package/dist/generators/generateRouteConfigType.js +29 -11
- package/dist/generators/generateRouteConfigType.js.map +1 -1
- package/dist/generators/generateRouter.d.ts +7 -4
- package/dist/generators/generateRouter.js +164 -65
- package/dist/generators/generateRouter.js.map +1 -1
- package/dist/generators/generateRouterFastify.d.ts +7 -4
- package/dist/generators/generateRouterFastify.js +106 -86
- package/dist/generators/generateRouterFastify.js.map +1 -1
- package/dist/generators/generateRouterHono.d.ts +7 -4
- package/dist/generators/generateRouterHono.js +59 -414
- package/dist/generators/generateRouterHono.js.map +1 -1
- package/dist/generators/generateRouterPartsHono.d.ts +14 -0
- package/dist/generators/generateRouterPartsHono.js +180 -0
- package/dist/generators/generateRouterPartsHono.js.map +1 -0
- package/dist/generators/generateUnifiedDocs.d.ts +1 -1
- package/dist/generators/generateUnifiedDocs.js +15 -8
- package/dist/generators/generateUnifiedDocs.js.map +1 -1
- package/dist/generators/generateUnifiedHandler.d.ts +2 -2
- package/dist/generators/generateUnifiedHandler.js +42 -18
- package/dist/generators/generateUnifiedHandler.js.map +1 -1
- package/dist/guardOptions.js +12 -7
- package/dist/guardOptions.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +123 -4
- package/dist/index.js.map +1 -1
- package/dist/utils/copyFiles.d.ts +1 -1
- package/dist/utils/copyFiles.js +42 -7
- package/dist/utils/copyFiles.js.map +1 -1
- package/dist/utils/importExt.d.ts +1 -1
- package/dist/utils/mcpGate.d.ts +10 -0
- package/dist/utils/mcpGate.js +118 -0
- package/dist/utils/mcpGate.js.map +1 -0
- package/dist/utils/pathCasing.d.ts +4 -0
- package/dist/utils/pathCasing.js +40 -0
- package/dist/utils/pathCasing.js.map +1 -0
- package/dist/utils/resolveImportStyle.js +13 -5
- package/dist/utils/resolveImportStyle.js.map +1 -1
- package/dist/utils/writeFileSafely.js +6 -0
- package/dist/utils/writeFileSafely.js.map +1 -1
- package/package.json +22 -5
- package/src/bin.ts +1 -1
- package/src/client/encodeQueryParams.ts +39 -35
- package/src/constants.ts +1 -1
- package/src/copy/autoIncludePlanner.ts +93 -27
- package/src/copy/autoIncludePlannerGuarded.ts +171 -48
- package/src/copy/autoIncludeRuntimeGuarded.ts +25 -7
- package/src/copy/buildModelOpenApi.ts +229 -176
- package/src/copy/concurrency.ts +10 -8
- package/src/copy/docsRenderer.ts +14 -15
- package/src/copy/errorMapper.ts +68 -19
- package/src/copy/guardHelpers.ts +30 -9
- package/src/copy/materializedCount.ts +17 -5
- package/src/copy/mcpRuntime.ts +1017 -0
- package/src/copy/misc.ts +88 -68
- package/src/copy/ndjson.ts +72 -0
- package/src/copy/operationDefinitions.ts +177 -25
- package/src/copy/operationPipeline.ts +267 -0
- package/src/copy/operationRuntime.ts +124 -26
- package/src/copy/operationSchemas.ts +2773 -0
- package/src/copy/pagination.ts +35 -13
- package/src/copy/parseQueryParams.ts +47 -48
- package/src/copy/projectionDefaults.ts +6 -4
- package/src/copy/routeConfig.express.ts +16 -4
- package/src/copy/routeConfig.fastify.ts +16 -2
- package/src/copy/routeConfig.hono.ts +46 -14
- package/src/copy/routeConfig.ts +175 -40
- package/src/copy/routerParts.ts +524 -0
- package/src/copy/scalarTypes.ts +7 -2
- package/src/copy/sse.ts +72 -19
- package/src/generators/generateFastifyHandler.ts +77 -20
- package/src/generators/generateHonoHandler.ts +62 -14
- package/src/generators/generateHonoOpenApi.ts +93 -0
- package/src/generators/generateImportPrismaStatement.ts +93 -81
- package/src/generators/generateMcpApp.ts +133 -0
- package/src/generators/generateModelMcp.ts +208 -0
- package/src/generators/generateModelMetadata.ts +40 -22
- package/src/generators/generateOperationCore.ts +81 -33
- package/src/generators/generateRelationMeta.ts +23 -11
- package/src/generators/generateRouteConfigType.ts +39 -13
- package/src/generators/generateRouter.ts +171 -68
- package/src/generators/generateRouterFastify.ts +117 -89
- package/src/generators/generateRouterHono.ts +84 -464
- package/src/generators/generateRouterPartsHono.ts +220 -0
- package/src/generators/generateUnifiedDocs.ts +29 -8
- package/src/generators/generateUnifiedHandler.ts +47 -24
- package/src/generators/generateUnifiedScalarUI.ts +1 -1
- package/src/guardOptions.ts +105 -92
- package/src/index.ts +161 -6
- package/src/utils/copyFiles.ts +235 -168
- package/src/utils/importExt.ts +2 -2
- package/src/utils/mcpGate.ts +157 -0
- package/src/utils/pathCasing.ts +42 -0
- package/src/utils/resolveImportStyle.ts +37 -15
- package/src/utils/strings.ts +2 -2
- package/src/utils/writeFileSafely.ts +12 -2
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { DMMF } from '@prisma/generator-helper'
|
|
1
|
+
import type { DMMF } from '@prisma/generator-helper'
|
|
2
2
|
import { generateRouteConfigType } from './generateRouteConfigType'
|
|
3
|
-
import { ImportStyle } from '../utils/resolveImportStyle'
|
|
3
|
+
import type { ImportStyle } from '../utils/resolveImportStyle'
|
|
4
4
|
import { importExt } from '../utils/importExt'
|
|
5
|
-
import { WriteStrategy, FindManyPaginatedMode } from '../constants'
|
|
5
|
+
import type { WriteStrategy, FindManyPaginatedMode } from '../constants'
|
|
6
|
+
import { modelPathSegment, type PathCase } from '../utils/pathCasing'
|
|
6
7
|
import { OPERATION_METADATA } from '../copy/operationDefinitions'
|
|
7
8
|
|
|
8
9
|
function pathExpr(basePath: string, suffix: string): string {
|
|
@@ -55,10 +56,11 @@ function emitReadOp(
|
|
|
55
56
|
const opKind = opKindFor(meta.name)
|
|
56
57
|
|
|
57
58
|
const postReadBlock = meta.supportsPostRead
|
|
58
|
-
? ` if (
|
|
59
|
+
? ` if (resolvePostReadsEnabled(config.disablePostReads, opConfig.disablePostReads)) {
|
|
59
60
|
const postPath = ${meta.name === 'findMany' ? "basePath ? `${basePath}/read` : '/read'" : `path`}
|
|
60
61
|
router.post(
|
|
61
62
|
postPath,
|
|
63
|
+
...(opConfig.authorize ? [opConfig.authorize] : []),
|
|
62
64
|
parseBodyAsQuery,
|
|
63
65
|
setShape(opConfig, '${opKind}'),
|
|
64
66
|
...opConfig.operationBefore,
|
|
@@ -77,12 +79,14 @@ function emitReadOp(
|
|
|
77
79
|
const path = ${pathValue}
|
|
78
80
|
router.get(
|
|
79
81
|
path,
|
|
82
|
+
...(opConfig.authorize ? [opConfig.authorize] : []),
|
|
80
83
|
parseQuery,
|
|
81
84
|
setShape(opConfig, '${opKind}'),
|
|
82
85
|
...opConfig.operationBefore,
|
|
83
86
|
requireVariantKey(),
|
|
84
87
|
variantBeforeDispatcher(opConfig),
|
|
85
88
|
maybeProgressiveSSE(opConfig, core.${meta.coreName}, '${meta.name}'),
|
|
89
|
+
${meta.name === 'findMany' ? 'maybeNdjsonFindMany(core.findMany),' : ''}
|
|
86
90
|
${handlerName} as RequestHandler,
|
|
87
91
|
variantAfterDispatcher(opConfig),
|
|
88
92
|
...opConfig.operationAfter,
|
|
@@ -107,6 +111,7 @@ function emitWriteOp(
|
|
|
107
111
|
const path = ${pathValue}
|
|
108
112
|
router.${meta.method}(
|
|
109
113
|
path,
|
|
114
|
+
...(opConfig.authorize ? [opConfig.authorize] : []),
|
|
110
115
|
setShape(opConfig, '${opKind}'),
|
|
111
116
|
...opConfig.operationBefore,
|
|
112
117
|
requireVariantKey(),
|
|
@@ -123,22 +128,26 @@ export function generateRouterFunction({
|
|
|
123
128
|
model,
|
|
124
129
|
enums,
|
|
125
130
|
guardShapesImport,
|
|
131
|
+
clientImport,
|
|
126
132
|
importStyle,
|
|
127
133
|
writeStrategy,
|
|
128
134
|
findManyPaginatedMode,
|
|
129
135
|
dropGuard,
|
|
136
|
+
pathCase,
|
|
130
137
|
}: {
|
|
131
138
|
model: DMMF.Model
|
|
132
139
|
enums: DMMF.DatamodelEnum[]
|
|
133
140
|
guardShapesImport: string | null
|
|
141
|
+
clientImport?: string
|
|
134
142
|
importStyle: ImportStyle
|
|
135
143
|
writeStrategy: WriteStrategy
|
|
136
144
|
findManyPaginatedMode: FindManyPaginatedMode
|
|
137
145
|
dropGuard: boolean
|
|
146
|
+
pathCase: PathCase
|
|
138
147
|
}): string {
|
|
139
148
|
const ext = importExt(importStyle)
|
|
140
149
|
const modelName = model.name
|
|
141
|
-
const
|
|
150
|
+
const modelSegment = modelPathSegment(modelName, pathCase)
|
|
142
151
|
const delegateKey = modelName.charAt(0).toLowerCase() + modelName.slice(1)
|
|
143
152
|
const routerFunctionName = `${modelName}Router`
|
|
144
153
|
|
|
@@ -173,24 +182,34 @@ import type {
|
|
|
173
182
|
QueryBuilderConfig,
|
|
174
183
|
FindManyPaginatedMode,
|
|
175
184
|
PaginationConfig,
|
|
185
|
+
PrismaClientLike,
|
|
176
186
|
} from '../routeConfig.target${ext}'
|
|
177
187
|
import { parseQueryParams } from '../parseQueryParams${ext}'
|
|
178
|
-
import { sanitizeKeys, normalizePrefix, getEnv, isPlainObject } from '../misc${ext}'
|
|
188
|
+
import { sanitizeKeys, normalizePrefix, getEnv, isPlainObject, resolveDropGuardEnv } from '../misc${ext}'
|
|
179
189
|
import { buildModelOpenApi } from '../buildModelOpenApi${ext}'
|
|
180
190
|
import {
|
|
181
191
|
normalizeOperation,
|
|
182
|
-
resolveOperationVariantKey,
|
|
183
192
|
validateCountSourceWhere,
|
|
184
193
|
validateOperationConfig,
|
|
185
194
|
validateUpdateEachConfig,
|
|
195
|
+
resolvePostReadsEnabled,
|
|
196
|
+
warnIfUnguardedRoutes,
|
|
186
197
|
} from '../routeConfig${ext}'
|
|
198
|
+
import type { RuntimeOperationOverride } from '../operationRuntime${ext}'
|
|
187
199
|
import type { NormalizedOperationConfig } from '../routeConfig${ext}'
|
|
188
200
|
import type { OperationContext } from '../operationRuntime${ext}'
|
|
189
201
|
import { transformResult } from '../operationRuntime${ext}'
|
|
190
202
|
import { HttpError, mapError } from '../errorMapper${ext}'
|
|
191
|
-
import {
|
|
192
|
-
|
|
203
|
+
import {
|
|
204
|
+
routeOperation,
|
|
205
|
+
prepareGuardOperation,
|
|
206
|
+
settleStage,
|
|
207
|
+
classifyError,
|
|
208
|
+
memoizeContext,
|
|
209
|
+
type ArgsChannel,
|
|
210
|
+
} from '../operationPipeline${ext}'
|
|
193
211
|
import { mergePaginationConfig } from '../pagination${ext}'
|
|
212
|
+
import { acceptsNdjson, runNdjsonFindMany } from '../ndjson${ext}'
|
|
194
213
|
import {
|
|
195
214
|
acceptsEventStream,
|
|
196
215
|
runProgressiveEndpoint,
|
|
@@ -200,17 +219,21 @@ import {
|
|
|
200
219
|
} from '../sse${ext}'
|
|
201
220
|
import { relationModels } from '../relationModels${ext}'
|
|
202
221
|
import { runAutoIncludeProgressive } from '../autoIncludeRuntime${ext}'
|
|
203
|
-
import { applyDroppedGuard } from '../projectionDefaults${ext}'
|
|
204
222
|
import type { OpKind } from '../projectionDefaults${ext}'
|
|
205
223
|
import { MODEL_FIELDS, MODEL_ENUMS } from './${modelName}Metadata${ext}'
|
|
206
224
|
|
|
207
|
-
${generateRouteConfigType(modelName, 'RequestHandler', guardShapesImport, importStyle, 'express')}
|
|
225
|
+
${generateRouteConfigType(modelName, 'RequestHandler', guardShapesImport, importStyle, 'express', clientImport)}
|
|
208
226
|
const _env = getEnv()
|
|
209
227
|
|
|
210
228
|
const FIND_MANY_PAGINATED_MODE: FindManyPaginatedMode = '${findManyPaginatedMode}'
|
|
211
|
-
|
|
229
|
+
// Fixed at generation time. The environment bypass is resolved per router,
|
|
230
|
+
// because it requires the configuration's explicit \`allowE2EGuardBypass: true\`:
|
|
231
|
+
// false or omitted means the environment cannot drop guards for this target.
|
|
232
|
+
const DROP_GUARD = ${dropGuard}
|
|
212
233
|
|
|
213
234
|
type OperationConfigLike = {
|
|
235
|
+
authorize?: RequestHandler
|
|
236
|
+
override?: RuntimeOperationOverride
|
|
214
237
|
before?: RequestHandler[]
|
|
215
238
|
after?: RequestHandler[]
|
|
216
239
|
shape?: unknown
|
|
@@ -233,7 +256,7 @@ type NormalizedOp = NormalizedOperationConfig<RequestHandler, RequestHandler> &
|
|
|
233
256
|
}
|
|
234
257
|
|
|
235
258
|
type ExtendedRequest = Request & {
|
|
236
|
-
prisma?:
|
|
259
|
+
prisma?: PrismaClientLike
|
|
237
260
|
postgres?: unknown
|
|
238
261
|
sqlite?: unknown
|
|
239
262
|
}
|
|
@@ -243,11 +266,35 @@ type LocalsBag = {
|
|
|
243
266
|
routeConfig?: { pagination?: PaginationConfig }
|
|
244
267
|
guardShape?: Record<string, unknown>
|
|
245
268
|
guardCaller?: string
|
|
269
|
+
operationOverride?: RuntimeOperationOverride
|
|
270
|
+
getContext?: () => Promise<unknown>
|
|
246
271
|
guardVariantKey?: string
|
|
247
|
-
|
|
272
|
+
routeFailure?: { ok: false; failure: HttpError }
|
|
273
|
+
guardFailure?: { ok: false; failure: HttpError }
|
|
248
274
|
data?: unknown
|
|
249
275
|
}
|
|
250
276
|
|
|
277
|
+
function queryChannel(res: Response): ArgsChannel {
|
|
278
|
+
return {
|
|
279
|
+
read: () => readLocals(res).parsedQuery,
|
|
280
|
+
write: (next) => {
|
|
281
|
+
readLocals(res).parsedQuery = next
|
|
282
|
+
},
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function bodyChannel(req: Request): ArgsChannel {
|
|
287
|
+
return {
|
|
288
|
+
read: () =>
|
|
289
|
+
isPlainObject(req.body)
|
|
290
|
+
? (req.body as Record<string, unknown>)
|
|
291
|
+
: undefined,
|
|
292
|
+
write: (next) => {
|
|
293
|
+
req.body = next
|
|
294
|
+
},
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
251
298
|
function normalizeExpressOperation(
|
|
252
299
|
config: OperationConfigLike | undefined,
|
|
253
300
|
): NormalizedOp {
|
|
@@ -351,16 +398,19 @@ function variantAfterDispatcher(opConfig: NormalizedOp): RequestHandler {
|
|
|
351
398
|
|
|
352
399
|
function requireVariantKey(): RequestHandler {
|
|
353
400
|
return (_req, res, next) => {
|
|
354
|
-
const
|
|
355
|
-
|
|
356
|
-
|
|
401
|
+
const locals = readLocals(res)
|
|
402
|
+
try {
|
|
403
|
+
if (locals.routeFailure) settleStage(locals.routeFailure)
|
|
404
|
+
if (locals.guardFailure) settleStage(locals.guardFailure)
|
|
405
|
+
} catch (err) {
|
|
406
|
+
next(err)
|
|
357
407
|
return
|
|
358
408
|
}
|
|
359
|
-
next(
|
|
409
|
+
next()
|
|
360
410
|
}
|
|
361
411
|
}
|
|
362
412
|
|
|
363
|
-
export function ${routerFunctionName}<TCtx = unknown, TPrisma =
|
|
413
|
+
export function ${routerFunctionName}<TCtx = unknown, TPrisma extends PrismaClientLike = PrismaClientLike>(config: ${modelName}RouteConfig<TCtx, TPrisma> = {}) {
|
|
364
414
|
validateCountSourceWhere(config.pagination?.countSource, '${modelName} pagination')
|
|
365
415
|
validateCountSourceWhere(
|
|
366
416
|
(config.findManyPaginated && typeof config.findManyPaginated === 'object' ? config.findManyPaginated : undefined)?.pagination?.countSource,
|
|
@@ -370,6 +420,7 @@ export function ${routerFunctionName}<TCtx = unknown, TPrisma = any>(config: ${m
|
|
|
370
420
|
const router = express.Router()
|
|
371
421
|
|
|
372
422
|
const isEnabled = (value: unknown): boolean => value !== false && !!(config.enableAll || value)
|
|
423
|
+
warnIfUnguardedRoutes('${modelName}', ['findMany', 'findUnique', 'findUniqueOrThrow', 'findFirst', 'findFirstOrThrow', 'findManyPaginated', 'count', 'aggregate', 'groupBy', 'create', 'createMany', 'createManyAndReturn', 'update', 'updateMany', 'updateManyAndReturn', 'upsert', 'delete', 'deleteMany'], config, isEnabled)
|
|
373
424
|
|
|
374
425
|
const opFor = (key: string): NormalizedOp => {
|
|
375
426
|
const raw = (config as unknown as Record<string, unknown>)[key] as OperationConfigLike | undefined
|
|
@@ -378,13 +429,12 @@ export function ${routerFunctionName}<TCtx = unknown, TPrisma = any>(config: ${m
|
|
|
378
429
|
}
|
|
379
430
|
|
|
380
431
|
const customPrefix = normalizePrefix(config.customUrlPrefix || '')
|
|
381
|
-
const modelPrefix = config.addModelPrefix !== false ? '/${
|
|
432
|
+
const modelPrefix = config.addModelPrefix !== false ? '/${modelSegment}' : ''
|
|
382
433
|
const basePath = customPrefix + modelPrefix
|
|
383
434
|
|
|
384
435
|
const openApiDisabled = config.disableOpenApi === true
|
|
385
436
|
|| (config.disableOpenApi !== false && (_env.DISABLE_OPENAPI === 'true' || _env.NODE_ENV === 'production'))
|
|
386
437
|
|
|
387
|
-
const postReadsEnabled = !config.disablePostReads
|
|
388
438
|
|
|
389
439
|
let _openApiJsonCache: unknown = undefined
|
|
390
440
|
const getOpenApiJson = (): unknown => {
|
|
@@ -394,7 +444,7 @@ export function ${routerFunctionName}<TCtx = unknown, TPrisma = any>(config: ${m
|
|
|
394
444
|
MODEL_FIELDS as unknown as Parameters<typeof buildModelOpenApi>[1],
|
|
395
445
|
MODEL_ENUMS as unknown as Parameters<typeof buildModelOpenApi>[2],
|
|
396
446
|
config as unknown as Parameters<typeof buildModelOpenApi>[3],
|
|
397
|
-
{ format: 'json', writeStrategy: '${writeStrategy}' },
|
|
447
|
+
{ format: 'json', writeStrategy: '${writeStrategy}', pathSegment: '${modelSegment}' },
|
|
398
448
|
)
|
|
399
449
|
}
|
|
400
450
|
return _openApiJsonCache
|
|
@@ -407,7 +457,7 @@ export function ${routerFunctionName}<TCtx = unknown, TPrisma = any>(config: ${m
|
|
|
407
457
|
MODEL_FIELDS as unknown as Parameters<typeof buildModelOpenApi>[1],
|
|
408
458
|
MODEL_ENUMS as unknown as Parameters<typeof buildModelOpenApi>[2],
|
|
409
459
|
config as unknown as Parameters<typeof buildModelOpenApi>[3],
|
|
410
|
-
{ format: 'yaml', writeStrategy: '${writeStrategy}' },
|
|
460
|
+
{ format: 'yaml', writeStrategy: '${writeStrategy}', pathSegment: '${modelSegment}' },
|
|
411
461
|
) as string
|
|
412
462
|
}
|
|
413
463
|
return _openApiYamlCache
|
|
@@ -429,6 +479,8 @@ export function ${routerFunctionName}<TCtx = unknown, TPrisma = any>(config: ${m
|
|
|
429
479
|
const extReq = req as ExtendedRequest
|
|
430
480
|
const locals = readLocals(res)
|
|
431
481
|
return {
|
|
482
|
+
operationOverride: locals.operationOverride,
|
|
483
|
+
resolveOperationContext: locals.getContext,
|
|
432
484
|
prisma: extReq.prisma,
|
|
433
485
|
postgres: extReq.postgres,
|
|
434
486
|
sqlite: extReq.sqlite,
|
|
@@ -436,16 +488,12 @@ export function ${routerFunctionName}<TCtx = unknown, TPrisma = any>(config: ${m
|
|
|
436
488
|
body: req.body,
|
|
437
489
|
guardShape: locals.guardShape,
|
|
438
490
|
guardCaller: locals.guardCaller,
|
|
491
|
+
guardVariantKey: locals.guardVariantKey,
|
|
439
492
|
paginationConfig: locals.routeConfig?.pagination,
|
|
440
493
|
findManyPaginatedMode: FIND_MANY_PAGINATED_MODE,
|
|
441
494
|
}
|
|
442
495
|
}
|
|
443
496
|
|
|
444
|
-
const buildResolveContext = (req: Request): (() => unknown | Promise<unknown>) | undefined => {
|
|
445
|
-
if (typeof config.resolveContext !== 'function') return undefined
|
|
446
|
-
return () => (config.resolveContext as (r: Request) => unknown | Promise<unknown>)(req)
|
|
447
|
-
}
|
|
448
|
-
|
|
449
497
|
const parseQuery: RequestHandler = (req, res, next) => {
|
|
450
498
|
const rawQuery = req.query
|
|
451
499
|
if (rawQuery && Object.keys(rawQuery).length > 0) {
|
|
@@ -463,10 +511,24 @@ export function ${routerFunctionName}<TCtx = unknown, TPrisma = any>(config: ${m
|
|
|
463
511
|
next()
|
|
464
512
|
}
|
|
465
513
|
|
|
514
|
+
/**
|
|
515
|
+
* The environment bypass, and it is OPT-IN for this target.
|
|
516
|
+
*
|
|
517
|
+
* \`PGE_DROP_GUARD=true\` (deprecated alias \`E2E=true\`) downgrading enforcement
|
|
518
|
+
* in a deployed environment is a real hazard: on most platforms it is an
|
|
519
|
+
* ordinary config var, set on staging, copied forward, flagged as
|
|
520
|
+
* security-relevant nowhere. It is honoured only when this configuration
|
|
521
|
+
* states \`allowE2EGuardBypass: true\` — false or omitted means the environment
|
|
522
|
+
* cannot drop guards. The generation-time \`DROP_GUARD\` literal is unchanged.
|
|
523
|
+
*/
|
|
524
|
+
const dropGuard = DROP_GUARD || (config.allowE2EGuardBypass === true && resolveDropGuardEnv(_env))
|
|
525
|
+
|
|
466
526
|
const setShape = (opConfig: NormalizedOp, opKind: OpKind): RequestHandler => {
|
|
467
527
|
return async (req, res, next) => {
|
|
528
|
+
const locals = readLocals(res)
|
|
468
529
|
try {
|
|
469
|
-
|
|
530
|
+
locals.operationOverride = opConfig.override
|
|
531
|
+
locals.getContext = memoizeContext(() => config.resolveContext?.(req))
|
|
470
532
|
const merged = mergePaginationConfig(config.pagination, opConfig.pagination)
|
|
471
533
|
if (merged) {
|
|
472
534
|
locals.routeConfig = { pagination: merged }
|
|
@@ -477,51 +539,69 @@ export function ${routerFunctionName}<TCtx = unknown, TPrisma = any>(config: ${m
|
|
|
477
539
|
const caller = config.guard?.resolveVariant?.(req) ?? headerValue ?? undefined
|
|
478
540
|
if (typeof caller === 'string') locals.guardCaller = caller
|
|
479
541
|
|
|
480
|
-
const
|
|
481
|
-
if (!
|
|
482
|
-
locals.
|
|
542
|
+
const routed = routeOperation({ guardRouting: opConfig.guardRouting, caller })
|
|
543
|
+
if (!routed.ok) {
|
|
544
|
+
locals.routeFailure = routed
|
|
483
545
|
next()
|
|
484
546
|
return
|
|
485
547
|
}
|
|
486
548
|
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
opKind,
|
|
502
|
-
{
|
|
503
|
-
readQuery: locals.parsedQuery,
|
|
504
|
-
writeBody: isPlainObject(req.body)
|
|
505
|
-
? (req.body as Record<string, unknown>)
|
|
506
|
-
: undefined,
|
|
507
|
-
},
|
|
508
|
-
() => {
|
|
509
|
-
if (!locals.parsedQuery) locals.parsedQuery = {}
|
|
510
|
-
return locals.parsedQuery
|
|
511
|
-
},
|
|
512
|
-
() => {
|
|
513
|
-
if (!isPlainObject(req.body)) {
|
|
514
|
-
req.body = {}
|
|
515
|
-
}
|
|
516
|
-
return req.body as Record<string, unknown>
|
|
517
|
-
},
|
|
518
|
-
)
|
|
519
|
-
}
|
|
549
|
+
if (routed.variantKey !== undefined) locals.guardVariantKey = routed.variantKey
|
|
550
|
+
|
|
551
|
+
const guard = await prepareGuardOperation(routed, {
|
|
552
|
+
guardShape: opConfig.guardShape,
|
|
553
|
+
opKind,
|
|
554
|
+
policy: { dropGuard, validateResolvedShapes: false },
|
|
555
|
+
getContext: typeof config.resolveContext === 'function' ? locals.getContext : undefined,
|
|
556
|
+
args: queryChannel(res),
|
|
557
|
+
writeArgs: bodyChannel(req),
|
|
558
|
+
})
|
|
559
|
+
if (!guard.ok) {
|
|
560
|
+
locals.guardFailure = guard
|
|
561
|
+
next()
|
|
562
|
+
return
|
|
520
563
|
}
|
|
521
564
|
|
|
565
|
+
locals.guardShape = guard.guardShape
|
|
566
|
+
|
|
522
567
|
next()
|
|
523
568
|
} catch (err) {
|
|
524
|
-
next(
|
|
569
|
+
next(classifyError(err))
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
const maybeNdjsonFindMany = (
|
|
575
|
+
coreFn: (ctx: OperationContext) => Promise<unknown>,
|
|
576
|
+
): RequestHandler => {
|
|
577
|
+
return async (req, res, next) => {
|
|
578
|
+
if (res.headersSent || res.writableEnded) return next()
|
|
579
|
+
if (req.method !== 'GET') return next()
|
|
580
|
+
if (!acceptsNdjson(req.headers.accept)) return next()
|
|
581
|
+
|
|
582
|
+
const locals = readLocals(res)
|
|
583
|
+
const rawArgs = (locals.parsedQuery ?? {}) as Record<string, unknown>
|
|
584
|
+
const args = { ...rawArgs }
|
|
585
|
+
const paginationCfg = mergePaginationConfig(config.pagination, undefined)
|
|
586
|
+
const requestedTake = typeof rawArgs.take === 'number' ? rawArgs.take : undefined
|
|
587
|
+
const initialSkip = typeof rawArgs.skip === 'number' ? rawArgs.skip : 0
|
|
588
|
+
|
|
589
|
+
try {
|
|
590
|
+
await runNdjsonFindMany({
|
|
591
|
+
res,
|
|
592
|
+
isClosed: () => req.destroyed,
|
|
593
|
+
fetchPage: (skip, pageTake) =>
|
|
594
|
+
coreFn({
|
|
595
|
+
...buildContext(req, res),
|
|
596
|
+
parsedQuery: { ...args, skip, take: pageTake },
|
|
597
|
+
}),
|
|
598
|
+
initialSkip,
|
|
599
|
+
requestedTake,
|
|
600
|
+
defaultLimit: paginationCfg?.defaultLimit,
|
|
601
|
+
maxLimit: paginationCfg?.maxLimit ?? 1000,
|
|
602
|
+
})
|
|
603
|
+
} catch (err) {
|
|
604
|
+
next(err)
|
|
525
605
|
}
|
|
526
606
|
}
|
|
527
607
|
}
|
|
@@ -552,6 +632,23 @@ export function ${routerFunctionName}<TCtx = unknown, TPrisma = any>(config: ${m
|
|
|
552
632
|
}
|
|
553
633
|
|
|
554
634
|
if (progressiveConfig.mode === 'autoInclude') {
|
|
635
|
+
if (progressiveConfig.experimental !== true) {
|
|
636
|
+
if (_env.NODE_ENV !== 'production') {
|
|
637
|
+
console.warn(
|
|
638
|
+
'[prisma-generator-express] auto-include progressive is experimental; set \`experimental: true\` on the progressive config to enable it. Falling back.'
|
|
639
|
+
)
|
|
640
|
+
}
|
|
641
|
+
if (progressiveConfig.fallback === 'error') {
|
|
642
|
+
emitTerminalSSEError(res, 'auto-include requires experimental: true')
|
|
643
|
+
return
|
|
644
|
+
}
|
|
645
|
+
await runSingleResultSSE({
|
|
646
|
+
req,
|
|
647
|
+
res,
|
|
648
|
+
coreQueryFn: () => coreFn(buildContext(req, res)),
|
|
649
|
+
})
|
|
650
|
+
return
|
|
651
|
+
}
|
|
555
652
|
const isAutoIncludeReadable =
|
|
556
653
|
baseOp === 'findUnique' || baseOp === 'findUniqueOrThrow' ||
|
|
557
654
|
baseOp === 'findFirst' || baseOp === 'findFirstOrThrow' ||
|
|
@@ -619,11 +716,16 @@ export function ${routerFunctionName}<TCtx = unknown, TPrisma = any>(config: ${m
|
|
|
619
716
|
}
|
|
620
717
|
|
|
621
718
|
const ctx = await config.resolveContext(req)
|
|
719
|
+
const prismaClient = (req as ExtendedRequest).prisma
|
|
720
|
+
if (!prismaClient) {
|
|
721
|
+
emitTerminalSSEError(res, 'Progressive endpoint requires req.prisma to be set')
|
|
722
|
+
return
|
|
723
|
+
}
|
|
622
724
|
await runProgressiveEndpoint({
|
|
623
725
|
req,
|
|
624
726
|
res,
|
|
625
727
|
ctx,
|
|
626
|
-
prisma:
|
|
728
|
+
prisma: prismaClient,
|
|
627
729
|
variant: variant as string,
|
|
628
730
|
stages: progressiveConfig.stages,
|
|
629
731
|
stageRegistry,
|
|
@@ -679,6 +781,7 @@ ${writeOpBlocks}
|
|
|
679
781
|
const path = basePath ? \`\${basePath}/each\` : '/each'
|
|
680
782
|
router.post(
|
|
681
783
|
path,
|
|
784
|
+
...(opConfig.authorize ? [opConfig.authorize] : []),
|
|
682
785
|
setShape(opConfig, 'noop'),
|
|
683
786
|
...opConfig.operationBefore,
|
|
684
787
|
async (req: Request, res: Response, next: NextFunction) => {
|
|
@@ -707,4 +810,4 @@ ${writeOpBlocks}
|
|
|
707
810
|
return router
|
|
708
811
|
}
|
|
709
812
|
`
|
|
710
|
-
}
|
|
813
|
+
}
|