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.
Files changed (130) hide show
  1. package/README.md +188 -3432
  2. package/dist/client/encodeQueryParams.js +3 -1
  3. package/dist/client/encodeQueryParams.js.map +1 -1
  4. package/dist/copy/misc.d.ts +1 -0
  5. package/dist/copy/misc.js +14 -0
  6. package/dist/copy/misc.js.map +1 -1
  7. package/dist/copy/operationDefinitions.d.ts +4 -1
  8. package/dist/copy/operationDefinitions.js +133 -9
  9. package/dist/copy/operationDefinitions.js.map +1 -1
  10. package/dist/generators/generateFastifyHandler.d.ts +2 -2
  11. package/dist/generators/generateFastifyHandler.js +68 -12
  12. package/dist/generators/generateFastifyHandler.js.map +1 -1
  13. package/dist/generators/generateHonoHandler.d.ts +2 -2
  14. package/dist/generators/generateHonoHandler.js +53 -6
  15. package/dist/generators/generateHonoHandler.js.map +1 -1
  16. package/dist/generators/generateHonoOpenApi.d.ts +13 -0
  17. package/dist/generators/generateHonoOpenApi.js +70 -0
  18. package/dist/generators/generateHonoOpenApi.js.map +1 -0
  19. package/dist/generators/generateImportPrismaStatement.d.ts +1 -0
  20. package/dist/generators/generateImportPrismaStatement.js +6 -0
  21. package/dist/generators/generateImportPrismaStatement.js.map +1 -1
  22. package/dist/generators/generateMcpApp.d.ts +10 -0
  23. package/dist/generators/generateMcpApp.js +113 -0
  24. package/dist/generators/generateMcpApp.js.map +1 -0
  25. package/dist/generators/generateModelMcp.d.ts +9 -0
  26. package/dist/generators/generateModelMcp.js +145 -0
  27. package/dist/generators/generateModelMcp.js.map +1 -0
  28. package/dist/generators/generateModelMetadata.js +29 -15
  29. package/dist/generators/generateModelMetadata.js.map +1 -1
  30. package/dist/generators/generateOperationCore.d.ts +3 -3
  31. package/dist/generators/generateOperationCore.js +51 -16
  32. package/dist/generators/generateOperationCore.js.map +1 -1
  33. package/dist/generators/generateRelationMeta.js +2 -2
  34. package/dist/generators/generateRelationMeta.js.map +1 -1
  35. package/dist/generators/generateRouteConfigType.d.ts +2 -2
  36. package/dist/generators/generateRouteConfigType.js +29 -11
  37. package/dist/generators/generateRouteConfigType.js.map +1 -1
  38. package/dist/generators/generateRouter.d.ts +7 -4
  39. package/dist/generators/generateRouter.js +164 -65
  40. package/dist/generators/generateRouter.js.map +1 -1
  41. package/dist/generators/generateRouterFastify.d.ts +7 -4
  42. package/dist/generators/generateRouterFastify.js +106 -86
  43. package/dist/generators/generateRouterFastify.js.map +1 -1
  44. package/dist/generators/generateRouterHono.d.ts +7 -4
  45. package/dist/generators/generateRouterHono.js +59 -414
  46. package/dist/generators/generateRouterHono.js.map +1 -1
  47. package/dist/generators/generateRouterPartsHono.d.ts +14 -0
  48. package/dist/generators/generateRouterPartsHono.js +180 -0
  49. package/dist/generators/generateRouterPartsHono.js.map +1 -0
  50. package/dist/generators/generateUnifiedDocs.d.ts +1 -1
  51. package/dist/generators/generateUnifiedDocs.js +15 -8
  52. package/dist/generators/generateUnifiedDocs.js.map +1 -1
  53. package/dist/generators/generateUnifiedHandler.d.ts +2 -2
  54. package/dist/generators/generateUnifiedHandler.js +42 -18
  55. package/dist/generators/generateUnifiedHandler.js.map +1 -1
  56. package/dist/guardOptions.js +12 -7
  57. package/dist/guardOptions.js.map +1 -1
  58. package/dist/index.d.ts +3 -1
  59. package/dist/index.js +123 -4
  60. package/dist/index.js.map +1 -1
  61. package/dist/utils/copyFiles.d.ts +1 -1
  62. package/dist/utils/copyFiles.js +42 -7
  63. package/dist/utils/copyFiles.js.map +1 -1
  64. package/dist/utils/importExt.d.ts +1 -1
  65. package/dist/utils/mcpGate.d.ts +10 -0
  66. package/dist/utils/mcpGate.js +118 -0
  67. package/dist/utils/mcpGate.js.map +1 -0
  68. package/dist/utils/pathCasing.d.ts +4 -0
  69. package/dist/utils/pathCasing.js +40 -0
  70. package/dist/utils/pathCasing.js.map +1 -0
  71. package/dist/utils/resolveImportStyle.js +13 -5
  72. package/dist/utils/resolveImportStyle.js.map +1 -1
  73. package/dist/utils/writeFileSafely.js +6 -0
  74. package/dist/utils/writeFileSafely.js.map +1 -1
  75. package/package.json +22 -5
  76. package/src/bin.ts +1 -1
  77. package/src/client/encodeQueryParams.ts +39 -35
  78. package/src/constants.ts +1 -1
  79. package/src/copy/autoIncludePlanner.ts +93 -27
  80. package/src/copy/autoIncludePlannerGuarded.ts +171 -48
  81. package/src/copy/autoIncludeRuntimeGuarded.ts +25 -7
  82. package/src/copy/buildModelOpenApi.ts +229 -176
  83. package/src/copy/concurrency.ts +10 -8
  84. package/src/copy/docsRenderer.ts +14 -15
  85. package/src/copy/errorMapper.ts +68 -19
  86. package/src/copy/guardHelpers.ts +30 -9
  87. package/src/copy/materializedCount.ts +17 -5
  88. package/src/copy/mcpRuntime.ts +1017 -0
  89. package/src/copy/misc.ts +88 -68
  90. package/src/copy/ndjson.ts +72 -0
  91. package/src/copy/operationDefinitions.ts +177 -25
  92. package/src/copy/operationPipeline.ts +267 -0
  93. package/src/copy/operationRuntime.ts +124 -26
  94. package/src/copy/operationSchemas.ts +2773 -0
  95. package/src/copy/pagination.ts +35 -13
  96. package/src/copy/parseQueryParams.ts +47 -48
  97. package/src/copy/projectionDefaults.ts +6 -4
  98. package/src/copy/routeConfig.express.ts +16 -4
  99. package/src/copy/routeConfig.fastify.ts +16 -2
  100. package/src/copy/routeConfig.hono.ts +46 -14
  101. package/src/copy/routeConfig.ts +175 -40
  102. package/src/copy/routerParts.ts +524 -0
  103. package/src/copy/scalarTypes.ts +7 -2
  104. package/src/copy/sse.ts +72 -19
  105. package/src/generators/generateFastifyHandler.ts +77 -20
  106. package/src/generators/generateHonoHandler.ts +62 -14
  107. package/src/generators/generateHonoOpenApi.ts +93 -0
  108. package/src/generators/generateImportPrismaStatement.ts +93 -81
  109. package/src/generators/generateMcpApp.ts +133 -0
  110. package/src/generators/generateModelMcp.ts +208 -0
  111. package/src/generators/generateModelMetadata.ts +40 -22
  112. package/src/generators/generateOperationCore.ts +81 -33
  113. package/src/generators/generateRelationMeta.ts +23 -11
  114. package/src/generators/generateRouteConfigType.ts +39 -13
  115. package/src/generators/generateRouter.ts +171 -68
  116. package/src/generators/generateRouterFastify.ts +117 -89
  117. package/src/generators/generateRouterHono.ts +84 -464
  118. package/src/generators/generateRouterPartsHono.ts +220 -0
  119. package/src/generators/generateUnifiedDocs.ts +29 -8
  120. package/src/generators/generateUnifiedHandler.ts +47 -24
  121. package/src/generators/generateUnifiedScalarUI.ts +1 -1
  122. package/src/guardOptions.ts +105 -92
  123. package/src/index.ts +161 -6
  124. package/src/utils/copyFiles.ts +235 -168
  125. package/src/utils/importExt.ts +2 -2
  126. package/src/utils/mcpGate.ts +157 -0
  127. package/src/utils/pathCasing.ts +42 -0
  128. package/src/utils/resolveImportStyle.ts +37 -15
  129. package/src/utils/strings.ts +2 -2
  130. package/src/utils/writeFileSafely.ts +12 -2
