prisma-generator-express 1.28.0 → 1.29.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 +244 -14
- package/dist/constants.d.ts +1 -0
- package/dist/generators/generateFastifyHandler.d.ts +4 -0
- package/dist/generators/generateFastifyHandler.js +78 -0
- package/dist/generators/generateFastifyHandler.js.map +1 -0
- package/dist/generators/generateOperationCore.d.ts +6 -0
- package/dist/generators/generateOperationCore.js +534 -0
- package/dist/generators/generateOperationCore.js.map +1 -0
- package/dist/generators/generateQueryBuilderHelper.js +85 -69
- package/dist/generators/generateQueryBuilderHelper.js.map +1 -1
- package/dist/generators/generateRouter.js +1 -25
- package/dist/generators/generateRouter.js.map +1 -1
- package/dist/generators/generateRouterFastify.d.ts +5 -0
- package/dist/generators/generateRouterFastify.js +512 -0
- package/dist/generators/generateRouterFastify.js.map +1 -0
- package/dist/generators/generateUnifiedDocs.d.ts +2 -1
- package/dist/generators/generateUnifiedDocs.js +147 -82
- package/dist/generators/generateUnifiedDocs.js.map +1 -1
- package/dist/generators/generateUnifiedHandler.d.ts +0 -1
- package/dist/generators/generateUnifiedHandler.js +47 -516
- package/dist/generators/generateUnifiedHandler.js.map +1 -1
- package/dist/generators/generateUnifiedScalarUI.d.ts +2 -0
- package/dist/generators/generateUnifiedScalarUI.js +127 -1324
- package/dist/generators/generateUnifiedScalarUI.js.map +1 -1
- package/dist/index.js +33 -8
- package/dist/index.js.map +1 -1
- package/dist/utils/copyFiles.d.ts +2 -1
- package/dist/utils/copyFiles.js +64 -38
- package/dist/utils/copyFiles.js.map +1 -1
- package/dist/utils/writeFileSafely.js +3 -0
- package/dist/utils/writeFileSafely.js.map +1 -1
- package/package.json +4 -1
- package/src/client/encodeQueryParams.ts +1 -1
- package/src/constants.ts +2 -0
- package/src/copy/createOutputValidatorMiddleware.ts +9 -12
- package/src/copy/docsRenderer.ts +1285 -0
- package/src/copy/parseQueryParams.ts +4 -8
- package/src/copy/routeConfig.ts +10 -4
- package/src/generators/generateFastifyHandler.ts +86 -0
- package/src/generators/generateOperationCore.ts +545 -0
- package/src/generators/generateQueryBuilderHelper.ts +86 -70
- package/src/generators/generateRouter.ts +1 -25
- package/src/generators/generateRouterFastify.ts +522 -0
- package/src/generators/generateUnifiedDocs.ts +164 -81
- package/src/generators/generateUnifiedHandler.ts +45 -533
- package/src/generators/generateUnifiedScalarUI.ts +134 -1323
- package/src/index.ts +45 -9
- package/src/utils/copyFiles.ts +79 -45
- package/src/utils/writeFileSafely.ts +4 -0
|
@@ -1,19 +1,46 @@
|
|
|
1
|
-
|
|
1
|
+
import { Target } from '../constants.js'
|
|
2
|
+
|
|
3
|
+
export function generateUnifiedDocs(
|
|
4
|
+
models: string[],
|
|
5
|
+
target: Target = 'express',
|
|
6
|
+
): string {
|
|
2
7
|
const imports = models
|
|
3
|
-
.map((model) => `import { ${model}Docs } from './${model}/${model}Docs'`)
|
|
8
|
+
.map((model) => `import { ${model}Docs } from './${model}/${model}Docs.js'`)
|
|
4
9
|
.join('\n')
|
|
5
10
|
|
|
6
11
|
const handlersEntries = models
|
|
7
12
|
.map((model) => ` ${model}: ${model}Docs`)
|
|
8
13
|
.join(',\n')
|
|
9
14
|
|
|
15
|
+
const frameworkImport =
|
|
16
|
+
target === 'fastify'
|
|
17
|
+
? `import type { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify'`
|
|
18
|
+
: `import { Request, Response } from 'express'`
|
|
19
|
+
|
|
20
|
+
const routeConfigImport = `import type { RouteConfig } from './routeConfig.js'`
|
|
21
|
+
|
|
22
|
+
const handlerType =
|
|
23
|
+
target === 'fastify'
|
|
24
|
+
? `(config: any) => (request: FastifyRequest, reply: FastifyReply) => Promise<void>`
|
|
25
|
+
: `(config: any) => (req: Request, res: Response) => any`
|
|
26
|
+
|
|
27
|
+
const combinedDocsReturn =
|
|
28
|
+
target === 'fastify'
|
|
29
|
+
? generateFastifyCombinedDocs()
|
|
30
|
+
: generateExpressCombinedDocs()
|
|
31
|
+
|
|
32
|
+
const registerDocs =
|
|
33
|
+
target === 'fastify'
|
|
34
|
+
? generateFastifyRegisterDocs()
|
|
35
|
+
: generateExpressRegisterDocs()
|
|
36
|
+
|
|
10
37
|
return `${imports}
|
|
11
|
-
|
|
12
|
-
|
|
38
|
+
${frameworkImport}
|
|
39
|
+
${routeConfigImport}
|
|
13
40
|
|
|
14
41
|
const _env = typeof process !== 'undefined' && process.env ? process.env : {} as Record<string, string | undefined>
|
|
15
42
|
|
|
16
|
-
const docsHandlers: Record<string,
|
|
43
|
+
const docsHandlers: Record<string, ${handlerType}> = {
|
|
17
44
|
${handlersEntries}
|
|
18
45
|
}
|
|
19
46
|
|
|
@@ -63,96 +90,128 @@ function isPlaygroundAvailable(config?: ModelDocsConfig) {
|
|
|
63
90
|
return true
|
|
64
91
|
}
|
|
65
92
|
|
|
66
|
-
|
|
93
|
+
function buildCombinedHtml(
|
|
94
|
+
config: CombinedDocsConfig,
|
|
95
|
+
registeredModels: string[],
|
|
96
|
+
) {
|
|
67
97
|
const title = config.title || 'API Documentation'
|
|
68
98
|
const description = config.description || ''
|
|
69
99
|
const version = config.version || ''
|
|
100
|
+
const basePath = removeTrailingSlash(config.basePath || '/docs')
|
|
101
|
+
const generatedAt = new Date().toISOString()
|
|
102
|
+
|
|
103
|
+
const modelRows = registeredModels.map((m) => {
|
|
104
|
+
const lower = m.toLowerCase()
|
|
105
|
+
const docsUrl = basePath + '/' + lower
|
|
106
|
+
const scalarUrl = docsUrl + '?ui=scalar'
|
|
107
|
+
const jsonUrl = docsUrl + '?ui=json'
|
|
108
|
+
const yamlUrl = docsUrl + '?ui=yaml'
|
|
109
|
+
const playgroundUrl = docsUrl + '?ui=playground'
|
|
110
|
+
const modelCfg = config.modelConfigs[m]
|
|
111
|
+
const modelPlayground = isPlaygroundAvailable(modelCfg)
|
|
112
|
+
const playgroundLink = modelPlayground
|
|
113
|
+
? ', <a href="' + playgroundUrl + '" class="text-inherit underline">playground</a>'
|
|
114
|
+
: ''
|
|
115
|
+
return '<tr>' +
|
|
116
|
+
'<td class="text-left py-2 px-2 border-b border-gray-300 align-top">' + escapeHtml(m) + '</td>' +
|
|
117
|
+
'<td class="text-left py-2 px-2 border-b border-gray-300 align-top"><a href="' + docsUrl + '" class="text-inherit underline">' + escapeHtml(docsUrl) + '</a></td>' +
|
|
118
|
+
'<td class="text-left py-2 px-2 border-b border-gray-300 align-top">' +
|
|
119
|
+
'<a href="' + scalarUrl + '" class="text-inherit underline">scalar</a>, ' +
|
|
120
|
+
'<a href="' + jsonUrl + '" class="text-inherit underline">json</a>, ' +
|
|
121
|
+
'<a href="' + yamlUrl + '" class="text-inherit underline">yaml</a>' +
|
|
122
|
+
playgroundLink +
|
|
123
|
+
'</td>' +
|
|
124
|
+
'</tr>'
|
|
125
|
+
}).join('')
|
|
126
|
+
|
|
127
|
+
const descriptionHtml = description
|
|
128
|
+
? '<div class="mt-1.5 text-gray-500 text-sm">' + escapeHtml(description) + '</div>'
|
|
129
|
+
: ''
|
|
130
|
+
const versionHtml = version
|
|
131
|
+
? '<div>Version: ' + escapeHtml(version) + '</div>'
|
|
132
|
+
: ''
|
|
133
|
+
|
|
134
|
+
return '<!DOCTYPE html>' +
|
|
135
|
+
'<html lang="en">' +
|
|
136
|
+
'<head>' +
|
|
137
|
+
'<meta charset="utf-8" />' +
|
|
138
|
+
'<meta name="viewport" content="width=device-width, initial-scale=1" />' +
|
|
139
|
+
'<title>' + escapeHtml(title) + '</title>' +
|
|
140
|
+
'<script src="https://cdn.tailwindcss.com"></' + 'script>' +
|
|
141
|
+
'</head>' +
|
|
142
|
+
'<body class="m-0 bg-white text-gray-900 font-serif leading-normal">' +
|
|
143
|
+
'<div class="max-w-[980px] mx-auto px-7 pt-10 pb-16">' +
|
|
144
|
+
'<div class="border-b-2 border-gray-900 pb-3.5 mb-[18px]">' +
|
|
145
|
+
'<div class="text-[28px] font-bold tracking-wide">' + escapeHtml(title) + '</div>' +
|
|
146
|
+
descriptionHtml +
|
|
147
|
+
'<div class="mt-3 flex gap-x-5 text-[13px] text-gray-500">' +
|
|
148
|
+
versionHtml +
|
|
149
|
+
'<div>Generated: ' + escapeHtml(generatedAt) + '</div>' +
|
|
150
|
+
'</div>' +
|
|
151
|
+
'</div>' +
|
|
152
|
+
'<div class="mt-[22px]">' +
|
|
153
|
+
'<h2 class="m-0 mb-2.5 text-lg border-t border-gray-300 pt-3.5">Models</h2>' +
|
|
154
|
+
'<table class="w-full border-collapse text-[13px]">' +
|
|
155
|
+
'<thead>' +
|
|
156
|
+
'<tr>' +
|
|
157
|
+
'<th class="text-left py-2 px-2 border-b border-gray-300 align-top font-bold">Model</th>' +
|
|
158
|
+
'<th class="text-left py-2 px-2 border-b border-gray-300 align-top font-bold">Documentation</th>' +
|
|
159
|
+
'<th class="text-left py-2 px-2 border-b border-gray-300 align-top font-bold">Views</th>' +
|
|
160
|
+
'</tr>' +
|
|
161
|
+
'</thead>' +
|
|
162
|
+
'<tbody>' + modelRows + '</tbody>' +
|
|
163
|
+
'</table>' +
|
|
164
|
+
'</div>' +
|
|
165
|
+
'</div>' +
|
|
166
|
+
'</body>' +
|
|
167
|
+
'</html>'
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function getRegisteredModels(config: CombinedDocsConfig) {
|
|
171
|
+
return Object.keys(config.modelConfigs).filter((m) => {
|
|
172
|
+
const cfg = config.modelConfigs[m]
|
|
173
|
+
return m in docsHandlers && !isOpenApiDisabled(cfg?.disableOpenApi ?? config.disableOpenApi)
|
|
174
|
+
})
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
${combinedDocsReturn}
|
|
178
|
+
|
|
179
|
+
${registerDocs}
|
|
180
|
+
`
|
|
181
|
+
}
|
|
70
182
|
|
|
183
|
+
function generateExpressCombinedDocs(): string {
|
|
184
|
+
return `export function generateCombinedDocs(config: CombinedDocsConfig) {
|
|
71
185
|
return (req: Request, res: Response) => {
|
|
72
|
-
const registeredModels =
|
|
73
|
-
const cfg = config.modelConfigs[m]
|
|
74
|
-
return m in docsHandlers && !isOpenApiDisabled(cfg?.disableOpenApi ?? config.disableOpenApi)
|
|
75
|
-
})
|
|
186
|
+
const registeredModels = getRegisteredModels(config)
|
|
76
187
|
|
|
77
188
|
if (registeredModels.length === 0) {
|
|
78
189
|
return res.status(404).send('OpenAPI documentation is disabled')
|
|
79
190
|
}
|
|
80
191
|
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
const docsUrl = basePath + '/' + lower
|
|
87
|
-
const scalarUrl = docsUrl + '?ui=scalar'
|
|
88
|
-
const jsonUrl = docsUrl + '?ui=json'
|
|
89
|
-
const yamlUrl = docsUrl + '?ui=yaml'
|
|
90
|
-
const playgroundUrl = docsUrl + '?ui=playground'
|
|
91
|
-
const modelCfg = config.modelConfigs[m]
|
|
92
|
-
const modelPlayground = isPlaygroundAvailable(modelCfg)
|
|
93
|
-
const playgroundLink = modelPlayground
|
|
94
|
-
? ', <a href="' + playgroundUrl + '" class="text-inherit underline">playground</a>'
|
|
95
|
-
: ''
|
|
96
|
-
return '<tr>' +
|
|
97
|
-
'<td class="text-left py-2 px-2 border-b border-gray-300 align-top">' + escapeHtml(m) + '</td>' +
|
|
98
|
-
'<td class="text-left py-2 px-2 border-b border-gray-300 align-top"><a href="' + docsUrl + '" class="text-inherit underline">' + escapeHtml(docsUrl) + '</a></td>' +
|
|
99
|
-
'<td class="text-left py-2 px-2 border-b border-gray-300 align-top">' +
|
|
100
|
-
'<a href="' + scalarUrl + '" class="text-inherit underline">scalar</a>, ' +
|
|
101
|
-
'<a href="' + jsonUrl + '" class="text-inherit underline">json</a>, ' +
|
|
102
|
-
'<a href="' + yamlUrl + '" class="text-inherit underline">yaml</a>' +
|
|
103
|
-
playgroundLink +
|
|
104
|
-
'</td>' +
|
|
105
|
-
'</tr>'
|
|
106
|
-
}).join('')
|
|
107
|
-
|
|
108
|
-
const descriptionHtml = description
|
|
109
|
-
? '<div class="mt-1.5 text-gray-500 text-sm">' + escapeHtml(description) + '</div>'
|
|
110
|
-
: ''
|
|
111
|
-
const versionHtml = version
|
|
112
|
-
? '<div>Version: ' + escapeHtml(version) + '</div>'
|
|
113
|
-
: ''
|
|
192
|
+
const html = buildCombinedHtml(config, registeredModels)
|
|
193
|
+
res.type('html').send(html)
|
|
194
|
+
}
|
|
195
|
+
}`
|
|
196
|
+
}
|
|
114
197
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
'<meta charset="utf-8" />' +
|
|
120
|
-
'<meta name="viewport" content="width=device-width, initial-scale=1" />' +
|
|
121
|
-
'<title>' + escapeHtml(title) + '</title>' +
|
|
122
|
-
'<script src="https://cdn.tailwindcss.com"></' + 'script>' +
|
|
123
|
-
'</head>' +
|
|
124
|
-
'<body class="m-0 bg-white text-gray-900 font-serif leading-normal">' +
|
|
125
|
-
'<div class="max-w-[980px] mx-auto px-7 pt-10 pb-16">' +
|
|
126
|
-
'<div class="border-b-2 border-gray-900 pb-3.5 mb-[18px]">' +
|
|
127
|
-
'<div class="text-[28px] font-bold tracking-wide">' + escapeHtml(title) + '</div>' +
|
|
128
|
-
descriptionHtml +
|
|
129
|
-
'<div class="mt-3 flex gap-x-5 text-[13px] text-gray-500">' +
|
|
130
|
-
versionHtml +
|
|
131
|
-
'<div>Generated: ' + escapeHtml(generatedAt) + '</div>' +
|
|
132
|
-
'</div>' +
|
|
133
|
-
'</div>' +
|
|
134
|
-
'<div class="mt-[22px]">' +
|
|
135
|
-
'<h2 class="m-0 mb-2.5 text-lg border-t border-gray-300 pt-3.5">Models</h2>' +
|
|
136
|
-
'<table class="w-full border-collapse text-[13px]">' +
|
|
137
|
-
'<thead>' +
|
|
138
|
-
'<tr>' +
|
|
139
|
-
'<th class="text-left py-2 px-2 border-b border-gray-300 align-top font-bold">Model</th>' +
|
|
140
|
-
'<th class="text-left py-2 px-2 border-b border-gray-300 align-top font-bold">Documentation</th>' +
|
|
141
|
-
'<th class="text-left py-2 px-2 border-b border-gray-300 align-top font-bold">Views</th>' +
|
|
142
|
-
'</tr>' +
|
|
143
|
-
'</thead>' +
|
|
144
|
-
'<tbody>' + modelRows + '</tbody>' +
|
|
145
|
-
'</table>' +
|
|
146
|
-
'</div>' +
|
|
147
|
-
'</div>' +
|
|
148
|
-
'</body>' +
|
|
149
|
-
'</html>'
|
|
198
|
+
function generateFastifyCombinedDocs(): string {
|
|
199
|
+
return `export function generateCombinedDocs(config: CombinedDocsConfig) {
|
|
200
|
+
return async (request: FastifyRequest, reply: FastifyReply) => {
|
|
201
|
+
const registeredModels = getRegisteredModels(config)
|
|
150
202
|
|
|
151
|
-
|
|
203
|
+
if (registeredModels.length === 0) {
|
|
204
|
+
return reply.code(404).send('OpenAPI documentation is disabled')
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const html = buildCombinedHtml(config, registeredModels)
|
|
208
|
+
return reply.type('text/html').send(html)
|
|
152
209
|
}
|
|
210
|
+
}`
|
|
153
211
|
}
|
|
154
212
|
|
|
155
|
-
|
|
213
|
+
function generateExpressRegisterDocs(): string {
|
|
214
|
+
return `export function registerModelDocs(
|
|
156
215
|
app: any,
|
|
157
216
|
basePath: string = '/docs',
|
|
158
217
|
configs: CombinedDocsConfig['modelConfigs'] = {},
|
|
@@ -173,6 +232,30 @@ export function registerModelDocs(
|
|
|
173
232
|
console.log(' Registered docs: ' + docPath)
|
|
174
233
|
app.get(docPath, handler(cfg))
|
|
175
234
|
})
|
|
235
|
+
}`
|
|
176
236
|
}
|
|
177
|
-
|
|
237
|
+
|
|
238
|
+
function generateFastifyRegisterDocs(): string {
|
|
239
|
+
return `export function registerModelDocs(
|
|
240
|
+
app: FastifyInstance,
|
|
241
|
+
basePath: string = '/docs',
|
|
242
|
+
configs: CombinedDocsConfig['modelConfigs'] = {},
|
|
243
|
+
options?: { disableOpenApi?: boolean }
|
|
244
|
+
) {
|
|
245
|
+
const normalizedBase = removeTrailingSlash(basePath)
|
|
246
|
+
const registeredModels = Object.keys(configs).filter((m) => {
|
|
247
|
+
const cfg = configs[m]
|
|
248
|
+
return m in docsHandlers && !isOpenApiDisabled(cfg?.disableOpenApi ?? options?.disableOpenApi)
|
|
249
|
+
})
|
|
250
|
+
|
|
251
|
+
if (registeredModels.length === 0) return
|
|
252
|
+
|
|
253
|
+
registeredModels.forEach((model) => {
|
|
254
|
+
const handler = docsHandlers[model]
|
|
255
|
+
const cfg = configs[model] || {}
|
|
256
|
+
const docPath = normalizedBase + '/' + model.toLowerCase()
|
|
257
|
+
console.log(' Registered docs: ' + docPath)
|
|
258
|
+
app.get(docPath, handler(cfg))
|
|
259
|
+
})
|
|
260
|
+
}`
|
|
178
261
|
}
|