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
|
@@ -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
|
+
}
|
package/src/copy/scalarTypes.ts
CHANGED
|
@@ -1,2 +1,7 @@
|
|
|
1
|
-
export const NUMERIC_SCALAR_TYPES = new Set([
|
|
2
|
-
|
|
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'])
|