@@ -0,0 +1,524 @@
1
+ import type { Context } from 'hono'
2
+ import type { ContentfulStatusCode } from 'hono/utils/http-status'
3
+ import { HTTPException } from 'hono/http-exception'
4
+ import { parseQueryParams } from './parseQueryParams'
5
+ import { sanitizeKeys, isPlainObject } from './misc'
6
+ import {
7
+ normalizeOperation,
8
+ resolveGuardPolicy,
9
+ resolveOperationVariantKey,
10
+ validateOperationConfig,
11
+ type BaseRouteConfig,
12
+ type GuardPolicy,
13
+ type NormalizedOperationConfig,
14
+ type PaginationConfig,
15
+ } from './routeConfig'
16
+ import type {
17
+ HonoBeforeHook,
18
+ HonoAfterHook,
19
+ HonoEnvBase,
20
+ GeneratedHonoEnv,
21
+ HonoInternalVariables,
22
+ } from './routeConfig.hono'
23
+ import { mergePaginationConfig } from './pagination'
24
+ import { mapError } from './errorMapper'
25
+ import {
26
+ routeOperation,
27
+ prepareGuardOperation,
28
+ settleStage,
29
+ memoizeContext,
30
+ type ArgsChannel,
31
+ } from './operationPipeline'
32
+ import { transformResult } from './operationRuntime'
33
+ import type { OpKind } from './projectionDefaults'
34
+
35
+ /**
36
+ * Phase 9 static assembly boundary, Hono side.
37
+ *
38
+ * Every operation route is created here, one function per operation, reachable
39
+ * only through the per-op named exports of a model's `*RouterParts.ts`. The
40
+ * legacy router factory registers all of them; a bundle that imports only the
41
+ * parts it serves omits the rest, and heavy features (OpenAPI renderer, the
42
+ * query-builder UI, SSE/ndjson — Express-only — and materialized-view counting,
43
+ * which pagination loads lazily) are unreachable unless a used part references
44
+ * them.
45
+ */
46
+
47
+ export type HandlerContext = Context<{ Variables: HonoInternalVariables }>
48
+
49
+ type JsonLike =
50
+ | string
51
+ | number
52
+ | boolean
53
+ | null
54
+ | unknown[]
55
+ | Record<string, unknown>
56
+
57
+ /**
58
+ * The config surface a route needs, picked from the real BaseRouteConfig so
59
+ * every emitted `*RouteConfig` is compatible by construction.
60
+ */
61
+ export type HonoRouteConfigLike<TCtx, TEnv extends HonoEnvBase> = Pick<
62
+ BaseRouteConfig<
63
+ HonoBeforeHook<TEnv>,
64
+ Context<GeneratedHonoEnv<TEnv>>,
65
+ Record<string, unknown>,
66
+ TCtx
67
+ >,
68
+ 'resolveContext' | 'pagination' | 'guard' | 'disablePostReads'
69
+ > & {
70
+ allowE2EGuardBypass?: boolean
71
+ }
72
+
73
+ export type HonoOpConfig<TEnv extends HonoEnvBase> = NormalizedOperationConfig<
74
+ HonoBeforeHook<TEnv>,
75
+ HonoAfterHook<TEnv>
76
+ >
77
+
78
+ export async function parseQueryMiddleware(c: HandlerContext): Promise<void> {
79
+ const raw = c.req.query() as Record<string, unknown>
80
+ if (raw && Object.keys(raw).length > 0) {
81
+ c.set('parsedQuery', parseQueryParams(raw) as Record<string, unknown>)
82
+ }
83
+ }
84
+
85
+ export async function parseBodyAsQueryMiddleware(
86
+ c: HandlerContext,
87
+ ): Promise<void> {
88
+ let body: unknown
89
+ try {
90
+ body = await c.req.json()
91
+ } catch {
92
+ throw new HTTPException(400, {
93
+ message: 'Request body must be a JSON object',
94
+ })
95
+ }
96
+ if (!body || typeof body !== 'object' || Array.isArray(body)) {
97
+ throw new HTTPException(400, {
98
+ message: 'Request body must be a JSON object',
99
+ })
100
+ }
101
+ c.set('parsedQuery', sanitizeKeys(body as Record<string, unknown>))
102
+ }
103
+
104
+ export async function parseWriteBodyMiddleware(
105
+ c: HandlerContext,
106
+ ): Promise<void> {
107
+ let body: unknown
108
+ try {
109
+ body = await c.req.json()
110
+ } catch {
111
+ throw new HTTPException(400, {
112
+ message: 'Request body must be a JSON object',
113
+ })
114
+ }
115
+ if (!body || typeof body !== 'object' || Array.isArray(body)) {
116
+ throw new HTTPException(400, {
117
+ message: 'Request body must be a JSON object',
118
+ })
119
+ }
120
+ c.set('body', body)
121
+ }
122
+
123
+ export async function parseUpdateEachBodyMiddleware(
124
+ c: HandlerContext,
125
+ ): Promise<void> {
126
+ let body: unknown
127
+ try {
128
+ body = await c.req.json()
129
+ } catch {
130
+ throw new HTTPException(400, {
131
+ message: 'updateEach body must be an array of { where, data } items',
132
+ })
133
+ }
134
+ if (!Array.isArray(body)) {
135
+ throw new HTTPException(400, {
136
+ message: 'updateEach body must be an array of { where, data } items',
137
+ })
138
+ }
139
+ c.set('body', body)
140
+ }
141
+
142
+ function queryChannel(c: HandlerContext): ArgsChannel {
143
+ return {
144
+ read: () => c.get('parsedQuery'),
145
+ write: (next) => c.set('parsedQuery', next),
146
+ }
147
+ }
148
+
149
+ function bodyChannel(c: HandlerContext): ArgsChannel {
150
+ return {
151
+ read: () => {
152
+ const body = c.get('body')
153
+ return isPlainObject(body) ? body : undefined
154
+ },
155
+ write: (next) => c.set('body', next),
156
+ }
157
+ }
158
+
159
+ export type HonoOperationConfigInput<TEnv extends HonoEnvBase> = {
160
+ authorize?: HonoBeforeHook<TEnv>
161
+ override?: import('./operationRuntime').RuntimeOperationOverride
162
+ before?: HonoBeforeHook<TEnv>[]
163
+ after?: HonoAfterHook<TEnv>[]
164
+ shape?: unknown
165
+ variants?: Record<
166
+ string,
167
+ {
168
+ shape?: unknown
169
+ before?: HonoBeforeHook<TEnv>[]
170
+ after?: HonoAfterHook<TEnv>[]
171
+ }
172
+ >
173
+ pagination?: Partial<PaginationConfig>
174
+ disablePostReads?: boolean
175
+ }
176
+
177
+ export function normalizeHonoOperation<TEnv extends HonoEnvBase>(
178
+ config: HonoOperationConfigInput<TEnv> | undefined,
179
+ ): HonoOpConfig<TEnv> {
180
+ return normalizeOperation<HonoBeforeHook<TEnv>, HonoAfterHook<TEnv>>(config)
181
+ }
182
+
183
+ /**
184
+ * Validates and normalizes one operation config exactly as the legacy router
185
+ * does, through the same shared code path.
186
+ */
187
+ export function opConfigFor<TEnv extends HonoEnvBase>(
188
+ raw: HonoOperationConfigInput<TEnv> | undefined,
189
+ location: string,
190
+ policy: Partial<GuardPolicy>,
191
+ ): HonoOpConfig<TEnv> {
192
+ validateOperationConfig(raw, location, policy)
193
+ return normalizeHonoOperation<TEnv>(raw)
194
+ }
195
+
196
+ export function createShapeMiddleware<
197
+ TCtx,
198
+ TPrisma,
199
+ TEnv extends HonoEnvBase,
200
+ >(input: {
201
+ config: HonoRouteConfigLike<TCtx, TEnv>
202
+ opConfig: HonoOpConfig<TEnv>
203
+ opKind: OpKind
204
+ dropGuard: boolean
205
+ }): (c: Context<GeneratedHonoEnv<TEnv>>) => Promise<void> {
206
+ const policy = {
207
+ dropGuard: input.dropGuard,
208
+ validateResolvedShapes: resolveGuardPolicy(
209
+ input.config as Partial<GuardPolicy>,
210
+ ).validateResolvedShapes,
211
+ }
212
+ return async (c: Context<GeneratedHonoEnv<TEnv>>): Promise<void> => {
213
+ const vars = c as unknown as HandlerContext
214
+ vars.set('operationOverride', input.opConfig.override)
215
+ const getContext = memoizeContext(() => input.config.resolveContext?.(c))
216
+ vars.set('getContext', getContext)
217
+ // legacy key, still populated for pre-existing typed hooks
218
+ vars.set('resolveOperationContext', getContext)
219
+
220
+ const merged = mergePaginationConfig(
221
+ input.config.pagination,
222
+ input.opConfig.pagination,
223
+ )
224
+ if (merged) vars.set('routeConfig', { pagination: merged })
225
+
226
+ const headerName = input.config.guard?.variantHeader || 'x-api-variant'
227
+ const headerValue = c.req.header(headerName)
228
+ const caller =
229
+ input.config.guard?.resolveVariant?.(c) ?? headerValue ?? undefined
230
+ if (typeof caller === 'string') vars.set('guardCaller', caller)
231
+
232
+ const resolution = resolveOperationVariantKey(
233
+ input.opConfig.guardRouting,
234
+ caller,
235
+ )
236
+ const routed = routeOperation({
237
+ guardRouting: input.opConfig.guardRouting,
238
+ caller,
239
+ })
240
+ if (!routed.ok) {
241
+ // legacy key keeps the pre-1.x public shape for existing typed hooks
242
+ if (!resolution.ok) vars.set('guardVariantFailure', resolution)
243
+ vars.set('guardVariantStageFailure', routed)
244
+ return
245
+ }
246
+ vars.set('guardVariantStageFailure', undefined)
247
+ vars.set('guardVariantFailure', undefined)
248
+
249
+ if (routed.variantKey !== undefined)
250
+ vars.set('guardVariantKey', routed.variantKey)
251
+
252
+ const guard = await prepareGuardOperation(routed, {
253
+ guardShape: input.opConfig.guardShape,
254
+ opKind: input.opKind,
255
+ policy,
256
+ getContext:
257
+ typeof input.config.resolveContext === 'function'
258
+ ? vars.get('getContext')
259
+ : undefined,
260
+ args: queryChannel(c as unknown as HandlerContext),
261
+ writeArgs: bodyChannel(c as unknown as HandlerContext),
262
+ })
263
+ if (!guard.ok) {
264
+ // legacy string key, still populated for pre-existing typed hooks
265
+ vars.set(
266
+ 'guardShapeFailure',
267
+ guard.failure.message.replace(
268
+ 'guard shape could not be resolved: ',
269
+ '',
270
+ ),
271
+ )
272
+ vars.set('guardShapeStageFailure', guard)
273
+ return
274
+ }
275
+ vars.set('guardShapeFailure', undefined)
276
+ vars.set('guardShapeStageFailure', undefined)
277
+
278
+ vars.set('guardShape', guard.guardShape)
279
+ }
280
+ }
281
+
282
+ export function createSettleGuard<TEnv extends HonoEnvBase>(): (
283
+ c: Context<GeneratedHonoEnv<TEnv>>,
284
+ ) => void {
285
+ return (c: Context<GeneratedHonoEnv<TEnv>>): void => {
286
+ const vars = c as unknown as HandlerContext
287
+ const failure = vars.get('guardVariantStageFailure')
288
+ if (failure) settleStage(failure)
289
+
290
+ const shapeFailure = vars.get('guardShapeStageFailure')
291
+ if (shapeFailure) settleStage(shapeFailure)
292
+ }
293
+ }
294
+
295
+ async function runBeforeHooks<TEnv extends HonoEnvBase>(
296
+ hooks: readonly HonoBeforeHook<TEnv>[],
297
+ c: Context<GeneratedHonoEnv<TEnv>>,
298
+ ): Promise<Response | undefined> {
299
+ for (const hook of hooks) {
300
+ const result = await hook(c)
301
+ if (result instanceof Response) return result
302
+ }
303
+ return undefined
304
+ }
305
+
306
+ async function runAfterHooks<TEnv extends HonoEnvBase>(
307
+ hooks: readonly HonoAfterHook<TEnv>[],
308
+ c: Context<GeneratedHonoEnv<TEnv>>,
309
+ ): Promise<Response | undefined> {
310
+ for (const hook of hooks) {
311
+ const result = await hook(c)
312
+ if (result instanceof Response) return result
313
+ }
314
+ return undefined
315
+ }
316
+
317
+ export function sendResult(c: HandlerContext): Response {
318
+ const data = c.get('resultData')
319
+ const status = (c.get('resultStatus') as number | undefined) ?? 200
320
+ if (data === undefined) {
321
+ throw new HTTPException(500, { message: 'No data set by handler' })
322
+ }
323
+ return c.json(
324
+ transformResult(data) as JsonLike,
325
+ status as ContentfulStatusCode,
326
+ )
327
+ }
328
+
329
+ export function sendError(c: HandlerContext, error: unknown): Response {
330
+ if (error instanceof HTTPException) {
331
+ return c.json(
332
+ { message: error.message },
333
+ error.status as ContentfulStatusCode,
334
+ )
335
+ }
336
+ const httpError = mapError(error)
337
+ return c.json(
338
+ { message: httpError.message },
339
+ httpError.status as ContentfulStatusCode,
340
+ )
341
+ }
342
+
343
+ export function createReadRoute<
344
+ TCtx,
345
+ TPrisma,
346
+ TEnv extends HonoEnvBase,
347
+ >(input: {
348
+ config: HonoRouteConfigLike<TCtx, TEnv>
349
+ opConfig: HonoOpConfig<TEnv>
350
+ opKind: OpKind
351
+ handler: (c: HandlerContext) => Promise<void>
352
+ parse: 'query' | 'body'
353
+ dropGuard: boolean
354
+ settleBeforeHooks: boolean
355
+ }): (c: Context<GeneratedHonoEnv<TEnv>>) => Promise<Response> {
356
+ const shape = createShapeMiddleware<TCtx, TPrisma, TEnv>(input)
357
+ const settleGuard = createSettleGuard<TEnv>()
358
+ const parseFn =
359
+ input.parse === 'query' ? parseQueryMiddleware : parseBodyAsQueryMiddleware
360
+ return async (c: Context<GeneratedHonoEnv<TEnv>>): Promise<Response> => {
361
+ try {
362
+ const authorized = await runBeforeHooks<TEnv>(
363
+ input.opConfig.authorize ? [input.opConfig.authorize] : [],
364
+ c,
365
+ )
366
+ if (authorized) return authorized
367
+ await parseFn(c as unknown as HandlerContext)
368
+ await shape(c)
369
+
370
+ if (input.settleBeforeHooks) settleGuard(c)
371
+
372
+ const operationBefore = await runBeforeHooks<TEnv>(
373
+ input.opConfig.operationBefore,
374
+ c,
375
+ )
376
+ if (operationBefore) return operationBefore
377
+
378
+ if (!input.settleBeforeHooks) settleGuard(c)
379
+
380
+ const key = (c as unknown as HandlerContext).get('guardVariantKey')
381
+ const variantHooks =
382
+ key !== undefined ? input.opConfig.variantHooks[key] : undefined
383
+
384
+ const variantBefore = await runBeforeHooks<TEnv>(
385
+ variantHooks?.before ?? [],
386
+ c,
387
+ )
388
+ if (variantBefore) return variantBefore
389
+ await input.handler(c as unknown as HandlerContext)
390
+ const variantAfter = await runAfterHooks<TEnv>(
391
+ variantHooks?.after ?? [],
392
+ c,
393
+ )
394
+ if (variantAfter) return variantAfter
395
+ const operationAfter = await runAfterHooks<TEnv>(
396
+ input.opConfig.operationAfter,
397
+ c,
398
+ )
399
+ if (operationAfter) return operationAfter
400
+ return sendResult(c as unknown as HandlerContext)
401
+ } catch (error: unknown) {
402
+ return sendError(c as unknown as HandlerContext, error)
403
+ }
404
+ }
405
+ }
406
+
407
+ export function createWriteRoute<
408
+ TCtx,
409
+ TPrisma,
410
+ TEnv extends HonoEnvBase,
411
+ >(input: {
412
+ config: HonoRouteConfigLike<TCtx, TEnv>
413
+ opConfig: HonoOpConfig<TEnv>
414
+ opKind: OpKind
415
+ handler: (c: HandlerContext) => Promise<void>
416
+ dropGuard: boolean
417
+ settleBeforeHooks: boolean
418
+ }): (c: Context<GeneratedHonoEnv<TEnv>>) => Promise<Response> {
419
+ const shape = createShapeMiddleware<TCtx, TPrisma, TEnv>(input)
420
+ const settleGuard = createSettleGuard<TEnv>()
421
+ return async (c: Context<GeneratedHonoEnv<TEnv>>): Promise<Response> => {
422
+ try {
423
+ const authorized = await runBeforeHooks<TEnv>(
424
+ input.opConfig.authorize ? [input.opConfig.authorize] : [],
425
+ c,
426
+ )
427
+ if (authorized) return authorized
428
+ await parseWriteBodyMiddleware(c as unknown as HandlerContext)
429
+ await shape(c)
430
+
431
+ if (input.settleBeforeHooks) settleGuard(c)
432
+
433
+ const operationBefore = await runBeforeHooks<TEnv>(
434
+ input.opConfig.operationBefore,
435
+ c,
436
+ )
437
+ if (operationBefore) return operationBefore
438
+
439
+ if (!input.settleBeforeHooks) settleGuard(c)
440
+
441
+ const key = (c as unknown as HandlerContext).get('guardVariantKey')
442
+ const variantHooks =
443
+ key !== undefined ? input.opConfig.variantHooks[key] : undefined
444
+
445
+ const variantBefore = await runBeforeHooks<TEnv>(
446
+ variantHooks?.before ?? [],
447
+ c,
448
+ )
449
+ if (variantBefore) return variantBefore
450
+ await input.handler(c as unknown as HandlerContext)
451
+ const variantAfter = await runAfterHooks<TEnv>(
452
+ variantHooks?.after ?? [],
453
+ c,
454
+ )
455
+ if (variantAfter) return variantAfter
456
+ const operationAfter = await runAfterHooks<TEnv>(
457
+ input.opConfig.operationAfter,
458
+ c,
459
+ )
460
+ if (operationAfter) return operationAfter
461
+ return sendResult(c as unknown as HandlerContext)
462
+ } catch (error: unknown) {
463
+ return sendError(c as unknown as HandlerContext, error)
464
+ }
465
+ }
466
+ }
467
+
468
+ export function createUpdateEachRoute<
469
+ TCtx,
470
+ TPrisma,
471
+ TEnv extends HonoEnvBase,
472
+ >(input: {
473
+ config: HonoRouteConfigLike<TCtx, TEnv>
474
+ opConfig: HonoOpConfig<TEnv>
475
+ handler: (c: HandlerContext) => Promise<void>
476
+ dropGuard: boolean
477
+ }): (c: Context<GeneratedHonoEnv<TEnv>>) => Promise<Response> {
478
+ const shape = createShapeMiddleware<TCtx, TPrisma, TEnv>({
479
+ ...input,
480
+ opKind: 'noop',
481
+ })
482
+ return async (c: Context<GeneratedHonoEnv<TEnv>>): Promise<Response> => {
483
+ try {
484
+ await parseUpdateEachBodyMiddleware(c as unknown as HandlerContext)
485
+ await shape(c)
486
+ const beforeResponse = await runBeforeHooks<TEnv>(
487
+ input.opConfig.operationBefore,
488
+ c,
489
+ )
490
+ if (beforeResponse) return beforeResponse
491
+ await input.handler(c as unknown as HandlerContext)
492
+ const afterResponse = await runAfterHooks<TEnv>(
493
+ input.opConfig.operationAfter,
494
+ c,
495
+ )
496
+ if (afterResponse) return afterResponse
497
+ return sendResult(c as unknown as HandlerContext)
498
+ } catch (error: unknown) {
499
+ return sendError(c as unknown as HandlerContext, error)
500
+ }
501
+ }
502
+ }
503
+
504
+ /**
505
+ * OpenAPI endpoints for one model. Takes prebuilt document getters so this
506
+ * module never imports the renderer: a bundle that registers no OpenAPI part
507
+ * keeps `yaml` and `buildModelOpenApi` out of its graph entirely.
508
+ */
509
+ export function registerOpenApiRoutes(
510
+ app: {
511
+ get: (path: string, handler: (c: HandlerContext) => Response) => void
512
+ },
513
+ basePath: string,
514
+ getJson: () => unknown,
515
+ getYaml: () => string,
516
+ ): void {
517
+ const jsonPath = basePath ? `${basePath}/openapi.json` : '/openapi.json'
518
+ const yamlPath = basePath ? `${basePath}/openapi.yaml` : '/openapi.yaml'
519
+ app.get(jsonPath, (c) => c.json(getJson() as JsonLike))
520
+ app.get(yamlPath, (c) => {
521
+ c.header('Content-Type', 'application/yaml')
522
+ return c.body(getYaml())
523
+ })
524
+ }
@@ -1,2 +1,7 @@
1
- export const NUMERIC_SCALAR_TYPES = new Set(['Int', 'BigInt', 'Float', 'Decimal'])
2
- export const STRING_NUMERIC_TYPES = new Set(['BigInt', 'Decimal'])
1
+ export const NUMERIC_SCALAR_TYPES = new Set([
2
+ 'Int',
3
+ 'BigInt',
4
+ 'Float',
5
+ 'Decimal',
6
+ ])
7
+ export const STRING_NUMERIC_TYPES = new Set(['BigInt', 'Decimal'])