prisma-generator-express 1.67.0 → 1.69.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 +223 -28
- package/dist/copy/operationDefinitions.d.ts +4 -1
- package/dist/copy/operationDefinitions.js +64 -3
- package/dist/copy/operationDefinitions.js.map +1 -1
- package/dist/generators/generateFastifyHandler.js +55 -9
- package/dist/generators/generateFastifyHandler.js.map +1 -1
- package/dist/generators/generateHonoHandler.js +44 -4
- 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/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 +9 -5
- package/dist/generators/generateModelMetadata.js.map +1 -1
- package/dist/generators/generateRouter.js +62 -56
- package/dist/generators/generateRouter.js.map +1 -1
- package/dist/generators/generateRouterFastify.js +56 -64
- package/dist/generators/generateRouterFastify.js.map +1 -1
- package/dist/generators/generateRouterHono.js +43 -411
- 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/generateUnifiedHandler.js +36 -20
- package/dist/generators/generateUnifiedHandler.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +101 -3
- package/dist/index.js.map +1 -1
- package/dist/utils/copyFiles.d.ts +1 -1
- package/dist/utils/copyFiles.js +20 -1
- package/dist/utils/copyFiles.js.map +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/writeFileSafely.js +6 -0
- package/dist/utils/writeFileSafely.js.map +1 -1
- package/package.json +18 -3
- package/src/copy/buildModelOpenApi.ts +76 -126
- package/src/copy/docsRenderer.ts +14 -15
- package/src/copy/mcpRuntime.ts +1017 -0
- package/src/copy/operationDefinitions.ts +78 -4
- package/src/copy/operationPipeline.ts +267 -0
- package/src/copy/operationRuntime.ts +24 -14
- package/src/copy/operationSchemas.ts +2773 -0
- package/src/copy/pagination.ts +10 -2
- package/src/copy/routeConfig.hono.ts +34 -5
- package/src/copy/routeConfig.ts +1 -1
- package/src/copy/routerParts.ts +524 -0
- package/src/generators/generateFastifyHandler.ts +55 -9
- package/src/generators/generateHonoHandler.ts +44 -4
- package/src/generators/generateHonoOpenApi.ts +93 -0
- package/src/generators/generateMcpApp.ts +133 -0
- package/src/generators/generateModelMcp.ts +208 -0
- package/src/generators/generateModelMetadata.ts +16 -10
- package/src/generators/generateRouter.ts +62 -56
- package/src/generators/generateRouterFastify.ts +56 -64
- package/src/generators/generateRouterHono.ts +49 -459
- package/src/generators/generateRouterPartsHono.ts +220 -0
- package/src/generators/generateUnifiedHandler.ts +36 -21
- package/src/index.ts +116 -3
- package/src/utils/copyFiles.ts +36 -0
- package/src/utils/mcpGate.ts +157 -0
- package/src/utils/writeFileSafely.ts +6 -0
|
@@ -52,14 +52,14 @@ function emitReadOp(meta, modelName) {
|
|
|
52
52
|
? meta.name === 'findMany'
|
|
53
53
|
? ` if (resolvePostReadsEnabled(config.disablePostReads, opConfig.disablePostReads)) {
|
|
54
54
|
const postPath = basePath ? \`\${basePath}/read\` : '/read'
|
|
55
|
-
app.post(postPath,
|
|
55
|
+
app.post(postPath, createReadRoute<TCtx, TPrisma, TEnv>({ config, opConfig, opKind: '${opKind}', handler: ${handlerName}, parse: 'body', dropGuard, settleBeforeHooks: SETTLE_BEFORE_HOOKS }))
|
|
56
56
|
}`
|
|
57
|
-
: ` if (resolvePostReadsEnabled(config.disablePostReads, opConfig.disablePostReads)) app.post(path,
|
|
57
|
+
: ` if (resolvePostReadsEnabled(config.disablePostReads, opConfig.disablePostReads)) app.post(path, createReadRoute<TCtx, TPrisma, TEnv>({ config, opConfig, opKind: '${opKind}', handler: ${handlerName}, parse: 'body', dropGuard, settleBeforeHooks: SETTLE_BEFORE_HOOKS }))`
|
|
58
58
|
: '';
|
|
59
59
|
return ` if (isEnabled(config.${meta.configKey})) {
|
|
60
60
|
const opConfig = opFor('${meta.configKey}')
|
|
61
61
|
const path = ${pathValue}
|
|
62
|
-
app.get(path,
|
|
62
|
+
app.get(path, createReadRoute<TCtx, TPrisma, TEnv>({ config, opConfig, opKind: '${opKind}', handler: ${handlerName}, parse: 'query', dropGuard, settleBeforeHooks: SETTLE_BEFORE_HOOKS }))
|
|
63
63
|
${postReadLine}
|
|
64
64
|
}`;
|
|
65
65
|
}
|
|
@@ -71,7 +71,7 @@ function emitWriteOp(meta, modelName) {
|
|
|
71
71
|
return ` if (isEnabled(config.${meta.configKey})) {
|
|
72
72
|
const opConfig = opFor('${meta.configKey}')
|
|
73
73
|
const path = ${pathValue}
|
|
74
|
-
app.${meta.method}(path,
|
|
74
|
+
app.${meta.method}(path, createWriteRoute<TCtx, TPrisma, TEnv>({ config, opConfig, opKind: '${opKind}', handler: ${handlerName}, dropGuard, settleBeforeHooks: SETTLE_BEFORE_HOOKS }))
|
|
75
75
|
}`;
|
|
76
76
|
}
|
|
77
77
|
function generateHonoRouterFunction({ model, enums, guardShapesImport, clientImport, importStyle, writeStrategy, dropGuard, pathCase, }) {
|
|
@@ -87,44 +87,38 @@ function generateHonoRouterFunction({ model, enums, guardShapesImport, clientImp
|
|
|
87
87
|
.map((m) => emitWriteOp(m, modelName))
|
|
88
88
|
.join('\n\n');
|
|
89
89
|
return `import { Hono } from 'hono'
|
|
90
|
-
import type { Context } from 'hono'
|
|
91
|
-
import type { ContentfulStatusCode } from 'hono/utils/http-status'
|
|
92
|
-
import { HTTPException } from 'hono/http-exception'
|
|
93
90
|
import {
|
|
94
91
|
${handlerImports}
|
|
95
92
|
} from './${modelName}Handlers${ext}'
|
|
96
93
|
import type {
|
|
97
94
|
RouteConfig,
|
|
98
|
-
HonoBeforeHook,
|
|
99
|
-
HonoAfterHook,
|
|
100
95
|
HonoEnvBase,
|
|
101
|
-
HonoInternalVariables,
|
|
102
96
|
GeneratedHonoEnv,
|
|
103
|
-
PaginationConfig,
|
|
104
97
|
PrismaClientLike,
|
|
105
98
|
} from '../routeConfig.target${ext}'
|
|
106
|
-
import {
|
|
107
|
-
import { normalizePrefix, getEnv, sanitizeKeys, isPlainObject, resolveDropGuardEnv } from '../misc${ext}'
|
|
99
|
+
import { normalizePrefix, getEnv, resolveDropGuardEnv } from '../misc${ext}'
|
|
108
100
|
import { buildModelOpenApi } from '../buildModelOpenApi${ext}'
|
|
109
101
|
import {
|
|
110
|
-
normalizeOperation,
|
|
111
|
-
resolveOperationVariantKey,
|
|
112
102
|
resolveGuardPolicy,
|
|
113
|
-
|
|
103
|
+
resolvePostReadsEnabled,
|
|
114
104
|
validateCountSourceWhere,
|
|
115
|
-
validateOperationConfig,
|
|
116
105
|
validateUpdateEachConfig,
|
|
117
|
-
resolvePostReadsEnabled,
|
|
118
106
|
warnIfUnguardedRoutes,
|
|
119
107
|
} from '../routeConfig${ext}'
|
|
120
|
-
import
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
108
|
+
import {
|
|
109
|
+
createReadRoute,
|
|
110
|
+
createWriteRoute,
|
|
111
|
+
createUpdateEachRoute,
|
|
112
|
+
opConfigFor,
|
|
113
|
+
normalizeHonoOperation,
|
|
114
|
+
registerOpenApiRoutes,
|
|
115
|
+
sendError,
|
|
116
|
+
type HandlerContext,
|
|
117
|
+
type HonoOpConfig,
|
|
118
|
+
} from '../routerParts${ext}'
|
|
119
|
+
// The legacy router factory preserves pre-parts behavior: materialized-view
|
|
120
|
+
// counting is registered on import. The per-op parts boundary keeps this
|
|
121
|
+
// module out of opt-in small bundles.
|
|
128
122
|
import { MODEL_FIELDS, MODEL_ENUMS } from './${modelName}Metadata${ext}'
|
|
129
123
|
|
|
130
124
|
${(0, generateRouteConfigType_1.generateRouteConfigType)(modelName, 'HonoBeforeHook', guardShapesImport, importStyle, 'hono', clientImport)}
|
|
@@ -135,247 +129,6 @@ const _env = getEnv()
|
|
|
135
129
|
// which is upstream behaviour. See generateRouterHono.ts.
|
|
136
130
|
const DROP_GUARD = ${dropGuard}
|
|
137
131
|
|
|
138
|
-
/**
|
|
139
|
-
* DELIBERATELY NOT RECURSIVE.
|
|
140
|
-
*
|
|
141
|
-
* A self-referential JSON type sent through \`c.json()\` makes Hono's response
|
|
142
|
-
* inference instantiate without a fixed point, and TypeScript answers
|
|
143
|
-
* "type instantiation is excessively deep" — in the emitted file, for every
|
|
144
|
-
* consumer. One level is all this needs: the value is serialised, not walked.
|
|
145
|
-
*/
|
|
146
|
-
type JsonLike = string | number | boolean | null | unknown[] | Record<string, unknown>
|
|
147
|
-
|
|
148
|
-
type OperationConfigLike<TEnv extends HonoEnvBase> = {
|
|
149
|
-
authorize?: HonoBeforeHook<TEnv>
|
|
150
|
-
override?: RuntimeOperationOverride
|
|
151
|
-
before?: HonoBeforeHook<TEnv>[]
|
|
152
|
-
after?: HonoAfterHook<TEnv>[]
|
|
153
|
-
shape?: unknown
|
|
154
|
-
variants?: Record<
|
|
155
|
-
string,
|
|
156
|
-
{
|
|
157
|
-
shape?: unknown
|
|
158
|
-
before?: HonoBeforeHook<TEnv>[]
|
|
159
|
-
after?: HonoAfterHook<TEnv>[]
|
|
160
|
-
}
|
|
161
|
-
>
|
|
162
|
-
pagination?: Partial<PaginationConfig>
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
type NormalizedOp<TEnv extends HonoEnvBase> = NormalizedOperationConfig<
|
|
166
|
-
HonoBeforeHook<TEnv>,
|
|
167
|
-
HonoAfterHook<TEnv>
|
|
168
|
-
>
|
|
169
|
-
|
|
170
|
-
function normalizeHonoOperation<TEnv extends HonoEnvBase>(
|
|
171
|
-
config: OperationConfigLike<TEnv> | undefined,
|
|
172
|
-
): NormalizedOp<TEnv> {
|
|
173
|
-
return normalizeOperation<HonoBeforeHook<TEnv>, HonoAfterHook<TEnv>>(config)
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
type HandlerContext = Context<{ Variables: HonoInternalVariables }>
|
|
177
|
-
|
|
178
|
-
async function parseQueryMiddleware(c: HandlerContext): Promise<void> {
|
|
179
|
-
const raw = c.req.query() as Record<string, unknown>
|
|
180
|
-
if (raw && Object.keys(raw).length > 0) {
|
|
181
|
-
c.set('parsedQuery', parseQueryParams(raw) as Record<string, unknown>)
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
async function parseBodyAsQueryMiddleware(c: HandlerContext): Promise<void> {
|
|
186
|
-
let body: unknown
|
|
187
|
-
try {
|
|
188
|
-
body = await c.req.json()
|
|
189
|
-
} catch {
|
|
190
|
-
throw new HTTPException(400, { message: 'Request body must be a JSON object' })
|
|
191
|
-
}
|
|
192
|
-
if (!body || typeof body !== 'object' || Array.isArray(body)) {
|
|
193
|
-
throw new HTTPException(400, { message: 'Request body must be a JSON object' })
|
|
194
|
-
}
|
|
195
|
-
c.set('parsedQuery', sanitizeKeys(body as Record<string, unknown>))
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
async function parseUpdateEachBodyMiddleware(c: HandlerContext): Promise<void> {
|
|
199
|
-
let body: unknown
|
|
200
|
-
try {
|
|
201
|
-
body = await c.req.json()
|
|
202
|
-
} catch {
|
|
203
|
-
throw new HTTPException(400, { message: 'updateEach body must be an array of { where, data } items' })
|
|
204
|
-
}
|
|
205
|
-
if (!Array.isArray(body)) {
|
|
206
|
-
throw new HTTPException(400, { message: 'updateEach body must be an array of { where, data } items' })
|
|
207
|
-
}
|
|
208
|
-
c.set('body', body)
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
async function parseWriteBodyMiddleware(c: HandlerContext): Promise<void> {
|
|
212
|
-
let body: unknown
|
|
213
|
-
try {
|
|
214
|
-
body = await c.req.json()
|
|
215
|
-
} catch {
|
|
216
|
-
throw new HTTPException(400, { message: 'Request body must be a JSON object' })
|
|
217
|
-
}
|
|
218
|
-
if (!body || typeof body !== 'object' || Array.isArray(body)) {
|
|
219
|
-
throw new HTTPException(400, { message: 'Request body must be a JSON object' })
|
|
220
|
-
}
|
|
221
|
-
c.set('body', body)
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
function makeShapeMiddleware<TCtx, TPrisma extends PrismaClientLike, TEnv extends HonoEnvBase>(
|
|
225
|
-
config: ${modelName}RouteConfig<TCtx, TPrisma, TEnv>,
|
|
226
|
-
opConfig: NormalizedOp<TEnv>,
|
|
227
|
-
opKind: OpKind,
|
|
228
|
-
) {
|
|
229
|
-
const policy = resolveGuardPolicy(config)
|
|
230
|
-
|
|
231
|
-
/**
|
|
232
|
-
* The environment bypass, honoured unless the consumer turned it off.
|
|
233
|
-
*
|
|
234
|
-
* \`PGE_DROP_GUARD=true\` downgrading enforcement in a deployed environment is
|
|
235
|
-
* a real hazard, and it is also upstream behaviour (under its deprecated
|
|
236
|
-
* \`E2E=true\` spelling) — so it is a control, not a decision made here.
|
|
237
|
-
*/
|
|
238
|
-
const dropGuard = DROP_GUARD || (policy.allowE2EGuardBypass && resolveDropGuardEnv(_env))
|
|
239
|
-
|
|
240
|
-
return async (c: Context<GeneratedHonoEnv<TEnv>>): Promise<void> => {
|
|
241
|
-
/**
|
|
242
|
-
* INTERNAL REQUEST STATE IS SET THROUGH A CONCRETE CONTEXT.
|
|
243
|
-
*
|
|
244
|
-
* \`GeneratedHonoEnv<TEnv>['Variables']\` is \`HonoInternalVariables & TEnv['Variables']\`,
|
|
245
|
-
* and inside this generic function TypeScript cannot know that the consumer's
|
|
246
|
-
* half does not also declare \`routeConfig\` — so setting that key straight
|
|
247
|
-
* onto \`c\` is unassignable to the intersection and the emitted router does not
|
|
248
|
-
* typecheck under \`strict\` for ANY consumer. These keys belong to this
|
|
249
|
-
* router, so they are written through the internal shape they were declared
|
|
250
|
-
* in.
|
|
251
|
-
*/
|
|
252
|
-
const vars = c as unknown as HandlerContext
|
|
253
|
-
vars.set('operationOverride', opConfig.override)
|
|
254
|
-
let context: Promise<unknown> | undefined
|
|
255
|
-
vars.set('resolveOperationContext', () => context ??= Promise.resolve(config.resolveContext?.(c)))
|
|
256
|
-
|
|
257
|
-
const merged = mergePaginationConfig(config.pagination, opConfig.pagination)
|
|
258
|
-
if (merged) vars.set('routeConfig', { pagination: merged })
|
|
259
|
-
|
|
260
|
-
const headerName = config.guard?.variantHeader || 'x-api-variant'
|
|
261
|
-
const headerValue = c.req.header(headerName)
|
|
262
|
-
const caller = config.guard?.resolveVariant?.(c) ?? headerValue ?? undefined
|
|
263
|
-
if (typeof caller === 'string') vars.set('guardCaller', caller)
|
|
264
|
-
|
|
265
|
-
const resolution = resolveOperationVariantKey(opConfig.guardRouting, caller)
|
|
266
|
-
if (!resolution.ok) {
|
|
267
|
-
vars.set('guardVariantFailure', resolution)
|
|
268
|
-
return
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
const resolvedKey =
|
|
272
|
-
opConfig.guardRouting.kind === 'named'
|
|
273
|
-
? resolution.key
|
|
274
|
-
: undefined
|
|
275
|
-
if (resolvedKey !== undefined) vars.set('guardVariantKey', resolvedKey)
|
|
276
|
-
|
|
277
|
-
if (opConfig.guardShape) {
|
|
278
|
-
const resolveCtx = typeof config.resolveContext === 'function'
|
|
279
|
-
? vars.get('resolveOperationContext')
|
|
280
|
-
: undefined
|
|
281
|
-
|
|
282
|
-
/**
|
|
283
|
-
* With \`validateResolvedShapes\`, resolved ONCE here and the validated value
|
|
284
|
-
* is what travels onward — see resolveGuardShapeOnce. Passing the function
|
|
285
|
-
* on would let it return a different shape when it is enforced than when it
|
|
286
|
-
* was checked.
|
|
287
|
-
*
|
|
288
|
-
* Without it the raw shape is passed through untouched, which is upstream
|
|
289
|
-
* behaviour: it is resolved at the point of use, and a function returning
|
|
290
|
-
* something unusable is not this router's business to refuse.
|
|
291
|
-
*/
|
|
292
|
-
let effectiveShape: Record<string, unknown> | undefined = opConfig.guardShape
|
|
293
|
-
if (policy.validateResolvedShapes) {
|
|
294
|
-
const resolution = await resolveGuardShapeOnce(opConfig.guardShape, resolvedKey, resolveCtx)
|
|
295
|
-
if (!resolution.ok) {
|
|
296
|
-
vars.set('guardShapeFailure', resolution.problem)
|
|
297
|
-
return
|
|
298
|
-
}
|
|
299
|
-
// \`ok\` means the resolved value is a shape object; the fallback keeps the
|
|
300
|
-
// declared shape rather than storing something the context cannot hold.
|
|
301
|
-
effectiveShape = isPlainObject(resolution.shape) ? resolution.shape : opConfig.guardShape
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
if (!dropGuard) {
|
|
305
|
-
vars.set('guardShape', effectiveShape)
|
|
306
|
-
} else {
|
|
307
|
-
await applyDroppedGuard(
|
|
308
|
-
effectiveShape,
|
|
309
|
-
resolvedKey,
|
|
310
|
-
resolveCtx,
|
|
311
|
-
opKind,
|
|
312
|
-
{
|
|
313
|
-
readQuery: vars.get('parsedQuery'),
|
|
314
|
-
writeBody: isPlainObject(vars.get('body'))
|
|
315
|
-
? (vars.get('body') as Record<string, unknown>)
|
|
316
|
-
: undefined,
|
|
317
|
-
},
|
|
318
|
-
() => {
|
|
319
|
-
let target = vars.get('parsedQuery')
|
|
320
|
-
if (!target) {
|
|
321
|
-
target = {}
|
|
322
|
-
vars.set('parsedQuery', target)
|
|
323
|
-
}
|
|
324
|
-
return target
|
|
325
|
-
},
|
|
326
|
-
() => {
|
|
327
|
-
let target = vars.get('body')
|
|
328
|
-
if (!isPlainObject(target)) {
|
|
329
|
-
target = {}
|
|
330
|
-
vars.set('body', target)
|
|
331
|
-
}
|
|
332
|
-
return target as Record<string, unknown>
|
|
333
|
-
},
|
|
334
|
-
)
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
async function runBeforeHooks<TEnv extends HonoEnvBase>(
|
|
341
|
-
hooks: readonly HonoBeforeHook<TEnv>[],
|
|
342
|
-
c: Context<GeneratedHonoEnv<TEnv>>,
|
|
343
|
-
): Promise<Response | undefined> {
|
|
344
|
-
for (const hook of hooks) {
|
|
345
|
-
const result = await hook(c)
|
|
346
|
-
if (result instanceof Response) return result
|
|
347
|
-
}
|
|
348
|
-
return undefined
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
async function runAfterHooks<TEnv extends HonoEnvBase>(
|
|
352
|
-
hooks: readonly HonoAfterHook<TEnv>[],
|
|
353
|
-
c: Context<GeneratedHonoEnv<TEnv>>,
|
|
354
|
-
): Promise<Response | undefined> {
|
|
355
|
-
for (const hook of hooks) {
|
|
356
|
-
const result = await hook(c)
|
|
357
|
-
if (result instanceof Response) return result
|
|
358
|
-
}
|
|
359
|
-
return undefined
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
function sendResult(c: HandlerContext): Response {
|
|
363
|
-
const data = c.get('resultData')
|
|
364
|
-
const status = (c.get('resultStatus') as number | undefined) ?? 200
|
|
365
|
-
if (data === undefined) {
|
|
366
|
-
throw new HTTPException(500, { message: 'No data set by handler' })
|
|
367
|
-
}
|
|
368
|
-
return c.json(transformResult(data) as JsonLike, status as ContentfulStatusCode)
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
function sendError(c: HandlerContext, error: unknown): Response {
|
|
372
|
-
if (error instanceof HTTPException) {
|
|
373
|
-
return c.json({ message: error.message }, error.status as ContentfulStatusCode)
|
|
374
|
-
}
|
|
375
|
-
const httpError = mapError(error)
|
|
376
|
-
return c.json({ message: httpError.message }, httpError.status as ContentfulStatusCode)
|
|
377
|
-
}
|
|
378
|
-
|
|
379
132
|
export function ${routerFunctionName}<TCtx = unknown, TPrisma extends PrismaClientLike = PrismaClientLike, TEnv extends HonoEnvBase = HonoEnvBase>(config: ${modelName}RouteConfig<TCtx, TPrisma, TEnv> = {}): Hono<GeneratedHonoEnv<TEnv>> {
|
|
380
133
|
validateCountSourceWhere(config.pagination?.countSource, '${modelName} pagination')
|
|
381
134
|
validateCountSourceWhere(
|
|
@@ -389,16 +142,33 @@ export function ${routerFunctionName}<TCtx = unknown, TPrisma extends PrismaClie
|
|
|
389
142
|
warnIfUnguardedRoutes('${modelName}', ['findMany', 'findUnique', 'findUniqueOrThrow', 'findFirst', 'findFirstOrThrow', 'findManyPaginated', 'count', 'aggregate', 'groupBy', 'create', 'createMany', 'createManyAndReturn', 'update', 'updateMany', 'updateManyAndReturn', 'upsert', 'delete', 'deleteMany'], config, isEnabled)
|
|
390
143
|
|
|
391
144
|
const customPrefix = normalizePrefix(config.customUrlPrefix || '')
|
|
392
|
-
|
|
145
|
+
const modelPrefix = config.addModelPrefix !== false ? '/${modelSegment}' : ''
|
|
393
146
|
const basePath = customPrefix + modelPrefix
|
|
394
147
|
|
|
148
|
+
const POLICY = resolveGuardPolicy(config)
|
|
149
|
+
const SETTLE_BEFORE_HOOKS = POLICY.guardResolutionOrder === 'before-hooks'
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* The environment bypass, honoured unless the consumer turned it off.
|
|
153
|
+
* \`PGE_DROP_GUARD=true\` downgrading enforcement in a deployed environment is
|
|
154
|
+
* a real hazard, and it is also upstream behaviour (under its deprecated
|
|
155
|
+
* \`E2E=true\` spelling) — so it is a control, not a decision made here.
|
|
156
|
+
*/
|
|
157
|
+
const dropGuard = DROP_GUARD || (POLICY.allowE2EGuardBypass && resolveDropGuardEnv(_env))
|
|
158
|
+
|
|
159
|
+
const opFor = <K extends keyof ${modelName}RouteConfig<TCtx, TPrisma, TEnv>>(
|
|
160
|
+
key: K,
|
|
161
|
+
): HonoOpConfig<TEnv> => {
|
|
162
|
+
const raw = config[key] as unknown as Parameters<typeof normalizeHonoOperation<TEnv>>[0]
|
|
163
|
+
return opConfigFor<TEnv>(raw, '${modelName}.' + String(key), POLICY)
|
|
164
|
+
}
|
|
165
|
+
|
|
395
166
|
const openApiDisabled = config.disableOpenApi === true
|
|
396
167
|
|| (config.disableOpenApi !== false && (
|
|
397
168
|
_env.NODE_ENV === 'production'
|
|
398
169
|
|| _env.DISABLE_OPENAPI === 'true'
|
|
399
170
|
))
|
|
400
171
|
|
|
401
|
-
|
|
402
172
|
let _openApiJsonCache: unknown = undefined
|
|
403
173
|
const getOpenApiJson = (): unknown => {
|
|
404
174
|
if (_openApiJsonCache === undefined) {
|
|
@@ -438,129 +208,7 @@ export function ${routerFunctionName}<TCtx = unknown, TPrisma extends PrismaClie
|
|
|
438
208
|
})
|
|
439
209
|
|
|
440
210
|
if (!openApiDisabled) {
|
|
441
|
-
|
|
442
|
-
const openapiYamlPath = basePath ? \`\${basePath}/openapi.yaml\` : '/openapi.yaml'
|
|
443
|
-
app.get(openapiJsonPath, (c) => c.json(getOpenApiJson() as JsonLike))
|
|
444
|
-
app.get(openapiYamlPath, (c) => {
|
|
445
|
-
c.header('Content-Type', 'application/yaml')
|
|
446
|
-
return c.body(getOpenApiYaml())
|
|
447
|
-
})
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
const POLICY = resolveGuardPolicy(config)
|
|
451
|
-
const SETTLE_BEFORE_HOOKS = POLICY.guardResolutionOrder === 'before-hooks'
|
|
452
|
-
|
|
453
|
-
/**
|
|
454
|
-
* The guard-resolution failures, raised in one place so both handlers and both
|
|
455
|
-
* orderings share exactly one definition of what a failure is.
|
|
456
|
-
*
|
|
457
|
-
* WHERE it is called is the behavioural difference, and it is the
|
|
458
|
-
* \`guardResolutionOrder\` control. \`'before-hooks'\` runs it before any operation
|
|
459
|
-
* hook, because a hook that returns a Response — an auth gate, a cache, a
|
|
460
|
-
* short-circuit for a known caller — would otherwise answer a request whose
|
|
461
|
-
* guard was never established. \`'after-hooks'\` is upstream behaviour.
|
|
462
|
-
*
|
|
463
|
-
* \`guardShapeFailure\` is only ever set when \`validateResolvedShapes\` is on, so
|
|
464
|
-
* that branch is inert rather than merely unreached when it is off.
|
|
465
|
-
*/
|
|
466
|
-
const settleGuard = (c: Context<GeneratedHonoEnv<TEnv>>): void => {
|
|
467
|
-
const vars = c as unknown as HandlerContext
|
|
468
|
-
const failure = vars.get('guardVariantFailure')
|
|
469
|
-
if (failure) {
|
|
470
|
-
throw new HTTPException(400, {
|
|
471
|
-
message: formatGuardVariantResolutionError(failure),
|
|
472
|
-
})
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
const shapeFailure = vars.get('guardShapeFailure')
|
|
476
|
-
if (shapeFailure) {
|
|
477
|
-
throw new HTTPException(500, {
|
|
478
|
-
message: 'guard shape could not be resolved: ' + shapeFailure,
|
|
479
|
-
})
|
|
480
|
-
}
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
const handleRead = (
|
|
484
|
-
opConfig: NormalizedOp<TEnv>,
|
|
485
|
-
handlerFn: (c: HandlerContext) => Promise<void>,
|
|
486
|
-
parseFn: (c: HandlerContext) => Promise<void>,
|
|
487
|
-
opKind: OpKind,
|
|
488
|
-
) => async (c: Context<GeneratedHonoEnv<TEnv>>): Promise<Response> => {
|
|
489
|
-
try {
|
|
490
|
-
const authorized = await runBeforeHooks<TEnv>(opConfig.authorize ? [opConfig.authorize] : [], c)
|
|
491
|
-
if (authorized) return authorized
|
|
492
|
-
await parseFn(c as unknown as HandlerContext)
|
|
493
|
-
await makeShapeMiddleware<TCtx, TPrisma, TEnv>(config, opConfig, opKind)(c)
|
|
494
|
-
|
|
495
|
-
// Settled BEFORE any hook can answer the request, when asked for.
|
|
496
|
-
if (SETTLE_BEFORE_HOOKS) settleGuard(c)
|
|
497
|
-
|
|
498
|
-
const operationBefore = await runBeforeHooks<TEnv>(opConfig.operationBefore, c)
|
|
499
|
-
if (operationBefore) return operationBefore
|
|
500
|
-
|
|
501
|
-
// Upstream order: hooks first, guard failure after.
|
|
502
|
-
if (!SETTLE_BEFORE_HOOKS) settleGuard(c)
|
|
503
|
-
|
|
504
|
-
const key = (c as unknown as HandlerContext).get('guardVariantKey')
|
|
505
|
-
const variantHooks =
|
|
506
|
-
key !== undefined ? opConfig.variantHooks[key] : undefined
|
|
507
|
-
|
|
508
|
-
const variantBefore = await runBeforeHooks<TEnv>(variantHooks?.before ?? [], c)
|
|
509
|
-
if (variantBefore) return variantBefore
|
|
510
|
-
await handlerFn(c as unknown as HandlerContext)
|
|
511
|
-
const variantAfter = await runAfterHooks<TEnv>(variantHooks?.after ?? [], c)
|
|
512
|
-
if (variantAfter) return variantAfter
|
|
513
|
-
const operationAfter = await runAfterHooks<TEnv>(opConfig.operationAfter, c)
|
|
514
|
-
if (operationAfter) return operationAfter
|
|
515
|
-
return sendResult(c as unknown as HandlerContext)
|
|
516
|
-
} catch (error: unknown) {
|
|
517
|
-
return sendError(c as unknown as HandlerContext, error)
|
|
518
|
-
}
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
const handleWrite = (
|
|
522
|
-
opConfig: NormalizedOp<TEnv>,
|
|
523
|
-
handlerFn: (c: HandlerContext) => Promise<void>,
|
|
524
|
-
opKind: OpKind,
|
|
525
|
-
) => async (c: Context<GeneratedHonoEnv<TEnv>>): Promise<Response> => {
|
|
526
|
-
try {
|
|
527
|
-
const authorized = await runBeforeHooks<TEnv>(opConfig.authorize ? [opConfig.authorize] : [], c)
|
|
528
|
-
if (authorized) return authorized
|
|
529
|
-
await parseWriteBodyMiddleware(c as unknown as HandlerContext)
|
|
530
|
-
await makeShapeMiddleware<TCtx, TPrisma, TEnv>(config, opConfig, opKind)(c)
|
|
531
|
-
|
|
532
|
-
// Settled BEFORE any hook can answer the request, when asked for.
|
|
533
|
-
if (SETTLE_BEFORE_HOOKS) settleGuard(c)
|
|
534
|
-
|
|
535
|
-
const operationBefore = await runBeforeHooks<TEnv>(opConfig.operationBefore, c)
|
|
536
|
-
if (operationBefore) return operationBefore
|
|
537
|
-
|
|
538
|
-
// Upstream order: hooks first, guard failure after.
|
|
539
|
-
if (!SETTLE_BEFORE_HOOKS) settleGuard(c)
|
|
540
|
-
|
|
541
|
-
const key = (c as unknown as HandlerContext).get('guardVariantKey')
|
|
542
|
-
const variantHooks =
|
|
543
|
-
key !== undefined ? opConfig.variantHooks[key] : undefined
|
|
544
|
-
|
|
545
|
-
const variantBefore = await runBeforeHooks<TEnv>(variantHooks?.before ?? [], c)
|
|
546
|
-
if (variantBefore) return variantBefore
|
|
547
|
-
await handlerFn(c as unknown as HandlerContext)
|
|
548
|
-
const variantAfter = await runAfterHooks<TEnv>(variantHooks?.after ?? [], c)
|
|
549
|
-
if (variantAfter) return variantAfter
|
|
550
|
-
const operationAfter = await runAfterHooks<TEnv>(opConfig.operationAfter, c)
|
|
551
|
-
if (operationAfter) return operationAfter
|
|
552
|
-
return sendResult(c as unknown as HandlerContext)
|
|
553
|
-
} catch (error: unknown) {
|
|
554
|
-
return sendError(c as unknown as HandlerContext, error)
|
|
555
|
-
}
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
const opFor = <K extends keyof ${modelName}RouteConfig<TCtx, TPrisma, TEnv>>(
|
|
559
|
-
key: K,
|
|
560
|
-
): NormalizedOp<TEnv> => {
|
|
561
|
-
const raw = config[key] as unknown as OperationConfigLike<TEnv> | undefined
|
|
562
|
-
validateOperationConfig(raw, '${modelName}.' + String(key), POLICY)
|
|
563
|
-
return normalizeHonoOperation(raw)
|
|
211
|
+
registerOpenApiRoutes(app, basePath, getOpenApiJson, getOpenApiYaml)
|
|
564
212
|
}
|
|
565
213
|
|
|
566
214
|
${readOpBlocks}
|
|
@@ -573,9 +221,6 @@ ${writeOpBlocks}
|
|
|
573
221
|
* endpoint is a batch of { where, data } applied directly — and the only thing
|
|
574
222
|
* between it and an unguarded mass mutation is a console.warn suppressed in
|
|
575
223
|
* production. A warning is not a security boundary.
|
|
576
|
-
*
|
|
577
|
-
* Removing the route outright was a breaking change to a published package,
|
|
578
|
-
* so by default it registers exactly as it did, warning and all.
|
|
579
224
|
*/
|
|
580
225
|
if (!POLICY.enableUpdateEach) {
|
|
581
226
|
throw new Error(
|
|
@@ -587,9 +232,9 @@ ${writeOpBlocks}
|
|
|
587
232
|
)
|
|
588
233
|
}
|
|
589
234
|
|
|
590
|
-
const rawUpdateEach = config.updateEach as unknown as
|
|
235
|
+
const rawUpdateEach = config.updateEach as unknown as Parameters<typeof normalizeHonoOperation<TEnv>>[0]
|
|
591
236
|
validateUpdateEachConfig(rawUpdateEach, '${modelName}.updateEach')
|
|
592
|
-
const opConfig = normalizeHonoOperation(rawUpdateEach)
|
|
237
|
+
const opConfig = normalizeHonoOperation<TEnv>(rawUpdateEach)
|
|
593
238
|
if (opConfig.operationBefore.length === 0 && _env.NODE_ENV !== 'production') {
|
|
594
239
|
console.warn(
|
|
595
240
|
'[${modelName}Router] updateEach is enabled without a before hook. ' +
|
|
@@ -597,20 +242,7 @@ ${writeOpBlocks}
|
|
|
597
242
|
)
|
|
598
243
|
}
|
|
599
244
|
const path = basePath ? \`\${basePath}/each\` : '/each'
|
|
600
|
-
app.post(path,
|
|
601
|
-
try {
|
|
602
|
-
await parseUpdateEachBodyMiddleware(c as unknown as HandlerContext)
|
|
603
|
-
await makeShapeMiddleware<TCtx, TPrisma, TEnv>(config, opConfig, 'noop')(c)
|
|
604
|
-
const beforeResponse = await runBeforeHooks<TEnv>(opConfig.operationBefore, c)
|
|
605
|
-
if (beforeResponse) return beforeResponse
|
|
606
|
-
await ${modelName}UpdateEach(c as unknown as HandlerContext)
|
|
607
|
-
const afterResponse = await runAfterHooks<TEnv>(opConfig.operationAfter, c)
|
|
608
|
-
if (afterResponse) return afterResponse
|
|
609
|
-
return sendResult(c as unknown as HandlerContext)
|
|
610
|
-
} catch (error: unknown) {
|
|
611
|
-
return sendError(c as unknown as HandlerContext, error)
|
|
612
|
-
}
|
|
613
|
-
})
|
|
245
|
+
app.post(path, createUpdateEachRoute<TCtx, TPrisma, TEnv>({ config, opConfig, handler: ${modelName}UpdateEach, dropGuard }))
|
|
614
246
|
}
|
|
615
247
|
|
|
616
248
|
return app
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generateRouterHono.js","sourceRoot":"","sources":["../../src/generators/generateRouterHono.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"generateRouterHono.js","sourceRoot":"","sources":["../../src/generators/generateRouterHono.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { DMMF } from '@prisma/generator-helper';
|
|
2
|
+
import type { ImportStyle } from '../utils/resolveImportStyle';
|
|
3
|
+
import type { WriteStrategy } from '../constants';
|
|
4
|
+
import { type PathCase } from '../utils/pathCasing';
|
|
5
|
+
export declare function generateHonoRouterParts({ model, enums, guardShapesImport, clientImport, importStyle, writeStrategy, dropGuard, pathCase, }: {
|
|
6
|
+
model: DMMF.Model;
|
|
7
|
+
enums: DMMF.DatamodelEnum[];
|
|
8
|
+
guardShapesImport: string | null;
|
|
9
|
+
clientImport?: string;
|
|
10
|
+
importStyle: ImportStyle;
|
|
11
|
+
writeStrategy: WriteStrategy;
|
|
12
|
+
dropGuard: boolean;
|
|
13
|
+
pathCase: PathCase;
|
|
14
|
+
}): string;
|